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