Imported Upstream version 1.6.13
[platform/upstream/libpng.git] / contrib / examples / pngpixel.c
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 /* Return component 'c' of pixel 'x' from the given row. */
31 static unsigned int
32 component(png_const_bytep row, png_uint_32 x, unsigned int c,
33    unsigned int bit_depth, unsigned int channels)
34 {
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
38     * work:
39     */
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);
42
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;
46
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
49     * first, so:
50     */
51    switch (bit_depth)
52    {
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];
58       default:
59          /* This should never happen; it indicates a bug in this program or in
60           * libpng itself:
61           */
62          fprintf(stderr, "pngpixel: invalid bit depth %u\n", bit_depth);
63          exit(1);
64    }
65 }
66
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.
69  */
70 static void
71 print_pixel(png_structp png_ptr, png_infop info_ptr, png_const_bytep row,
72    png_uint_32 x)
73 {
74    PNG_CONST unsigned int bit_depth = png_get_bit_depth(png_ptr, info_ptr);
75
76    switch (png_get_color_type(png_ptr, info_ptr))
77    {
78       case PNG_COLOR_TYPE_GRAY:
79          printf("GRAY %u\n", component(row, x, 0, bit_depth, 1));
80          return;
81
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:
85        */
86       case PNG_COLOR_TYPE_PALETTE:
87          {
88             PNG_CONST unsigned int index = component(row, x, 0, bit_depth, 1);
89             png_colorp palette = NULL;
90             int num_palette = 0;
91
92             if ((png_get_PLTE(png_ptr, info_ptr, &palette, &num_palette) &
93                PNG_INFO_PLTE) && num_palette > 0 && palette != NULL)
94             {
95                png_bytep trans_alpha = NULL;
96                int num_trans = 0;
97                if ((png_get_tRNS(png_ptr, info_ptr, &trans_alpha, &num_trans,
98                   NULL) & PNG_INFO_tRNS) && num_trans > 0 &&
99                   trans_alpha != NULL)
100                   printf("INDEXED %u = %d %d %d %d\n", index,
101                      palette[index].red, palette[index].green,
102                      palette[index].blue,
103                      index < num_trans ? trans_alpha[index] : 255);
104
105                else /* no transparency */
106                   printf("INDEXED %u = %d %d %d\n", index,
107                      palette[index].red, palette[index].green,
108                      palette[index].blue);
109             }
110
111             else
112                printf("INDEXED %u = invalid index\n", index);
113          }
114          return;
115
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));
120          return;
121
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));
125          return;
126
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));
132          return;
133
134       default:
135          png_error(png_ptr, "pngpixel: invalid color type");
136    }
137 }
138
139 int main(int argc, const char **argv)
140 {
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:
146     */
147    volatile int result = 1/*fail*/;
148
149    if (argc == 4)
150    {
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;
155
156       if (f != NULL)
157       {
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.
163           */
164          png_structp png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING,
165             NULL, NULL, NULL);
166
167          if (png_ptr != NULL)
168          {
169             png_infop info_ptr = png_create_info_struct(png_ptr);
170
171             if (info_ptr != NULL)
172             {
173                /* Declare stack variables to hold pointers to locally allocated
174                 * data.
175                 */
176
177                /* Initialize the error control buffer: */
178                if (setjmp(png_jmpbuf(png_ptr)) == 0)
179                {
180                   png_uint_32 width, height;
181                   int bit_depth, color_type, interlace_method,
182                      compression_method, filter_method;
183                   png_bytep row_tmp;
184
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*).
190                    */
191                   png_init_io(png_ptr, f);
192
193                   /* And read the first part of the PNG file - the header and
194                    * all the information up to the first pixel.
195                    */
196                   png_read_info(png_ptr, info_ptr);
197
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.
202                    */
203                   row = png_malloc(png_ptr, png_get_rowbytes(png_ptr,
204                      info_ptr));
205
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.
208                    */
209                   row_tmp = row;
210
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.
214                    */
215                   if (png_get_IHDR(png_ptr, info_ptr, &width, &height,
216                      &bit_depth, &color_type, &interlace_method,
217                      &compression_method, &filter_method))
218                   {
219                      int passes, pass;
220
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
224                       * necessary:
225                       */
226                      switch (interlace_method)
227                      {
228                         case PNG_INTERLACE_NONE:
229                            passes = 1;
230                            break;
231
232                         case PNG_INTERLACE_ADAM7:
233                            passes = PNG_INTERLACE_ADAM7_PASSES;
234                            break;
235
236                         default:
237                            png_error(png_ptr, "pngpixel: unknown interlace");
238                      }
239
240                      /* Now read the pixels, pass-by-pass, row-by-row: */
241                      png_start_read_image(png_ptr);
242
243                      for (pass=0; pass<passes; ++pass)
244                      {
245                         png_uint_32 ystart, xstart, ystep, xstep;
246                         png_uint_32 py;
247
248                         if (interlace_method == PNG_INTERLACE_ADAM7)
249                         {
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
255                             * too narrow.
256                             */
257                            if (PNG_PASS_COLS(width, pass) == 0)
258                               continue;
259
260                            /* We need the starting pixel and the offset
261                             * between each pixel in this pass; use the macros
262                             * in png.h:
263                             */
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);
268                         }
269
270                         else
271                         {
272                            ystart = xstart = 0;
273                            ystep = xstep = 1;
274                         }
275
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.
279                          */
280                         for (py = ystart; py < height; py += ystep)
281                         {
282                            png_uint_32 px, ppx;
283
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
292                             * NULL pointer.
293                             */
294                            png_read_row(png_ptr, row_tmp, NULL);
295
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:
299                             */
300                            if (y == py) for (px = xstart, ppx = 0;
301                               px < width; px += xstep, ++ppx) if (x == px)
302                            {
303                               /* 'ppx' is the index of the pixel in the row
304                                * buffer.
305                                */
306                               print_pixel(png_ptr, info_ptr, row_tmp, ppx);
307
308                               /* Now terminate the loops early - we have
309                                * found and handled the required data.
310                                */
311                               goto pass_loop_end;
312                            } /* x loop */
313                         } /* y loop */
314                      } /* pass loop */
315
316                      /* Finally free the temporary buffer: */
317                   pass_loop_end:
318                      row = NULL;
319                      png_free(png_ptr, row_tmp);
320                   }
321
322                   else
323                      png_error(png_ptr, "pngpixel: png_get_IHDR failed");
324
325                }
326
327                else
328                {
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:
332                    */
333                   if (row != NULL)
334                   {
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
338                       * png_free:
339                       */
340                      png_bytep row_tmp = row;
341                      row = NULL;
342                      png_free(png_ptr, row_tmp);
343                   }
344                }
345
346                png_destroy_info_struct(png_ptr, &info_ptr);
347             }
348
349             else
350                fprintf(stderr, "pngpixel: out of memory allocating png_info\n");
351
352             png_destroy_read_struct(&png_ptr, NULL, NULL);
353          }
354
355          else
356             fprintf(stderr, "pngpixel: out of memory allocating png_struct\n");
357       }
358
359       else
360          fprintf(stderr, "pngpixel: %s: could not open file\n", argv[3]);
361    }
362
363    else
364       /* Wrong number of arguments */
365       fprintf(stderr, "pngpixel: usage: pngpixel x y png-file\n");
366
367    return result;
368 }