fukasawa e60969
/*---------------------------------------------------------------------------
fukasawa e60969
fukasawa e60969
   wpng - simple PNG-writing program                             writepng.c
fukasawa e60969
fukasawa e60969
  ---------------------------------------------------------------------------
fukasawa e60969
fukasawa e60969
      Copyright (c) 1998-2007 Greg Roelofs.  All rights reserved.
fukasawa e60969
fukasawa e60969
      This software is provided "as is," without warranty of any kind,
fukasawa e60969
      express or implied.  In no event shall the author or contributors
fukasawa e60969
      be held liable for any damages arising in any way from the use of
fukasawa e60969
      this software.
fukasawa e60969
fukasawa e60969
      The contents of this file are DUAL-LICENSED.  You may modify and/or
fukasawa e60969
      redistribute this software according to the terms of one of the
fukasawa e60969
      following two licenses (at your option):
fukasawa e60969
fukasawa e60969
fukasawa e60969
      LICENSE 1 ("BSD-like with advertising clause"):
fukasawa e60969
fukasawa e60969
      Permission is granted to anyone to use this software for any purpose,
fukasawa e60969
      including commercial applications, and to alter it and redistribute
fukasawa e60969
      it freely, subject to the following restrictions:
fukasawa e60969
fukasawa e60969
      1. Redistributions of source code must retain the above copyright
fukasawa e60969
         notice, disclaimer, and this list of conditions.
fukasawa e60969
      2. Redistributions in binary form must reproduce the above copyright
fukasawa e60969
         notice, disclaimer, and this list of conditions in the documenta-
fukasawa e60969
         tion and/or other materials provided with the distribution.
fukasawa e60969
      3. All advertising materials mentioning features or use of this
fukasawa e60969
         software must display the following acknowledgment:
fukasawa e60969
fukasawa e60969
            This product includes software developed by Greg Roelofs
fukasawa e60969
            and contributors for the book, "PNG: The Definitive Guide,"
fukasawa e60969
            published by O'Reilly and Associates.
fukasawa e60969
fukasawa e60969
fukasawa e60969
      LICENSE 2 (GNU GPL v2 or later):
fukasawa e60969
fukasawa e60969
      This program is free software; you can redistribute it and/or modify
fukasawa e60969
      it under the terms of the GNU General Public License as published by
fukasawa e60969
      the Free Software Foundation; either version 2 of the License, or
fukasawa e60969
      (at your option) any later version.
fukasawa e60969
fukasawa e60969
      This program is distributed in the hope that it will be useful,
fukasawa e60969
      but WITHOUT ANY WARRANTY; without even the implied warranty of
fukasawa e60969
      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
fukasawa e60969
      GNU General Public License for more details.
fukasawa e60969
fukasawa e60969
      You should have received a copy of the GNU General Public License
fukasawa e60969
      along with this program; if not, write to the Free Software Foundation,
fukasawa e60969
      Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
fukasawa e60969
fukasawa e60969
  ---------------------------------------------------------------------------*/
fukasawa e60969
fukasawa e60969
fukasawa e60969
#include <stdlib.h>     /* for exit() prototype */</stdlib.h>
fukasawa e60969
#include <zlib.h></zlib.h>
fukasawa e60969
fukasawa e60969
#include "png.h"        /* libpng header, includes setjmp.h */
fukasawa e60969
#include "writepng.h"   /* typedefs, common macros, public prototypes */
fukasawa e60969
fukasawa e60969
fukasawa e60969
/* local prototype */
fukasawa e60969
fukasawa e60969
static void writepng_error_handler(png_structp png_ptr, png_const_charp msg);
fukasawa e60969
fukasawa e60969
fukasawa e60969
fukasawa e60969
void writepng_version_info(void)
fukasawa e60969
{
fukasawa e60969
  fprintf(stderr, "   Compiled with libpng %s; using libpng %s.\n",
fukasawa e60969
    PNG_LIBPNG_VER_STRING, png_libpng_ver);
fukasawa e60969
  fprintf(stderr, "   Compiled with zlib %s; using zlib %s.\n",
fukasawa e60969
    ZLIB_VERSION, zlib_version);
fukasawa e60969
}
fukasawa e60969
fukasawa e60969
fukasawa e60969
fukasawa e60969
fukasawa e60969
/* returns 0 for success, 2 for libpng problem, 4 for out of memory, 11 for
fukasawa e60969
 *  unexpected pnmtype; note that outfile might be stdout */
