Improve single-threaded operation. It's now safe(r?) to close a canvas while it...
[synfig.git] / gtkmm-osx / trunk / libpng-1.2.5 / contrib / gregbook / writepng.c
1 /*---------------------------------------------------------------------------
2
3    wpng - simple PNG-writing program                             writepng.c
4
5   ---------------------------------------------------------------------------
6
7       Copyright (c) 1998-2000 Greg Roelofs.  All rights reserved.
8
9       This software is provided "as is," without warranty of any kind,
10       express or implied.  In no event shall the author or contributors
11       be held liable for any damages arising in any way from the use of
12       this software.
13
14       Permission is granted to anyone to use this software for any purpose,
15       including commercial applications, and to alter it and redistribute
16       it freely, subject to the following restrictions:
17
18       1. Redistributions of source code must retain the above copyright
19          notice, disclaimer, and this list of conditions.
20       2. Redistributions in binary form must reproduce the above copyright
21          notice, disclaimer, and this list of conditions in the documenta-
22          tion and/or other materials provided with the distribution.
23       3. All advertising materials mentioning features or use of this
24          software must display the following acknowledgment:
25
26             This product includes software developed by Greg Roelofs
27             and contributors for the book, "PNG: The Definitive Guide,"
28             published by O'Reilly and Associates.
29
30   ---------------------------------------------------------------------------*/
31
32
33 #include <stdlib.h>     /* for exit() prototype */
34
35 #include "png.h"        /* libpng header; includes zlib.h and setjmp.h */
36 #include "writepng.h"   /* typedefs, common macros, public prototypes */
37
38
39 /* local prototype */
40
41 static void writepng_error_handler(png_structp png_ptr, png_const_charp msg);
42
43
44
45 void writepng_version_info(void)
46 {
47   fprintf(stderr, "   Compiled with libpng %s; using libpng %s.\n",
48     PNG_LIBPNG_VER_STRING, png_libpng_ver);
49   fprintf(stderr, "   Compiled with zlib %s; using zlib %s.\n",
50     ZLIB_VERSION, zlib_version);
51 }
52
53
54
55
56 /* returns 0 for success, 2 for libpng problem, 4 for out of memory, 11 for
57  *  unexpected pnmtype; note that outfile might be stdout */
58
59 int writepng_init(mainprog_info *mainprog_ptr)
60 {
61     png_structp  png_ptr;       /* note:  temporary variables! */
62     png_infop  info_ptr;
63     int color_type, interlace_type;
64
65
66     /* could also replace libpng warning-handler (final NULL), but no need: */
67
68     png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, mainprog_ptr,
69       writepng_error_handler, NULL);
70     if (!png_ptr)
71         return 4;   /* out of memory */
72
73     info_ptr = png_create_info_struct(png_ptr);
74     if (!info_ptr) {
75         png_destroy_write_struct(&png_ptr, NULL);
76         return 4;   /* out of memory */
77     }
78
79
80     /* setjmp() must be called in every function that calls a PNG-writing
81      * libpng function, unless an alternate error handler was installed--
82      * but compatible error handlers must either use longjmp() themselves
83      * (as in this program) or exit immediately, so here we go: */
84
85     if (setjmp(mainprog_ptr->jmpbuf)) {
86         png_destroy_write_struct(&png_ptr, &info_ptr);
87         return 2;
88     }
89
90
91     /* make sure outfile is (re)opened in BINARY mode */
92
93     png_init_io(png_ptr, mainprog_ptr->outfile);
94
95
96     /* set the compression levels--in general, always want to leave filtering
97      * turned on (except for palette images) and allow all of the filters,
98      * which is the default; want 32K zlib window, unless entire image buffer
99      * is 16K or smaller (unknown here)--also the default; usually want max
100      * compression (NOT the default); and remaining compression flags should
101      * be left alone */
102
103     png_set_compression_level(png_ptr, Z_BEST_COMPRESSION);
104 /*
105     >> this is default for no filtering; Z_FILTERED is default otherwise:
106     png_set_compression_strategy(png_ptr, Z_DEFAULT_STRATEGY);
107     >> these are all defaults:
108     png_set_compression_mem_level(png_ptr, 8);
109     png_set_compression_window_bits(png_ptr, 15);
110     png_set_compression_method(png_ptr, 8);
111  */
112
113
114     /* set the image parameters appropriately */
115
116     if (mainprog_ptr->pnmtype == 5)
117         color_type = PNG_COLOR_TYPE_GRAY;
118     else if (mainprog_ptr->pnmtype == 6)
119         color_type = PNG_COLOR_TYPE_RGB;
120     else if (mainprog_ptr->pnmtype == 8)
121         color_type = PNG_COLOR_TYPE_RGB_ALPHA;
122     else {
123         png_destroy_write_struct(&png_ptr, &info_ptr);
124         return 11;
125     }
126
127     interlace_type = mainprog_ptr->interlaced? PNG_INTERLACE_ADAM7 :
128                                                PNG_INTERLACE_NONE;
129
130     png_set_IHDR(png_ptr, info_ptr, mainprog_ptr->width, mainprog_ptr->height,
131       mainprog_ptr->sample_depth, color_type, interlace_type,
132       PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT);
133
134     if (mainprog_ptr->gamma > 0.0)
135         png_set_gAMA(png_ptr, info_ptr, mainprog_ptr->gamma);
136
137     if (mainprog_ptr->have_bg) {   /* we know it's RGBA, not gray+alpha */
138         png_color_16  background;
139
140         background.red = mainprog_ptr->bg_red;
141         background.green = mainprog_ptr->bg_green;
142         background.blue = mainprog_ptr->bg_blue;
143         png_set_bKGD(png_ptr, info_ptr, &background);
144     }
145
146     if (mainprog_ptr->have_time) {
147         png_time  modtime;
148
149         png_convert_from_time_t(&modtime, mainprog_ptr->modtime);
150         png_set_tIME(png_ptr, info_ptr, &modtime);
151     }
152
153     if (mainprog_ptr->have_text) {
154         png_text  text[6];
155         int  num_text = 0;
156
157         if (mainprog_ptr->have_text & TEXT_TITLE) {
158             text[num_text].compression = PNG_TEXT_COMPRESSION_NONE;
159             text[num_text].key = "Title";
160             text[num_text].text = mainprog_ptr->title;
161             ++num_text;
162         }
163         if (mainprog_ptr->have_text & TEXT_AUTHOR) {
164             text[num_text].compression = PNG_TEXT_COMPRESSION_NONE;
165             text[num_text].key = "Author";
166             text[num_text].text = mainprog_ptr->author;
167             ++num_text;
168         }
169         if (mainprog_ptr->have_text & TEXT_DESC) {
170             text[num_text].compression = PNG_TEXT_COMPRESSION_NONE;
171             text[num_text].key = "Description";
172             text[num_text].text = mainprog_ptr->desc;
173             ++num_text;
174         }
175         if (mainprog_ptr->have_text & TEXT_COPY) {
176             text[num_text].compression = PNG_TEXT_COMPRESSION_NONE;
177             text[num_text].key = "Copyright";
178             text[num_text].text = mainprog_ptr->copyright;
179             ++num_text;
180         }
181         if (mainprog_ptr->have_text & TEXT_EMAIL) {
182             text[num_text].compression = PNG_TEXT_COMPRESSION_NONE;
183             text[num_text].key = "E-mail";
184             text[num_text].text = mainprog_ptr->email;
185             ++num_text;
186         }
187         if (mainprog_ptr->have_text & TEXT_URL) {
188             text[num_text].compression = PNG_TEXT_COMPRESSION_NONE;
189             text[num_text].key = "URL";
190             text[num_text].text = mainprog_ptr->url;
191             ++num_text;
192         }
193         png_set_text(png_ptr, info_ptr, text, num_text);
194     }
195
196
197     /* write all chunks up to (but not including) first IDAT */
198
199     png_write_info(png_ptr, info_ptr);
200
201
202     /* if we wanted to write any more text info *after* the image data, we
203      * would set up text struct(s) here and call png_set_text() again, with
204      * just the new data; png_set_tIME() could also go here, but it would
205      * have no effect since we already called it above (only one tIME chunk
206      * allowed) */
207
208
209     /* set up the transformations:  for now, just pack low-bit-depth pixels
210      * into bytes (one, two or four pixels per byte) */
211
212     png_set_packing(png_ptr);
213 /*  png_set_shift(png_ptr, &sig_bit);  to scale low-bit-depth values */
214
215
216     /* make sure we save our pointers for use in writepng_encode_image() */
217
218     mainprog_ptr->png_ptr = png_ptr;
219     mainprog_ptr->info_ptr = info_ptr;
220
221
222     /* OK, that's all we need to do for now; return happy */
223
224     return 0;
225 }
226
227
228
229
230
231 /* returns 0 for success, 2 for libpng (longjmp) problem */
232
233 int writepng_encode_image(mainprog_info *mainprog_ptr)
234 {
235     png_structp png_ptr = (png_structp)mainprog_ptr->png_ptr;
236     png_infop info_ptr = (png_infop)mainprog_ptr->info_ptr;
237
238
239     /* as always, setjmp() must be called in every function that calls a
240      * PNG-writing libpng function */
241
242     if (setjmp(mainprog_ptr->jmpbuf)) {
243         png_destroy_write_struct(&png_ptr, &info_ptr);
244         mainprog_ptr->png_ptr = NULL;
245         mainprog_ptr->info_ptr = NULL;
246         return 2;
247     }
248
249
250     /* and now we just write the whole image; libpng takes care of interlacing
251      * for us */
252
253     png_write_image(png_ptr, mainprog_ptr->row_pointers);
254
255
256     /* since that's it, we also close out the end of the PNG file now--if we
257      * had any text or time info to write after the IDATs, second argument
258      * would be info_ptr, but we optimize slightly by sending NULL pointer: */
259
260     png_write_end(png_ptr, NULL);
261
262     return 0;
263 }
264
265
266
267
268
269 /* returns 0 if succeeds, 2 if libpng problem */
270
271 int writepng_encode_row(mainprog_info *mainprog_ptr)  /* NON-interlaced only! */
272 {
273     png_structp png_ptr = (png_structp)mainprog_ptr->png_ptr;
274     png_infop info_ptr = (png_infop)mainprog_ptr->info_ptr;
275
276
277     /* as always, setjmp() must be called in every function that calls a
278      * PNG-writing libpng function */
279
280     if (setjmp(mainprog_ptr->jmpbuf)) {
281         png_destroy_write_struct(&png_ptr, &info_ptr);
282         mainprog_ptr->png_ptr = NULL;
283         mainprog_ptr->info_ptr = NULL;
284         return 2;
285     }
286
287
288     /* image_data points at our one row of image data */
289
290     png_write_row(png_ptr, mainprog_ptr->image_data);
291
292     return 0;
293 }
294
295
296
297
298
299 /* returns 0 if succeeds, 2 if libpng problem */
300
301 int writepng_encode_finish(mainprog_info *mainprog_ptr)   /* NON-interlaced! */
302 {
303     png_structp png_ptr = (png_structp)mainprog_ptr->png_ptr;
304     png_infop info_ptr = (png_infop)mainprog_ptr->info_ptr;
305
306
307     /* as always, setjmp() must be called in every function that calls a
308      * PNG-writing libpng function */
309
310     if (setjmp(mainprog_ptr->jmpbuf)) {
311         png_destroy_write_struct(&png_ptr, &info_ptr);
312         mainprog_ptr->png_ptr = NULL;
313         mainprog_ptr->info_ptr = NULL;
314         return 2;
315     }
316
317
318     /* close out PNG file; if we had any text or time info to write after
319      * the IDATs, second argument would be info_ptr: */
320
321     png_write_end(png_ptr, NULL);
322
323     return 0;
324 }
325
326
327
328
329
330 void writepng_cleanup(mainprog_info *mainprog_ptr)
331 {
332     png_structp png_ptr = (png_structp)mainprog_ptr->png_ptr;
333     png_infop info_ptr = (png_infop)mainprog_ptr->info_ptr;
334
335     if (png_ptr && info_ptr)
336         png_destroy_write_struct(&png_ptr, &info_ptr);
337 }
338
339
340
341
342
343 static void writepng_error_handler(png_structp png_ptr, png_const_charp msg)
344 {
345     mainprog_info  *mainprog_ptr;
346
347     /* This function, aside from the extra step of retrieving the "error
348      * pointer" (below) and the fact that it exists within the application
349      * rather than within libpng, is essentially identical to libpng's
350      * default error handler.  The second point is critical:  since both
351      * setjmp() and longjmp() are called from the same code, they are
352      * guaranteed to have compatible notions of how big a jmp_buf is,
353      * regardless of whether _BSD_SOURCE or anything else has (or has not)
354      * been defined. */
355
356     fprintf(stderr, "writepng libpng error: %s\n", msg);
357     fflush(stderr);
358
359     mainprog_ptr = png_get_error_ptr(png_ptr);
360     if (mainprog_ptr == NULL) {         /* we are completely hosed now */
361         fprintf(stderr,
362           "writepng severe error:  jmpbuf not recoverable; terminating.\n");
363         fflush(stderr);
364         exit(99);
365     }
366
367     longjmp(mainprog_ptr->jmpbuf, 1);
368 }