Blame contrib/gregbook/readpng2.c

Packit 0ba690
/*---------------------------------------------------------------------------
Packit 0ba690
Packit 0ba690
   rpng2 - progressive-model PNG display program                 readpng2.c
Packit 0ba690
Packit 0ba690
  ---------------------------------------------------------------------------
Packit 0ba690
Packit 0ba690
      Copyright (c) 1998-2007 Greg Roelofs.  All rights reserved.
Packit 0ba690
Packit 0ba690
      This software is provided "as is," without warranty of any kind,
Packit 0ba690
      express or implied.  In no event shall the author or contributors
Packit 0ba690
      be held liable for any damages arising in any way from the use of
Packit 0ba690
      this software.
Packit 0ba690
Packit 0ba690
      The contents of this file are DUAL-LICENSED.  You may modify and/or
Packit 0ba690
      redistribute this software according to the terms of one of the
Packit 0ba690
      following two licenses (at your option):
Packit 0ba690
Packit 0ba690
Packit 0ba690
      LICENSE 1 ("BSD-like with advertising clause"):
Packit 0ba690
Packit 0ba690
      Permission is granted to anyone to use this software for any purpose,
Packit 0ba690
      including commercial applications, and to alter it and redistribute
Packit 0ba690
      it freely, subject to the following restrictions:
Packit 0ba690
Packit 0ba690
      1. Redistributions of source code must retain the above copyright
Packit 0ba690
         notice, disclaimer, and this list of conditions.
Packit 0ba690
      2. Redistributions in binary form must reproduce the above copyright
Packit 0ba690
         notice, disclaimer, and this list of conditions in the documenta-
Packit 0ba690
         tion and/or other materials provided with the distribution.
Packit 0ba690
      3. All advertising materials mentioning features or use of this
Packit 0ba690
         software must display the following acknowledgment:
Packit 0ba690
Packit 0ba690
            This product includes software developed by Greg Roelofs
Packit 0ba690
            and contributors for the book, "PNG: The Definitive Guide,"
Packit 0ba690
            published by O'Reilly and Associates.
Packit 0ba690
Packit 0ba690
Packit 0ba690
      LICENSE 2 (GNU GPL v2 or later):
Packit 0ba690
Packit 0ba690
      This program is free software; you can redistribute it and/or modify
Packit 0ba690
      it under the terms of the GNU General Public License as published by
Packit 0ba690
      the Free Software Foundation; either version 2 of the License, or
Packit 0ba690
      (at your option) any later version.
Packit 0ba690
Packit 0ba690
      This program is distributed in the hope that it will be useful,
Packit 0ba690
      but WITHOUT ANY WARRANTY; without even the implied warranty of
Packit 0ba690
      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
Packit 0ba690
      GNU General Public License for more details.
Packit 0ba690
Packit 0ba690
      You should have received a copy of the GNU General Public License
Packit 0ba690
      along with this program; if not, write to the Free Software Foundation,
Packit 0ba690
      Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
Packit 0ba690
Packit 0ba690
  ---------------------------------------------------------------------------*/
Packit 0ba690
Packit 0ba690
Packit 0ba690
#include <stdlib.h>     /* for exit() prototype */
Packit 0ba690
Packit 0ba690
#include "png.h"        /* libpng header; includes zlib.h and setjmp.h */
Packit 0ba690
#include "readpng2.h"   /* typedefs, common macros, public prototypes */
Packit 0ba690
Packit 0ba690
Packit 0ba690
/* local prototypes */
Packit 0ba690
Packit 0ba690
static void readpng2_info_callback(png_structp png_ptr, png_infop info_ptr);
Packit 0ba690
static void readpng2_row_callback(png_structp png_ptr, png_bytep new_row,
Packit 0ba690
                                 png_uint_32 row_num, int pass);
