1 | /*---------------------------------------------------------------------------
|
---|
2 |
|
---|
3 | rpng - simple PNG display program readpng.c
|
---|
4 |
|
---|
5 | ---------------------------------------------------------------------------
|
---|
6 |
|
---|
7 | Copyright (c) 1998-2007,2017 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 | The contents of this file are DUAL-LICENSED. You may modify and/or
|
---|
15 | redistribute this software according to the terms of one of the
|
---|
16 | following two licenses (at your option):
|
---|
17 |
|
---|
18 |
|
---|
19 | LICENSE 1 ("BSD-like with advertising clause"):
|
---|
20 |
|
---|
21 | Permission is granted to anyone to use this software for any purpose,
|
---|
22 | including commercial applications, and to alter it and redistribute
|
---|
23 | it freely, subject to the following restrictions:
|
---|
24 |
|
---|
25 | 1. Redistributions of source code must retain the above copyright
|
---|
26 | notice, disclaimer, and this list of conditions.
|
---|
27 | 2. Redistributions in binary form must reproduce the above copyright
|
---|
28 | notice, disclaimer, and this list of conditions in the documenta-
|
---|
29 | tion and/or other materials provided with the distribution.
|
---|
30 | 3. All advertising materials mentioning features or use of this
|
---|
31 | software must display the following acknowledgment:
|
---|
32 |
|
---|
33 | This product includes software developed by Greg Roelofs
|
---|
34 | and contributors for the book, "PNG: The Definitive Guide,"
|
---|
35 | published by O'Reilly and Associates.
|
---|
36 |
|
---|
37 |
|
---|
38 | LICENSE 2 (GNU GPL v2 or later):
|
---|
39 |
|
---|
40 | This program is free software; you can redistribute it and/or modify
|
---|
41 | it under the terms of the GNU General Public License as published by
|
---|
42 | the Free Software Foundation; either version 2 of the License, or
|
---|
43 | (at your option) any later version.
|
---|
44 |
|
---|
45 | This program is distributed in the hope that it will be useful,
|
---|
46 | but WITHOUT ANY WARRANTY; without even the implied warranty of
|
---|
47 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
---|
48 | GNU General Public License for more details.
|
---|
49 |
|
---|
50 | You should have received a copy of the GNU General Public License
|
---|
51 | along with this program; if not, write to the Free Software Foundation,
|
---|
52 | Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
---|
53 |
|
---|
54 | ---------------------------------------------------------------------------*/
|
---|
55 |
|
---|
56 | #include <stdio.h>
|
---|
57 | #include <stdlib.h>
|
---|
58 | #include <zlib.h>
|
---|
59 |
|
---|
60 | #include "png.h" /* libpng header */
|
---|
61 | #include "readpng.h" /* typedefs, common macros, public prototypes */
|
---|
62 |
|
---|
63 | /* future versions of libpng will provide this macro: */
|
---|
64 | #ifndef png_jmpbuf
|
---|
65 | # define png_jmpbuf(png_ptr) ((png_ptr)->jmpbuf)
|
---|
66 | #endif
|
---|
67 |
|
---|
68 |
|
---|
69 | static png_structp png_ptr = NULL;
|
---|
70 | static png_infop info_ptr = NULL;
|
---|
71 |
|
---|
72 | png_uint_32 width, height;
|
---|
73 | int bit_depth, color_type;
|
---|
74 | uch *image_data = NULL;
|
---|
75 |
|
---|
76 |
|
---|
77 | void readpng_version_info(void)
|
---|
78 | {
|
---|
79 | fprintf(stderr, " Compiled with libpng %s; using libpng %s.\n",
|
---|
80 | PNG_LIBPNG_VER_STRING, png_libpng_ver);
|
---|
81 | fprintf(stderr, " Compiled with zlib %s; using zlib %s.\n",
|
---|
82 | ZLIB_VERSION, zlib_version);
|
---|
83 | }
|
---|
84 |
|
---|
85 |
|
---|
86 | /* return value = 0 for success, 1 for bad sig, 2 for bad IHDR, 4 for no mem */
|
---|
87 |
|
---|
88 | int readpng_init(FILE *infile, ulg *pWidth, ulg *pHeight)
|
---|
89 | {
|
---|
90 | uch sig[8];
|
---|
91 |
|
---|
92 |
|
---|
93 | /* first do a quick check that the file really is a PNG image; could
|
---|
94 | * have used slightly more general png_sig_cmp() function instead */
|
---|
95 |
|
---|
96 | fread(sig, 1, 8, infile);
|
---|
97 | if (png_sig_cmp(sig, 0, 8))
|
---|
98 | return 1; /* bad signature */
|
---|
99 |
|
---|
100 |
|
---|
101 | /* could pass pointers to user-defined error handlers instead of NULLs: */
|
---|
102 |
|
---|
103 | png_ptr = png_create_read_struct(png_get_libpng_ver(NULL), NULL, NULL,
|
---|
104 | NULL);
|
---|
105 | if (!png_ptr)
|
---|
106 | return 4; /* out of memory */
|
---|
107 |
|
---|
108 | info_ptr = png_create_info_struct(png_ptr);
|
---|
109 | if (!info_ptr) {
|
---|
110 | png_destroy_read_struct(&png_ptr, NULL, NULL);
|
---|
111 | return 4; /* out of memory */
|
---|
112 | }
|
---|
113 |
|
---|
114 |
|
---|
115 | /* we could create a second info struct here (end_info), but it's only
|
---|
116 | * useful if we want to keep pre- and post-IDAT chunk info separated
|
---|
117 | * (mainly for PNG-aware image editors and converters) */
|
---|
118 |
|
---|
119 |
|
---|
120 | /* setjmp() must be called in every function that calls a PNG-reading
|
---|
121 | * libpng function */
|
---|
122 |
|
---|
123 | if (setjmp(png_jmpbuf(png_ptr))) {
|
---|
124 | png_destroy_read_struct(&png_ptr, &info_ptr, NULL);
|
---|
125 | return 2;
|
---|
126 | }
|
---|
127 |
|
---|
128 |
|
---|
129 | png_init_io(png_ptr, infile);
|
---|
130 | png_set_sig_bytes(png_ptr, 8); /* we already read the 8 signature bytes */
|
---|
131 |
|
---|
132 | png_read_info(png_ptr, info_ptr); /* read all PNG info up to image data */
|
---|
133 |
|
---|
134 |
|
---|
135 | /* alternatively, could make separate calls to png_get_image_width(),
|
---|
136 | * etc., but want bit_depth and color_type for later [don't care about
|
---|
137 | * compression_type and filter_type => NULLs] */
|
---|
138 |
|
---|
139 | png_get_IHDR(png_ptr, info_ptr, &width, &height, &bit_depth, &color_type,
|
---|
140 | NULL, NULL, NULL);
|
---|
141 | *pWidth = width;
|
---|
142 | *pHeight = height;
|
---|
143 |
|
---|
144 |
|
---|
145 | /* OK, that's all we need for now; return happy */
|
---|
146 |
|
---|
147 | return 0;
|
---|
148 | }
|
---|
149 |
|
---|
150 |
|
---|
151 |
|
---|
152 |
|
---|
153 | /* returns 0 if succeeds, 1 if fails due to no bKGD chunk, 2 if libpng error;
|
---|
154 | * scales values to 8-bit if necessary */
|
---|
155 |
|
---|
156 | int readpng_get_bgcolor(uch *red, uch *green, uch *blue)
|
---|
157 | {
|
---|
158 | png_color_16p pBackground;
|
---|
159 |
|
---|
160 |
|
---|
161 | /* setjmp() must be called in every function that calls a PNG-reading
|
---|
162 | * libpng function */
|
---|
163 |
|
---|
164 | if (setjmp(png_jmpbuf(png_ptr))) {
|
---|
165 | png_destroy_read_struct(&png_ptr, &info_ptr, NULL);
|
---|
166 | return 2;
|
---|
167 | }
|
---|
168 |
|
---|
169 |
|
---|
170 | if (!png_get_valid(png_ptr, info_ptr, PNG_INFO_bKGD))
|
---|
171 | return 1;
|
---|
172 |
|
---|
173 | /* it is not obvious from the libpng documentation, but this function
|
---|
174 | * takes a pointer to a pointer, and it always returns valid red, green
|
---|
175 | * and blue values, regardless of color_type: */
|
---|
176 |
|
---|
177 | png_get_bKGD(png_ptr, info_ptr, &pBackground);
|
---|
178 |
|
---|
179 |
|
---|
180 | /* however, it always returns the raw bKGD data, regardless of any
|
---|
181 | * bit-depth transformations, so check depth and adjust if necessary */
|
---|
182 |
|
---|
183 | if (bit_depth == 16) {
|
---|
184 | *red = pBackground->red >> 8;
|
---|
185 | *green = pBackground->green >> 8;
|
---|
186 | *blue = pBackground->blue >> 8;
|
---|
187 | } else if (color_type == PNG_COLOR_TYPE_GRAY && bit_depth < 8) {
|
---|
188 | if (bit_depth == 1)
|
---|
189 | *red = *green = *blue = pBackground->gray? 255 : 0;
|
---|
190 | else if (bit_depth == 2)
|
---|
191 | *red = *green = *blue = (255/3) * pBackground->gray;
|
---|
192 | else /* bit_depth == 4 */
|
---|
193 | *red = *green = *blue = (255/15) * pBackground->gray;
|
---|
194 | } else {
|
---|
195 | *red = (uch)pBackground->red;
|
---|
196 | *green = (uch)pBackground->green;
|
---|
197 | *blue = (uch)pBackground->blue;
|
---|
198 | }
|
---|
199 |
|
---|
200 | return 0;
|
---|
201 | }
|
---|
202 |
|
---|
203 |
|
---|
204 |
|
---|
205 |
|
---|
206 | /* display_exponent == LUT_exponent * CRT_exponent */
|
---|
207 |
|
---|
208 | uch *readpng_get_image(double display_exponent, int *pChannels, ulg *pRowbytes)
|
---|
209 | {
|
---|
210 | double gamma;
|
---|
211 | png_uint_32 i, rowbytes;
|
---|
212 | png_bytepp row_pointers = NULL;
|
---|
213 |
|
---|
214 |
|
---|
215 | /* setjmp() must be called in every function that calls a PNG-reading
|
---|
216 | * libpng function */
|
---|
217 |
|
---|
218 | if (setjmp(png_jmpbuf(png_ptr))) {
|
---|
219 | free(image_data);
|
---|
220 | image_data = NULL;
|
---|
221 | free(row_pointers);
|
---|
222 | row_pointers = NULL;
|
---|
223 | png_destroy_read_struct(&png_ptr, &info_ptr, NULL);
|
---|
224 | return NULL;
|
---|
225 | }
|
---|
226 |
|
---|
227 |
|
---|
228 | /* expand palette images to RGB, low-bit-depth grayscale images to 8 bits,
|
---|
229 | * transparency chunks to full alpha channel; strip 16-bit-per-sample
|
---|
230 | * images to 8 bits per sample; and convert grayscale to RGB[A] */
|
---|
231 |
|
---|
232 | if (color_type == PNG_COLOR_TYPE_PALETTE)
|
---|
233 | png_set_expand(png_ptr);
|
---|
234 | if (color_type == PNG_COLOR_TYPE_GRAY && bit_depth < 8)
|
---|
235 | png_set_expand(png_ptr);
|
---|
236 | if (png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS))
|
---|
237 | png_set_expand(png_ptr);
|
---|
238 | #ifdef PNG_READ_16_TO_8_SUPPORTED
|
---|
239 | if (bit_depth == 16)
|
---|
240 | # ifdef PNG_READ_SCALE_16_TO_8_SUPPORTED
|
---|
241 | png_set_scale_16(png_ptr);
|
---|
242 | # else
|
---|
243 | png_set_strip_16(png_ptr);
|
---|
244 | # endif
|
---|
245 | #endif
|
---|
246 | if (color_type == PNG_COLOR_TYPE_GRAY ||
|
---|
247 | color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
|
---|
248 | png_set_gray_to_rgb(png_ptr);
|
---|
249 |
|
---|
250 |
|
---|
251 | /* unlike the example in the libpng documentation, we have *no* idea where
|
---|
252 | * this file may have come from--so if it doesn't have a file gamma, don't
|
---|
253 | * do any correction ("do no harm") */
|
---|
254 |
|
---|
255 | if (png_get_gAMA(png_ptr, info_ptr, &gamma))
|
---|
256 | png_set_gamma(png_ptr, display_exponent, gamma);
|
---|
257 |
|
---|
258 |
|
---|
259 | /* all transformations have been registered; now update info_ptr data,
|
---|
260 | * get rowbytes and channels, and allocate image memory */
|
---|
261 |
|
---|
262 | png_read_update_info(png_ptr, info_ptr);
|
---|
263 |
|
---|
264 | *pRowbytes = rowbytes = png_get_rowbytes(png_ptr, info_ptr);
|
---|
265 | *pChannels = (int)png_get_channels(png_ptr, info_ptr);
|
---|
266 |
|
---|
267 | /* Guard against integer overflow */
|
---|
268 | if (height > ((size_t)(-1))/rowbytes) {
|
---|
269 | fprintf(stderr, "readpng: image_data buffer would be too large\n",
|
---|
270 | return NULL;
|
---|
271 | }
|
---|
272 |
|
---|
273 | if ((image_data = (uch *)malloc(rowbytes*height)) == NULL) {
|
---|
274 | png_destroy_read_struct(&png_ptr, &info_ptr, NULL);
|
---|
275 | return NULL;
|
---|
276 | }
|
---|
277 | if ((row_pointers = (png_bytepp)malloc(height*sizeof(png_bytep))) == NULL) {
|
---|
278 | png_destroy_read_struct(&png_ptr, &info_ptr, NULL);
|
---|
279 | free(image_data);
|
---|
280 | image_data = NULL;
|
---|
281 | return NULL;
|
---|
282 | }
|
---|
283 |
|
---|
284 | Trace((stderr, "readpng_get_image: channels = %d, rowbytes = %ld, height = %ld\n",
|
---|
285 | *pChannels, rowbytes, height));
|
---|
286 |
|
---|
287 |
|
---|
288 | /* set the individual row_pointers to point at the correct offsets */
|
---|
289 |
|
---|
290 | for (i = 0; i < height; ++i)
|
---|
291 | row_pointers[i] = image_data + i*rowbytes;
|
---|
292 |
|
---|
293 |
|
---|
294 | /* now we can go ahead and just read the whole image */
|
---|
295 |
|
---|
296 | png_read_image(png_ptr, row_pointers);
|
---|
297 |
|
---|
298 |
|
---|
299 | /* and we're done! (png_read_end() can be omitted if no processing of
|
---|
300 | * post-IDAT text/time/etc. is desired) */
|
---|
301 |
|
---|
302 | free(row_pointers);
|
---|
303 | row_pointers = NULL;
|
---|
304 |
|
---|
305 | png_read_end(png_ptr, NULL);
|
---|
306 |
|
---|
307 | return image_data;
|
---|
308 | }
|
---|
309 |
|
---|
310 |
|
---|
311 | void readpng_cleanup(int free_image_data)
|
---|
312 | {
|
---|
313 | if (free_image_data && image_data) {
|
---|
314 | free(image_data);
|
---|
315 | image_data = NULL;
|
---|
316 | }
|
---|
317 |
|
---|
318 | if (png_ptr && info_ptr) {
|
---|
319 | png_destroy_read_struct(&png_ptr, &info_ptr, NULL);
|
---|
320 | png_ptr = NULL;
|
---|
321 | info_ptr = NULL;
|
---|
322 | }
|
---|
323 | }
|
---|