pngpixel.c 15 KB

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