1 /* === S Y N F I G ========================================================= */
2 /*!	\file trgt_mng.cpp
3 **	\brief MNG Target Module
4 **
5 **	$Id$
6 **
7 **	\legal
8 **	Copyright (c) 2007 Paul Wise
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 ** === N O T E S ===========================================================
23 **
24 ** You will need to read the PNG and MNG specs to understand this code
25 **
26 ** ========================================================================= */
27 
28 /* === H E A D E R S ======================================================= */
29 
30 #ifdef USING_PCH
31 #	include "pch.h"
32 #else
33 #ifdef HAVE_CONFIG_H
34 #	include <config.h>
35 #endif
36 
37 #include <synfig/listimporter.h>
38 #include <synfig/general.h>
39 
40 #include "trgt_mng.h"
41 #include <libmng.h>
42 #include <ETL/stringf>
43 #include <cstdio>
44 #include <algorithm>
45 #include <functional>
46 #include <ETL/misc>
47 
48 #endif
49 
50 /* === M A C R O S ========================================================= */
51 
52 using namespace synfig;
53 using namespace std;
54 using namespace etl;
55 
56 /* === G L O B A L S ======================================================= */
57 
58 SYNFIG_TARGET_INIT(mng_trgt);
59 SYNFIG_TARGET_SET_NAME(mng_trgt,"mng");
60 SYNFIG_TARGET_SET_EXT(mng_trgt,"mng");
61 SYNFIG_TARGET_SET_VERSION(mng_trgt,"0.1");
62 SYNFIG_TARGET_SET_CVS_ID(mng_trgt,"$Id$");
63 
64 /* === M E T H O D S ======================================================= */
65 
66 static mng_ptr MNG_DECL
mng_alloc_proc(mng_size_t size)67 mng_alloc_proc(mng_size_t size)
68 {
69 	return (mng_ptr)calloc(1,size);
70 }
71 
72 static void MNG_DECL
mng_free_proc(mng_ptr ptr,mng_size_t size)73 mng_free_proc(mng_ptr ptr, mng_size_t size __attribute__ ((unused)))
74 {
75 	free(ptr); return;
76 }
77 
78 static mng_bool MNG_DECL
mng_null_proc(mng_handle mng)79 mng_null_proc(mng_handle mng __attribute__ ((unused)))
80 {
81 	// synfig::info("%s:%d mng_trgt::mng_null_proc was called", __FILE__, __LINE__);
82 	return MNG_TRUE;
83 }
84 
85 static mng_bool MNG_DECL
mng_write_proc(mng_handle mng,mng_ptr buf,mng_uint32 size,mng_uint32 * written)86 mng_write_proc(mng_handle mng, mng_ptr buf, mng_uint32 size, mng_uint32* written)
87 {
88 	FILE* file = (FILE*)mng_get_userdata (mng);
89 	*written = fwrite(buf, 1, size, file);
90 	return MNG_TRUE;
91 }
92 
93 static mng_bool MNG_DECL
mng_error_proc(mng_handle mng,mng_int32 error,mng_int8 severity,mng_chunkid chunkname,mng_uint32 chunkseq,mng_int32 extra1,mng_int32 extra2,mng_pchar errortext)94 mng_error_proc(mng_handle mng __attribute__ ((unused)), mng_int32 error __attribute__ ((unused)),
95 			   mng_int8 severity __attribute__ ((unused)), mng_chunkid chunkname __attribute__ ((unused)),
96 			   mng_uint32 chunkseq __attribute__ ((unused)), mng_int32 extra1 __attribute__ ((unused)),
97 			   mng_int32 extra2 __attribute__ ((unused)), mng_pchar errortext)
98 {
99 	synfig::error("%s:%d mng_trgt: error: %s", __FILE__, __LINE__, errortext);
100 	return MNG_TRUE;
101 }
102 
mng_trgt(const char * Filename,const synfig::TargetParam &)103 mng_trgt::mng_trgt(const char *Filename, const synfig::TargetParam & /* params */):
104 	file(NULL),
105 	w(),
106 	h(),
107 	mng(NULL),
108 	multi_image(),
109 	ready(false),
110 	imagecount(),
111 	filename(Filename),
112 	buffer(NULL),
113 	color_buffer(NULL),
114 	zstream(),
115 	zbuffer(NULL),
116 	zbuffer_len(0)
117 { }
118 
~mng_trgt()119 mng_trgt::~mng_trgt()
120 {
121 	synfig::info("mng_trgt: ~mng_trgt");
122 	if (mng != MNG_NULL)
123 	{
124 		mng_putchunk_mend(mng);
125 		if (mng_write(mng) != 0)
126 		{
127 			mng_int8 severity;
128 			mng_chunkid chunkname;
129 			mng_uint32 chunkseq;
130 			mng_int32 extra1;
131 			mng_int32 extra2;
132 			mng_pchar errortext;
133 			mng_getlasterror(mng, &severity, &chunkname, &chunkseq, &extra1,&extra2, &errortext);
134 			synfig::error("mng_trgt: error: couldn't write mng: %s",errortext);
135 		}
136 		mng_cleanup (&mng);
137 	}
138 	if (file != NULL) { fclose(file); file=NULL; }
139 	if (buffer != NULL) { delete [] buffer; buffer = NULL; }
140 	if (color_buffer != NULL) { delete [] color_buffer; color_buffer = NULL; }
141 	if (zbuffer != NULL) { free(zbuffer); zbuffer = NULL; zbuffer_len = 0; }
142 }
143 
144 bool
set_rend_desc(RendDesc * given_desc)145 mng_trgt::set_rend_desc(RendDesc *given_desc)
146 {
147 	desc=*given_desc;
148 	imagecount=desc.get_frame_start();
149 	if (desc.get_frame_end()-desc.get_frame_start()>0)
150 		multi_image=true;
151 	else
152 		multi_image=false;
153 	return true;
154 }
155 
156 
157 bool
init(synfig::ProgressCallback *)158 mng_trgt::init(synfig::ProgressCallback * /* cb */)
159 {
160 	// synfig::info("%s:%d mng_trgt::init()", __FILE__, __LINE__);
161 
162 	int frame_rate, num_frames, play_time;
163 	int num_layers = 1;
164 
165 	if (multi_image)
166 	{
167 		frame_rate = int(desc.get_frame_rate());
168 		printf("frame rt %d\n", frame_rate);
169 		num_frames = desc.get_frame_end()-desc.get_frame_start();
170 		play_time = num_frames;// / frame_rate;
171 	}
172 	else
173 	{
174 		frame_rate = 0;
175 		num_frames = 1;
176 		play_time = 0;
177 	}
178 
179 	time_t t = time (NULL);
180 	struct tm* gmt = gmtime(&t);
181 	w=desc.get_w(); h=desc.get_h();
182 	file = fopen(filename.c_str(), POPEN_BINARY_WRITE_TYPE);
183 	if (file == NULL) goto cleanup_on_error;
184 	mng = mng_initialize((mng_ptr)file, mng_alloc_proc, mng_free_proc, MNG_NULL);
185 	if (mng == MNG_NULL) goto cleanup_on_error;
186 	if (mng_setcb_errorproc(mng, mng_error_proc) != 0) goto cleanup_on_error;
187 	if (mng_setcb_writedata(mng, mng_write_proc) != 0) goto cleanup_on_error;
188 	if (mng_setcb_openstream(mng, mng_null_proc) != 0) goto cleanup_on_error;
189 	if (mng_setcb_closestream(mng, mng_null_proc) != 0) goto cleanup_on_error;
190 	if (mng_create(mng) != 0) goto cleanup_on_error;
191 	if (mng_putchunk_mhdr(mng, w, h, frame_rate, num_layers, num_frames, play_time, MNG_SIMPLICITY_VALID|MNG_SIMPLICITY_SIMPLEFEATURES) != 0) goto cleanup_on_error;
192 	if (mng_putchunk_term(mng, MNG_TERMACTION_REPEAT, MNG_ITERACTION_LASTFRAME, 0, 0x7fffffff) != 0) goto cleanup_on_error;
193 	{
194 		char title[] = MNG_TEXT_TITLE;
195 		if (mng_putchunk_text(mng, sizeof(title), title,
196 							  get_canvas()->get_name().length(), const_cast<char *>(get_canvas()->get_name().c_str())) != 0)
197 			goto cleanup_on_error;
198 
199 		char description[] = MNG_TEXT_DESCRIPTION;
200 		if (mng_putchunk_text(mng, sizeof(description), description,
201 							  get_canvas()->get_description().length(), const_cast<char *>(get_canvas()->get_description().c_str())) != 0)
202 			goto cleanup_on_error;
203 
204 		char software[] = MNG_TEXT_SOFTWARE; char synfig[] = "SYNFIG";
205 		if (mng_putchunk_text(mng, sizeof(software), software,
206 							  sizeof(synfig), synfig) != 0)
207 			goto cleanup_on_error;
208 	}
209 	if (mng_putchunk_gama(mng, MNG_FALSE, (int)(gamma().get_gamma()*100000)) != 0) goto cleanup_on_error;
210 	if (mng_putchunk_phys(mng, MNG_FALSE, round_to_int(desc.get_x_res()),round_to_int(desc.get_y_res()), MNG_UNIT_METER) != 0) goto cleanup_on_error;
211 	if (mng_putchunk_time(mng, gmt->tm_year + 1900, gmt->tm_mon + 1, gmt->tm_mday, gmt->tm_hour, gmt->tm_min, gmt->tm_sec) != 0) goto cleanup_on_error;
212 	buffer=new unsigned char[(4*w)+1];
213 	if (buffer == NULL) goto cleanup_on_error;
214 	color_buffer=new Color[w];
215 	if (color_buffer == NULL) goto cleanup_on_error;
216 	return true;
217 
218 cleanup_on_error:
219 	ready=false;
220 	if (mng != MNG_NULL)
221 	{
222 		mng_int8 severity;
223 		mng_chunkid chunkname;
224 		mng_uint32 chunkseq;
225 		mng_int32 extra1;
226 		mng_int32 extra2;
227 		mng_pchar errortext;
228 		mng_getlasterror (mng, &severity, &chunkname, &chunkseq, &extra1,&extra2, &errortext);
229 		synfig::error("mng_trgt: libmng: %s",errortext);
230 		mng_cleanup (&mng);
231 	}
232 
233 	if (file && file!=stdout)
234 		fclose(file);
235 	file=NULL;
236 
237 	if (buffer != NULL)
238 	{
239 		delete [] buffer;
240 		buffer = NULL;
241 	}
242 
243 	if (color_buffer != NULL)
244 	{
245 		delete [] color_buffer;
246 		color_buffer = NULL;
247 	}
248 
249 	return false;
250 }
251 
252 void
end_frame()253 mng_trgt::end_frame()
254 {
255 	// synfig::info("%s:%d mng_trgt::end_frame()", __FILE__, __LINE__);
256 
257 	if (deflate(&zstream,Z_FINISH) != Z_STREAM_END)
258 	{
259 		synfig::error("%s:%d deflate()", __FILE__, __LINE__);
260 		return;
261 	}
262 	if (deflateEnd(&zstream) != Z_OK)
263 	{
264 		synfig::error("%s:%d deflateEnd()", __FILE__, __LINE__);
265 		return;
266 	}
267 	if (mng != MNG_NULL)
268 	{
269 		mng_putchunk_idat(mng, zstream.next_out-zbuffer, zbuffer);
270 		mng_putchunk_iend(mng);
271 	}
272 	imagecount++;
273 	ready=false;
274 }
275 
276 bool
start_frame(synfig::ProgressCallback * callback)277 mng_trgt::start_frame(synfig::ProgressCallback *callback __attribute__ ((unused)))
278 {
279 	// synfig::info("%s:%d mng_trgt::start_frame()", __FILE__, __LINE__);
280 
281 	if (mng == MNG_NULL)
282 	{
283 		synfig::error("%s:%d mng == MNG_NULL", __FILE__, __LINE__);
284 		return false;
285 	}
286 
287 	if (mng_putchunk_ihdr(mng, w, h, MNG_BITDEPTH_8, MNG_COLORTYPE_RGBA, MNG_COMPRESSION_DEFLATE, MNG_FILTER_ADAPTIVE, MNG_INTERLACE_NONE) != 0)
288 	{
289 		synfig::error("%s:%d mng_putchunk_ihdr()", __FILE__, __LINE__);
290 		return false;
291 	}
292 
293 	zstream.zalloc = Z_NULL;
294 	zstream.zfree = Z_NULL;
295 	zstream.opaque = Z_NULL;
296 
297 	if (deflateInit(&zstream, /* Z_BEST_COMPRESSION */ Z_DEFAULT_COMPRESSION) != Z_OK)
298 	{
299 		synfig::error("%s:%d deflateInit()", __FILE__, __LINE__);
300 		return false;
301 	}
302 
303 	if (zbuffer == NULL)
304 	{
305 		zbuffer_len = deflateBound(&zstream,((4*w)+1)*h); // don't forget the 'filter' byte - one per scanline
306 		zbuffer = (unsigned char*)realloc(zbuffer, zbuffer_len);
307 	}
308 
309 	zstream.avail_out = zbuffer_len;
310 	zstream.next_out = zbuffer;
311 
312 	ready=true;
313 
314 	return true;
315 }
316 
317 Color*
start_scanline(int scanline)318 mng_trgt::start_scanline(int scanline __attribute__ ((unused)))
319 {
320 	return color_buffer;
321 }
322 
323 bool
end_scanline()324 mng_trgt::end_scanline()
325 {
326 	if (!file || !ready)
327 	{
328 		synfig::error("%s:%d !file or !ready", __FILE__, __LINE__);
329 		return false;
330 	}
331 
332 	*buffer = MNG_FILTER_NONE;
333 	convert_color_format(buffer+1, color_buffer, desc.get_w(), PF_RGB|PF_A, gamma());
334 
335 	zstream.next_in = buffer;
336 	zstream.avail_in = (4*w)+1;
337 
338 	if (deflate(&zstream,Z_NO_FLUSH) != Z_OK) {
339 		synfig::error("%s:%d deflate()", __FILE__, __LINE__);
340 		return false;
341 	}
342 
343 	return true;
344 }
345