Skip to content

Commit

Permalink
Updated rwpng
Browse files Browse the repository at this point in the history
  • Loading branch information
kornelski committed Dec 5, 2015
1 parent 1af8767 commit f8c24a4
Show file tree
Hide file tree
Showing 3 changed files with 79 additions and 43 deletions.
96 changes: 64 additions & 32 deletions src/rwpng.c
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
---------------------------------------------------------------------------
© 1998-2000 by Greg Roelofs.
© 2009-2014 by Kornel Lesiński.
© 2009-2015 by Kornel Lesiński.
All rights reserved.
Expand Down Expand Up @@ -35,6 +35,7 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <limits.h>

#include "png.h"
#include "rwpng.h"
Expand All @@ -55,6 +56,10 @@
#define omp_get_max_threads() 1
#endif

#if PNG_LIBPNG_VER < 10500
typedef png_const_charp png_const_bytep;
#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);
Expand All @@ -66,17 +71,22 @@ void rwpng_version_info(FILE *fp)
const char *pngver = png_get_header_ver(NULL);

#if USE_COCOA
fprintf(fp, " Using libpng %s and Apple Cocoa image reader.\n", pngver);
fprintf(fp, " Color profiles are supported via Cocoa. Using libpng %s.\n", pngver);
#elif USE_LCMS
fprintf(fp, " Using libpng %s with Little CMS color profile support.\n", pngver);
fprintf(fp, " Color profiles are supported via Little CMS. Using libpng %s.\n", pngver);
#else
fprintf(fp, " Using libpng %s and Apple Cocoa image reader.\n", pngver);
fprintf(fp, " Compiled without support for color profiles. Using libpng %s.\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);
"Please recompile pngquant with the current version of libpng (1.6 or later).\n", fp);
} else if (strcmp(pngver, "1.6.") < 0) {
#if defined(PNG_UNKNOWN_CHUNKS_SUPPORTED)
fputs("\nWARNING: Your version of libpng is old and has buggy support for custom chunks.\n"
"Please recompile pngquant with the current version of libpng (1.6 or later).\n", fp);
#endif
}
#endif
}
Expand Down Expand Up @@ -113,11 +123,7 @@ static void user_write_data(png_structp png_ptr, png_bytep data, png_size_t leng
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)) {
if (!fwrite(data, length, 1, write_state->outfile)) {
write_state->retval = CANT_WRITE_ERROR;
}

Expand All @@ -130,15 +136,15 @@ static void user_flush_data(png_structp png_ptr)
}


