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