diff --git a/README.md b/README.md index 5321568..2b11c5c 100644 --- a/README.md +++ b/README.md @@ -44,27 +44,30 @@ but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. -###`rwpng.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. +### `rwpng.c` + +© 1997-2002 by Greg Roelofs. +© 2009-2014 by Kornel Lesiński. + +All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, +are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/posterize.c b/posterize.c index dcc1d26..b0c8bff 100644 --- a/posterize.c +++ b/posterize.c @@ -506,7 +506,7 @@ int main(int argc, char *argv[]) png24_image img; pngquant_error retval; - if ((retval = rwpng_read_image24(input, &img))) { + if ((retval = rwpng_read_image24(input, &img, verbose))) { fprintf(stderr, "Error: cannot read PNG from %s\n", input_name); return retval; } diff --git a/rwpng.c b/rwpng.c index 8fca7ce..f0931b1 100644 --- a/rwpng.c +++ b/rwpng.c @@ -4,36 +4,43 @@ --------------------------------------------------------------------------- - Copyright (c) 1998-2000 Greg Roelofs. All rights reserved. + © 1998-2000 by Greg Roelofs. + © 2009-2014 by Kornel Lesiński. - 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. + All rights reserved. - 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: + Redistribution and use in source and binary forms, with or without modification, + are permitted provided that the following conditions are met: - 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: + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. - This product includes software developed by Greg Roelofs - and contributors for the book, "PNG: The Definitive Guide," - published by O'Reilly and Associates. + 2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ---------------------------------------------------------------------------*/ #include #include +#include #include "png.h" #include "rwpng.h" +#if USE_LCMS +#include "lcms2.h" +#endif #ifndef Z_BEST_COMPRESSION #define Z_BEST_COMPRESSION 9 @@ -42,22 +49,41 @@ #define Z_BEST_SPEED 1 #endif +#ifdef _OPENMP +#include +#else +#define omp_get_max_threads() 1 +#endif + static void rwpng_error_handler(png_structp png_ptr, png_const_charp msg); +static void rwpng_warning_stderr_handler(png_structp png_ptr, png_const_charp msg); +static void rwpng_warning_silent_handler(png_structp png_ptr, png_const_charp msg); int rwpng_read_image24_cocoa(FILE *infile, png24_image *mainprog_ptr); void rwpng_version_info(FILE *fp) { - fprintf(fp, " Compiled with libpng %s; using libpng %s.\n", - PNG_LIBPNG_VER_STRING, png_get_header_ver(NULL)); + const char *pngver = png_get_header_ver(NULL); + #if USE_COCOA - fputs(" Compiled with Apple Cocoa image reader.\n", fp); + fprintf(fp, " Using libpng %s and Apple Cocoa image reader.\n", pngver); +#elif USE_LCMS + fprintf(fp, " Using libpng %s with Little CMS color profile support.\n", pngver); +#else + fprintf(fp, " Using libpng %s and Apple Cocoa image reader.\n", pngver); +#endif + +#if PNG_LIBPNG_VER < 10600 + if (strcmp(pngver, "1.3.") < 0) { + fputs("\nWARNING: Your version of libpng is outdated and may produce corrupted files.\n" + "Please recompile pngquant with a newer version of libpng (1.5 or later).\n", fp); + } #endif } struct rwpng_read_data { - FILE *fp; + FILE *const fp; png_size_t bytes_read; }; @@ -66,13 +92,49 @@ static void user_read_data(png_structp png_ptr, png_bytep data, png_size_t lengt struct rwpng_read_data *read_data = (struct rwpng_read_data *)png_get_io_ptr(png_ptr); png_size_t read = fread(data, 1, length, read_data->fp); - if (!read) png_error(png_ptr, "Read error"); + if (!read) { + png_error(png_ptr, "Read error"); + } read_data->bytes_read += read; } +struct rwpng_write_state { + FILE *outfile; + png_size_t maximum_file_size; + png_size_t bytes_written; + pngquant_error retval; +}; + +static void user_write_data(png_structp png_ptr, png_bytep data, png_size_t length) +{ + struct rwpng_write_state *write_state = (struct rwpng_write_state *)png_get_io_ptr(png_ptr); + + if (SUCCESS != write_state->retval) { + return; + } + + if (write_state->maximum_file_size && write_state->bytes_written + length > write_state->maximum_file_size) { + write_state->retval = TOO_LARGE_FILE; + } + + if (!fwrite(data, 1, length, write_state->outfile)) { + write_state->retval = CANT_WRITE_ERROR; + } + + write_state->bytes_written += length; +} + +static void user_flush_data(png_structp png_ptr) +{ + // libpng never calls this :( +} + + static png_bytepp rwpng_create_row_pointers(png_infop info_ptr, png_structp png_ptr, unsigned char *base, unsigned int height, unsigned int rowbytes) { - if (!rowbytes) rowbytes = png_get_rowbytes(png_ptr, info_ptr); + if (!rowbytes) { + rowbytes = png_get_rowbytes(png_ptr, info_ptr); + } png_bytepp row_pointers = malloc(height * sizeof(row_pointers[0])); if (!row_pointers) return NULL; @@ -82,6 +144,30 @@ static png_bytepp rwpng_create_row_pointers(png_infop info_ptr, png_structp png_ return row_pointers; } +static int read_chunk_callback(png_structp png_ptr, png_unknown_chunkp in_chunk) +{ + if (0 == memcmp("iCCP", in_chunk->name, 5) || + 0 == memcmp("cHRM", in_chunk->name, 5) || + 0 == memcmp("gAMA", in_chunk->name, 5)) { + return 0; // not handled + } + + struct rwpng_chunk **head = (struct rwpng_chunk **)png_get_user_chunk_ptr(png_ptr); + + struct rwpng_chunk *chunk = malloc(sizeof(struct rwpng_chunk)); + memcpy(chunk->name, in_chunk->name, 5); + chunk->size = in_chunk->size; + chunk->location = in_chunk->location; + chunk->data = in_chunk->size ? malloc(in_chunk->size) : NULL; + if (in_chunk->size) { + memcpy(chunk->data, in_chunk->data, in_chunk->size); + } + + chunk->next = *head; + *head = chunk; + + return 1; // marks as "handled", libpng won't store it +} /* retval: @@ -93,7 +179,7 @@ static png_bytepp rwpng_create_row_pointers(png_infop info_ptr, png_structp png_ 26 = wrong PNG color type (no alpha channel) */ -pngquant_error rwpng_read_image24_libpng(FILE *infile, png24_image *mainprog_ptr) +pngquant_error rwpng_read_image24_libpng(FILE *infile, png24_image *mainprog_ptr, int verbose) { png_structp png_ptr = NULL; png_infop info_ptr = NULL; @@ -101,7 +187,7 @@ pngquant_error rwpng_read_image24_libpng(FILE *infile, png24_image *mainprog_ptr int color_type, bit_depth; png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, mainprog_ptr, - rwpng_error_handler, NULL); + rwpng_error_handler, verbose ? rwpng_warning_stderr_handler : rwpng_warning_silent_handler); if (!png_ptr) { return PNG_OUT_OF_MEMORY_ERROR; /* out of memory */ } @@ -120,6 +206,8 @@ pngquant_error rwpng_read_image24_libpng(FILE *infile, png24_image *mainprog_ptr return LIBPNG_FATAL_ERROR; /* fatal libpng error (via longjmp()) */ } + png_set_read_user_chunk_fn(png_ptr, &mainprog_ptr->chunks, read_chunk_callback); + struct rwpng_read_data read_data = {infile, 0}; png_set_read_fn(png_ptr, &read_data, user_read_data); @@ -131,7 +219,7 @@ pngquant_error rwpng_read_image24_libpng(FILE *infile, png24_image *mainprog_ptr * compression_type and filter_type => NULLs] */ png_get_IHDR(png_ptr, info_ptr, &mainprog_ptr->width, &mainprog_ptr->height, - &bit_depth, &color_type, NULL, NULL, NULL); + &bit_depth, &color_type, NULL, NULL, NULL); /* expand palette images to RGB, low-bit-depth grayscale images to 8 bits, @@ -142,9 +230,7 @@ pngquant_error rwpng_read_image24_libpng(FILE *infile, png24_image *mainprog_ptr if (!(color_type & PNG_COLOR_MASK_ALPHA)) { #ifdef PNG_READ_FILLER_SUPPORTED - /* GRP: expand palette to RGB, and grayscale or RGB to GA or RGBA */ - if (color_type == PNG_COLOR_TYPE_PALETTE) - png_set_expand(png_ptr); + png_set_expand(png_ptr); png_set_filler(png_ptr, 65535L, PNG_FILLER_AFTER); #else fprintf(stderr, "pngquant readpng: image is neither RGBA nor GA\n"); @@ -153,24 +239,23 @@ pngquant_error rwpng_read_image24_libpng(FILE *infile, png24_image *mainprog_ptr return mainprog_ptr->retval; #endif } -/* - if (color_type == PNG_COLOR_TYPE_GRAY && bit_depth < 8) - png_set_expand(png_ptr); - if (png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS)) - png_set_expand(png_ptr); - */ - if (bit_depth == 16) + + if (bit_depth == 16) { png_set_strip_16(png_ptr); + } - if (color_type == PNG_COLOR_TYPE_GRAY || - color_type == PNG_COLOR_TYPE_GRAY_ALPHA) + if (!(color_type & PNG_COLOR_MASK_COLOR)) { png_set_gray_to_rgb(png_ptr); + } - /* get and save the gamma info (if any) for writing */ + /* get source gamma for gamma correction, or use sRGB default */ - double gamma; - mainprog_ptr->gamma = png_get_gAMA(png_ptr, info_ptr, &gamma) ? gamma : 0.45455; + double gamma = 0.45455; + if (!png_get_valid(png_ptr, info_ptr, PNG_INFO_sRGB)) { + png_get_gAMA(png_ptr, info_ptr, &gamma); + } + mainprog_ptr->gamma = gamma; png_set_interlace_handling(png_ptr); @@ -198,6 +283,93 @@ pngquant_error rwpng_read_image24_libpng(FILE *infile, png24_image *mainprog_ptr png_read_end(png_ptr, NULL); +#if USE_LCMS +#if PNG_LIBPNG_VER < 10500 + png_charp ProfileData; +#else + png_bytep ProfileData; +#endif + png_uint_32 ProfileLen; + + cmsHPROFILE hInProfile = NULL; + + /* color_type is read from the image before conversion to RGBA */ + int COLOR_PNG = color_type & PNG_COLOR_MASK_COLOR; + + mainprog_ptr->lcms_status = NONE; + + /* embedded ICC profile */ + if (png_get_iCCP(png_ptr, info_ptr, &(png_charp){0}, &(int){0}, &ProfileData, &ProfileLen)) { + + hInProfile = cmsOpenProfileFromMem(ProfileData, ProfileLen); + cmsColorSpaceSignature colorspace = cmsGetColorSpace(hInProfile); + + /* only RGB (and GRAY) valid for PNGs */ + if (colorspace == cmsSigRgbData && COLOR_PNG) { + mainprog_ptr->lcms_status = ICCP; + } else { + if (colorspace == cmsSigGrayData && !COLOR_PNG) { + mainprog_ptr->lcms_status = ICCP_WARN_GRAY; + } + cmsCloseProfile(hInProfile); + hInProfile = NULL; + } + } + + /* build RGB profile from cHRM and gAMA */ + if (hInProfile == NULL && COLOR_PNG && + !png_get_valid(png_ptr, info_ptr, PNG_INFO_sRGB) && + png_get_valid(png_ptr, info_ptr, PNG_INFO_gAMA) && + png_get_valid(png_ptr, info_ptr, PNG_INFO_cHRM)) { + + cmsCIExyY WhitePoint; + cmsCIExyYTRIPLE Primaries; + + png_get_cHRM(png_ptr, info_ptr, &WhitePoint.x, &WhitePoint.y, + &Primaries.Red.x, &Primaries.Red.y, + &Primaries.Green.x, &Primaries.Green.y, + &Primaries.Blue.x, &Primaries.Blue.y); + + WhitePoint.Y = Primaries.Red.Y = Primaries.Green.Y = Primaries.Blue.Y = 1.0; + + cmsToneCurve *GammaTable[3]; + GammaTable[0] = GammaTable[1] = GammaTable[2] = cmsBuildGamma(NULL, 1/gamma); + + hInProfile = cmsCreateRGBProfile(&WhitePoint, &Primaries, GammaTable); + + cmsFreeToneCurve(GammaTable[0]); + + mainprog_ptr->lcms_status = GAMA_CHRM; + } + + /* transform image to sRGB colorspace */ + if (hInProfile != NULL) { + + cmsHPROFILE hOutProfile = cmsCreate_sRGBProfile(); + cmsHTRANSFORM hTransform = cmsCreateTransform(hInProfile, TYPE_RGBA_8, + hOutProfile, TYPE_RGBA_8, + INTENT_PERCEPTUAL, + omp_get_max_threads() > 1 ? cmsFLAGS_NOCACHE : 0); + + #pragma omp parallel for \ + if (mainprog_ptr->height*mainprog_ptr->width > 8000) \ + schedule(static) + for (unsigned int i = 0; i < mainprog_ptr->height; i++) { + /* It is safe to use the same block for input and output, + when both are of the same TYPE. */ + cmsDoTransform(hTransform, row_pointers[i], + row_pointers[i], + mainprog_ptr->width); + } + + cmsDeleteTransform(hTransform); + cmsCloseProfile(hOutProfile); + cmsCloseProfile(hInProfile); + + mainprog_ptr->gamma = 0.45455; + } +#endif + png_destroy_read_struct(&png_ptr, &info_ptr, NULL); mainprog_ptr->file_size = read_data.bytes_read; @@ -206,24 +378,51 @@ pngquant_error rwpng_read_image24_libpng(FILE *infile, png24_image *mainprog_ptr return SUCCESS; } +static void rwpng_free_chunks(struct rwpng_chunk *chunk) { + if (!chunk) return; + rwpng_free_chunks(chunk->next); + free(chunk->data); + free(chunk); +} + +void rwpng_free_image24(png24_image *image) +{ + free(image->row_pointers); + image->row_pointers = NULL; + + free(image->rgba_data); + image->rgba_data = NULL; + + rwpng_free_chunks(image->chunks); + image->chunks = NULL; +} -pngquant_error rwpng_read_image24(FILE *infile, png24_image *input_image_p) +void rwpng_free_image8(png8_image *image) { - if (!infile) return READ_ERROR; + free(image->indexed_data); + image->indexed_data = NULL; + + free(image->row_pointers); + image->row_pointers = NULL; + rwpng_free_chunks(image->chunks); + image->chunks = NULL; +} + +pngquant_error rwpng_read_image24(FILE *infile, png24_image *input_image_p, int verbose) +{ #if USE_COCOA return rwpng_read_image24_cocoa(infile, input_image_p); #else - return rwpng_read_image24_libpng(infile, input_image_p); + return rwpng_read_image24_libpng(infile, input_image_p, verbose); #endif } -static pngquant_error rwpng_write_image_init(rwpng_png_image *mainprog_ptr, png_structpp png_ptr_p, png_infopp info_ptr_p, FILE *outfile, int fast_compression) +static pngquant_error rwpng_write_image_init(rwpng_png_image *mainprog_ptr, png_structpp png_ptr_p, png_infopp info_ptr_p, int fast_compression) { - if (!outfile) return CANT_WRITE_ERROR; - /* could also replace libpng warning-handler (final NULL), but no need: */ + *png_ptr_p = png_create_write_struct(PNG_LIBPNG_VER_STRING, mainprog_ptr, rwpng_error_handler, NULL); if (!(*png_ptr_p)) { @@ -236,7 +435,6 @@ static pngquant_error rwpng_write_image_init(rwpng_png_image *mainprog_ptr, png_ return LIBPNG_INIT_ERROR; /* 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 @@ -247,9 +445,8 @@ static pngquant_error rwpng_write_image_init(rwpng_png_image *mainprog_ptr, png_ return LIBPNG_INIT_ERROR; /* libpng error (via longjmp()) */ } - png_init_io(*png_ptr_p, outfile); - png_set_compression_level(*png_ptr_p, fast_compression ? Z_BEST_SPEED : Z_BEST_COMPRESSION); + png_set_compression_mem_level(*png_ptr_p, fast_compression ? 9 : 5); // judging by optipng results, smaller mem makes libpng compress slightly better return SUCCESS; } @@ -270,23 +467,27 @@ void rwpng_write_end(png_infopp info_ptr_p, png_structpp png_ptr_p, png_bytepp r void rwpng_set_gamma(png_infop info_ptr, png_structp png_ptr, double gamma) { - if (gamma > 0.0) { - png_set_gAMA(png_ptr, info_ptr, gamma); - - if (gamma > 0.45454 && gamma < 0.45456) { - png_set_sRGB(png_ptr, info_ptr, 0); // 0 = Perceptual - } - } + /* remap sets gamma to 0.45455 */ + png_set_gAMA(png_ptr, info_ptr, gamma); + png_set_sRGB(png_ptr, info_ptr, 0); // 0 = Perceptual } -pngquant_error rwpng_write_image8(FILE *outfile, png8_image *mainprog_ptr) +pngquant_error rwpng_write_image8(FILE *outfile, const png8_image *mainprog_ptr) { png_structp png_ptr; png_infop info_ptr; - pngquant_error retval = rwpng_write_image_init((rwpng_png_image*)mainprog_ptr, &png_ptr, &info_ptr, outfile, mainprog_ptr->fast_compression); + pngquant_error retval = rwpng_write_image_init((rwpng_png_image*)mainprog_ptr, &png_ptr, &info_ptr, mainprog_ptr->fast_compression); if (retval) return retval; + struct rwpng_write_state write_state; + write_state = (struct rwpng_write_state){ + .outfile = outfile, + .maximum_file_size = mainprog_ptr->maximum_file_size, + .retval = SUCCESS, + }; + png_set_write_fn(png_ptr, &write_state, user_write_data, user_flush_data); + // Palette images generally don't gain anything from filtering png_set_filter(png_ptr, PNG_FILTER_TYPE_BASE, PNG_FILTER_VALUE_NONE); @@ -294,6 +495,7 @@ pngquant_error rwpng_write_image8(FILE *outfile, png8_image *mainprog_ptr) /* set the image parameters appropriately */ int sample_depth; +#if PNG_LIBPNG_VER > 10400 /* old libpng corrupts files with low depth */ if (mainprog_ptr->num_palette <= 2) sample_depth = 1; else if (mainprog_ptr->num_palette <= 4) @@ -301,8 +503,28 @@ pngquant_error rwpng_write_image8(FILE *outfile, png8_image *mainprog_ptr) else if (mainprog_ptr->num_palette <= 16) sample_depth = 4; else +#endif sample_depth = 8; + struct rwpng_chunk *chunk = mainprog_ptr->chunks; + int chunk_num=0; + while(chunk) { + png_unknown_chunk pngchunk = { + .size = chunk->size, + .data = chunk->data, + .location = chunk->location, + }; + memcpy(pngchunk.name, chunk->name, 5); + png_set_unknown_chunks(png_ptr, info_ptr, &pngchunk, 1); + + #if defined(PNG_HAVE_IHDR) && PNG_LIBPNG_VER < 10600 + png_set_unknown_chunk_location(png_ptr, info_ptr, chunk_num, pngchunk.location ? pngchunk.location : PNG_HAVE_IHDR); + #endif + + chunk = chunk->next; + chunk_num++; + } + png_set_IHDR(png_ptr, info_ptr, mainprog_ptr->width, mainprog_ptr->height, sample_depth, PNG_COLOR_TYPE_PALETTE, 0, PNG_COMPRESSION_TYPE_DEFAULT, @@ -310,22 +532,25 @@ pngquant_error rwpng_write_image8(FILE *outfile, png8_image *mainprog_ptr) png_set_PLTE(png_ptr, info_ptr, &mainprog_ptr->palette[0], mainprog_ptr->num_palette); - if (mainprog_ptr->num_trans > 0) + if (mainprog_ptr->num_trans > 0) { png_set_tRNS(png_ptr, info_ptr, mainprog_ptr->trans, mainprog_ptr->num_trans, NULL); + } rwpng_write_end(&info_ptr, &png_ptr, mainprog_ptr->row_pointers); - return SUCCESS; + return write_state.retval; } -pngquant_error rwpng_write_image24(FILE *outfile, png24_image *mainprog_ptr, int filter) +pngquant_error rwpng_write_image24(FILE *outfile, const png24_image *mainprog_ptr, int filter) { png_structp png_ptr; png_infop info_ptr; - pngquant_error retval = rwpng_write_image_init((rwpng_png_image*)mainprog_ptr, &png_ptr, &info_ptr, outfile, 0); + pngquant_error retval = rwpng_write_image_init((rwpng_png_image*)mainprog_ptr, &png_ptr, &info_ptr, 0); if (retval) return retval; + png_init_io(png_ptr, outfile); + rwpng_set_gamma(info_ptr, png_ptr, mainprog_ptr->gamma); png_set_IHDR(png_ptr, info_ptr, mainprog_ptr->width, mainprog_ptr->height, @@ -345,6 +570,13 @@ pngquant_error rwpng_write_image24(FILE *outfile, png24_image *mainprog_ptr, int } +static void rwpng_warning_stderr_handler(png_structp png_ptr, png_const_charp msg) { + fprintf(stderr, " %s\n", msg); +} + +static void rwpng_warning_silent_handler(png_structp png_ptr, png_const_charp msg) { +} + static void rwpng_error_handler(png_structp png_ptr, png_const_charp msg) { rwpng_png_image *mainprog_ptr; diff --git a/rwpng.h b/rwpng.h index 3d50d51..061c1b2 100644 --- a/rwpng.h +++ b/rwpng.h @@ -4,35 +4,38 @@ --------------------------------------------------------------------------- - Copyright (c) 1998-2000 Greg Roelofs. All rights reserved. + © 1998-2000 by Greg Roelofs. + © 2009-2014 by Kornel Lesiński. - 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. + All rights reserved. - 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: + Redistribution and use in source and binary forms, with or without modification, + are permitted provided that the following conditions are met: - 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: + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. - This product includes software developed by Greg Roelofs - and contributors for the book, "PNG: The Definitive Guide," - published by O'Reilly and Associates. + 2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ---------------------------------------------------------------------------*/ #ifndef RWPNG_H #define RWPNG_H -#include "png.h" /* libpng header; includes zlib.h */ +#include "png.h" /* if this include fails, you need to install libpng (e.g. libpng-devel package) */ #include #ifndef USE_COCOA @@ -47,27 +50,50 @@ typedef enum { NOT_OVERWRITING_ERROR = 15, CANT_WRITE_ERROR = 16, OUT_OF_MEMORY_ERROR = 17, - WRONG_ARCHITECTURE = 18, // Missing SSE3 + WRONG_ARCHITECTURE = 18, // Missing SSE PNG_OUT_OF_MEMORY_ERROR = 24, LIBPNG_FATAL_ERROR = 25, LIBPNG_INIT_ERROR = 35, + TOO_LARGE_FILE = 98, TOO_LOW_QUALITY = 99, } pngquant_error; +struct rwpng_chunk { + struct rwpng_chunk *next; + png_byte *data; + png_size_t size; + png_byte name[5]; + png_byte location; +}; + +#if USE_LCMS +typedef enum { + NONE = 0, + ICCP = 1, // used ICC profile + ICCP_WARN_GRAY = 2, // ignore and warn about GRAY ICC profile + GAMA_CHRM = 3, // used gAMA and cHARM +} lcms_transform; +#endif + typedef struct { jmp_buf jmpbuf; png_uint_32 width; png_uint_32 height; + png_size_t file_size; double gamma; unsigned char **row_pointers; unsigned char *rgba_data; - png_size_t file_size; + struct rwpng_chunk *chunks; +#if USE_LCMS + lcms_transform lcms_status; +#endif } png24_image; typedef struct { jmp_buf jmpbuf; png_uint_32 width; png_uint_32 height; + png_size_t maximum_file_size; double gamma; unsigned char **row_pointers; unsigned char *indexed_data; @@ -75,6 +101,7 @@ typedef struct { unsigned int num_trans; png_color palette[256]; unsigned char trans[256]; + struct rwpng_chunk *chunks; char fast_compression; } png8_image; @@ -88,9 +115,10 @@ typedef union { void rwpng_version_info(FILE *fp); -pngquant_error rwpng_read_image24(FILE *infile, png24_image *mainprog_ptr); - -pngquant_error rwpng_write_image8(FILE *outfile, png8_image *mainprog_ptr); -pngquant_error rwpng_write_image24(FILE *outfile, png24_image *mainprog_ptr, int filter); +pngquant_error rwpng_read_image24(FILE *infile, png24_image *mainprog_ptr, int verbose); +pngquant_error rwpng_write_image8(FILE *outfile, const png8_image *mainprog_ptr); +pngquant_error rwpng_write_image24(FILE *outfile, const png24_image *mainprog_ptr, int filter); +void rwpng_free_image24(png24_image *); +void rwpng_free_image8(png8_image *); #endif diff --git a/rwpng_cocoa.m b/rwpng_cocoa.m index 7de0f96..394759d 100644 --- a/rwpng_cocoa.m +++ b/rwpng_cocoa.m @@ -38,11 +38,12 @@ int rwpng_read_image24_cocoa(FILE *fp, png24_image *out) colorspace, kCGImageAlphaPremultipliedLast); + CGColorSpaceRelease(colorspace); + if (!context) return READ_ERROR; CGContextDrawImage(context, CGRectMake(0.0, 0.0, width, height), image); CGContextRelease(context); - CGColorSpaceRelease(colorspace); } // reverse premultiplication