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