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