fukasawa e60969
fukasawa e60969
int writepng_init(mainprog_info *mainprog_ptr)
fukasawa e60969
{
fukasawa e60969
    png_structp  png_ptr;       /* note:  temporary variables! */
fukasawa e60969
    png_infop  info_ptr;
fukasawa e60969
    int color_type, interlace_type;
fukasawa e60969
fukasawa e60969
fukasawa e60969
    /* could also replace libpng warning-handler (final NULL), but no need: */
fukasawa e60969
fukasawa e60969
    png_ptr = png_create_write_struct(png_get_libpng_ver(NULL), mainprog_ptr,
fukasawa e60969
      writepng_error_handler, NULL);
fukasawa e60969
    if (!png_ptr)
fukasawa e60969
        return 4;   /* out of memory */
fukasawa e60969
fukasawa e60969
    info_ptr = png_create_info_struct(png_ptr);
fukasawa e60969
    if (!info_ptr) {
fukasawa e60969
        png_destroy_write_struct(&png_ptr, NULL);
fukasawa e60969
        return 4;   /* out of memory */
fukasawa e60969
    }
fukasawa e60969
fukasawa e60969
fukasawa e60969
    /* setjmp() must be called in every function that calls a PNG-writing
fukasawa e60969
     * libpng function, unless an alternate error handler was installed--
fukasawa e60969
     * but compatible error handlers must either use longjmp() themselves
fukasawa e60969
     * (as in this program) or some other method to return control to
fukasawa e60969
     * application code, so here we go: */
fukasawa e60969
fukasawa e60969
    if (setjmp(mainprog_ptr->jmpbuf)) {
fukasawa e60969
        png_destroy_write_struct(&png_ptr, &info_ptr);
fukasawa e60969
        return 2;
fukasawa e60969
    }
fukasawa e60969
fukasawa e60969
fukasawa e60969
    /* make sure outfile is (re)opened in BINARY mode */
fukasawa e60969
fukasawa e60969
    png_init_io(png_ptr, mainprog_ptr->outfile);
fukasawa e60969
fukasawa e60969
fukasawa e60969
    /* set the compression levels--in general, always want to leave filtering
fukasawa e60969
     * turned on (except for palette images) and allow all of the filters,
fukasawa e60969
     * which is the default; want 32K zlib window, unless entire image buffer
fukasawa e60969
     * is 16K or smaller (unknown here)--also the default; usually want max
fukasawa e60969
     * compression (NOT the default); and remaining compression flags should
fukasawa e60969
     * be left alone */
fukasawa e60969
fukasawa e60969
    png_set_compression_level(png_ptr, Z_BEST_COMPRESSION);
fukasawa e60969
/*
fukasawa e60969
    >> this is default for no filtering; Z_FILTERED is default otherwise:
fukasawa e60969
    png_set_compression_strategy(png_ptr, Z_DEFAULT_STRATEGY);
fukasawa e60969
    >> these are all defaults:
fukasawa e60969
    png_set_compression_mem_level(png_ptr, 8);
fukasawa e60969
    png_set_compression_window_bits(png_ptr, 15);
fukasawa e60969
    png_set_compression_method(png_ptr, 8);
fukasawa e60969
 */
fukasawa e60969
fukasawa e60969
fukasawa e60969
    /* set the image parameters appropriately */
fukasawa e60969
fukasawa e60969
    if (mainprog_ptr->pnmtype == 5)
fukasawa e60969
        color_type = PNG_COLOR_TYPE_GRAY;
fukasawa e60969
    else if (mainprog_ptr->pnmtype == 6)
fukasawa e60969
        color_type = PNG_COLOR_TYPE_RGB;
fukasawa e60969
    else if (mainprog_ptr->pnmtype == 8)
fukasawa e60969
        color_type = PNG_COLOR_TYPE_RGB_ALPHA;
fukasawa e60969
    else {
fukasawa e60969
        png_destroy_write_struct(&png_ptr, &info_ptr);
fukasawa e60969
        return 11;
fukasawa e60969
    }
fukasawa e60969
fukasawa e60969
    interlace_type = mainprog_ptr->interlaced? PNG_INTERLACE_ADAM7 :
fukasawa e60969
                                               PNG_INTERLACE_NONE;
fukasawa e60969
fukasawa e60969
    png_set_IHDR(png_ptr, info_ptr, mainprog_ptr->width, mainprog_ptr->height,
fukasawa e60969
      mainprog_ptr->sample_depth, color_type, interlace_type,
fukasawa e60969
      PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT);
fukasawa e60969
fukasawa e60969
    if (mainprog_ptr->gamma > 0.0)
fukasawa e60969
        png_set_gAMA(png_ptr, info_ptr, mainprog_ptr->gamma);
fukasawa e60969
fukasawa e60969
    if (mainprog_ptr->have_bg) {   /* we know it's RGBA, not gray+alpha */
fukasawa e60969
        png_color_16  background;
fukasawa e60969
fukasawa e60969
        background.red = mainprog_ptr->bg_red;
fukasawa e60969
        background.green = mainprog_ptr->bg_green;
fukasawa e60969
        background.blue = mainprog_ptr->bg_blue;
fukasawa e60969
        png_set_bKGD(png_ptr, info_ptr, &background);
fukasawa e60969
    }
fukasawa e60969
fukasawa e60969
    if (mainprog_ptr->have_time) {
fukasawa e60969
        png_time  modtime;
fukasawa e60969
fukasawa e60969
        png_convert_from_time_t(&modtime, mainprog_ptr->modtime);
fukasawa e60969
        png_set_tIME(png_ptr, info_ptr, &modtime);
fukasawa e60969
    }
fukasawa e60969
fukasawa e60969
    if (mainprog_ptr->have_text) {
fukasawa e60969
        png_text  text[6];
fukasawa e60969
        int  num_text = 0;
fukasawa e60969
fukasawa e60969
        if (mainprog_ptr->have_text & TEXT_TITLE) {
fukasawa e60969
            text[num_text].compression = PNG_TEXT_COMPRESSION_NONE;
fukasawa e60969
            text[num_text].key = "Title";
fukasawa e60969
            text[num_text].text = mainprog_ptr->title;
fukasawa e60969
            ++num_text;
fukasawa e60969
        }
fukasawa e60969
        if (mainprog_ptr->have_text & TEXT_AUTHOR) {
fukasawa e60969
            text[num_text].compression = PNG_TEXT_COMPRESSION_NONE;
fukasawa e60969
            text[num_text].key = "Author";
fukasawa e60969
            text[num_text].text = mainprog_ptr->author;
fukasawa e60969
            ++num_text;
fukasawa e60969
        }
fukasawa e60969
        if (mainprog_ptr->have_text & TEXT_DESC) {
fukasawa e60969
            text[num_text].compression = PNG_TEXT_COMPRESSION_NONE;
fukasawa e60969
            text[num_text].key = "Description";
fukasawa e60969
            text[num_text].text = mainprog_ptr->desc;
fukasawa e60969
            ++num_text;
fukasawa e60969
        }
fukasawa e60969
        if (mainprog_ptr->have_text & TEXT_COPY) {
fukasawa e60969
            text[num_text].compression = PNG_TEXT_COMPRESSION_NONE;
fukasawa e60969
            text[num_text].key = "Copyright";
fukasawa e60969
            text[num_text].text = mainprog_ptr->copyright;
fukasawa e60969
            ++num_text;
fukasawa e60969
        }
fukasawa e60969
        if (mainprog_ptr->have_text & TEXT_EMAIL) {
fukasawa e60969
            text[num_text].compression = PNG_TEXT_COMPRESSION_NONE;
fukasawa e60969
            text[num_text].key = "E-mail";
fukasawa e60969
            text[num_text].text = mainprog_ptr->email;
fukasawa e60969
            ++num_text;
fukasawa e60969
        }
fukasawa e60969
        if (mainprog_ptr->have_text & TEXT_URL) {
fukasawa e60969
            text[num_text].compression = PNG_TEXT_COMPRESSION_NONE;
fukasawa e60969
            text[num_text].key = "URL";
fukasawa e60969
            text[num_text].text = mainprog_ptr->url;
fukasawa e60969
            ++num_text;
fukasawa e60969
        }
fukasawa e60969
        png_set_text(png_ptr, info_ptr, text, num_text);
fukasawa e60969
    }
fukasawa e60969
fukasawa e60969
fukasawa e60969
    /* write all chunks up to (but not including) first IDAT */
fukasawa e60969
fukasawa e60969
    png_write_info(png_ptr, info_ptr);
fukasawa e60969
fukasawa e60969
fukasawa e60969
    /* if we wanted to write any more text info *after* the image data, we
fukasawa e60969
     * would set up text struct(s) here and call png_set_text() again, with
fukasawa e60969
     * just the new data; png_set_tIME() could also go here, but it would
fukasawa e60969
     * have no effect since we already called it above (only one tIME chunk
fukasawa e60969
     * allowed) */
fukasawa e60969
fukasawa e60969
fukasawa e60969
    /* set up the transformations:  for now, just pack low-bit-depth pixels
fukasawa e60969
     * into bytes (one, two or four pixels per byte) */
fukasawa e60969
fukasawa e60969
    png_set_packing(png_ptr);
fukasawa e60969
/*  png_set_shift(png_ptr, &sig_bit);  to scale low-bit-depth values */
fukasawa e60969
fukasawa e60969
fukasawa e60969
    /* make sure we save our pointers for use in writepng_encode_image() */
fukasawa e60969
fukasawa e60969
    mainprog_ptr->png_ptr = png_ptr;
fukasawa e60969
    mainprog_ptr->info_ptr = info_ptr;
fukasawa e60969
fukasawa e60969
fukasawa e60969
    /* OK, that's all we need to do for now; return happy */
fukasawa e60969
fukasawa e60969
    return 0;
fukasawa e60969
}
fukasawa e60969
fukasawa e60969
fukasawa e60969
fukasawa e60969
fukasawa e60969
fukasawa e60969
/* returns 0 for success, 2 for libpng (longjmp) problem */
fukasawa e60969
fukasawa e60969
int writepng_encode_image(mainprog_info *mainprog_ptr)
fukasawa e60969
{
fukasawa e60969
    png_structp png_ptr = (png_structp)mainprog_ptr->png_ptr;
fukasawa e60969
    png_infop info_ptr = (png_infop)mainprog_ptr->info_ptr;
fukasawa e60969
fukasawa e60969
fukasawa e60969
    /* as always, setjmp() must be called in every function that calls a
fukasawa e60969
     * PNG-writing libpng function */
fukasawa e60969
fukasawa e60969
    if (setjmp(mainprog_ptr->jmpbuf)) {
fukasawa e60969
        png_destroy_write_struct(&png_ptr, &info_ptr);
fukasawa e60969
        mainprog_ptr->png_ptr = NULL;
fukasawa e60969
        mainprog_ptr->info_ptr = NULL;
fukasawa e60969
        return 2;
fukasawa e60969
    }
fukasawa e60969
fukasawa e60969
fukasawa e60969
    /* and now we just write the whole image; libpng takes care of interlacing
fukasawa e60969
     * for us */
fukasawa e60969
fukasawa e60969
    png_write_image(png_ptr, mainprog_ptr->row_pointers);
fukasawa e60969
fukasawa e60969
fukasawa e60969
    /* since that's it, we also close out the end of the PNG file now--if we
fukasawa e60969
     * had any text or time info to write after the IDATs, second argument
fukasawa e60969
     * would be info_ptr, but we optimize slightly by sending NULL pointer: */
fukasawa e60969
fukasawa e60969
    png_write_end(png_ptr, NULL);
fukasawa e60969
fukasawa e60969
    return 0;
fukasawa e60969
}
fukasawa e60969
fukasawa e60969
fukasawa e60969
fukasawa e60969
fukasawa e60969
fukasawa e60969
/* returns 0 if succeeds, 2 if libpng problem */
fukasawa e60969
fukasawa e60969
int writepng_encode_row(mainprog_info *mainprog_ptr)  /* NON-interlaced only! */
fukasawa e60969
{
fukasawa e60969
    png_structp png_ptr = (png_structp)mainprog_ptr->png_ptr;
fukasawa e60969
    png_infop info_ptr = (png_infop)mainprog_ptr->info_ptr;
fukasawa e60969
fukasawa e60969
fukasawa e60969
    /* as always, setjmp() must be called in every function that calls a
fukasawa e60969
     * PNG-writing libpng function */
fukasawa e60969
fukasawa e60969
    if (setjmp(mainprog_ptr->jmpbuf)) {
fukasawa e60969
        png_destroy_write_struct(&png_ptr, &info_ptr);
fukasawa e60969
        mainprog_ptr->png_ptr = NULL;
fukasawa e60969
        mainprog_ptr->info_ptr = NULL;
fukasawa e60969
        return 2;
fukasawa e60969
    }
fukasawa e60969
fukasawa e60969
fukasawa e60969
    /* image_data points at our one row of image data */
fukasawa e60969
fukasawa e60969
    png_write_row(png_ptr, mainprog_ptr->image_data);
fukasawa e60969
fukasawa e60969
    return 0;
fukasawa e60969
}
fukasawa e60969
fukasawa e60969
fukasawa e60969
fukasawa e60969
fukasawa e60969
fukasawa e60969
/* returns 0 if succeeds, 2 if libpng problem */
fukasawa e60969
fukasawa e60969
int writepng_encode_finish(mainprog_info *mainprog_ptr)   /* NON-interlaced! */
fukasawa e60969
{
fukasawa e60969
    png_structp png_ptr = (png_structp)mainprog_ptr->png_ptr;
fukasawa e60969
    png_infop info_ptr = (png_infop)mainprog_ptr->info_ptr;
fukasawa e60969
fukasawa e60969
fukasawa e60969
    /* as always, setjmp() must be called in every function that calls a
fukasawa e60969
     * PNG-writing libpng function */
fukasawa e60969
fukasawa e60969
    if (setjmp(mainprog_ptr->jmpbuf)) {
fukasawa e60969
        png_destroy_write_struct(&png_ptr, &info_ptr);
fukasawa e60969
        mainprog_ptr->png_ptr = NULL;
fukasawa e60969
        mainprog_ptr->info_ptr = NULL;
fukasawa e60969
        return 2;
fukasawa e60969
    }
fukasawa e60969
fukasawa e60969
fukasawa e60969
    /* close out PNG file; if we had any text or time info to write after
fukasawa e60969
     * the IDATs, second argument would be info_ptr: */
fukasawa e60969
fukasawa e60969
    png_write_end(png_ptr, NULL);
fukasawa e60969
fukasawa e60969
    return 0;
fukasawa e60969
}
fukasawa e60969
fukasawa e60969
fukasawa e60969
fukasawa e60969
fukasawa e60969
fukasawa e60969
void writepng_cleanup(mainprog_info *mainprog_ptr)
fukasawa e60969
{
fukasawa e60969
    png_structp png_ptr = (png_structp)mainprog_ptr->png_ptr;
fukasawa e60969
    png_infop info_ptr = (png_infop)mainprog_ptr->info_ptr;
fukasawa e60969
fukasawa e60969
    if (png_ptr && info_ptr)
fukasawa e60969
        png_destroy_write_struct(&png_ptr, &info_ptr);
fukasawa e60969
}
fukasawa e60969
fukasawa e60969
fukasawa e60969
fukasawa e60969
fukasawa e60969
fukasawa e60969
static void writepng_error_handler(png_structp png_ptr, png_const_charp msg)
fukasawa e60969
{
fukasawa e60969
    mainprog_info  *mainprog_ptr;
fukasawa e60969
fukasawa e60969
    /* This function, aside from the extra step of retrieving the "error
fukasawa e60969
     * pointer" (below) and the fact that it exists within the application
fukasawa e60969
     * rather than within libpng, is essentially identical to libpng's
fukasawa e60969
     * default error handler.  The second point is critical:  since both
fukasawa e60969
     * setjmp() and longjmp() are called from the same code, they are
fukasawa e60969
     * guaranteed to have compatible notions of how big a jmp_buf is,
fukasawa e60969
     * regardless of whether _BSD_SOURCE or anything else has (or has not)
fukasawa e60969
     * been defined. */
fukasawa e60969
fukasawa e60969
    fprintf(stderr, "writepng libpng error: %s\n", msg);
fukasawa e60969
    fflush(stderr);
fukasawa e60969
fukasawa e60969
    mainprog_ptr = png_get_error_ptr(png_ptr);
fukasawa e60969
    if (mainprog_ptr == NULL) {         /* we are completely hosed now */
fukasawa e60969
        fprintf(stderr,
fukasawa e60969
          "writepng severe error:  jmpbuf not recoverable; terminating.\n");
fukasawa e60969
        fflush(stderr);
fukasawa e60969
        exit(99);
fukasawa e60969
    }
fukasawa e60969
fukasawa e60969
    /* Now we have our data structure we can use the information in it
fukasawa e60969
     * to return control to our own higher level code (all the points
fukasawa e60969
     * where 'setjmp' is called in this file.)  This will work with other
fukasawa e60969
     * error handling mechanisms as well - libpng always calls png_error
fukasawa e60969
     * when it can proceed no further, thus, so long as the error handler
fukasawa e60969
     * is intercepted, application code can do its own error recovery.
fukasawa e60969
     */
fukasawa e60969
    longjmp(mainprog_ptr->jmpbuf, 1);
fukasawa e60969
}