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