Home | History | Annotate | Download | only in examples
      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. */
     33 static unsigned int
     34 component(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  */
     72 static void
     73 print_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 
    141 int 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