1 /*---------------------------------------------------------------------------
3 wpng - simple PNG-writing program writepng.c
5 ---------------------------------------------------------------------------
7 Copyright (c) 1998-2000 Greg Roelofs. All rights reserved.
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
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:
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:
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.
30 ---------------------------------------------------------------------------*/
33 #include <stdlib.h> /* for exit() prototype */
35 #include "png.h" /* libpng header; includes zlib.h and setjmp.h */
36 #include "writepng.h" /* typedefs, common macros, public prototypes */
41 static void writepng_error_handler(png_structp png_ptr, png_const_charp msg);
45 void writepng_version_info(void)
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);
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 */
59 int writepng_init(mainprog_info *mainprog_ptr)
61 png_structp png_ptr; /* note: temporary variables! */
63 int color_type, interlace_type;
66 /* could also replace libpng warning-handler (final NULL), but no need: */
68 png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, mainprog_ptr,
69 writepng_error_handler, NULL);
71 return 4; /* out of memory */
73 info_ptr = png_create_info_struct(png_ptr);
75 png_destroy_write_struct(&png_ptr, NULL);
76 return 4; /* out of memory */
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: */
85 if (setjmp(mainprog_ptr->jmpbuf)) {
86 png_destroy_write_struct(&png_ptr, &info_ptr);
91 /* make sure outfile is (re)opened in BINARY mode */
93 png_init_io(png_ptr, mainprog_ptr->outfile);
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
103 png_set_compression_level(png_ptr, Z_BEST_COMPRESSION);
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);
114 /* set the image parameters appropriately */
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;
123 png_destroy_write_struct(&png_ptr, &info_ptr);
127 interlace_type = mainprog_ptr->interlaced? PNG_INTERLACE_ADAM7 :
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);
134 if (mainprog_ptr->gamma > 0.0)
135 png_set_gAMA(png_ptr, info_ptr, mainprog_ptr->gamma);
137 if (mainprog_ptr->have_bg) { /* we know it's RGBA, not gray+alpha */
138 png_color_16 background;
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);
146 if (mainprog_ptr->have_time) {
149 png_convert_from_time_t(&modtime, mainprog_ptr->modtime);
150 png_set_tIME(png_ptr, info_ptr, &modtime);
153 if (mainprog_ptr->have_text) {
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;
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;
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;
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;
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;
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;
193 png_set_text(png_ptr, info_ptr, text, num_text);
197 /* write all chunks up to (but not including) first IDAT */
199 png_write_info(png_ptr, info_ptr);
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
209 /* set up the transformations: for now, just pack low-bit-depth pixels
210 * into bytes (one, two or four pixels per byte) */
212 png_set_packing(png_ptr);
213 /* png_set_shift(png_ptr, &sig_bit); to scale low-bit-depth values */
216 /* make sure we save our pointers for use in writepng_encode_image() */
218 mainprog_ptr->png_ptr = png_ptr;
219 mainprog_ptr->info_ptr = info_ptr;
222 /* OK, that's all we need to do for now; return happy */
231 /* returns 0 for success, 2 for libpng (longjmp) problem */
233 int writepng_encode_image(mainprog_info *mainprog_ptr)
235 png_structp png_ptr = (png_structp)mainprog_ptr->png_ptr;
236 png_infop info_ptr = (png_infop)mainprog_ptr->info_ptr;
239 /* as always, setjmp() must be called in every function that calls a
240 * PNG-writing libpng function */
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;
250 /* and now we just write the whole image; libpng takes care of interlacing
253 png_write_image(png_ptr, mainprog_ptr->row_pointers);
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: */
260 png_write_end(png_ptr, NULL);
269 /* returns 0 if succeeds, 2 if libpng problem */
271 int writepng_encode_row(mainprog_info *mainprog_ptr) /* NON-interlaced only! */
273 png_structp png_ptr = (png_structp)mainprog_ptr->png_ptr;
274 png_infop info_ptr = (png_infop)mainprog_ptr->info_ptr;
277 /* as always, setjmp() must be called in every function that calls a
278 * PNG-writing libpng function */
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;
288 /* image_data points at our one row of image data */
290 png_write_row(png_ptr, mainprog_ptr->image_data);
299 /* returns 0 if succeeds, 2 if libpng problem */
301 int writepng_encode_finish(mainprog_info *mainprog_ptr) /* NON-interlaced! */
303 png_structp png_ptr = (png_structp)mainprog_ptr->png_ptr;
304 png_infop info_ptr = (png_infop)mainprog_ptr->info_ptr;
307 /* as always, setjmp() must be called in every function that calls a
308 * PNG-writing libpng function */
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;
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: */
321 png_write_end(png_ptr, NULL);
330 void writepng_cleanup(mainprog_info *mainprog_ptr)
332 png_structp png_ptr = (png_structp)mainprog_ptr->png_ptr;
333 png_infop info_ptr = (png_infop)mainprog_ptr->info_ptr;
335 if (png_ptr && info_ptr)
336 png_destroy_write_struct(&png_ptr, &info_ptr);
343 static void writepng_error_handler(png_structp png_ptr, png_const_charp msg)
345 mainprog_info *mainprog_ptr;
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)
356 fprintf(stderr, "writepng libpng error: %s\n", msg);
359 mainprog_ptr = png_get_error_ptr(png_ptr);
360 if (mainprog_ptr == NULL) { /* we are completely hosed now */
362 "writepng severe error: jmpbuf not recoverable; terminating.\n");
367 longjmp(mainprog_ptr->jmpbuf, 1);