(root)/
libpng-1.6.40/
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  #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)(((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     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              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 */