Packit 0ba690
static void readpng2_end_callback(png_structp png_ptr, png_infop info_ptr);
Packit 0ba690
static void readpng2_error_handler(png_structp png_ptr, png_const_charp msg);
Packit 0ba690
Packit 0ba690
Packit 0ba690
Packit 0ba690
Packit 0ba690
void readpng2_version_info(void)
Packit 0ba690
{
Packit 0ba690
#if defined(PNG_ASSEMBLER_CODE_SUPPORTED) && \
Packit 0ba690
    (defined(__i386__) || defined(_M_IX86) || defined(__x86_64__)) && \
Packit 0ba690
    defined(PNG_LIBPNG_VER) && (PNG_LIBPNG_VER >= 10200)
Packit 0ba690
    /*
Packit 0ba690
     * WARNING:  This preprocessor approach means that the following code
Packit 0ba690
     *           cannot be used with a libpng DLL older than 1.2.0--the
Packit 0ba690
     *           compiled-in symbols for the new functions will not exist.
Packit 0ba690
     *           (Could use dlopen() and dlsym() on Unix and corresponding
Packit 0ba690
     *           calls for Windows, but not portable...)
Packit 0ba690
     */
Packit 0ba690
    {
Packit 0ba690
        int mmxsupport = png_mmx_support();
Packit 0ba690
        if (mmxsupport < 0)
Packit 0ba690
            fprintf(stderr, "   Compiled with libpng %s; using libpng %s "
Packit 0ba690
              "without MMX support.\n", PNG_LIBPNG_VER_STRING, png_libpng_ver);
Packit 0ba690
        else {
Packit 0ba690
            int compilerID;
Packit 0ba690
            png_uint_32 mmx_mask = png_get_mmx_flagmask(
Packit 0ba690
              PNG_SELECT_READ | PNG_SELECT_WRITE, &compilerID);
Packit 0ba690
Packit 0ba690
            fprintf(stderr, "   Compiled with libpng %s; using libpng %s "
Packit 0ba690
              "with MMX support\n   (%s version).", PNG_LIBPNG_VER_STRING,
Packit 0ba690
              png_libpng_ver, compilerID == 1? "MSVC++" :
Packit 0ba690
              (compilerID == 2? "GNU C" : "unknown"));
Packit 0ba690
            fprintf(stderr, "  Processor (x86%s) %s MMX instructions.\n",
Packit 0ba690
#if defined(__x86_64__)
Packit 0ba690
              "_64",
Packit 0ba690
#else
Packit 0ba690
              "",
Packit 0ba690
#endif
Packit 0ba690
              mmxsupport? "supports" : "does not support");
Packit 0ba690
            if (mmxsupport > 0) {
Packit 0ba690
                int num_optims = 0;
Packit 0ba690
Packit 0ba690
                fprintf(stderr,
Packit 0ba690
                  "      Potential MMX optimizations supported by libpng:\n");
Packit 0ba690
                if (mmx_mask & PNG_ASM_FLAG_MMX_READ_FILTER_SUB)
Packit 0ba690
                    ++num_optims;
Packit 0ba690
                if (mmx_mask & PNG_ASM_FLAG_MMX_READ_FILTER_UP)
Packit 0ba690
                    ++num_optims;
Packit 0ba690
                if (mmx_mask & PNG_ASM_FLAG_MMX_READ_FILTER_AVG)
Packit 0ba690
                    ++num_optims;
Packit 0ba690
                if (mmx_mask & PNG_ASM_FLAG_MMX_READ_FILTER_PAETH)
Packit 0ba690
                    ++num_optims;
Packit 0ba690
                if (num_optims)
Packit 0ba690
                    fprintf(stderr,
Packit 0ba690
                      "         decoding %s row filters (reading)\n",
Packit 0ba690
                      (num_optims == 4)? "all non-trivial" : "some");
Packit 0ba690
                if (mmx_mask & PNG_ASM_FLAG_MMX_READ_COMBINE_ROW) {
Packit 0ba690
                    fprintf(stderr, "         combining rows (reading)\n");
Packit 0ba690
                    ++num_optims;
Packit 0ba690
                }
Packit 0ba690
                if (mmx_mask & PNG_ASM_FLAG_MMX_READ_INTERLACE) {
Packit 0ba690
                    fprintf(stderr,
Packit 0ba690
                      "         expanding interlacing (reading)\n");
Packit 0ba690
                    ++num_optims;
Packit 0ba690
                }
Packit 0ba690
                mmx_mask &= ~( PNG_ASM_FLAG_MMX_READ_COMBINE_ROW  \
Packit 0ba690
                             | PNG_ASM_FLAG_MMX_READ_INTERLACE    \
Packit 0ba690
                             | PNG_ASM_FLAG_MMX_READ_FILTER_SUB   \
Packit 0ba690
                             | PNG_ASM_FLAG_MMX_READ_FILTER_UP    \
Packit 0ba690
                             | PNG_ASM_FLAG_MMX_READ_FILTER_AVG   \
Packit 0ba690
                             | PNG_ASM_FLAG_MMX_READ_FILTER_PAETH );
Packit 0ba690
                if (mmx_mask) {
Packit 0ba690
                    fprintf(stderr, "         other (unknown)\n");
Packit 0ba690
                    ++num_optims;
Packit 0ba690
                }
Packit 0ba690
                if (num_optims == 0)
Packit 0ba690
                    fprintf(stderr, "         (none)\n");
Packit 0ba690
            }
Packit 0ba690
        }
Packit 0ba690
    }
Packit 0ba690
#else
Packit 0ba690
    fprintf(stderr, "   Compiled with libpng %s; using libpng %s "
Packit 0ba690
      "without MMX support.\n", PNG_LIBPNG_VER_STRING, png_libpng_ver);
Packit 0ba690
#endif
Packit 0ba690
Packit 0ba690
    fprintf(stderr, "   Compiled with zlib %s; using zlib %s.\n",
Packit 0ba690
      ZLIB_VERSION, zlib_version);
Packit 0ba690
}
Packit 0ba690
Packit 0ba690
Packit 0ba690
Packit 0ba690
Packit 0ba690
int readpng2_check_sig(uch *sig, int num)
Packit 0ba690
{
Packit 0ba690
    return !png_sig_cmp(sig, 0, num);
Packit 0ba690
}
Packit 0ba690
Packit 0ba690
Packit 0ba690
Packit 0ba690
Packit 0ba690
/* returns 0 for success, 2 for libpng problem, 4 for out of memory */
Packit 0ba690
Packit 0ba690
int readpng2_init(mainprog_info *mainprog_ptr)
Packit 0ba690
{
Packit 0ba690
    png_structp  png_ptr;       /* note:  temporary variables! */
Packit 0ba690
    png_infop  info_ptr;
Packit 0ba690
Packit 0ba690
Packit 0ba690
    /* could also replace libpng warning-handler (final NULL), but no need: */
Packit 0ba690
Packit 0ba690
    png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, mainprog_ptr,
Packit 0ba690
      readpng2_error_handler, NULL);
