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 #if defined(PNG_READ_SUPPORTED) && defined(PNG_SEQUENTIAL_READ_SUPPORTED)
32 /* Return component 'c' of pixel 'x' from the given row. */
34 component(png_const_bytep row
, png_uint_32 x
, unsigned int c
,
35 unsigned int bit_depth
, unsigned int channels
)
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
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
);
45 row
= (png_const_bytep
)(((const png_byte (*)[8])row
) + bit_offset_hi
);
46 row
+= bit_offset_lo
>> 3;
47 bit_offset_lo
&= 0x07;
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
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];
61 /* This should never happen; it indicates a bug in this program or in
64 fprintf(stderr
, "pngpixel: invalid bit depth %u\n", bit_depth
);
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.
73 print_pixel(png_structp png_ptr
, png_infop info_ptr
, png_const_bytep row
,
76 unsigned int bit_depth
= png_get_bit_depth(png_ptr
, info_ptr
);
78 switch (png_get_color_type(png_ptr
, info_ptr
))
80 case PNG_COLOR_TYPE_GRAY
:
81 printf("GRAY %u\n", component(row
, x
, 0, bit_depth
, 1));
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:
88 case PNG_COLOR_TYPE_PALETTE
:
90 int index
= component(row
, x
, 0, bit_depth
, 1);
91 png_colorp palette
= NULL
;
94 if ((png_get_PLTE(png_ptr
, info_ptr
, &palette
, &num_palette
) &
95 PNG_INFO_PLTE
) && num_palette
> 0 && palette
!= NULL
)
97 png_bytep trans_alpha
= NULL
;
99 if ((png_get_tRNS(png_ptr
, info_ptr
, &trans_alpha
, &num_trans
,
100 NULL
) & PNG_INFO_tRNS
) && num_trans
> 0 &&
102 printf("INDEXED %u = %d %d %d %d\n", index
,
103 palette
[index
].red
, palette
[index
].green
,
105 index
< num_trans
? trans_alpha
[index
] : 255);
107 else /* no transparency */
108 printf("INDEXED %u = %d %d %d\n", index
,
109 palette
[index
].red
, palette
[index
].green
,
110 palette
[index
].blue
);
114 printf("INDEXED %u = invalid index\n", index
);
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));
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));
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));
137 png_error(png_ptr
, "pngpixel: invalid color type");
141 int main(int argc
, const char **argv
)
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:
149 volatile int result
= 1/*fail*/;
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
;
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.
166 png_structp png_ptr
= png_create_read_struct(PNG_LIBPNG_VER_STRING
,
171 png_infop info_ptr
= png_create_info_struct(png_ptr
);
173 if (info_ptr
!= NULL
)
175 /* Declare stack variables to hold pointers to locally allocated
179 /* Initialize the error control buffer: */
180 if (setjmp(png_jmpbuf(png_ptr
)) == 0)
182 png_uint_32 width
, height
;
183 int bit_depth
, color_type
, interlace_method
,
184 compression_method
, filter_method
;
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*).
193 png_init_io(png_ptr
, f
);
195 /* And read the first part of the PNG file - the header and
196 * all the information up to the first pixel.
198 png_read_info(png_ptr
, info_ptr
);
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.
205 row
= png_malloc(png_ptr
, png_get_rowbytes(png_ptr
,
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.
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.
217 if (png_get_IHDR(png_ptr
, info_ptr
, &width
, &height
,
218 &bit_depth
, &color_type
, &interlace_method
,
219 &compression_method
, &filter_method
))
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
228 switch (interlace_method
)
230 case PNG_INTERLACE_NONE
:
234 case PNG_INTERLACE_ADAM7
:
235 passes
= PNG_INTERLACE_ADAM7_PASSES
;
239 png_error(png_ptr
, "pngpixel: unknown interlace");
242 /* Now read the pixels, pass-by-pass, row-by-row: */
243 png_start_read_image(png_ptr
);
245 for (pass
=0; pass
<passes
; ++pass
)
247 png_uint_32 ystart
, xstart
, ystep
, xstep
;
250 if (interlace_method
== PNG_INTERLACE_ADAM7
)
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
259 if (PNG_PASS_COLS(width
, pass
) == 0)
262 /* We need the starting pixel and the offset
263 * between each pixel in this pass; use the macros
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
);
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.
282 for (py
= ystart
; py
< height
; py
+= ystep
)
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
296 png_read_row(png_ptr
, row_tmp
, NULL
);
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:
302 if (y
== py
) for (px
= xstart
, ppx
= 0;
303 px
< width
; px
+= xstep
, ++ppx
) if (x
== px
)
305 /* 'ppx' is the index of the pixel in the row
308 print_pixel(png_ptr
, info_ptr
, row_tmp
, ppx
);
310 /* Now terminate the loops early - we have
311 * found and handled the required data.
318 /* Finally free the temporary buffer: */
321 png_free(png_ptr
, row_tmp
);
325 png_error(png_ptr
, "pngpixel: png_get_IHDR failed");
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:
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
342 png_bytep row_tmp
= row
;
344 png_free(png_ptr
, row_tmp
);
348 png_destroy_info_struct(png_ptr
, &info_ptr
);
352 fprintf(stderr
, "pngpixel: out of memory allocating png_info\n");
354 png_destroy_read_struct(&png_ptr
, NULL
, NULL
);
358 fprintf(stderr
, "pngpixel: out of memory allocating png_struct\n");
362 fprintf(stderr
, "pngpixel: %s: could not open file\n", argv
[3]);
366 /* Wrong number of arguments */
367 fprintf(stderr
, "pngpixel: usage: pngpixel x y png-file\n");
371 #endif /* READ && SEQUENTIAL_READ */