Skip to content
Newer
Older
100644 359 lines (248 sloc) 11.7 KB
5b56b7e @pornel Bam!
authored May 30, 2011
1 /*---------------------------------------------------------------------------
2
3 pngquant: RGBA -> RGBA-palette quantization program rwpng.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 #include <stdio.h>
33 #include <stdlib.h>
3c72382 @pornel rwpng update
authored Dec 12, 2011
34
35 #include "png.h"
36 #include "zlib.h"
37 #include "rwpng.h"
5b56b7e @pornel Bam!
authored May 30, 2011
38
39 static void rwpng_error_handler(png_structp png_ptr, png_const_charp msg);
40
41
42 void rwpng_version_info(void)
43 {
44 fprintf(stderr, " Compiled with libpng %s; using libpng %s.\n",
45 PNG_LIBPNG_VER_STRING, png_get_header_ver(NULL));
46 fprintf(stderr, " Compiled with zlib %s; using zlib %s.\n",
47 ZLIB_VERSION, zlib_version);
48 }
49
50
51
52 /*
53 retval:
54 0 = success
55 21 = bad sig
56 22 = bad IHDR
57 24 = insufficient memory
58 25 = libpng error (via longjmp())
59 26 = wrong PNG color type (no alpha channel)
60 */
61
62 pngquant_error rwpng_read_image(FILE *infile, read_info *mainprog_ptr)
63 {
64 png_structp png_ptr = NULL;
65 png_infop info_ptr = NULL;
66 png_uint_32 i, rowbytes;
67 int color_type, bit_depth;
68 unsigned char sig[8];
69
70
71 /* first do a quick check that the file really is a PNG image; could
72 * have used slightly more general png_sig_cmp() function instead */
73
74 if (!fread(sig, 8, 1, infile)) {
75 return READ_ERROR;
76 }
77
78 if (png_sig_cmp(sig, 0, 8)) {
79 return BAD_SIGNATURE_ERROR; /* bad signature */
80 }
81
82
83 png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, mainprog_ptr,
84 rwpng_error_handler, NULL);
85 if (!png_ptr) {
86 return PNG_OUT_OF_MEMORY_ERROR; /* out of memory */
87 }
88
89 info_ptr = png_create_info_struct(png_ptr);
90 if (!info_ptr) {
91 png_destroy_read_struct(&png_ptr, NULL, NULL);
92 return PNG_OUT_OF_MEMORY_ERROR; /* out of memory */
93 }
94
95 /* setjmp() must be called in every function that calls a non-trivial
96 * libpng function */
97
98 if (setjmp(mainprog_ptr->jmpbuf)) {
99 png_destroy_read_struct(&png_ptr, &info_ptr, NULL);
100 return LIBPNG_FATAL_ERROR; /* fatal libpng error (via longjmp()) */
101 }
102
103
104 png_init_io(png_ptr, infile);
105 png_set_sig_bytes(png_ptr, 8); /* we already read the 8 signature bytes */
106
107 png_read_info(png_ptr, info_ptr); /* read all PNG info up to image data */
108
109
110 /* alternatively, could make separate calls to png_get_image_width(),
111 * etc., but want bit_depth and color_type for later [don't care about
112 * compression_type and filter_type => NULLs] */
113
114 png_get_IHDR(png_ptr, info_ptr, &mainprog_ptr->width, &mainprog_ptr->height,
115 &bit_depth, &color_type, &mainprog_ptr->interlaced, NULL, NULL);
116
117
118 /* expand palette images to RGB, low-bit-depth grayscale images to 8 bits,
119 * transparency chunks to full alpha channel; strip 16-bit-per-sample
120 * images to 8 bits per sample; and convert grayscale to RGB[A] */
121
122 /* GRR TO DO: preserve all safe-to-copy ancillary PNG chunks */
123
124 if (!(color_type & PNG_COLOR_MASK_ALPHA)) {
125 /* GRP: expand palette to RGB, and grayscale or RGB to GA or RGBA */
126 if (color_type == PNG_COLOR_TYPE_PALETTE)
127 png_set_expand(png_ptr);
128 png_set_filler(png_ptr, 65535L, PNG_FILLER_AFTER);
129 }
130 /*
131 if (color_type == PNG_COLOR_TYPE_GRAY && bit_depth < 8)
132 png_set_expand(png_ptr);
133 if (png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS))
134 png_set_expand(png_ptr);
135 */
136 if (bit_depth == 16)
137 png_set_strip_16(png_ptr);
3c72382 @pornel rwpng update
authored Dec 12, 2011
138
5b56b7e @pornel Bam!
authored May 30, 2011
139 if (color_type == PNG_COLOR_TYPE_GRAY ||
140 color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
141 png_set_gray_to_rgb(png_ptr);
142
143
144 /* get and save the gamma info (if any) for writing */
145
146 if (!png_get_gAMA(png_ptr, info_ptr, &mainprog_ptr->gamma)) {
147 mainprog_ptr->gamma = 0.45455;
148 }
149
3c72382 @pornel rwpng update
authored Dec 12, 2011
150 png_set_interlace_handling(png_ptr);
151
5b56b7e @pornel Bam!
authored May 30, 2011
152 /* all transformations have been registered; now update info_ptr data,
153 * get rowbytes and channels, and allocate image memory */
154
155 png_read_update_info(png_ptr, info_ptr);
156
157 mainprog_ptr->rowbytes = rowbytes = png_get_rowbytes(png_ptr, info_ptr);
158 //mainprog_ptr->channels = (int)png_get_channels(png_ptr, info_ptr);
159
160 if ((mainprog_ptr->rgba_data = malloc(rowbytes*mainprog_ptr->height)) == NULL) {
161 fprintf(stderr, "pngquant readpng: unable to allocate image data\n");
162 png_destroy_read_struct(&png_ptr, &info_ptr, NULL);
163 return PNG_OUT_OF_MEMORY_ERROR;
164 }
165 if ((mainprog_ptr->row_pointers = (png_bytepp)malloc(mainprog_ptr->height*sizeof(png_bytep))) == NULL) {
166 fprintf(stderr, "pngquant readpng: unable to allocate row pointers\n");
167 png_destroy_read_struct(&png_ptr, &info_ptr, NULL);
168 free(mainprog_ptr->rgba_data);
169 mainprog_ptr->rgba_data = NULL;
170 return PNG_OUT_OF_MEMORY_ERROR;
171 }
172
173 /* set the individual row_pointers to point at the correct offsets */
174
175 for (i = 0; i < mainprog_ptr->height; ++i)
176 mainprog_ptr->row_pointers[i] = mainprog_ptr->rgba_data + i*rowbytes;
177
178
179 /* now we can go ahead and just read the whole image */
180
181 png_read_image(png_ptr, (png_bytepp)mainprog_ptr->row_pointers);
182
183
184 /* and we're done! (png_read_end() can be omitted if no processing of
185 * post-IDAT text/time/etc. is desired) */
186
187 png_read_end(png_ptr, NULL);
188
189 png_destroy_read_struct(&png_ptr, &info_ptr, NULL);
190
191 return SUCCESS;
192 }
193
194
195
196
197
198 /*
199 retval:
200 0 = success
201 34 = insufficient memory
202 35 = libpng error (via longjmp())
203 */
204
205 pngquant_error rwpng_write_image_init(FILE *outfile, read_info *mainprog_ptr)
206 {
207 png_structp png_ptr; /* note: temporary variables! */
208 png_infop info_ptr;
209
210
211 /* could also replace libpng warning-handler (final NULL), but no need: */
212
213 png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, mainprog_ptr,
214 rwpng_error_handler, NULL);
215 if (!png_ptr) {
216 return INIT_OUT_OF_MEMORY_ERROR; /* out of memory */
217 }
218 mainprog_ptr->png_ptr = png_ptr;
219
220 info_ptr = png_create_info_struct(png_ptr);
221 if (!info_ptr) {
222 png_destroy_write_struct(&png_ptr, NULL);
223 return INIT_OUT_OF_MEMORY_ERROR; /* out of memory */
224 }
225
226
227 /* setjmp() must be called in every function that calls a PNG-writing
228 * libpng function, unless an alternate error handler was installed--
229 * but compatible error handlers must either use longjmp() themselves
230 * (as in this program) or exit immediately, so here we go: */
231
232 if (setjmp(mainprog_ptr->jmpbuf)) {
233 png_destroy_write_struct(&png_ptr, &info_ptr);
234 return LIBPNG_INIT_ERROR; /* libpng error (via longjmp()) */
235 }
236
237
238 /* make sure outfile is (re)opened in BINARY mode */
239
240 png_init_io(png_ptr, outfile);
241
242
243 /* set the compression levels--in general, always want to leave filtering
244 * turned on (except for palette images) and allow all of the filters,
245 * which is the default; want 32K zlib window, unless entire image buffer
246 * is 16K or smaller (unknown here)--also the default; usually want max
247 * compression (NOT the default); and remaining compression flags should
248 * be left alone */
249
250 png_set_compression_level(png_ptr, Z_BEST_COMPRESSION);
251
252 png_set_IHDR(png_ptr, info_ptr, mainprog_ptr->width, mainprog_ptr->height,
253 8, PNG_COLOR_TYPE_RGB_ALPHA,
254 mainprog_ptr->interlaced, PNG_COMPRESSION_TYPE_DEFAULT,
255 PNG_FILTER_TYPE_DEFAULT);
256
257 if (mainprog_ptr->gamma > 0.0)
258 png_set_gAMA(png_ptr, info_ptr, mainprog_ptr->gamma);
259
24f6b15 @pornel Disabled PNG filters
authored Sep 24, 2011
260 // disable filtering as posterization aims to optimize unchanged values
261 png_set_filter(png_ptr, PNG_FILTER_TYPE_BASE, PNG_FILTER_VALUE_NONE);
5b56b7e @pornel Bam!
authored May 30, 2011
262
263 /* write all chunks up to (but not including) first IDAT */
264 png_write_info(png_ptr, info_ptr);
265
266 /* if we wanted to write any more text info *after* the image data, we
267 * would set up text struct(s) here and call png_set_text() again, with
268 * just the new data; png_set_tIME() could also go here, but it would
269 * have no effect since we already called it above (only one tIME chunk
270 * allowed) */
271
272
273 /* set up the transformations: for now, just pack low-bit-depth pixels
274 * into bytes (one, two or four pixels per byte) */
275
276 png_set_packing(png_ptr);
277
278 /* make sure we save our pointers for use in writepng_encode_image() */
279
280 mainprog_ptr->png_ptr = png_ptr;
281 mainprog_ptr->info_ptr = info_ptr;
282
283 /* OK, that's all we need to do for now; return happy */
284 return SUCCESS;
285 }
286
287
288
289
290
291
292 /* returns 0 for success, 45 for libpng (longjmp) problem */
293
294 pngquant_error rwpng_write_image_whole(read_info *mainprog_ptr)
295 {
296 png_structp png_ptr = (png_structp)mainprog_ptr->png_ptr;
297 png_infop info_ptr = (png_infop)mainprog_ptr->info_ptr;
298
299 /* as always, setjmp() must be called in every function that calls a
300 * PNG-writing libpng function */
301
302 if (setjmp(mainprog_ptr->jmpbuf)) {
303 png_destroy_write_struct(&png_ptr, &info_ptr);
304 mainprog_ptr->png_ptr = NULL;
305 mainprog_ptr->info_ptr = NULL;
306 return LIBPNG_WRITE_WHOLE_ERROR; /* libpng error (via longjmp()) */
307 }
308
309
310 png_write_flush(mainprog_ptr->png_ptr);
311
312 /* and now we just write the whole image; libpng takes care of interlacing
313 * for us */
314 png_write_image(png_ptr, mainprog_ptr->row_pointers);
315
316
317 /* since that's it, we also close out the end of the PNG file now--if we
318 * had any text or time info to write after the IDATs, second argument
319 * would be info_ptr, but we optimize slightly by sending NULL pointer: */
320
321 png_write_end(png_ptr, NULL);
322
323 png_destroy_write_struct(&png_ptr, &info_ptr);
324 mainprog_ptr->png_ptr = NULL;
325 mainprog_ptr->info_ptr = NULL;
326
327 return SUCCESS;
328 }
329
330
331
332
333 static void rwpng_error_handler(png_structp png_ptr, png_const_charp msg)
334 {
335 read_or_write_info *mainprog_ptr;
336
337 /* This function, aside from the extra step of retrieving the "error
338 * pointer" (below) and the fact that it exists within the application
339 * rather than within libpng, is essentially identical to libpng's
340 * default error handler. The second point is critical: since both
341 * setjmp() and longjmp() are called from the same code, they are
342 * guaranteed to have compatible notions of how big a jmp_buf is,
343 * regardless of whether _BSD_SOURCE or anything else has (or has not)
344 * been defined. */
345
346 fprintf(stderr, "rwpng libpng error: %s\n", msg);
347 fflush(stderr);
348
349 mainprog_ptr = png_get_error_ptr(png_ptr);
350 if (mainprog_ptr == NULL) { /* we are completely hosed now */
351 fprintf(stderr,
352 "rwpng severe error: jmpbuf not recoverable; terminating.\n");
353 fflush(stderr);
354 exit(99);
355 }
356
357 longjmp(mainprog_ptr->jmpbuf, 1);
358 }
Something went wrong with that request. Please try again.