Packit 0ba690
    if (!png_ptr)
Packit 0ba690
        return 4;   /* out of memory */
Packit 0ba690
Packit 0ba690
    info_ptr = png_create_info_struct(png_ptr);
Packit 0ba690
    if (!info_ptr) {
Packit 0ba690
        png_destroy_read_struct(&png_ptr, NULL, NULL);
Packit 0ba690
        return 4;   /* out of memory */
Packit 0ba690
    }
Packit 0ba690
Packit 0ba690
Packit 0ba690
    /* we could create a second info struct here (end_info), but it's only
Packit 0ba690
     * useful if we want to keep pre- and post-IDAT chunk info separated
Packit 0ba690
     * (mainly for PNG-aware image editors and converters) */
Packit 0ba690
Packit 0ba690
Packit 0ba690
    /* setjmp() must be called in every function that calls a PNG-reading
Packit 0ba690
     * libpng function, unless an alternate error handler was installed--
Packit 0ba690
     * but compatible error handlers must either use longjmp() themselves
Packit 0ba690
     * (as in this program) or exit immediately, so here we are: */
Packit 0ba690
Packit 0ba690
    if (setjmp(mainprog_ptr->jmpbuf)) {
Packit 0ba690
        png_destroy_read_struct(&png_ptr, &info_ptr, NULL);
Packit 0ba690
        return 2;
Packit 0ba690
    }
Packit 0ba690
Packit 0ba690
Packit 0ba690
#ifdef PNG_UNKNOWN_CHUNKS_SUPPORTED
Packit 0ba690
    /* prepare the reader to ignore all recognized chunks whose data won't be
Packit 0ba690
     * used, i.e., all chunks recognized by libpng except for IHDR, PLTE, IDAT,
Packit 0ba690
     * IEND, tRNS, bKGD, gAMA, and sRGB (small performance improvement) */
Packit 0ba690
    {
Packit 0ba690
        /* These byte strings were copied from png.h.  If a future libpng
Packit 0ba690
         * version recognizes more chunks, add them to this list.  If a
Packit 0ba690
         * future version of readpng2.c recognizes more chunks, delete them
Packit 0ba690
         * from this list. */
Packit 0ba690
        static const png_byte chunks_to_ignore[] = {
Packit 0ba690
             99,  72,  82,  77, '\0',  /* cHRM */
Packit 0ba690
            104,  73,  83,  84, '\0',  /* hIST */
Packit 0ba690
            105,  67,  67,  80, '\0',  /* iCCP */
Packit 0ba690
            105,  84,  88, 116, '\0',  /* iTXt */
Packit 0ba690
            111,  70,  70, 115, '\0',  /* oFFs */
Packit 0ba690
            112,  67,  65,  76, '\0',  /* pCAL */
Packit 0ba690
            112,  72,  89, 115, '\0',  /* pHYs */
Packit 0ba690
            115,  66,  73,  84, '\0',  /* sBIT */
Packit 0ba690
            115,  67,  65,  76, '\0',  /* sCAL */
Packit 0ba690
            115,  80,  76,  84, '\0',  /* sPLT */
Packit 0ba690
            115,  84,  69,  82, '\0',  /* sTER */
Packit 0ba690
            116,  69,  88, 116, '\0',  /* tEXt */
Packit 0ba690
            116,  73,  77,  69, '\0',  /* tIME */
Packit 0ba690
            122,  84,  88, 116, '\0'   /* zTXt */
Packit 0ba690
        };
Packit 0ba690
Packit 0ba690
        png_set_keep_unknown_chunks(png_ptr, 1 /* PNG_HANDLE_CHUNK_NEVER */,
Packit 0ba690
          chunks_to_ignore, sizeof(chunks_to_ignore)/5);
Packit 0ba690
    }
Packit 0ba690
#endif /* PNG_UNKNOWN_CHUNKS_SUPPORTED */
Packit 0ba690
Packit 0ba690
Packit 0ba690
    /* instead of doing png_init_io() here, now we set up our callback
Packit 0ba690
     * functions for progressive decoding */
Packit 0ba690
Packit 0ba690
    png_set_progressive_read_fn(png_ptr, mainprog_ptr,
Packit 0ba690
      readpng2_info_callback, readpng2_row_callback, readpng2_end_callback);
