src/third-party/libpng/contrib/examples/pngpixel.c (view raw)
1/*- pngpixel
2 *
3 * COPYRIGHT: Written by John Cunningham Bowler, 2011.
4 * To the extent possible under law, the author has waived all copyright and
5 * related or neighboring rights to this work. This work is published from:
6 * United States.
7 *
8 * Read a single pixel value from a PNG file.
9 *
10 * This code illustrates basic 'by-row' reading of a PNG file using libpng.
11 * Rows are read until a particular pixel is found; the value of this pixel is
12 * then printed on stdout.
13 *
14 * The code illustrates how to do this on interlaced as well as non-interlaced
15 * images. Normally you would call png_set_interlace_handling() to have libpng
16 * deal with the interlace for you, but that obliges you to buffer half of the
17 * image to assemble the interlaced rows. In this code
18 * png_set_interlace_handling() is not called and, instead, the code handles the
19 * interlace passes directly looking for the required pixel.
20 */
21#include <stdlib.h>
22#include <stdio.h>
23#include <setjmp.h> /* required for error handling */
24
25/* Normally use <png.h> here to get the installed libpng, but this is done to
26 * ensure the code picks up the local libpng implementation:
27 */
28#include "../../png.h"
29
30/* Return component 'c' of pixel 'x' from the given row. */
31static unsigned int
32component(png_const_bytep row, png_uint_32 x, unsigned int c,
33 unsigned int bit_depth, unsigned int channels)
34{
35 /* PNG images can be up to 2^31 pixels wide, but this means they can be up to
36 * 2^37 bits wide (for a 64-bit pixel - the largest possible) and hence 2^34
37 * bytes wide. Since the row fitted into memory, however, the following must
38 * work:
39 */
40 png_uint_32 bit_offset_hi = bit_depth * ((x >> 6) * channels);
41 png_uint_32 bit_offset_lo = bit_depth * ((x & 0x3f) * channels + c);
42
43 row = (png_const_bytep)(((PNG_CONST png_byte (*)[8])row) + bit_offset_hi);
44 row += bit_offset_lo >> 3;
45 bit_offset_lo &= 0x07;
46
47 /* PNG pixels are packed into bytes to put the first pixel in the highest
48 * bits of the byte and into two bytes for 16-bit values with the high 8 bits
49 * first, so:
50 */
51 switch (bit_depth)
52 {
53 case 1: return (row[0] >> (7-bit_offset_lo)) & 0x01;
54 case 2: return (row[0] >> (6-bit_offset_lo)) & 0x03;
55 case 4: return (row[0] >> (4-bit_offset_lo)) & 0x0f;
56 case 8: return row[0];
57 case 16: return (row[0] << 8) + row[1];
58 default:
59 /* This should never happen; it indicates a bug in this program or in
60 * libpng itself:
61 */
62 fprintf(stderr, "pngpixel: invalid bit depth %u\n", bit_depth);
63 exit(1);
64 }
65}
66
67/* Print a pixel from a row returned by libpng; determine the row format, find
68 * the pixel, and print the relevant information to stdout.
69 */
70static void
71print_pixel(png_structp png_ptr, png_infop info_ptr, png_const_bytep row,
72 png_uint_32 x)
73{
74 PNG_CONST unsigned int bit_depth = png_get_bit_depth(png_ptr, info_ptr);
75
76 switch (png_get_color_type(png_ptr, info_ptr))
77 {
78 case PNG_COLOR_TYPE_GRAY:
79 printf("GRAY %u\n", component(row, x, 0, bit_depth, 1));
80 return;
81
82 /* The palette case is slightly more difficult - the palette and, if
83 * present, the tRNS ('transparency', though the values are really
84 * opacity) data must be read to give the full picture:
85 */
86 case PNG_COLOR_TYPE_PALETTE:
87 {
88 PNG_CONST unsigned int index = component(row, x, 0, bit_depth, 1);
89 png_colorp palette = NULL;
90 int num_palette = 0;
91
92 if ((png_get_PLTE(png_ptr, info_ptr, &palette, &num_palette) &
93 PNG_INFO_PLTE) && num_palette > 0 && palette != NULL)
94 {
95 png_bytep trans_alpha = NULL;
96 int num_trans = 0;
97 if ((png_get_tRNS(png_ptr, info_ptr, &trans_alpha, &num_trans,
98 NULL) & PNG_INFO_tRNS) && num_trans > 0 &&
99 trans_alpha != NULL)
100 printf("INDEXED %u = %d %d %d %d\n", index,
101 palette[index].red, palette[index].green,
102 palette[index].blue,
103 index < num_trans ? trans_alpha[index] : 255);
104
105 else /* no transparency */
106 printf("INDEXED %u = %d %d %d\n", index,
107 palette[index].red, palette[index].green,
108 palette[index].blue);
109 }
110
111 else
112 printf("INDEXED %u = invalid index\n", index);
113 }
114 return;
115
116 case PNG_COLOR_TYPE_RGB:
117 printf("RGB %u %u %u\n", component(row, x, 0, bit_depth, 3),
118 component(row, x, 1, bit_depth, 3),
119 component(row, x, 2, bit_depth, 3));
120 return;
121
122 case PNG_COLOR_TYPE_GRAY_ALPHA:
123 printf("GRAY+ALPHA %u %u\n", component(row, x, 0, bit_depth, 2),
124 component(row, x, 1, bit_depth, 2));
125 return;
126
127 case PNG_COLOR_TYPE_RGB_ALPHA:
128 printf("RGBA %u %u %u %u\n", component(row, x, 0, bit_depth, 4),
129 component(row, x, 1, bit_depth, 4),
130 component(row, x, 2, bit_depth, 4),
131 component(row, x, 3, bit_depth, 4));
132 return;
133
134 default:
135 png_error(png_ptr, "pngpixel: invalid color type");
136 }
137}
138
139int main(int argc, const char **argv)
140{
141 /* This program uses the default, <setjmp.h> based, libpng error handling
142 * mechanism, therefore any local variable that exists before the call to
143 * setjmp and is changed after the call to setjmp returns successfully must
144 * be declared with 'volatile' to ensure that their values don't get
145 * destroyed by longjmp:
146 */
147 volatile int result = 1/*fail*/;
148
149 if (argc == 4)
150 {
151 long x = atol(argv[1]);
152 long y = atol(argv[2]);
153 FILE *f = fopen(argv[3], "rb");
154 volatile png_bytep row = NULL;
155
156 if (f != NULL)
157 {
158 /* libpng requires a callback function for handling errors; this
159 * callback must not return. The default callback function uses a
160 * stored <setjmp.h> style jmp_buf which is held in a png_struct and
161 * writes error messages to stderr. Creating the png_struct is a
162 * little tricky; just copy the following code.
163 */
164 png_structp png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING,
165 NULL, NULL, NULL);
166
167 if (png_ptr != NULL)
168 {
169 png_infop info_ptr = png_create_info_struct(png_ptr);
170
171 if (info_ptr != NULL)
172 {
173 /* Declare stack variables to hold pointers to locally allocated
174 * data.
175 */
176
177 /* Initialize the error control buffer: */
178 if (setjmp(png_jmpbuf(png_ptr)) == 0)
179 {
180 png_uint_32 width, height;
181 int bit_depth, color_type, interlace_method,
182 compression_method, filter_method;
183 png_bytep row_tmp;
184
185 /* Now associate the recently opened (FILE*) with the default
186 * libpng initialization functions. Sometimes libpng is
187 * compiled without stdio support (it can be difficult to do
188 * in some environments); in that case you will have to write
189 * your own read callback to read data from the (FILE*).
190 */
191 png_init_io(png_ptr, f);
192
193 /* And read the first part of the PNG file - the header and
194 * all the information up to the first pixel.
195 */
196 png_read_info(png_ptr, info_ptr);
197
198 /* This fills in enough information to tell us the width of
199 * each row in bytes, allocate the appropriate amount of
200 * space. In this case png_malloc is used - it will not
201 * return if memory isn't available.
202 */
203 row = png_malloc(png_ptr, png_get_rowbytes(png_ptr,
204 info_ptr));
205
206 /* To avoid the overhead of using a volatile auto copy row_tmp
207 * to a local here - just use row for the png_free below.
208 */
209 row_tmp = row;
210
211 /* All the information we need is in the header is returned by
212 * png_get_IHDR, if this fails we can now use 'png_error' to
213 * signal the error and return control to the setjmp above.
214 */
215 if (png_get_IHDR(png_ptr, info_ptr, &width, &height,
216 &bit_depth, &color_type, &interlace_method,
217 &compression_method, &filter_method))
218 {
219 int passes, pass;
220
221 /* png_set_interlace_handling returns the number of
222 * passes required as well as turning on libpng's
223 * handling, but since we do it ourselves this is
224 * necessary:
225 */
226 switch (interlace_method)
227 {
228 case PNG_INTERLACE_NONE:
229 passes = 1;
230 break;
231
232 case PNG_INTERLACE_ADAM7:
233 passes = PNG_INTERLACE_ADAM7_PASSES;
234 break;
235
236 default:
237 png_error(png_ptr, "pngpixel: unknown interlace");
238 }
239
240 /* Now read the pixels, pass-by-pass, row-by-row: */
241 png_start_read_image(png_ptr);
242
243 for (pass=0; pass<passes; ++pass)
244 {
245 png_uint_32 ystart, xstart, ystep, xstep;
246 png_uint_32 py;
247
248 if (interlace_method == PNG_INTERLACE_ADAM7)
249 {
250 /* Sometimes the whole pass is empty because the
251 * image is too narrow or too short. libpng
252 * expects to be called for each row that is
253 * present in the pass, so it may be necessary to
254 * skip the loop below (over py) if the image is
255 * too narrow.
256 */
257 if (PNG_PASS_COLS(width, pass) == 0)
258 continue;
259
260 /* We need the starting pixel and the offset
261 * between each pixel in this pass; use the macros
262 * in png.h:
263 */
264 xstart = PNG_PASS_START_COL(pass);
265 ystart = PNG_PASS_START_ROW(pass);
266 xstep = PNG_PASS_COL_OFFSET(pass);
267 ystep = PNG_PASS_ROW_OFFSET(pass);
268 }
269
270 else
271 {
272 ystart = xstart = 0;
273 ystep = xstep = 1;
274 }
275
276 /* To find the pixel, loop over 'py' for each pass
277 * reading a row and then checking to see if it
278 * contains the pixel.
279 */
280 for (py = ystart; py < height; py += ystep)
281 {
282 png_uint_32 px, ppx;
283
284 /* png_read_row takes two pointers. When libpng
285 * handles the interlace the first is filled in
286 * pixel-by-pixel, and the second receives the same
287 * pixels but they are replicated across the
288 * unwritten pixels so far for each pass. When we
289 * do the interlace, however, they just contain
290 * the pixels from the interlace pass - giving
291 * both is wasteful and pointless, so we pass a
292 * NULL pointer.
293 */
294 png_read_row(png_ptr, row_tmp, NULL);
295
296 /* Now find the pixel if it is in this row; there
297 * are, of course, much better ways of doing this
298 * than using a for loop:
299 */
300 if (y == py) for (px = xstart, ppx = 0;
301 px < width; px += xstep, ++ppx) if (x == px)
302 {
303 /* 'ppx' is the index of the pixel in the row
304 * buffer.
305 */
306 print_pixel(png_ptr, info_ptr, row_tmp, ppx);
307
308 /* Now terminate the loops early - we have
309 * found and handled the required data.
310 */
311 goto pass_loop_end;
312 } /* x loop */
313 } /* y loop */
314 } /* pass loop */
315
316 /* Finally free the temporary buffer: */
317 pass_loop_end:
318 row = NULL;
319 png_free(png_ptr, row_tmp);
320 }
321
322 else
323 png_error(png_ptr, "pngpixel: png_get_IHDR failed");
324
325 }
326
327 else
328 {
329 /* Else libpng has raised an error. An error message has
330 * already been output, so it is only necessary to clean up
331 * locally allocated data:
332 */
333 if (row != NULL)
334 {
335 /* The default implementation of png_free never errors out
336 * (it just crashes if something goes wrong), but the safe
337 * way of using it is still to clear 'row' before calling
338 * png_free:
339 */
340 png_bytep row_tmp = row;
341 row = NULL;
342 png_free(png_ptr, row_tmp);
343 }
344 }
345
346 png_destroy_info_struct(png_ptr, &info_ptr);
347 }
348
349 else
350 fprintf(stderr, "pngpixel: out of memory allocating png_info\n");
351
352 png_destroy_read_struct(&png_ptr, NULL, NULL);
353 }
354
355 else
356 fprintf(stderr, "pngpixel: out of memory allocating png_struct\n");
357 }
358
359 else
360 fprintf(stderr, "pngpixel: %s: could not open file\n", argv[3]);
361 }
362
363 else
364 /* Wrong number of arguments */
365 fprintf(stderr, "pngpixel: usage: pngpixel x y png-file\n");
366
367 return result;
368}