(root)/
libpng-1.6.40/
contrib/
gregbook/
readpng2.c
       1  /*---------------------------------------------------------------------------
       2  
       3     rpng2 - progressive-model PNG display program                 readpng2.c
       4  
       5    ---------------------------------------------------------------------------
       6  
       7        Copyright (c) 1998-2015 Greg Roelofs.  All rights reserved.
       8  
       9        This software is provided "as is," without warranty of any kind,
      10        express or implied.  In no event shall the author or contributors
      11        be held liable for any damages arising in any way from the use of
      12        this software.
      13  
      14        The contents of this file are DUAL-LICENSED.  You may modify and/or
      15        redistribute this software according to the terms of one of the
      16        following two licenses (at your option):
      17  
      18  
      19        LICENSE 1 ("BSD-like with advertising clause"):
      20  
      21        Permission is granted to anyone to use this software for any purpose,
      22        including commercial applications, and to alter it and redistribute
      23        it freely, subject to the following restrictions:
      24  
      25        1. Redistributions of source code must retain the above copyright
      26           notice, disclaimer, and this list of conditions.
      27        2. Redistributions in binary form must reproduce the above copyright
      28           notice, disclaimer, and this list of conditions in the documenta-
      29           tion and/or other materials provided with the distribution.
      30        3. All advertising materials mentioning features or use of this
      31           software must display the following acknowledgment:
      32  
      33              This product includes software developed by Greg Roelofs
      34              and contributors for the book, "PNG: The Definitive Guide,"
      35              published by O'Reilly and Associates.
      36  
      37  
      38        LICENSE 2 (GNU GPL v2 or later):
      39  
      40        This program is free software; you can redistribute it and/or modify
      41        it under the terms of the GNU General Public License as published by
      42        the Free Software Foundation; either version 2 of the License, or
      43        (at your option) any later version.
      44  
      45        This program is distributed in the hope that it will be useful,
      46        but WITHOUT ANY WARRANTY; without even the implied warranty of
      47        MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
      48        GNU General Public License for more details.
      49  
      50        You should have received a copy of the GNU General Public License
      51        along with this program; if not, write to the Free Software Foundation,
      52        Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
      53  
      54    ---------------------------------------------------------------------------
      55  
      56     Changelog:
      57       2015-11-12 - Check return value of png_get_bKGD() (Glenn R-P)
      58       2017-04-22 - Guard against integer overflow (Glenn R-P)
      59  
      60    ---------------------------------------------------------------------------*/
      61  
      62  
      63  #include <stdlib.h>     /* for exit() prototype */
      64  #include <setjmp.h>
      65  
      66  #include <zlib.h>
      67  #include "png.h"        /* libpng header from the local directory */
      68  #include "readpng2.h"   /* typedefs, common macros, public prototypes */
      69  
      70  
      71  /* local prototypes */
      72  
      73  static void readpng2_info_callback(png_structp png_ptr, png_infop info_ptr);
      74  static void readpng2_row_callback(png_structp png_ptr, png_bytep new_row,
      75                                   png_uint_32 row_num, int pass);
      76  static void readpng2_end_callback(png_structp png_ptr, png_infop info_ptr);
      77  static void readpng2_error_handler(png_structp png_ptr, png_const_charp msg);
      78  static void readpng2_warning_handler(png_structp png_ptr, png_const_charp msg);
      79  
      80  
      81  
      82  
      83  void readpng2_version_info(void)
      84  {
      85      fprintf(stderr, "   Compiled with libpng %s; using libpng %s\n",
      86        PNG_LIBPNG_VER_STRING, png_libpng_ver);
      87  
      88      fprintf(stderr, "   and with zlib %s; using zlib %s.\n",
      89        ZLIB_VERSION, zlib_version);
      90  }
      91  
      92  
      93  
      94  
      95  int readpng2_check_sig(uch *sig, int num)
      96  {
      97      return !png_sig_cmp(sig, 0, num);
      98  }
      99  
     100  
     101  
     102  
     103  /* returns 0 for success, 2 for libpng problem, 4 for out of memory */
     104  
     105  int readpng2_init(mainprog_info *mainprog_ptr)
     106  {
     107      png_structp  png_ptr;       /* note:  temporary variables! */
     108      png_infop  info_ptr;
     109  
     110  
     111      /* could also replace libpng warning-handler (final NULL), but no need: */
     112  
     113      png_ptr = png_create_read_struct(png_get_libpng_ver(NULL), mainprog_ptr,
     114        readpng2_error_handler, readpng2_warning_handler);
     115      if (!png_ptr)
     116          return 4;   /* out of memory */
     117  
     118      info_ptr = png_create_info_struct(png_ptr);
     119      if (!info_ptr) {
     120          png_destroy_read_struct(&png_ptr, NULL, NULL);
     121          return 4;   /* out of memory */
     122      }
     123  
     124  
     125      /* we could create a second info struct here (end_info), but it's only
     126       * useful if we want to keep pre- and post-IDAT chunk info separated
     127       * (mainly for PNG-aware image editors and converters) */
     128  
     129  
     130      /* setjmp() must be called in every function that calls a PNG-reading
     131       * libpng function, unless an alternate error handler was installed--
     132       * but compatible error handlers must either use longjmp() themselves
     133       * (as in this program) or exit immediately, so here we are: */
     134  
     135      if (setjmp(mainprog_ptr->jmpbuf)) {
     136          png_destroy_read_struct(&png_ptr, &info_ptr, NULL);
     137          return 2;
     138      }
     139  
     140  
     141  #ifdef PNG_HANDLE_AS_UNKNOWN_SUPPORTED
     142      /* prepare the reader to ignore all recognized chunks whose data won't be
     143       * used, i.e., all chunks recognized by libpng except for IHDR, PLTE, IDAT,
     144       * IEND, tRNS, bKGD, gAMA, and sRGB (small performance improvement) */
     145      {
     146          /* These byte strings were copied from png.h.  If a future version
     147           * of readpng2.c recognizes more chunks, add them to this list.
     148           */
     149          static const png_byte chunks_to_process[] = {
     150              98,  75,  71,  68, '\0',  /* bKGD */
     151             103,  65,  77,  65, '\0',  /* gAMA */
     152             115,  82,  71,  66, '\0',  /* sRGB */
     153             };
     154  
     155         /* Ignore all chunks except for IHDR, PLTE, tRNS, IDAT, and IEND */
     156         png_set_keep_unknown_chunks(png_ptr, -1 /* PNG_HANDLE_CHUNK_NEVER */,
     157            NULL, -1);
     158  
     159         /* But do not ignore chunks in the "chunks_to_process" list */
     160         png_set_keep_unknown_chunks(png_ptr,
     161            0 /* PNG_HANDLE_CHUNK_AS_DEFAULT */, chunks_to_process,
     162            sizeof(chunks_to_process)/5);
     163      }
     164  #endif /* PNG_HANDLE_AS_UNKNOWN_SUPPORTED */
     165  
     166  
     167      /* instead of doing png_init_io() here, now we set up our callback
     168       * functions for progressive decoding */
     169  
     170      png_set_progressive_read_fn(png_ptr, mainprog_ptr,
     171        readpng2_info_callback, readpng2_row_callback, readpng2_end_callback);
     172  
     173  
     174      /* make sure we save our pointers for use in readpng2_decode_data() */
     175  
     176      mainprog_ptr->png_ptr = png_ptr;
     177      mainprog_ptr->info_ptr = info_ptr;
     178  
     179  
     180      /* and that's all there is to initialization */
     181  
     182      return 0;
     183  }
     184  
     185  
     186  
     187  
     188  /* returns 0 for success, 2 for libpng (longjmp) problem */
     189  
     190  int readpng2_decode_data(mainprog_info *mainprog_ptr, uch *rawbuf, ulg length)
     191  {
     192      png_structp png_ptr = (png_structp)mainprog_ptr->png_ptr;
     193      png_infop info_ptr = (png_infop)mainprog_ptr->info_ptr;
     194  
     195  
     196      /* setjmp() must be called in every function that calls a PNG-reading
     197       * libpng function */
     198  
     199      if (setjmp(mainprog_ptr->jmpbuf)) {
     200          png_destroy_read_struct(&png_ptr, &info_ptr, NULL);
     201          mainprog_ptr->png_ptr = NULL;
     202          mainprog_ptr->info_ptr = NULL;
     203          return 2;
     204      }
     205  
     206  
     207      /* hand off the next chunk of input data to libpng for decoding */
     208  
     209      png_process_data(png_ptr, info_ptr, rawbuf, length);
     210  
     211      return 0;
     212  }
     213  
     214  
     215  
     216  
     217  static void readpng2_info_callback(png_structp png_ptr, png_infop info_ptr)
     218  {
     219      mainprog_info  *mainprog_ptr;
     220      int  color_type, bit_depth;
     221      png_uint_32 width, height;
     222  #ifdef PNG_FLOATING_POINT_SUPPORTED
     223      double  gamma;
     224  #else
     225      png_fixed_point gamma;
     226  #endif
     227  
     228  
     229      /* setjmp() doesn't make sense here, because we'd either have to exit(),
     230       * longjmp() ourselves, or return control to libpng, which doesn't want
     231       * to see us again.  By not doing anything here, libpng will instead jump
     232       * to readpng2_decode_data(), which can return an error value to the main
     233       * program. */
     234  
     235  
     236      /* retrieve the pointer to our special-purpose struct, using the png_ptr
     237       * that libpng passed back to us (i.e., not a global this time--there's
     238       * no real difference for a single image, but for a multithreaded browser
     239       * decoding several PNG images at the same time, one needs to avoid mixing
     240       * up different images' structs) */
     241  
     242      mainprog_ptr = png_get_progressive_ptr(png_ptr);
     243  
     244      if (mainprog_ptr == NULL) {         /* we be hosed */
     245          fprintf(stderr,
     246            "readpng2 error:  main struct not recoverable in info_callback.\n");
     247          fflush(stderr);
     248          return;
     249          /*
     250           * Alternatively, we could call our error-handler just like libpng
     251           * does, which would effectively terminate the program.  Since this
     252           * can only happen if png_ptr gets redirected somewhere odd or the
     253           * main PNG struct gets wiped, we're probably toast anyway.  (If
     254           * png_ptr itself is NULL, we would not have been called.)
     255           */
     256      }
     257  
     258  
     259      /* this is just like in the non-progressive case */
     260  
     261      png_get_IHDR(png_ptr, info_ptr, &width, &height, &bit_depth, &color_type,
     262         NULL, NULL, NULL);
     263      mainprog_ptr->width = (ulg)width;
     264      mainprog_ptr->height = (ulg)height;
     265  
     266  
     267      /* since we know we've read all of the PNG file's "header" (i.e., up
     268       * to IDAT), we can check for a background color here */
     269  
     270      if (mainprog_ptr->need_bgcolor)
     271      {
     272          png_color_16p pBackground;
     273  
     274          /* it is not obvious from the libpng documentation, but this function
     275           * takes a pointer to a pointer, and it always returns valid red,
     276           * green and blue values, regardless of color_type: */
     277          if (png_get_bKGD(png_ptr, info_ptr, &pBackground))
     278          {
     279  
     280             /* however, it always returns the raw bKGD data, regardless of any
     281              * bit-depth transformations, so check depth and adjust if necessary
     282              */
     283             if (bit_depth == 16) {
     284                 mainprog_ptr->bg_red   = pBackground->red   >> 8;
     285                 mainprog_ptr->bg_green = pBackground->green >> 8;
     286                 mainprog_ptr->bg_blue  = pBackground->blue  >> 8;
     287             } else if (color_type == PNG_COLOR_TYPE_GRAY && bit_depth < 8) {
     288                 if (bit_depth == 1)
     289                     mainprog_ptr->bg_red = mainprog_ptr->bg_green =
     290                       mainprog_ptr->bg_blue = pBackground->gray? 255 : 0;
     291                 else if (bit_depth == 2)
     292                     mainprog_ptr->bg_red = mainprog_ptr->bg_green =
     293                       mainprog_ptr->bg_blue = (255/3) * pBackground->gray;
     294                 else /* bit_depth == 4 */
     295                     mainprog_ptr->bg_red = mainprog_ptr->bg_green =
     296                       mainprog_ptr->bg_blue = (255/15) * pBackground->gray;
     297             } else {
     298                 mainprog_ptr->bg_red   = (uch)pBackground->red;
     299                 mainprog_ptr->bg_green = (uch)pBackground->green;
     300                 mainprog_ptr->bg_blue  = (uch)pBackground->blue;
     301             }
     302          }
     303      }
     304  
     305  
     306      /* as before, let libpng expand palette images to RGB, low-bit-depth
     307       * grayscale images to 8 bits, transparency chunks to full alpha channel;
     308       * strip 16-bit-per-sample images to 8 bits per sample; and convert
     309       * grayscale to RGB[A] */
     310  
     311      if (color_type == PNG_COLOR_TYPE_PALETTE)
     312          png_set_expand(png_ptr);
     313      if (color_type == PNG_COLOR_TYPE_GRAY && bit_depth < 8)
     314          png_set_expand(png_ptr);
     315      if (png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS))
     316          png_set_expand(png_ptr);
     317  #ifdef PNG_READ_16_TO_8_SUPPORTED
     318      if (bit_depth == 16)
     319  #  ifdef PNG_READ_SCALE_16_TO_8_SUPPORTED
     320          png_set_scale_16(png_ptr);
     321  #  else
     322          png_set_strip_16(png_ptr);
     323  #  endif
     324  #endif
     325      if (color_type == PNG_COLOR_TYPE_GRAY ||
     326          color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
     327          png_set_gray_to_rgb(png_ptr);
     328  
     329  
     330      /* Unlike the basic viewer, which was designed to operate on local files,
     331       * this program is intended to simulate a web browser--even though we
     332       * actually read from a local file, too.  But because we are pretending
     333       * that most of the images originate on the Internet, we follow the recom-
     334       * mendation of the sRGB proposal and treat unlabelled images (no gAMA
     335       * chunk) as existing in the sRGB color space.  That is, we assume that
     336       * such images have a file gamma of 0.45455, which corresponds to a PC-like
     337       * display system.  This change in assumptions will have no effect on a
     338       * PC-like system, but on a Mac, SGI, NeXT or other system with a non-
     339       * identity lookup table, it will darken unlabelled images, which effec-
     340       * tively favors images from PC-like systems over those originating on
     341       * the local platform.  Note that mainprog_ptr->display_exponent is the
     342       * "gamma" value for the entire display system, i.e., the product of
     343       * LUT_exponent and CRT_exponent. */
     344  
     345  #ifdef PNG_FLOATING_POINT_SUPPORTED
     346      if (png_get_gAMA(png_ptr, info_ptr, &gamma))
     347          png_set_gamma(png_ptr, mainprog_ptr->display_exponent, gamma);
     348      else
     349          png_set_gamma(png_ptr, mainprog_ptr->display_exponent, 0.45455);
     350  #else
     351      if (png_get_gAMA_fixed(png_ptr, info_ptr, &gamma))
     352          png_set_gamma_fixed(png_ptr,
     353              (png_fixed_point)(100000*mainprog_ptr->display_exponent+.5), gamma);
     354      else
     355          png_set_gamma_fixed(png_ptr,
     356              (png_fixed_point)(100000*mainprog_ptr->display_exponent+.5), 45455);
     357  #endif
     358  
     359      /* we'll let libpng expand interlaced images, too */
     360  
     361      mainprog_ptr->passes = png_set_interlace_handling(png_ptr);
     362  
     363  
     364      /* all transformations have been registered; now update info_ptr data and
     365       * then get rowbytes and channels */
     366  
     367      png_read_update_info(png_ptr, info_ptr);
     368  
     369      mainprog_ptr->rowbytes = (int)png_get_rowbytes(png_ptr, info_ptr);
     370      mainprog_ptr->channels = png_get_channels(png_ptr, info_ptr);
     371  
     372  
     373      /* Call the main program to allocate memory for the image buffer and
     374       * initialize windows and whatnot.  (The old-style function-pointer
     375       * invocation is used for compatibility with a few supposedly ANSI
     376       * compilers that nevertheless barf on "fn_ptr()"-style syntax.) */
     377  
     378      (*mainprog_ptr->mainprog_init)();
     379  
     380  
     381      /* and that takes care of initialization */
     382  
     383      return;
     384  }
     385  
     386  
     387  
     388  
     389  
     390  static void readpng2_row_callback(png_structp png_ptr, png_bytep new_row,
     391                                    png_uint_32 row_num, int pass)
     392  {
     393      mainprog_info  *mainprog_ptr;
     394  
     395  
     396      /* first check whether the row differs from the previous pass; if not,
     397       * nothing to combine or display */
     398  
     399      if (!new_row)
     400          return;
     401  
     402  
     403      /* retrieve the pointer to our special-purpose struct so we can access
     404       * the old rows and image-display callback function */
     405  
     406      mainprog_ptr = png_get_progressive_ptr(png_ptr);
     407  
     408  
     409      /* save the pass number for optional use by the front end */
     410  
     411      mainprog_ptr->pass = pass;
     412  
     413  
     414      /* have libpng either combine the new row data with the existing row data
     415       * from previous passes (if interlaced) or else just copy the new row
     416       * into the main program's image buffer */
     417  
     418      png_progressive_combine_row(png_ptr, mainprog_ptr->row_pointers[row_num],
     419        new_row);
     420  
     421  
     422      /* finally, call the display routine in the main program with the number
     423       * of the row we just updated */
     424  
     425      (*mainprog_ptr->mainprog_display_row)(row_num);
     426  
     427  
     428      /* and we're ready for more */
     429  
     430      return;
     431  }
     432  
     433  
     434  
     435  
     436  
     437  static void readpng2_end_callback(png_structp png_ptr, png_infop info_ptr)
     438  {
     439      mainprog_info  *mainprog_ptr;
     440  
     441  
     442      /* retrieve the pointer to our special-purpose struct */
     443  
     444      mainprog_ptr = png_get_progressive_ptr(png_ptr);
     445  
     446  
     447      /* let the main program know that it should flush any buffered image
     448       * data to the display now and set a "done" flag or whatever, but note
     449       * that it SHOULD NOT DESTROY THE PNG STRUCTS YET--in other words, do
     450       * NOT call readpng2_cleanup() either here or in the finish_display()
     451       * routine; wait until control returns to the main program via
     452       * readpng2_decode_data() */
     453  
     454      (*mainprog_ptr->mainprog_finish_display)();
     455  
     456  
     457      /* all done */
     458  
     459      (void)info_ptr; /* Unused */
     460  
     461      return;
     462  }
     463  
     464  
     465  
     466  
     467  
     468  void readpng2_cleanup(mainprog_info *mainprog_ptr)
     469  {
     470      png_structp png_ptr = (png_structp)mainprog_ptr->png_ptr;
     471      png_infop info_ptr = (png_infop)mainprog_ptr->info_ptr;
     472  
     473      if (png_ptr && info_ptr)
     474          png_destroy_read_struct(&png_ptr, &info_ptr, NULL);
     475  
     476      mainprog_ptr->png_ptr = NULL;
     477      mainprog_ptr->info_ptr = NULL;
     478  }
     479  
     480  
     481  static void readpng2_warning_handler(png_structp png_ptr, png_const_charp msg)
     482  {
     483      fprintf(stderr, "readpng2 libpng warning: %s\n", msg);
     484      fflush(stderr);
     485      (void)png_ptr; /* Unused */
     486  }
     487  
     488  
     489  static void readpng2_error_handler(png_structp png_ptr, png_const_charp msg)
     490  {
     491      mainprog_info  *mainprog_ptr;
     492  
     493      /* This function, aside from the extra step of retrieving the "error
     494       * pointer" (below) and the fact that it exists within the application
     495       * rather than within libpng, is essentially identical to libpng's
     496       * default error handler.  The second point is critical:  since both
     497       * setjmp() and longjmp() are called from the same code, they are
     498       * guaranteed to have compatible notions of how big a jmp_buf is,
     499       * regardless of whether _BSD_SOURCE or anything else has (or has not)
     500       * been defined. */
     501  
     502      fprintf(stderr, "readpng2 libpng error: %s\n", msg);
     503      fflush(stderr);
     504  
     505      mainprog_ptr = png_get_error_ptr(png_ptr);
     506      if (mainprog_ptr == NULL) {         /* we are completely hosed now */
     507          fprintf(stderr,
     508            "readpng2 severe error:  jmpbuf not recoverable; terminating.\n");
     509          fflush(stderr);
     510          exit(99);
     511      }
     512  
     513      /* Now we have our data structure we can use the information in it
     514       * to return control to our own higher level code (all the points
     515       * where 'setjmp' is called in this file.)  This will work with other
     516       * error handling mechanisms as well - libpng always calls png_error
     517       * when it can proceed no further, thus, so long as the error handler
     518       * is intercepted, application code can do its own error recovery.
     519       */
     520      longjmp(mainprog_ptr->jmpbuf, 1);
     521  }