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