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