Packit 0ba690
Packit 0ba690
Packit 0ba690
    /*
Packit 0ba690
     * may as well enable or disable MMX routines here, if supported;
Packit 0ba690
     *
Packit 0ba690
     * to enable all:  mask = png_get_mmx_flagmask (
Packit 0ba690
     *                   PNG_SELECT_READ | PNG_SELECT_WRITE, &compilerID);
Packit 0ba690
     *                 flags = png_get_asm_flags (png_ptr);
Packit 0ba690
     *                 flags |= mask;
Packit 0ba690
     *                 png_set_asm_flags (png_ptr, flags);
Packit 0ba690
     *
Packit 0ba690
     * to disable all:  mask = png_get_mmx_flagmask (
Packit 0ba690
     *                   PNG_SELECT_READ | PNG_SELECT_WRITE, &compilerID);
Packit 0ba690
     *                  flags = png_get_asm_flags (png_ptr);
Packit 0ba690
     *                  flags &= ~mask;
Packit 0ba690
     *                  png_set_asm_flags (png_ptr, flags);
Packit 0ba690
     */
Packit 0ba690
Packit 0ba690
#if (defined(__i386__) || defined(_M_IX86) || defined(__x86_64__)) && \
Packit 0ba690
    defined(PNG_LIBPNG_VER) && (PNG_LIBPNG_VER >= 10200)
Packit 0ba690
    /*
Packit 0ba690
     * WARNING:  This preprocessor approach means that the following code
Packit 0ba690
     *           cannot be used with a libpng DLL older than 1.2.0--the
Packit 0ba690
     *           compiled-in symbols for the new functions will not exist.
Packit 0ba690
     *           (Could use dlopen() and dlsym() on Unix and corresponding
Packit 0ba690
     *           calls for Windows, but not portable...)
Packit 0ba690
     */
Packit 0ba690
    {
Packit 0ba690
#ifdef PNG_ASSEMBLER_CODE_SUPPORTED
Packit 0ba690
        png_uint_32 mmx_disable_mask = 0;
Packit 0ba690
        png_uint_32 asm_flags, mmx_mask;
Packit 0ba690
        int compilerID;
Packit 0ba690
Packit 0ba690
        if (mainprog_ptr->nommxfilters)
Packit 0ba690
            mmx_disable_mask |= ( PNG_ASM_FLAG_MMX_READ_FILTER_SUB   \
Packit 0ba690
                                | PNG_ASM_FLAG_MMX_READ_FILTER_UP    \
Packit 0ba690
                                | PNG_ASM_FLAG_MMX_READ_FILTER_AVG   \
Packit 0ba690
                                | PNG_ASM_FLAG_MMX_READ_FILTER_PAETH );
Packit 0ba690
        if (mainprog_ptr->nommxcombine)
Packit 0ba690
            mmx_disable_mask |= PNG_ASM_FLAG_MMX_READ_COMBINE_ROW;
Packit 0ba690
        if (mainprog_ptr->nommxinterlace)
Packit 0ba690
            mmx_disable_mask |= PNG_ASM_FLAG_MMX_READ_INTERLACE;
Packit 0ba690
        asm_flags = png_get_asm_flags(png_ptr);
Packit 0ba690
        png_set_asm_flags(png_ptr, asm_flags & ~mmx_disable_mask);
Packit 0ba690
Packit 0ba690
Packit 0ba690
        /* Now query libpng's asm settings, just for yuks.  Note that this
Packit 0ba690
         * differs from the querying of its *potential* MMX capabilities
Packit 0ba690
         * in readpng2_version_info(); this is true runtime verification. */
Packit 0ba690
Packit 0ba690
        asm_flags = png_get_asm_flags(png_ptr);
Packit 0ba690
        mmx_mask = png_get_mmx_flagmask(PNG_SELECT_READ | PNG_SELECT_WRITE,
Packit 0ba690
          &compilerID);
Packit 0ba690
        if (asm_flags & PNG_ASM_FLAG_MMX_SUPPORT_COMPILED)
Packit 0ba690
            fprintf(stderr,
Packit 0ba690
              "  MMX support (%s version) is compiled into libpng\n",
Packit 0ba690
              compilerID == 1? "MSVC++" :
Packit 0ba690
              (compilerID == 2? "GNU C" : "unknown"));
Packit 0ba690
        else
Packit 0ba690
            fprintf(stderr, "  MMX support is not compiled into libpng\n");
Packit 0ba690
        fprintf(stderr, "  MMX instructions are %ssupported by CPU\n",
Packit 0ba690
          (asm_flags & PNG_ASM_FLAG_MMX_SUPPORT_IN_CPU)? "" : "not ");
Packit 0ba690
        fprintf(stderr, "  MMX read support for combining rows is %sabled\n",
Packit 0ba690
          (asm_flags & PNG_ASM_FLAG_MMX_READ_COMBINE_ROW)? "en" : "dis");
Packit 0ba690
        fprintf(stderr,
Packit 0ba690
          "  MMX read support for expanding interlacing is %sabled\n",
Packit 0ba690
          (asm_flags & PNG_ASM_FLAG_MMX_READ_INTERLACE)? "en" : "dis");
Packit 0ba690
        fprintf(stderr, "  MMX read support for \"sub\" filter is %sabled\n",
Packit 0ba690
          (asm_flags & PNG_ASM_FLAG_MMX_READ_FILTER_SUB)? "en" : "dis");
Packit 0ba690
        fprintf(stderr, "  MMX read support for \"up\" filter is %sabled\n",
Packit 0ba690
          (asm_flags & PNG_ASM_FLAG_MMX_READ_FILTER_UP)? "en" : "dis");
Packit 0ba690
        fprintf(stderr, "  MMX read support for \"avg\" filter is %sabled\n",
Packit 0ba690
          (asm_flags & PNG_ASM_FLAG_MMX_READ_FILTER_AVG)? "en" : "dis");
Packit 0ba690
        fprintf(stderr, "  MMX read support for \"Paeth\" filter is %sabled\n",
Packit 0ba690
          (asm_flags & PNG_ASM_FLAG_MMX_READ_FILTER_PAETH)? "en" : "dis");
Packit 0ba690
        asm_flags &= (mmx_mask & ~( PNG_ASM_FLAG_MMX_READ_COMBINE_ROW  \
Packit 0ba690
                                  | PNG_ASM_FLAG_MMX_READ_INTERLACE    \
Packit 0ba690
                                  | PNG_ASM_FLAG_MMX_READ_FILTER_SUB   \
Packit 0ba690
                                  | PNG_ASM_FLAG_MMX_READ_FILTER_UP    \
Packit 0ba690
                                  | PNG_ASM_FLAG_MMX_READ_FILTER_AVG   \
Packit 0ba690
                                  | PNG_ASM_FLAG_MMX_READ_FILTER_PAETH ));
Packit 0ba690
        if (asm_flags)
Packit 0ba690
            fprintf(stderr,
Packit 0ba690
              "  additional MMX support is also enabled (0x%02lx)\n",
Packit 0ba690
              asm_flags);
Packit 0ba690
#else  /* !PNG_ASSEMBLER_CODE_SUPPORTED */
Packit 0ba690
        fprintf(stderr, "  MMX querying is disabled in libpng.\n");
Packit 0ba690
#endif /* ?PNG_ASSEMBLER_CODE_SUPPORTED */
Packit 0ba690
    }
