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:
8 * Read a single pixel value from a PNG file.
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.
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.
23 #include <setjmp.h> /* required for error handling */
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:
28 #include "../../png.h"
30 /* Return component 'c' of pixel 'x' from the given row. */
32 component(png_const_bytep row, png_uint_32 x, unsigned int c,
33 unsigned int bit_depth, unsigned int channels)
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
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);
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;
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
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];
59 /* This should never happen; it indicates a bug in this program or in
62 fprintf(stderr, "pngpixel: invalid bit depth %u\n", bit_depth);
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.
71 print_pixel(png_structp png_ptr, png_infop info_ptr, png_const_bytep row,
74 PNG_CONST unsigned int bit_depth = png_get_bit_depth(png_ptr, info_ptr);
76 switch (png_get_color_type(png_ptr, info_ptr))
78 case PNG_COLOR_TYPE_GRAY:
79 printf("GRAY %u\n", component(row, x, 0, bit_depth, 1));
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:
86 case PNG_COLOR_TYPE_PALETTE:
88 PNG_CONST unsigned int index = component(row, x, 0, bit_depth, 1);
89 png_colorp palette = NULL;
92 if ((png_get_PLTE(png_ptr, info_ptr, &palette, &num_palette) &
93 PNG_INFO_PLTE) && num_palette > 0 && palette != NULL)
95 png_bytep trans_alpha = NULL;
97 if ((png_get_tRNS(png_ptr, info_ptr, &trans_alpha, &num_trans,
98 NULL) & PNG_INFO_tRNS) && num_trans > 0 &&
100 printf("INDEXED %u = %d %d %d %d\n", index,
101 palette[index].red, palette[index].green,
103 index < num_trans ? trans_alpha[index] : 255);
105 else /* no transparency */
106 printf("INDEXED %u = %d %d %d\n", index,
107 palette[index].red, palette[index].green,
108 palette[index].blue);
112 printf("INDEXED %u = invalid index\n", index);
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));
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));
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));
135 png_error(png_ptr, "pngpixel: invalid color type");
139 int main(int argc, const char **argv)
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:
147 volatile int result = 1/*fail*/;
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;
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.
164 png_structp png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING,
169 png_infop info_ptr = png_create_info_struct(png_ptr);
171 if (info_ptr != NULL)
173 /* Declare stack variables to hold pointers to locally allocated
177 /* Initialize the error control buffer: */
178 if (setjmp(png_jmpbuf(png_ptr)) == 0)
180 png_uint_32 width, height;
181 int bit_depth, color_type, interlace_method,
182 compression_method, filter_method;
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*).
191 png_init_io(png_ptr, f);
193 /* And read the first part of the PNG file - the header and
194 * all the information up to the first pixel.
196 png_read_info(png_ptr, info_ptr);
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.
203 row = png_malloc(png_ptr, png_get_rowbytes(png_ptr,
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.
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.
215 if (png_get_IHDR(png_ptr, info_ptr, &width, &height,
216 &bit_depth, &color_type, &interlace_method,
217 &compression_method, &filter_method))
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
226 switch (interlace_method)
228 case PNG_INTERLACE_NONE:
232 case PNG_INTERLACE_ADAM7:
233 passes = PNG_INTERLACE_ADAM7_PASSES;
237 png_error(png_ptr, "pngpixel: unknown interlace");
240 /* Now read the pixels, pass-by-pass, row-by-row: */
241 png_start_read_image(png_ptr);
243 for (pass=0; pass<passes; ++pass)
245 png_uint_32 ystart, xstart, ystep, xstep;
248 if (interlace_method == PNG_INTERLACE_ADAM7)
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
257 if (PNG_PASS_COLS(width, pass) == 0)
260 /* We need the starting pixel and the offset
261 * between each pixel in this pass; use the macros
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);
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.
280 for (py = ystart; py < height; py += ystep)
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
294 png_read_row(png_ptr, row_tmp, NULL);
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:
300 if (y == py) for (px = xstart, ppx = 0;
301 px < width; px += xstep, ++ppx) if (x == px)
303 /* 'ppx' is the index of the pixel in the row
306 print_pixel(png_ptr, info_ptr, row_tmp, ppx);
308 /* Now terminate the loops early - we have
309 * found and handled the required data.
316 /* Finally free the temporary buffer: */
319 png_free(png_ptr, row_tmp);
323 png_error(png_ptr, "pngpixel: png_get_IHDR failed");
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:
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
340 png_bytep row_tmp = row;
342 png_free(png_ptr, row_tmp);
346 png_destroy_info_struct(png_ptr, &info_ptr);
350 fprintf(stderr, "pngpixel: out of memory allocating png_info\n");
352 png_destroy_read_struct(&png_ptr, NULL, NULL);
356 fprintf(stderr, "pngpixel: out of memory allocating png_struct\n");
360 fprintf(stderr, "pngpixel: %s: could not open file\n", argv[3]);
364 /* Wrong number of arguments */
365 fprintf(stderr, "pngpixel: usage: pngpixel x y png-file\n");