1 | /*---------------------------------------------------------------------------
|
---|
2 |
|
---|
3 | rpng - simple PNG display program readpng.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>
|
---|
34 |
|
---|
35 | #include "png.h" /* libpng header; includes zlib.h */
|
---|
36 | #include "readpng.h" /* typedefs, common macros, public prototypes */
|
---|
37 |
|
---|
38 | /* future versions of libpng will provide this macro: */
|
---|
39 | #ifndef png_jmpbuf
|
---|
40 | # define png_jmpbuf(png_ptr) ((png_ptr)->jmpbuf)
|
---|
41 | #endif
|
---|
42 |
|
---|
43 |
|
---|
44 | static png_structp png_ptr = NULL;
|
---|
45 | static png_infop info_ptr = NULL;
|
---|
46 |
|
---|
47 | png_uint_32 width, height;
|
---|
48 | int bit_depth, color_type;
|
---|
49 | uch *image_data = NULL;
|
---|
50 |
|
---|
51 |
|
---|
52 | void readpng_version_info(void)
|
---|
53 | {
|
---|
54 | fprintf(stderr, " Compiled with libpng %s; using libpng %s.\n",
|
---|
55 | PNG_LIBPNG_VER_STRING, png_libpng_ver);
|
---|
56 | fprintf(stderr, " Compiled with zlib %s; using zlib %s.\n",
|
---|
57 | ZLIB_VERSION, zlib_version);
|
---|
58 | }
|
---|
59 |
|
---|
60 |
|
---|
61 | /* return value = 0 for success, 1 for bad sig, 2 for bad IHDR, 4 for no mem */
|
---|
62 |
|
---|
63 | int readpng_init(FILE *infile, ulg *pWidth, ulg *pHeight)
|
---|
64 | {
|
---|
65 | uch sig[8];
|
---|
66 |
|
---|
67 |
|
---|
68 | /* first do a quick check that the file really is a PNG image; could
|
---|
69 | * have used slightly more general png_sig_cmp() function instead */
|
---|
70 |
|
---|
71 | fread(sig, 1, 8, infile);
|
---|
72 | if (!png_check_sig(sig, 8))
|
---|
73 | return 1; /* bad signature */
|
---|
74 |
|
---|
75 |
|
---|
76 | /* could pass pointers to user-defined error handlers instead of NULLs: */
|
---|
77 |
|
---|
78 | png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
|
---|
79 | if (!png_ptr)
|
---|
80 | return 4; /* out of memory */
|
---|
81 |
|
---|
82 | info_ptr = png_create_info_struct(png_ptr);
|
---|
83 | if (!info_ptr) {
|
---|
84 | png_destroy_read_struct(&png_ptr, NULL, NULL);
|
---|
85 | return 4; /* out of memory */
|
---|
86 | }
|
---|
87 |
|
---|
88 |
|
---|
89 | /* we could create a second info struct here (end_info), but it's only
|
---|
90 | * useful if we want to keep pre- and post-IDAT chunk info separated
|
---|
91 | * (mainly for PNG-aware image editors and converters) */
|
---|
92 |
|
---|
93 |
|
---|
94 | /* setjmp() must be called in every function that calls a PNG-reading
|
---|
95 | * libpng function */
|
---|
96 |
|
---|
97 | if (setjmp(png_jmpbuf(png_ptr))) {
|
---|
98 | png_destroy_read_struct(&png_ptr, &info_ptr, NULL);
|
---|
99 | return 2;
|
---|
100 | }
|
---|
101 |
|
---|
102 |
|
---|
103 | png_init_io(png_ptr, infile);
|
---|
104 | png_set_sig_bytes(png_ptr, 8); /* we already read the 8 signature bytes */
|
---|
105 |
|
---|
106 | png_read_info(png_ptr, info_ptr); /* read all PNG info up to image data */
|
---|
107 |
|
---|
108 |
|
---|
109 | /* alternatively, could make separate calls to png_get_image_width(),
|
---|
110 | * etc., but want bit_depth and color_type for later [don't care about
|
---|
111 | * compression_type and filter_type => NULLs] */
|
---|
112 |
|
---|
113 | png_get_IHDR(png_ptr, info_ptr, &width, &height, &bit_depth, &color_type,
|
---|
114 | NULL, NULL, NULL);
|
---|
115 | *pWidth = width;
|
---|
116 | *pHeight = height;
|
---|
117 |
|
---|
118 |
|
---|
119 | /* OK, that's all we need for now; return happy */
|
---|
120 |
|
---|
121 | return 0;
|
---|
122 | }
|
---|
123 |
|
---|
124 |
|
---|
125 |
|
---|
126 |
|
---|
127 | /* returns 0 if succeeds, 1 if fails due to no bKGD chunk, 2 if libpng error;
|
---|
128 | * scales values to 8-bit if necessary */
|
---|
129 |
|
---|
130 | int readpng_get_bgcolor(uch *red, uch *green, uch *blue)
|
---|
131 | {
|
---|
132 | png_color_16p pBackground;
|
---|
133 |
|
---|
134 |
|
---|
135 | /* setjmp() must be called in every function that calls a PNG-reading
|
---|
136 | * libpng function */
|
---|
137 |
|
---|
138 | if (setjmp(png_jmpbuf(png_ptr))) {
|
---|
139 | png_destroy_read_struct(&png_ptr, &info_ptr, NULL);
|
---|
140 | return 2;
|
---|
141 | }
|
---|
142 |
|
---|
143 |
|
---|
144 | if (!png_get_valid(png_ptr, info_ptr, PNG_INFO_bKGD))
|
---|
145 | return 1;
|
---|
146 |
|
---|
147 | /* it is not obvious from the libpng documentation, but this function
|
---|
148 | * takes a pointer to a pointer, and it always returns valid red, green
|
---|
149 | * and blue values, regardless of color_type: */
|
---|
150 |
|
---|
151 | png_get_bKGD(png_ptr, info_ptr, &pBackground);
|
---|
152 |
|
---|
153 |
|
---|
154 | /* however, it always returns the raw bKGD data, regardless of any
|
---|
155 | * bit-depth transformations, so check depth and adjust if necessary */
|
---|
156 |
|
---|
157 | if (bit_depth == 16) {
|
---|
158 | *red = pBackground->red >> 8;
|
---|
159 | *green = pBackground->green >> 8;
|
---|
160 | *blue = pBackground->blue >> 8;
|
---|
161 | } else if (color_type == PNG_COLOR_TYPE_GRAY && bit_depth < 8) {
|
---|
162 | if (bit_depth == 1)
|
---|
163 | *red = *green = *blue = pBackground->gray? 255 : 0;
|
---|
164 | else if (bit_depth == 2)
|
---|
165 | *red = *green = *blue = (255/3) * pBackground->gray;
|
---|
166 | else /* bit_depth == 4 */
|
---|
167 | *red = *green = *blue = (255/15) * pBackground->gray;
|
---|
168 | } else {
|
---|
169 | *red = (uch)pBackground->red;
|
---|
170 | *green = (uch)pBackground->green;
|
---|
171 | *blue = (uch)pBackground->blue;
|
---|
172 | }
|
---|
173 |
|
---|
174 | return 0;
|
---|
175 | }
|
---|
176 |
|
---|
177 |
|
---|
178 |
|
---|
179 |
|
---|
180 | /* display_exponent == LUT_exponent * CRT_exponent */
|
---|
181 |
|
---|
182 | uch *readpng_get_image(double display_exponent, int *pChannels, ulg *pRowbytes)
|
---|
183 | {
|
---|
184 | double gamma;
|
---|
185 | png_uint_32 i, rowbytes;
|
---|
186 | png_bytepp row_pointers = NULL;
|
---|
187 |
|
---|
188 |
|
---|
189 | /* setjmp() must be called in every function that calls a PNG-reading
|
---|
190 | * libpng function */
|
---|
191 |
|
---|
192 | if (setjmp(png_jmpbuf(png_ptr))) {
|
---|
193 | png_destroy_read_struct(&png_ptr, &info_ptr, NULL);
|
---|
194 | return NULL;
|
---|
195 | }
|
---|
196 |
|
---|
197 |
|
---|
198 | /* expand palette images to RGB, low-bit-depth grayscale images to 8 bits,
|
---|
199 | * transparency chunks to full alpha channel; strip 16-bit-per-sample
|
---|
200 | * images to 8 bits per sample; and convert grayscale to RGB[A] */
|
---|
201 |
|
---|
202 | if (color_type == PNG_COLOR_TYPE_PALETTE)
|
---|
203 | png_set_expand(png_ptr);
|
---|
204 | if (color_type == PNG_COLOR_TYPE_GRAY && bit_depth < 8)
|
---|
205 | png_set_expand(png_ptr);
|
---|
206 | if (png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS))
|
---|
207 | png_set_expand(png_ptr);
|
---|
208 | if (bit_depth == 16)
|
---|
209 | png_set_strip_16(png_ptr);
|
---|
210 | if (color_type == PNG_COLOR_TYPE_GRAY ||
|
---|
211 | color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
|
---|
212 | png_set_gray_to_rgb(png_ptr);
|
---|
213 |
|
---|
214 |
|
---|
215 | /* unlike the example in the libpng documentation, we have *no* idea where
|
---|
216 | * this file may have come from--so if it doesn't have a file gamma, don't
|
---|
217 | * do any correction ("do no harm") */
|
---|
218 |
|
---|
219 | if (png_get_gAMA(png_ptr, info_ptr, &gamma))
|
---|
220 | png_set_gamma(png_ptr, display_exponent, gamma);
|
---|
221 |
|
---|
222 |
|
---|
223 | /* all transformations have been registered; now update info_ptr data,
|
---|
224 | * get rowbytes and channels, and allocate image memory */
|
---|
225 |
|
---|
226 | png_read_update_info(png_ptr, info_ptr);
|
---|
227 |
|
---|
228 | *pRowbytes = rowbytes = png_get_rowbytes(png_ptr, info_ptr);
|
---|
229 | *pChannels = (int)png_get_channels(png_ptr, info_ptr);
|
---|
230 |
|
---|
231 | if ((image_data = (uch *)malloc(rowbytes*height)) == NULL) {
|
---|
232 | png_destroy_read_struct(&png_ptr, &info_ptr, NULL);
|
---|
233 | return NULL;
|
---|
234 | }
|
---|
235 | if ((row_pointers = (png_bytepp)malloc(height*sizeof(png_bytep))) == NULL) {
|
---|
236 | png_destroy_read_struct(&png_ptr, &info_ptr, NULL);
|
---|
237 | free(image_data);
|
---|
238 | image_data = NULL;
|
---|
239 | return NULL;
|
---|
240 | }
|
---|
241 |
|
---|
242 | Trace((stderr, "readpng_get_image: channels = %d, rowbytes = %ld, height = %ld\n", *pChannels, rowbytes, height));
|
---|
243 |
|
---|
244 |
|
---|
245 | /* set the individual row_pointers to point at the correct offsets */
|
---|
246 |
|
---|
247 | for (i = 0; i < height; ++i)
|
---|
248 | row_pointers[i] = image_data + i*rowbytes;
|
---|
249 |
|
---|
250 |
|
---|
251 | /* now we can go ahead and just read the whole image */
|
---|
252 |
|
---|
253 | png_read_image(png_ptr, row_pointers);
|
---|
254 |
|
---|
255 |
|
---|
256 | /* and we're done! (png_read_end() can be omitted if no processing of
|
---|
257 | * post-IDAT text/time/etc. is desired) */
|
---|
258 |
|
---|
259 | free(row_pointers);
|
---|
260 | row_pointers = NULL;
|
---|
261 |
|
---|
262 | png_read_end(png_ptr, NULL);
|
---|
263 |
|
---|
264 | return image_data;
|
---|
265 | }
|
---|
266 |
|
---|
267 |
|
---|
268 | void readpng_cleanup(int free_image_data)
|
---|
269 | {
|
---|
270 | if (free_image_data && image_data) {
|
---|
271 | free(image_data);
|
---|
272 | image_data = NULL;
|
---|
273 | }
|
---|
274 |
|
---|
275 | if (png_ptr && info_ptr) {
|
---|
276 | png_destroy_read_struct(&png_ptr, &info_ptr, NULL);
|
---|
277 | png_ptr = NULL;
|
---|
278 | info_ptr = NULL;
|
---|
279 | }
|
---|
280 | }
|
---|