Packit 0ba690
#endif
Packit 0ba690
Packit 0ba690
Packit 0ba690
    /* make sure we save our pointers for use in readpng2_decode_data() */
Packit 0ba690
Packit 0ba690
    mainprog_ptr->png_ptr = png_ptr;
Packit 0ba690
    mainprog_ptr->info_ptr = info_ptr;
Packit 0ba690
Packit 0ba690
Packit 0ba690
    /* and that's all there is to initialization */
Packit 0ba690
Packit 0ba690
    return 0;
Packit 0ba690
}
Packit 0ba690
Packit 0ba690
Packit 0ba690
Packit 0ba690
Packit 0ba690
/* returns 0 for success, 2 for libpng (longjmp) problem */
Packit 0ba690
Packit 0ba690
int readpng2_decode_data(mainprog_info *mainprog_ptr, uch *rawbuf, ulg length)
Packit 0ba690
{
Packit 0ba690
    png_structp png_ptr = (png_structp)mainprog_ptr->png_ptr;
Packit 0ba690
    png_infop info_ptr = (png_infop)mainprog_ptr->info_ptr;
Packit 0ba690
Packit 0ba690
Packit 0ba690
    /* setjmp() must be called in every function that calls a PNG-reading
Packit 0ba690
     * libpng function */
Packit 0ba690
Packit 0ba690
    if (setjmp(mainprog_ptr->jmpbuf)) {
Packit 0ba690
        png_destroy_read_struct(&png_ptr, &info_ptr, NULL);
Packit 0ba690
        mainprog_ptr->png_ptr = NULL;
Packit 0ba690
        mainprog_ptr->info_ptr = NULL;
Packit 0ba690
        return 2;
Packit 0ba690
    }
Packit 0ba690
Packit 0ba690
Packit 0ba690
    /* hand off the next chunk of input data to libpng for decoding */
Packit 0ba690
Packit 0ba690
    png_process_data(png_ptr, info_ptr, rawbuf, length);
Packit 0ba690
Packit 0ba690
    return 0;
Packit 0ba690
}
Packit 0ba690
Packit 0ba690
Packit 0ba690
Packit 0ba690
Packit 0ba690
static void readpng2_info_callback(png_structp png_ptr, png_infop info_ptr)
Packit 0ba690
{
Packit 0ba690
    mainprog_info  *mainprog_ptr;
Packit 0ba690
    int  color_type, bit_depth;
Packit 0ba690
    png_uint_32 width, height;
Packit 0ba690
    double  gamma;
Packit 0ba690
Packit 0ba690
Packit 0ba690
    /* setjmp() doesn't make sense here, because we'd either have to exit(),
Packit 0ba690
     * longjmp() ourselves, or return control to libpng, which doesn't want
Packit 0ba690
     * to see us again.  By not doing anything here, libpng will instead jump
Packit 0ba690
     * to readpng2_decode_data(), which can return an error value to the main
Packit 0ba690
     * program. */
Packit 0ba690
Packit 0ba690
Packit 0ba690
    /* retrieve the pointer to our special-purpose struct, using the png_ptr
Packit 0ba690
     * that libpng passed back to us (i.e., not a global this time--there's
Packit 0ba690
     * no real difference for a single image, but for a multithreaded browser
Packit 0ba690
     * decoding several PNG images at the same time, one needs to avoid mixing
Packit 0ba690
     * up different images' structs) */
Packit 0ba690
Packit 0ba690
    mainprog_ptr = png_get_progressive_ptr(png_ptr);
Packit 0ba690
Packit 0ba690
    if (mainprog_ptr == NULL) {         /* we be hosed */
Packit 0ba690
        fprintf(stderr,
Packit 0ba690
          "readpng2 error:  main struct not recoverable in info_callback.\n");
Packit 0ba690
        fflush(stderr);
Packit 0ba690
        return;
Packit 0ba690
        /*
Packit 0ba690
         * Alternatively, we could call our error-handler just like libpng
Packit 0ba690
         * does, which would effectively terminate the program.  Since this
Packit 0ba690
         * can only happen if png_ptr gets redirected somewhere odd or the
Packit 0ba690
         * main PNG struct gets wiped, we're probably toast anyway.  (If
Packit 0ba690
         * png_ptr itself is NULL, we would not have been called.)
Packit 0ba690
         */
Packit 0ba690
    }
Packit 0ba690
Packit 0ba690
Packit 0ba690
    /* this is just like in the non-progressive case */
Packit 0ba690
Packit 0ba690
    png_get_IHDR(png_ptr, info_ptr, &width, &height, &bit_depth, &color_type,
Packit 0ba690
       NULL, NULL, NULL);
Packit 0ba690
    mainprog_ptr->width = (ulg)width;
Packit 0ba690
    mainprog_ptr->height = (ulg)height;
Packit 0ba690
Packit 0ba690
Packit 0ba690
    /* since we know we've read all of the PNG file's "header" (i.e., up
Packit 0ba690
     * to IDAT), we can check for a background color here */
Packit 0ba690
Packit 0ba690
    if (mainprog_ptr->need_bgcolor &&
Packit 0ba690
        png_get_valid(png_ptr, info_ptr, PNG_INFO_bKGD))
Packit 0ba690
    {
Packit 0ba690
        png_color_16p pBackground;
Packit 0ba690
Packit 0ba690
        /* it is not obvious from the libpng documentation, but this function
Packit 0ba690
         * takes a pointer to a pointer, and it always returns valid red,
Packit 0ba690
         * green and blue values, regardless of color_type: */
Packit 0ba690
        png_get_bKGD(png_ptr, info_ptr, &pBackground);
Packit 0ba690
Packit 0ba690
        /* however, it always returns the raw bKGD data, regardless of any
Packit 0ba690
         * bit-depth transformations, so check depth and adjust if necessary */
Packit 0ba690
        if (bit_depth == 16) {
Packit 0ba690
            mainprog_ptr->bg_red   = pBackground->red   >> 8;
Packit 0ba690
            mainprog_ptr->bg_green = pBackground->green >> 8;
Packit 0ba690
            mainprog_ptr->bg_blue  = pBackground->blue  >> 8;
Packit 0ba690
        } else if (color_type == PNG_COLOR_TYPE_GRAY && bit_depth < 8) {
Packit 0ba690
            if (bit_depth == 1)
Packit 0ba690
                mainprog_ptr->bg_red = mainprog_ptr->bg_green =
Packit 0ba690
                  mainprog_ptr->bg_blue = pBackground->gray? 255 : 0;
Packit 0ba690
            else if (bit_depth == 2)
Packit 0ba690
                mainprog_ptr->bg_red = mainprog_ptr->bg_green =
Packit 0ba690
                  mainprog_ptr->bg_blue = (255/3) * pBackground->gray;
Packit 0ba690
            else /* bit_depth == 4 */
Packit 0ba690
                mainprog_ptr->bg_red = mainprog_ptr->bg_green =
Packit 0ba690
                  mainprog_ptr->bg_blue = (255/15) * pBackground->gray;
Packit 0ba690
        } else {
Packit 0ba690
            mainprog_ptr->bg_red   = (uch)pBackground->red;
Packit 0ba690
            mainprog_ptr->bg_green = (uch)pBackground->green;
Packit 0ba690
            mainprog_ptr->bg_blue  = (uch)pBackground->blue;
Packit 0ba690
        }
Packit 0ba690
    }
Packit 0ba690
Packit 0ba690
Packit 0ba690
    /* as before, let libpng expand palette images to RGB, low-bit-depth
Packit 0ba690
     * grayscale images to 8 bits, transparency chunks to full alpha channel;
Packit 0ba690
     * strip 16-bit-per-sample images to 8 bits per sample; and convert
Packit 0ba690
     * grayscale to RGB[A] */
Packit 0ba690
Packit 0ba690
    if (color_type == PNG_COLOR_TYPE_PALETTE)
Packit 0ba690
        png_set_expand(png_ptr);
Packit 0ba690
    if (color_type == PNG_COLOR_TYPE_GRAY && bit_depth < 8)
Packit 0ba690
        png_set_expand(png_ptr);
Packit 0ba690
    if (png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS))