static png_bytepp rwpng_create_row_pointers(png_infop info_ptr, png_structp png_ptr, unsigned char *base, unsigned int height, unsigned int rowbytes)
static png_bytepp rwpng_create_row_pointers(png_infop info_ptr, png_structp png_ptr, unsigned char *base, unsigned int height, png_size_t rowbytes)
{
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;
for(unsigned int row = 0; row < height; ++row) {
for(size_t row = 0; row < height; row++) {
row_pointers[row] = base + row * rowbytes;
}
return row_pointers;
Expand Down Expand Up @@ -179,7 +185,7 @@ static int read_chunk_callback(png_structp png_ptr, png_unknown_chunkp in_chunk)
26 = wrong PNG color type (no alpha channel)
*/

pngquant_error rwpng_read_image24_libpng(FILE *infile, png24_image *mainprog_ptr, int verbose)
static pngquant_error rwpng_read_image24_libpng(FILE *infile, png24_image *mainprog_ptr, int verbose)
{
png_structp png_ptr = NULL;
png_infop info_ptr = NULL;
Expand All @@ -206,21 +212,29 @@ pngquant_error rwpng_read_image24_libpng(FILE *infile, png24_image *mainprog_ptr
return LIBPNG_FATAL_ERROR; /* fatal libpng error (via longjmp()) */
}

#if PNG_LIBPNG_VER >= 10500 && defined(PNG_UNKNOWN_CHUNKS_SUPPORTED)
/* copy standard chunks too */
png_set_keep_unknown_chunks(png_ptr, PNG_HANDLE_CHUNK_IF_SAFE, (png_const_bytep)"pHYs\0iTXt\0tEXt\0zTXt", 4);
#endif
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);

png_read_info(png_ptr, info_ptr); /* read all PNG info up to image data */


/* alternatively, could make separate calls to png_get_image_width(),
* etc., but want bit_depth and color_type for later [don't care about
* 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);

// For overflow safety reject images that won't fit in 32-bit
if (mainprog_ptr->width > INT_MAX/mainprog_ptr->height) {
png_destroy_read_struct(&png_ptr, &info_ptr, NULL);
return PNG_OUT_OF_MEMORY_ERROR; /* not quite true, but whatever */
}

/* expand palette images to RGB, low-bit-depth grayscale images to 8 bits,
* transparency chunks to full alpha channel; strip 16-bit-per-sample
Expand All @@ -235,7 +249,7 @@ pngquant_error rwpng_read_image24_libpng(FILE *infile, png24_image *mainprog_ptr
#else
fprintf(stderr, "pngquant readpng: image is neither RGBA nor GA\n");
png_destroy_read_struct(&png_ptr, &info_ptr, NULL);
mainprog_ptr->retval = 26;
mainprog_ptr->retval = WRONG_INPUT_COLOR_TYPE;
return mainprog_ptr->retval;
#endif
}
Expand All @@ -248,12 +262,22 @@ pngquant_error rwpng_read_image24_libpng(FILE *infile, png24_image *mainprog_ptr
png_set_gray_to_rgb(png_ptr);
}


/* get source gamma for gamma correction, or use sRGB default */

double gamma = 0.45455;
if (!png_get_valid(png_ptr, info_ptr, PNG_INFO_sRGB)) {
if (png_get_valid(png_ptr, info_ptr, PNG_INFO_sRGB)) {
mainprog_ptr->input_color = RWPNG_SRGB;
mainprog_ptr->output_color = RWPNG_SRGB;
} else {
png_get_gAMA(png_ptr, info_ptr, &gamma);
if (gamma > 0 && gamma <= 1.0) {
mainprog_ptr->input_color = RWPNG_GAMA_ONLY;
mainprog_ptr->output_color = RWPNG_GAMA_ONLY;
} else {
fprintf(stderr, "pngquant readpng: ignored out-of-range gamma %f\n", gamma);
mainprog_ptr->input_color = RWPNG_NONE;
mainprog_ptr->output_color = RWPNG_NONE;
gamma = 0.45455;
}
}
mainprog_ptr->gamma = gamma;

Expand All @@ -266,7 +290,7 @@ pngquant_error rwpng_read_image24_libpng(FILE *infile, png24_image *mainprog_ptr

rowbytes = png_get_rowbytes(png_ptr, info_ptr);

if ((mainprog_ptr->rgba_data = malloc(rowbytes*mainprog_ptr->height)) == NULL) {
if ((mainprog_ptr->rgba_data = malloc(rowbytes * mainprog_ptr->height)) == NULL) {
fprintf(stderr, "pngquant readpng: unable to allocate image data\n");
png_destroy_read_struct(&png_ptr, &info_ptr, NULL);
return PNG_OUT_OF_MEMORY_ERROR;
Expand Down Expand Up @@ -296,8 +320,6 @@ pngquant_error rwpng_read_image24_libpng(FILE *infile, png24_image *mainprog_ptr
/* 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)) {

Expand All @@ -306,10 +328,12 @@ pngquant_error rwpng_read_image24_libpng(FILE *infile, png24_image *mainprog_ptr

/* only RGB (and GRAY) valid for PNGs */
if (colorspace == cmsSigRgbData && COLOR_PNG) {
mainprog_ptr->lcms_status = ICCP;
mainprog_ptr->input_color = RWPNG_ICCP;
mainprog_ptr->output_color = RWPNG_SRGB;
} else {
if (colorspace == cmsSigGrayData && !COLOR_PNG) {
mainprog_ptr->lcms_status = ICCP_WARN_GRAY;
mainprog_ptr->input_color = RWPNG_ICCP_WARN_GRAY;
mainprog_ptr->output_color = RWPNG_SRGB;
}
cmsCloseProfile(hInProfile);
hInProfile = NULL;
Expand Down Expand Up @@ -339,7 +363,8 @@ pngquant_error rwpng_read_image24_libpng(FILE *infile, png24_image *mainprog_ptr

cmsFreeToneCurve(GammaTable[0]);

mainprog_ptr->lcms_status = GAMA_CHRM;
mainprog_ptr->input_color = RWPNG_GAMA_CHRM;
mainprog_ptr->output_color = RWPNG_SRGB;
}

/* transform image to sRGB colorspace */
Expand Down Expand Up @@ -452,7 +477,7 @@ static pngquant_error rwpng_write_image_init(rwpng_png_image *mainprog_ptr, png_
}


void rwpng_write_end(png_infopp info_ptr_p, png_structpp png_ptr_p, png_bytepp row_pointers)
static void rwpng_write_end(png_infopp info_ptr_p, png_structpp png_ptr_p, png_bytepp row_pointers)
{
png_write_info(*png_ptr_p, *info_ptr_p);

Expand All @@ -465,11 +490,14 @@ void rwpng_write_end(png_infopp info_ptr_p, png_structpp png_ptr_p, png_bytepp r
png_destroy_write_struct(png_ptr_p, info_ptr_p);
}

void rwpng_set_gamma(png_infop info_ptr, png_structp png_ptr, double gamma)
static void rwpng_set_gamma(png_infop info_ptr, png_structp png_ptr, double gamma, rwpng_color_transform color)
{
/* remap sets gamma to 0.45455 */
png_set_gAMA(png_ptr, info_ptr, gamma);
png_set_sRGB(png_ptr, info_ptr, 0); // 0 = Perceptual
if (color != RWPNG_GAMA_ONLY && color != RWPNG_NONE) {
png_set_gAMA(png_ptr, info_ptr, gamma);
}
if (color == RWPNG_SRGB) {
png_set_sRGB(png_ptr, info_ptr, 0); // 0 = Perceptual
}
}

pngquant_error rwpng_write_image8(FILE *outfile, const png8_image *mainprog_ptr)
Expand All @@ -491,7 +519,7 @@ pngquant_error rwpng_write_image8(FILE *outfile, const png8_image *mainprog_ptr)
// Palette images generally don't gain anything from filtering
png_set_filter(png_ptr, PNG_FILTER_TYPE_BASE, PNG_FILTER_VALUE_NONE);

rwpng_set_gamma(info_ptr, png_ptr, mainprog_ptr->gamma);
rwpng_set_gamma(info_ptr, png_ptr, mainprog_ptr->gamma, mainprog_ptr->output_color);

/* set the image parameters appropriately */
int sample_depth;
Expand Down Expand Up @@ -538,6 +566,10 @@ pngquant_error rwpng_write_image8(FILE *outfile, const png8_image *mainprog_ptr)

rwpng_write_end(&info_ptr, &png_ptr, mainprog_ptr->row_pointers);

if (SUCCESS == write_state.retval && write_state.maximum_file_size && write_state.bytes_written > write_state.maximum_file_size) {
return TOO_LARGE_FILE;
}

return write_state.retval;
}

Expand All @@ -551,7 +583,7 @@ pngquant_error rwpng_write_image24(FILE *outfile, const png24_image *mainprog_pt

png_init_io(png_ptr, outfile);

rwpng_set_gamma(info_ptr, png_ptr, mainprog_ptr->gamma);
rwpng_set_gamma(info_ptr, png_ptr, mainprog_ptr->gamma, mainprog_ptr->output_color);

png_set_IHDR(png_ptr, info_ptr, mainprog_ptr->width, mainprog_ptr->height,
8, PNG_COLOR_TYPE_RGB_ALPHA,
Expand Down Expand Up @@ -589,7 +621,7 @@ static void rwpng_error_handler(png_structp png_ptr, png_const_charp msg)
* regardless of whether _BSD_SOURCE or anything else has (or has not)
* been defined. */

fprintf(stderr, " error: %s\n", msg);
fprintf(stderr, " error: %s (libpng failed)\n", msg);
fflush(stderr);

mainprog_ptr = png_get_error_ptr(png_ptr);
Expand Down
24 changes: 13 additions & 11 deletions src/rwpng.h
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
---------------------------------------------------------------------------
© 1998-2000 by Greg Roelofs.
© 2009-2014 by Kornel Lesiński.
© 2009-2015 by Kornel Lesiński.
All rights reserved.
Expand Down Expand Up @@ -53,6 +53,7 @@ typedef enum {
WRONG_ARCHITECTURE = 18, // Missing SSE
PNG_OUT_OF_MEMORY_ERROR = 24,
LIBPNG_FATAL_ERROR = 25,
WRONG_INPUT_COLOR_TYPE = 26,
LIBPNG_INIT_ERROR = 35,
TOO_LARGE_FILE = 98,
TOO_LOW_QUALITY = 99,
Expand All @@ -66,14 +67,15 @@ struct rwpng_chunk {
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
RWPNG_NONE,
RWPNG_SRGB, // sRGB chunk
RWPNG_ICCP, // used ICC profile
RWPNG_ICCP_WARN_GRAY, // ignore and warn about GRAY ICC profile
RWPNG_GAMA_CHRM, // used gAMA and cHRM
RWPNG_GAMA_ONLY, // used gAMA only (i.e. not sRGB)
RWPNG_COCOA, // Colors handled by Cocoa reader
} rwpng_color_transform;

typedef struct {
jmp_buf jmpbuf;
Expand All @@ -84,9 +86,8 @@ typedef struct {
unsigned char **row_pointers;
unsigned char *rgba_data;
struct rwpng_chunk *chunks;
#if USE_LCMS
lcms_transform lcms_status;
#endif
rwpng_color_transform input_color;
rwpng_color_transform output_color;
} png24_image;

typedef struct {
Expand All @@ -102,6 +103,7 @@ typedef struct {
png_color palette[256];
unsigned char trans[256];
struct rwpng_chunk *chunks;
rwpng_color_transform output_color;
char fast_compression;
} png8_image;

Expand Down
2 changes: 2 additions & 0 deletions src/rwpng_cocoa.m
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,8 @@ int rwpng_read_image24_cocoa(FILE *fp, png24_image *out)
}

out->gamma = 0.45455;
out->input_color = RWPNG_COCOA;
out->output_color = RWPNG_SRGB;
out->width = width;
out->height = height;
out->rgba_data = (unsigned char *)pixel_data;
Expand Down

0 comments on commit f8c24a4

Please sign in to comment.