5eb084444de024b1eaf8ff4d338679f9606b7148
[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 __attribute__ ((unused)))
72 {
73         free(ptr); return;
74 }
75
76 static mng_bool MNG_DECL
77 mng_null_proc(mng_handle mng __attribute__ ((unused)))
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 __attribute__ ((unused)), mng_int32 error __attribute__ ((unused)),
93                            mng_int8 severity __attribute__ ((unused)), mng_chunkid chunkname __attribute__ ((unused)),
94                            mng_uint32 chunkseq __attribute__ ((unused)), mng_int32 extra1 __attribute__ ((unused)),
95                            mng_int32 extra2 __attribute__ ((unused)), mng_pchar errortext)
96 {
97         synfig::error("%s:%d mng_trgt: error: %s", __FILE__, __LINE__, errortext);
98         return MNG_TRUE;
99 }
100
101 mng_trgt::mng_trgt(const char *Filename) : filename(Filename)
102 {
103         file=NULL;
104         buffer=NULL;
105         color_buffer=NULL;      
106         zbuffer=NULL;   
107         zbuffer_len=0;
108         ready=false;
109 }
110
111 mng_trgt::~mng_trgt()
112 {
113         synfig::info("mng_trgt: ~mng_trgt");
114         if (mng != MNG_NULL)
115         {
116                 mng_putchunk_mend(mng);
117                 if (mng_write(mng) != 0)
118                 {
119                         mng_int8 severity;
120                         mng_chunkid chunkname;
121                         mng_uint32 chunkseq;
122                         mng_int32 extra1;
123                         mng_int32 extra2;
124                         mng_pchar errortext;
125                         mng_getlasterror(mng, &severity, &chunkname, &chunkseq, &extra1,&extra2, &errortext);
126                         synfig::error("mng_trgt: error: couldn't write mng: %s",errortext);
127                 }
128                 mng_cleanup (&mng);
129         }
130         if (file != NULL) fclose(file); file=NULL;
131         if (buffer != NULL) { delete [] buffer; buffer = NULL; }
132         if (color_buffer != NULL) { delete [] color_buffer; color_buffer = NULL; }
133         if (zbuffer != NULL) { free(zbuffer); zbuffer = NULL; zbuffer_len = 0; }
134 }
135
136 bool
137 mng_trgt::set_rend_desc(RendDesc *given_desc)
138 {
139         desc=*given_desc;
140         imagecount=desc.get_frame_start();
141         if (desc.get_frame_end()-desc.get_frame_start()>0)
142                 multi_image=true;
143         else
144                 multi_image=false;
145         return true;
146 }
147
148
149 bool
150 mng_trgt::init()
151 {
152         // synfig::info("%s:%d mng_trgt::init()", __FILE__, __LINE__); 
153
154         int frame_rate, num_frames, play_time;
155         int num_layers = 1;
156
157         if (multi_image)
158         {
159                 frame_rate = int(desc.get_frame_rate());
160                 printf("frame rt %d\n", frame_rate);
161                 num_frames = desc.get_frame_end()-desc.get_frame_start();
162                 play_time = num_frames;// / frame_rate;
163         }
164         else
165         {
166                 frame_rate = 0;
167                 num_frames = 1;
168                 play_time = 0;
169         }
170
171         time_t t = time (NULL);
172         struct tm* gmt = gmtime(&t);
173         w=desc.get_w(); h=desc.get_h();
174         file = fopen(filename.c_str(), POPEN_BINARY_WRITE_TYPE);
175         if (file == NULL) goto cleanup_on_error;
176         mng = mng_initialize((mng_ptr)file, mng_alloc_proc, mng_free_proc, MNG_NULL);
177         if (mng == MNG_NULL) goto cleanup_on_error;
178         if (mng_setcb_errorproc(mng, mng_error_proc) != 0) goto cleanup_on_error;
179         if (mng_setcb_writedata(mng, mng_write_proc) != 0) goto cleanup_on_error;
180         if (mng_setcb_openstream(mng, mng_null_proc) != 0) goto cleanup_on_error;
181         if (mng_setcb_closestream(mng, mng_null_proc) != 0) goto cleanup_on_error;
182         if (mng_create(mng) != 0) goto cleanup_on_error;
183         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;
184         if (mng_putchunk_term(mng, MNG_TERMACTION_REPEAT, MNG_ITERACTION_LASTFRAME, 0, 0x7fffffff) != 0) goto cleanup_on_error;
185         {
186                 char title[] = MNG_TEXT_TITLE;
187                 if (mng_putchunk_text(mng, sizeof(title), title,
188                                                           get_canvas()->get_name().length(), const_cast<char *>(get_canvas()->get_name().c_str())) != 0)
189                         goto cleanup_on_error;
190
191                 char description[] = MNG_TEXT_DESCRIPTION;
192                 if (mng_putchunk_text(mng, sizeof(description), description,
193                                                           get_canvas()->get_description().length(), const_cast<char *>(get_canvas()->get_description().c_str())) != 0)
194                         goto cleanup_on_error;
195
196                 char software[] = MNG_TEXT_SOFTWARE; char synfig[] = "SYNFIG";
197                 if (mng_putchunk_text(mng, sizeof(software), software,
198                                                           sizeof(synfig), synfig) != 0)
199                         goto cleanup_on_error;
200         }
201         if (mng_putchunk_gama(mng, MNG_FALSE, (int)(gamma().get_gamma()*100000)) != 0) goto cleanup_on_error;
202         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;
203         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;
204         buffer=new unsigned char[(4*w)+1];
205         if (buffer == NULL) goto cleanup_on_error;
206         color_buffer=new Color[w];
207         if (color_buffer == NULL) goto cleanup_on_error;
208         return true;
209         
210 cleanup_on_error:
211         ready=false;
212         if (mng != MNG_NULL)
213         {
214                 mng_int8 severity;
215                 mng_chunkid chunkname;
216                 mng_uint32 chunkseq;
217                 mng_int32 extra1;
218                 mng_int32 extra2;
219                 mng_pchar errortext;
220                 mng_getlasterror (mng, &severity, &chunkname, &chunkseq, &extra1,&extra2, &errortext);
221                 synfig::error("mng_trgt: libmng: %s",errortext); 
222                 mng_cleanup (&mng);
223         }
224
225         if (file && file!=stdout)
226                 fclose(file);
227         file=NULL;
228
229         if (buffer != NULL)
230         {
231                 delete [] buffer;
232                 buffer = NULL;
233         }
234
235         if (color_buffer != NULL)
236         {
237                 delete [] color_buffer;
238                 color_buffer = NULL;
239         }
240
241         return false;   
242 }
243
244 void
245 mng_trgt::end_frame()
246 {
247         // synfig::info("%s:%d mng_trgt::end_frame()", __FILE__, __LINE__); 
248
249         if (deflate(&zstream,Z_FINISH) != Z_STREAM_END)
250         {
251                 synfig::error("%s:%d deflate()", __FILE__, __LINE__);
252                 return;
253         }
254         if (deflateEnd(&zstream) != Z_OK)
255         {
256                 synfig::error("%s:%d deflateEnd()", __FILE__, __LINE__);
257                 return;
258         }
259         if (mng != MNG_NULL)
260         {
261                 mng_putchunk_idat(mng, zstream.next_out-zbuffer, zbuffer);
262                 mng_putchunk_iend(mng);
263         }
264         imagecount++;
265         ready=false;
266 }
267
268 bool
269 mng_trgt::start_frame(synfig::ProgressCallback *callback __attribute__ ((unused)))
270 {
271         // synfig::info("%s:%d mng_trgt::start_frame()", __FILE__, __LINE__); 
272
273         if (mng == MNG_NULL)
274         {
275                 synfig::error("%s:%d mng == MNG_NULL", __FILE__, __LINE__);
276                 return false;
277         }
278
279         if (mng_putchunk_ihdr(mng, w, h, MNG_BITDEPTH_8, MNG_COLORTYPE_RGBA, MNG_COMPRESSION_DEFLATE, MNG_FILTER_ADAPTIVE, MNG_INTERLACE_NONE) != 0)
280         {
281                 synfig::error("%s:%d mng_putchunk_ihdr()", __FILE__, __LINE__);
282                 return false;
283         }
284         
285         zstream.zalloc = Z_NULL;
286         zstream.zfree = Z_NULL;
287         zstream.opaque = Z_NULL;
288
289         if (deflateInit(&zstream, /* Z_BEST_COMPRESSION */ Z_DEFAULT_COMPRESSION) != Z_OK)
290         {
291                 synfig::error("%s:%d deflateInit()", __FILE__, __LINE__);
292                 return false;
293         }
294
295         if (zbuffer == NULL)
296         {
297                 zbuffer_len = deflateBound(&zstream,((4*w)+1)*h); // don't forget the 'filter' byte - one per scanline
298                 zbuffer = (unsigned char*)realloc(zbuffer, zbuffer_len);
299         }
300
301         zstream.avail_out = zbuffer_len;
302         zstream.next_out = zbuffer;
303
304         ready=true;
305
306         return true;
307 }
308
309 Color*
310 mng_trgt::start_scanline(int scanline __attribute__ ((unused)))
311 {
312         return color_buffer;
313 }
314
315 bool
316 mng_trgt::end_scanline()
317 {
318         if (!file || !ready)
319         {
320                 synfig::error("%s:%d !file or !ready", __FILE__, __LINE__);
321                 return false;
322         }
323
324         *buffer = MNG_FILTER_NONE;
325         convert_color_format(buffer+1, color_buffer, desc.get_w(), PF_RGB|PF_A, gamma());
326
327         zstream.next_in = buffer;
328         zstream.avail_in = (4*w)+1;
329
330         if (deflate(&zstream,Z_NO_FLUSH) != Z_OK) {
331                 synfig::error("%s:%d deflate()", __FILE__, __LINE__);
332                 return false;
333         }
334
335         return true;
336 }