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 
yuv(const char * FILENAME,const synfig::TargetParam &)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_alpha_mode(TARGET_ALPHA_MODE_FILL);
71 }
72 
~yuv()73 yuv::~yuv()
74 {
75 }
76 
77 bool
init(synfig::ProgressCallback *)78 yuv::init(synfig::ProgressCallback * /* cb */)
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
set_rend_desc(RendDesc * given_desc)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
start_frame(synfig::ProgressCallback *)107 yuv::start_frame(synfig::ProgressCallback */*callback*/)
108 {
109 	fprintf(file.get(), "FRAME\n");
110 	return static_cast<bool>(file);
111 }
112 
113 Color *
start_scanline(int x)114 yuv::start_scanline(int x)
115 {
116 	return surface[x];
117 }
118 
119 bool
end_scanline()120 yuv::end_scanline()
121 {
122 	return static_cast<bool>(file);
123 }
124 
125 void
end_frame()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 }
235