d45f8add630881e0637d619bc37422c15240435a
[synfig.git] / synfig-core / trunk / 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$
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$");
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(),POPEN_BINARY_WRITE_TYPE) ),
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::init()
80 {
81         if (!file)
82                 return false;
83
84         fprintf(file.get(), "YUV4MPEG2 W%d H%d F%d:1 Ip\n",
85                         desc.get_w(), desc.get_h(),
86                         round_to_int(desc.get_frame_rate()));
87         return true;
88 }
89
90 bool
91 yuv::set_rend_desc(RendDesc *given_desc)
92 {
93         given_desc->clear_flags();
94
95         // Make sure our width is divisible by two
96         given_desc->set_w(given_desc->get_w()*2/2);
97         given_desc->set_h(given_desc->get_h()*2/2);
98
99         desc=*given_desc;
100
101         // Set up our surface
102         surface.set_wh(desc.get_w(),desc.get_h());
103
104         return true;
105 }
106
107 bool
108 yuv::start_frame(synfig::ProgressCallback */*callback*/)
109 {
110         fprintf(file.get(), "FRAME\n");
111         return static_cast<bool>(file);
112 }
113
114 Color *
115 yuv::start_scanline(int x)
116 {
117         return surface[x];
118 }
119
120 bool
121 yuv::end_scanline()
122 {
123         return static_cast<bool>(file);
124 }
125
126 void
127 yuv::end_frame()
128 {
129         const int w=desc.get_w(),h=desc.get_h();
130         int x,y;
131
132         assert(file);
133
134         // Output Y' channel, adjusting
135         // the gamma as we go
136         for(y=0;y<h;y++)
137                 for(x=0;x<w;x++)
138                 {
139                         Color& c(surface[y][x]);
140                         c=c.clamped();
141                         c.set_r(gamma().r_F32_to_F32(c.get_r()));
142                         c.set_g(gamma().g_F32_to_F32(c.get_g()));
143                         c.set_b(gamma().b_F32_to_F32(c.get_b()));
144                         float f(c.get_y());
145                         int i(max(min(round_to_int(c.get_y()*Y_RANGE),Y_RANGE),0)+Y_FLOOR);
146
147                         if(dithering)
148                         {
149                                 const float er(f-((float)i-Y_FLOOR)/Y_RANGE);
150                                 const Color error(er,er,er);
151
152                                 if(surface.get_h()>y+1)
153                                 {
154                                         surface[y+1][x-1]+=error * ((float)3/(float)16);
155                                         surface[y+1][x]+=error * ((float)5/(float)16);
156                                         if(surface.get_w()>x+1)
157                                                 surface[y+1][x+1]+=error * ((float)1/(float)16);
158                                 }
159                                 if(surface.get_w()>x+1)
160                                         surface[y][x+1]+=error * ((float)7/(float)16);
161                         }
162
163                         fputc(i,file.get());
164                 }
165
166
167         // Create new super-sampled surface
168         Surface sm_surface(w/2,h/2);
169         for(y=0;y<h;y+=2)
170                 for(x=0;x<w;x+=2)
171                 {
172                         Color c(Color::alpha());
173                         c+=surface[y][x];
174                         c+=surface[y+1][x];
175                         c+=surface[y][x+1];
176                         c+=surface[y+1][x+1];
177                         c/=4;
178                         sm_surface[y/2][x/2]=c;
179                 }
180
181         // Output U channel
182         for(y=0;y<sm_surface.get_h();y++)
183                 for(x=0;x<sm_surface.get_w();x++)
184                 {
185                         const Color& c(sm_surface[y][x]);
186                         const float f(c.get_u());
187                         const int i(max(min(round_to_int((f+0.5f)*UV_RANGE),UV_RANGE),0)+UV_FLOOR);
188
189                         if(dithering)
190                         {
191                                 const float er(f-((((float)i-UV_FLOOR)/UV_RANGE)-0.5f));
192                                 const Color error(Color::YUV(0,er,0));
193
194                                 if(sm_surface.get_h()>y+1)
195                                 {
196                                         sm_surface[y+1][x-1]+=error * ((float)3/(float)16);
197                                         sm_surface[y+1][x]+=error * ((float)5/(float)16);
198                                         if(sm_surface.get_w()>x+1)
199                                                 sm_surface[y+1][x+1]+=error * ((float)1/(float)16);
200                                 }
201                                 if(sm_surface.get_w()>x+1)
202                                         sm_surface[y][x+1]+=error * ((float)7/(float)16);
203                         }
204                         fputc(i,file.get());
205                 }
206
207         // Output V channel
208         for(y=0;y<sm_surface.get_h();y++)
209                 for(x=0;x<sm_surface.get_w();x++)
210                 {
211                         const Color& c(sm_surface[y][x]);
212                         const float f(c.get_v());
213                         const int i(max(min(round_to_int((f+0.5f)*UV_RANGE),UV_RANGE),0)+UV_FLOOR);
214
215                         if(dithering)
216                         {
217                                 const float er(f-((((float)i-UV_FLOOR)/UV_RANGE)-0.5f));
218                                 const Color error(Color::YUV(0,0,er));
219
220                                 if(sm_surface.get_h()>y+1)
221                                 {
222                                         sm_surface[y+1][x-1]+=error * ((float)3/(float)16);
223                                         sm_surface[y+1][x]+=error * ((float)5/(float)16);
224                                         if(sm_surface.get_w()>x+1)
225                                                 sm_surface[y+1][x+1]+=error * ((float)1/(float)16);
226                                 }
227                                 if(sm_surface.get_w()>x+1)
228                                         sm_surface[y][x+1]+=error * ((float)7/(float)16);
229                         }
230                         fputc(i,file.get());
231                 }
232
233         // Flush out the frame
234         fflush(file.get());
235 }