Packit 0ba690
        png_set_expand(png_ptr);
Packit 0ba690
    if (bit_depth == 16)
Packit 0ba690
        png_set_strip_16(png_ptr);
Packit 0ba690
    if (color_type == PNG_COLOR_TYPE_GRAY ||
Packit 0ba690
        color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
Packit 0ba690
        png_set_gray_to_rgb(png_ptr);
Packit 0ba690
Packit 0ba690
Packit 0ba690
    /* Unlike the basic viewer, which was designed to operate on local files,
Packit 0ba690
     * this program is intended to simulate a web browser--even though we
Packit 0ba690
     * actually read from a local file, too.  But because we are pretending
Packit 0ba690
     * that most of the images originate on the Internet, we follow the recom-
Packit 0ba690
     * mendation of the sRGB proposal and treat unlabelled images (no gAMA
Packit 0ba690
     * chunk) as existing in the sRGB color space.  That is, we assume that
Packit 0ba690
     * such images have a file gamma of 0.45455, which corresponds to a PC-like
Packit 0ba690
     * display system.  This change in assumptions will have no effect on a
Packit 0ba690
     * PC-like system, but on a Mac, SGI, NeXT or other system with a non-
Packit 0ba690
     * identity lookup table, it will darken unlabelled images, which effec-
Packit 0ba690
     * tively favors images from PC-like systems over those originating on
Packit 0ba690
     * the local platform.  Note that mainprog_ptr->display_exponent is the
Packit 0ba690
     * "gamma" value for the entire display system, i.e., the product of
Packit 0ba690
     * LUT_exponent and CRT_exponent. */
Packit 0ba690
Packit 0ba690
    if (png_get_gAMA(png_ptr, info_ptr, &gamma))
Packit 0ba690
        png_set_gamma(png_ptr, mainprog_ptr->display_exponent, gamma);
Packit 0ba690
    else
Packit 0ba690
        png_set_gamma(png_ptr, mainprog_ptr->display_exponent, 0.45455);
Packit 0ba690
Packit 0ba690
Packit 0ba690
    /* we'll let libpng expand interlaced images, too */
Packit 0ba690
Packit 0ba690
    mainprog_ptr->passes = png_set_interlace_handling(png_ptr);
Packit 0ba690
Packit 0ba690
Packit 0ba690
    /* all transformations have been registered; now update info_ptr data and
Packit 0ba690
     * then get rowbytes and channels */
Packit 0ba690
Packit 0ba690
    png_read_update_info(png_ptr, info_ptr);
Packit 0ba690
Packit 0ba690
    mainprog_ptr->rowbytes = (int)png_get_rowbytes(png_ptr, info_ptr);
Packit 0ba690
    mainprog_ptr->channels = png_get_channels(png_ptr, info_ptr);
Packit 0ba690
Packit 0ba690
Packit 0ba690
    /* Call the main program to allocate memory for the image buffer and
Packit 0ba690
     * initialize windows and whatnot.  (The old-style function-pointer
Packit 0ba690
     * invocation is used for compatibility with a few supposedly ANSI
Packit 0ba690
     * compilers that nevertheless barf on "fn_ptr()"-style syntax.) */
Packit 0ba690
Packit 0ba690
    (*mainprog_ptr->mainprog_init)();
Packit 0ba690
Packit 0ba690
Packit 0ba690
    /* and that takes care of initialization */
Packit 0ba690
Packit 0ba690
    return;
Packit 0ba690
}
Packit 0ba690
Packit 0ba690
Packit 0ba690
Packit 0ba690
Packit 0ba690
Packit 0ba690
static void readpng2_row_callback(png_structp png_ptr, png_bytep new_row,
Packit 0ba690
                                  png_uint_32 row_num, int pass)
