pngpixel.c 15 KB

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