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);
 | |
| }
 | 
