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. */ 33static unsigned int 34component(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)(((PNG_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 */ 72static void 73print_pixel(png_structp png_ptr, png_infop info_ptr, png_const_bytep row, 74 png_uint_32 x) 75{ 76 PNG_CONST 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 PNG_CONST 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 141int 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