Packit 0ba690
{
Packit 0ba690
    mainprog_info  *mainprog_ptr;
Packit 0ba690
Packit 0ba690
Packit 0ba690
    /* first check whether the row differs from the previous pass; if not,
Packit 0ba690
     * nothing to combine or display */
Packit 0ba690
Packit 0ba690
    if (!new_row)
Packit 0ba690
        return;
Packit 0ba690
Packit 0ba690
Packit 0ba690
    /* retrieve the pointer to our special-purpose struct so we can access
Packit 0ba690
     * the old rows and image-display callback function */
Packit 0ba690
Packit 0ba690
    mainprog_ptr = png_get_progressive_ptr(png_ptr);
Packit 0ba690
Packit 0ba690
Packit 0ba690
    /* save the pass number for optional use by the front end */
Packit 0ba690
Packit 0ba690
    mainprog_ptr->pass = pass;
Packit 0ba690
Packit 0ba690
Packit 0ba690
    /* have libpng either combine the new row data with the existing row data
Packit 0ba690
     * from previous passes (if interlaced) or else just copy the new row
Packit 0ba690
     * into the main program's image buffer */
Packit 0ba690
Packit 0ba690
    png_progressive_combine_row(png_ptr, mainprog_ptr->row_pointers[row_num],
Packit 0ba690
      new_row);
Packit 0ba690
Packit 0ba690
Packit 0ba690
    /* finally, call the display routine in the main program with the number
Packit 0ba690
     * of the row we just updated */
Packit 0ba690
Packit 0ba690
    (*mainprog_ptr->mainprog_display_row)(row_num);
Packit 0ba690
Packit 0ba690
Packit 0ba690
    /* and we're ready for more */
Packit 0ba690
Packit 0ba690
    return;
Packit 0ba690
}
Packit 0ba690
Packit 0ba690
Packit 0ba690
Packit 0ba690
Packit 0ba690
Packit 0ba690
static void readpng2_end_callback(png_structp png_ptr, png_infop info_ptr)
Packit 0ba690
{
Packit 0ba690
    mainprog_info  *mainprog_ptr;
Packit 0ba690
Packit 0ba690
Packit 0ba690
    /* retrieve the pointer to our special-purpose struct */
Packit 0ba690
Packit 0ba690
    mainprog_ptr = png_get_progressive_ptr(png_ptr);
Packit 0ba690
Packit 0ba690
Packit 0ba690
    /* let the main program know that it should flush any buffered image
Packit 0ba690
     * data to the display now and set a "done" flag or whatever, but note
Packit 0ba690
     * that it SHOULD NOT DESTROY THE PNG STRUCTS YET--in other words, do
Packit 0ba690
     * NOT call readpng2_cleanup() either here or in the finish_display()
Packit 0ba690
     * routine; wait until control returns to the main program via
Packit 0ba690
     * readpng2_decode_data() */
Packit 0ba690
Packit 0ba690
    (*mainprog_ptr->mainprog_finish_display)();
Packit 0ba690
Packit 0ba690
Packit 0ba690
    /* all done */
Packit 0ba690
Packit 0ba690
    return;
Packit 0ba690
}
Packit 0ba690
Packit 0ba690
Packit 0ba690
Packit 0ba690
Packit 0ba690
Packit 0ba690
void readpng2_cleanup(mainprog_info *mainprog_ptr)
Packit 0ba690
{
Packit 0ba690
    png_structp png_ptr = (png_structp)mainprog_ptr->png_ptr;
Packit 0ba690
    png_infop info_ptr = (png_infop)mainprog_ptr->info_ptr;
Packit 0ba690
Packit 0ba690
    if (png_ptr && info_ptr)
Packit 0ba690
        png_destroy_read_struct(&png_ptr, &info_ptr, NULL);
Packit 0ba690
Packit 0ba690
    mainprog_ptr->png_ptr = NULL;
Packit 0ba690
    mainprog_ptr->info_ptr = NULL;
Packit 0ba690
}
Packit 0ba690
Packit 0ba690
Packit 0ba690
Packit 0ba690
Packit 0ba690
Packit 0ba690
static void readpng2_error_handler(png_structp png_ptr, png_const_charp msg)
Packit 0ba690
{
Packit 0ba690
    mainprog_info  *mainprog_ptr;
Packit 0ba690
Packit 0ba690
    /* This function, aside from the extra step of retrieving the "error
Packit 0ba690
     * pointer" (below) and the fact that it exists within the application
Packit 0ba690
     * rather than within libpng, is essentially identical to libpng's
Packit 0ba690
     * default error handler.  The second point is critical:  since both
Packit 0ba690
     * setjmp() and longjmp() are called from the same code, they are
Packit 0ba690
     * guaranteed to have compatible notions of how big a jmp_buf is,
Packit 0ba690
     * regardless of whether _BSD_SOURCE or anything else has (or has not)
Packit 0ba690
     * been defined. */
Packit 0ba690
Packit 0ba690
    fprintf(stderr, "readpng2 libpng error: %s\n", msg);
Packit 0ba690
    fflush(stderr);
Packit 0ba690
Packit 0ba690
    mainprog_ptr = png_get_error_ptr(png_ptr);
Packit 0ba690
    if (mainprog_ptr == NULL) {         /* we are completely hosed now */
Packit 0ba690
        fprintf(stderr,
Packit 0ba690
          "readpng2 severe error:  jmpbuf not recoverable; terminating.\n");
Packit 0ba690
        fflush(stderr);
Packit 0ba690
        exit(99);
Packit 0ba690
    }
Packit 0ba690
Packit 0ba690
    longjmp(mainprog_ptr->jmpbuf, 1);
Packit 0ba690
}