Fix bugs in previous commit that caused FTBFS in synfig and ETL FTBFS with older...
[synfig.git] / synfig-core / tags / synfig_0_61_03 / synfig-core / src / modules / mod_yuv420p / trgt_yuv.cpp
1 /* === S Y N F I G ========================================================= */
2 /*!     \file trgt_yuv.cpp
3 **      \brief Template File
4 **
5 **      $Id: trgt_yuv.cpp,v 1.1.1.1 2005/01/04 01:23:14 darco Exp $
6 **
7 **      \legal
8 **      Copyright (c) 2002-2005 Robert B. Quattlebaum Jr., Adrian Bentley
9 **
10 **      This package is free software; you can redistribute it and/or
11 **      modify it under the terms of the GNU General Public License as
12 **      published by the Free Software Foundation; either version 2 of
13 **      the License, or (at your option) any later version.
14 **
15 **      This package is distributed in the hope that it will be useful,
16 **      but WITHOUT ANY WARRANTY; without even the implied warranty of
17 **      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
18 **      General Public License for more details.
19 **      \endlegal
20 */
21 /* ========================================================================= */
22
23 /* === H E A D E R S ======================================================= */
24
25 #define SYNFIG_TARGET
26
27 #ifdef USING_PCH
28 #       include "pch.h"
29 #else
30 #ifdef HAVE_CONFIG_H
31 #       include <config.h>
32 #endif
33
34 #include "trgt_yuv.h"
35 #include <ETL/stringf>
36 #include <cstdio>
37 #include <algorithm>
38 #include <functional>
39 #endif
40
41 using namespace synfig;
42 using namespace std;
43 using namespace etl;
44
45 /* === M A C R O S ========================================================= */
46
47 #define Y_FLOOR (16)
48 #define Y_CEIL  (235)
49 #define Y_RANGE (Y_CEIL-Y_FLOOR)
50
51 #define UV_FLOOR        (16)
52 #define UV_CEIL         (240)
53 #define UV_RANGE (UV_CEIL-UV_FLOOR)
54
55 /* === G L O B A L S ======================================================= */
56
57 SYNFIG_TARGET_INIT(yuv);
58 SYNFIG_TARGET_SET_NAME(yuv,"yuv420p");
59 SYNFIG_TARGET_SET_EXT(yuv,"yuv");
60 SYNFIG_TARGET_SET_VERSION(yuv,"0.1");
61 SYNFIG_TARGET_SET_CVS_ID(yuv,"$Id: trgt_yuv.cpp,v 1.1.1.1 2005/01/04 01:23:14 darco Exp $");
62
63 /* === M E T H O D S ======================================================= */
64
65 yuv::yuv(const char *FILENAME):
66         filename(FILENAME),
67         file( (filename=="-")?stdout:fopen(filename.c_str(),"wb") ),
68         dithering(true)
69 {
70         // YUV420P doesn't have an alpha channel
71         set_remove_alpha();
72 }
73
74 yuv::~yuv()
75 {
76 }
77
78 bool
79 yuv::set_rend_desc(RendDesc *given_desc)
80 {
81         given_desc->clear_flags();
82
83         // Make sure our width is divisible by two
84         given_desc->set_w(given_desc->get_w()*2/2);
85         given_desc->set_h(given_desc->get_h()*2/2);
86
87         desc=*given_desc;
88
89         // Set up our surface
90         surface.set_wh(desc.get_w(),desc.get_h());
91         
92         return true;
93 }
94
95 bool
96 yuv::start_frame(synfig::ProgressCallback *callback)
97 {
98         return static_cast<bool>(file);
99 }
100
101 Color *
102 yuv::start_scanline(int x)
103 {
104         return surface[x];
105 }
106
107 bool
108 yuv::end_scanline()
109 {
110         return static_cast<bool>(file);
111 }
112
113 void
114 yuv::end_frame()
115 {
116         const int w=desc.get_w(),h=desc.get_h();
117         int x,y;
118
119         assert(file);
120
121         // Output Y' channel, adjusting
122         // the gamma as we go
123         for(y=0;y<h;y++)
124                 for(x=0;x<w;x++)
125                 {
126                         Color& c(surface[y][x]);
127                         c=c.clamped();
128                         c.set_r(gamma().r_F32_to_F32(c.get_r()));
129                         c.set_g(gamma().g_F32_to_F32(c.get_g()));
130                         c.set_b(gamma().b_F32_to_F32(c.get_b()));
131                         float f(c.get_y());
132                         int i(max(min(round_to_int(c.get_y()*Y_RANGE),Y_RANGE),0)+Y_FLOOR);
133
134                         if(dithering)
135                         {
136                                 const float er(f-((float)i-Y_FLOOR)/Y_RANGE);
137                                 const Color error(er,er,er);
138
139                                 if(surface.get_h()>y+1)
140                                 {
141                                         surface[y+1][x-1]+=error * ((float)3/(float)16);
142                                         surface[y+1][x]+=error * ((float)5/(float)16);
143                                         if(surface.get_w()>x+1)
144                                                 surface[y+1][x+1]+=error * ((float)1/(float)16);
145                                 }
146                                 if(surface.get_w()>x+1)
147                                         surface[y][x+1]+=error * ((float)7/(float)16);
148                         }
149                         
150                         fputc(i,file.get());
151                 }
152         
153                 
154         // Create new super-sampled surface
155         Surface sm_surface(w/2,h/2);
156         for(y=0;y<h;y+=2)
157                 for(x=0;x<w;x+=2)
158                 {
159                         Color c(Color::alpha());
160                         c+=surface[y][x];
161                         c+=surface[y+1][x];
162                         c+=surface[y][x+1];
163                         c+=surface[y+1][x+1];
164                         c/=4;
165                         sm_surface[y/2][x/2]=c;
166                 }
167
168         // Output U channel
169         for(y=0;y<sm_surface.get_h();y++)
170                 for(x=0;x<sm_surface.get_w();x++)
171                 {
172                         const Color& c(sm_surface[y][x]);
173                         const float f(c.get_u());
174                         const int i(max(min(round_to_int((f+0.5f)*UV_RANGE),UV_RANGE),0)+UV_FLOOR);
175
176                         if(dithering)
177                         {
178                                 const float er(f-((((float)i-UV_FLOOR)/UV_RANGE)-0.5f));
179                                 const Color error(Color::YUV(0,er,0));
180
181                                 if(sm_surface.get_h()>y+1)
182                                 {
183                                         sm_surface[y+1][x-1]+=error * ((float)3/(float)16);
184                                         sm_surface[y+1][x]+=error * ((float)5/(float)16);
185                                         if(sm_surface.get_w()>x+1)
186                                                 sm_surface[y+1][x+1]+=error * ((float)1/(float)16);
187                                 }
188                                 if(sm_surface.get_w()>x+1)
189                                         sm_surface[y][x+1]+=error * ((float)7/(float)16);
190                         }
191                         fputc(i,file.get());
192                 }
193                                 
194         // Output V channel
195         for(y=0;y<sm_surface.get_h();y++)
196                 for(x=0;x<sm_surface.get_w();x++)
197                 {
198                         const Color& c(sm_surface[y][x]);
199                         const float f(c.get_v());
200                         const int i(max(min(round_to_int((f+0.5f)*UV_RANGE),UV_RANGE),0)+UV_FLOOR);
201
202                         if(dithering)
203                         {
204                                 const float er(f-((((float)i-UV_FLOOR)/UV_RANGE)-0.5f));
205                                 const Color error(Color::YUV(0,0,er));
206
207                                 if(sm_surface.get_h()>y+1)
208                                 {
209                                         sm_surface[y+1][x-1]+=error * ((float)3/(float)16);
210                                         sm_surface[y+1][x]+=error * ((float)5/(float)16);
211                                         if(sm_surface.get_w()>x+1)
212                                                 sm_surface[y+1][x+1]+=error * ((float)1/(float)16);
213                                 }
214                                 if(sm_surface.get_w()>x+1)
215                                         sm_surface[y][x+1]+=error * ((float)7/(float)16);
216                         }
217                         fputc(i,file.get());
218                 }
219         
220         // Flush out the frame
221         fflush(file.get());
222 }