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
, "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");