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