moreupdates
[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: trgt_yuv.cpp,v 1.1.1.1 2005/01/04 01:23:14 darco Exp $
6 **
7 **      \legal
8 **      Copyright (c) 2002 Robert B. Quattlebaum Jr.
9 **
10 **      This software and associated documentation
11 **      are CONFIDENTIAL and PROPRIETARY property of
12 **      the above-mentioned copyright holder.
13 **
14 **      You may not copy, print, publish, or in any
15 **      other way distribute this software without
16 **      a prior written agreement with
17 **      the copyright holder.
18 **      \endlegal
19 */
20 /* ========================================================================= */
21
22 /* === H E A D E R S ======================================================= */
23
24 #define SYNFIG_TARGET
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: trgt_yuv.cpp,v 1.1.1.1 2005/01/04 01:23:14 darco Exp $");
61
62 /* === M E T H O D S ======================================================= */
63
64 yuv::yuv(const char *FILENAME):
65         filename(FILENAME),
66         file( (filename=="-")?stdout:fopen(filename.c_str(),"wb") ),
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::set_rend_desc(RendDesc *given_desc)
79 {
80         given_desc->clear_flags();
81
82         // Make sure our width is divisible by two
83         given_desc->set_w(given_desc->get_w()*2/2);
84         given_desc->set_h(given_desc->get_h()*2/2);
85
86         desc=*given_desc;
87
88         // Set up our surface
89         surface.set_wh(desc.get_w(),desc.get_h());
90         
91         return true;
92 }
93
94 bool
95 yuv::start_frame(synfig::ProgressCallback *callback)
96 {
97         return static_cast<bool>(file);
98 }
99
100 Color *
101 yuv::start_scanline(int x)
102 {
103         return surface[x];
104 }
105
106 bool
107 yuv::end_scanline()
108 {
109         return static_cast<bool>(file);
110 }
111
112 void
113 yuv::end_frame()
114 {
115         const int w=desc.get_w(),h=desc.get_h();
116         int x,y;
117
118         assert(file);
119
120         // Output Y' channel, adjusting
121         // the gamma as we go
122         for(y=0;y<h;y++)
123                 for(x=0;x<w;x++)
124                 {
125                         Color& c(surface[y][x]);
126                         c=c.clamped();
127                         c.set_r(gamma().r_F32_to_F32(c.get_r()));
128                         c.set_g(gamma().g_F32_to_F32(c.get_g()));
129                         c.set_b(gamma().b_F32_to_F32(c.get_b()));
130                         float f(c.get_y());
131                         int i(max(min(round_to_int(c.get_y()*Y_RANGE),Y_RANGE),0)+Y_FLOOR);
132
133                         if(dithering)
134                         {
135                                 const float er(f-((float)i-Y_FLOOR)/Y_RANGE);
136                                 const Color error(er,er,er);
137
138                                 if(surface.get_h()>y+1)
139                                 {
140                                         surface[y+1][x-1]+=error * ((float)3/(float)16);
141                                         surface[y+1][x]+=error * ((float)5/(float)16);
142                                         if(surface.get_w()>x+1)
143                                                 surface[y+1][x+1]+=error * ((float)1/(float)16);
144                                 }
145                                 if(surface.get_w()>x+1)
146                                         surface[y][x+1]+=error * ((float)7/(float)16);
147                         }
148                         
149                         fputc(i,file.get());
150                 }
151         
152                 
153         // Create new super-sampled surface
154         Surface sm_surface(w/2,h/2);
155         for(y=0;y<h;y+=2)
156                 for(x=0;x<w;x+=2)
157                 {
158                         Color c(Color::alpha());
159                         c+=surface[y][x];
160                         c+=surface[y+1][x];
161                         c+=surface[y][x+1];
162                         c+=surface[y+1][x+1];
163                         c/=4;
164                         sm_surface[y/2][x/2]=c;
165                 }
166
167         // Output U channel
168         for(y=0;y<sm_surface.get_h();y++)
169                 for(x=0;x<sm_surface.get_w();x++)
170                 {
171                         const Color& c(sm_surface[y][x]);
172                         const float f(c.get_u());
173                         const int i(max(min(round_to_int((f+0.5f)*UV_RANGE),UV_RANGE),0)+UV_FLOOR);
174
175                         if(dithering)
176                         {
177                                 const float er(f-((((float)i-UV_FLOOR)/UV_RANGE)-0.5f));
178                                 const Color error(Color::YUV(0,er,0));
179
180                                 if(sm_surface.get_h()>y+1)
181                                 {
182                                         sm_surface[y+1][x-1]+=error * ((float)3/(float)16);
183                                         sm_surface[y+1][x]+=error * ((float)5/(float)16);
184                                         if(sm_surface.get_w()>x+1)
185                                                 sm_surface[y+1][x+1]+=error * ((float)1/(float)16);
186                                 }
187                                 if(sm_surface.get_w()>x+1)
188                                         sm_surface[y][x+1]+=error * ((float)7/(float)16);
189                         }
190                         fputc(i,file.get());
191                 }
192                                 
193         // Output V channel
194         for(y=0;y<sm_surface.get_h();y++)
195                 for(x=0;x<sm_surface.get_w();x++)
196                 {
197                         const Color& c(sm_surface[y][x]);
198                         const float f(c.get_v());
199                         const int i(max(min(round_to_int((f+0.5f)*UV_RANGE),UV_RANGE),0)+UV_FLOOR);
200
201                         if(dithering)
202                         {
203                                 const float er(f-((((float)i-UV_FLOOR)/UV_RANGE)-0.5f));
204                                 const Color error(Color::YUV(0,0,er));
205
206                                 if(sm_surface.get_h()>y+1)
207                                 {
208                                         sm_surface[y+1][x-1]+=error * ((float)3/(float)16);
209                                         sm_surface[y+1][x]+=error * ((float)5/(float)16);
210                                         if(sm_surface.get_w()>x+1)
211                                                 sm_surface[y+1][x+1]+=error * ((float)1/(float)16);
212                                 }
213                                 if(sm_surface.get_w()>x+1)
214                                         sm_surface[y][x+1]+=error * ((float)7/(float)16);
215                         }
216                         fputc(i,file.get());
217                 }
218         
219         // Flush out the frame
220         fflush(file.get());
221 }