summaryrefslogtreecommitdiff
path: root/common/im_png.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'common/im_png.cpp')
-rwxr-xr-xcommon/im_png.cpp533
1 files changed, 209 insertions, 324 deletions
diff --git a/common/im_png.cpp b/common/im_png.cpp
index 606693a..155afff 100755
--- a/common/im_png.cpp
+++ b/common/im_png.cpp
@@ -1,206 +1,115 @@
-// ---------------------------------------------------------------------------
-//
-// Copyright (c) 1998-1999 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.
-//
-// ---------------------------------------------------------------------------*/
-
-#ifndef _LINUX
-#include <setjmp.h>
-#endif
#include <stdio.h>
#include <stdlib.h>
#include <png.h>
#include "typedefs.h"
-#ifndef TRUE
-#define TRUE 1
-#define FALSE 0
-#endif
-
-#ifndef MAX
-#define MAX(a,b) ((a) > (b)? (a) : (b))
-#define MIN(a,b) ((a) < (b)? (a) : (b))
-#endif
-
#define alpha_composite(composite, fg, alpha, bg) { \
- ush temp = ((ush)(fg)*(ush)(alpha) + \
- (ush)(bg)*(ush)(255 - (ush)(alpha)) + (ush)128); \
- (composite) = (uch)((temp + (temp >> 8)) >> 8); \
+ unsigned short temp = ((unsigned short)(fg)*(unsigned short)(alpha) + \
+ (unsigned short)(bg)*(unsigned short)(255 - (unsigned short)(alpha)) + (unsigned short)128); \
+ (composite) = (unsigned char)((temp + (temp >> 8)) >> 8); \
}
-typedef unsigned char uch;
-typedef unsigned short ush;
-typedef unsigned long ulg;
-
-/* prototypes for public functions in readpng.c */
-/*
-void readpng_version_info(void);
-
-int readpng_init(FILE *infile, long *pWidth, long *pHeight);
-
-int readpng_get_bgcolor(uch *bg_red, uch *bg_green, uch *bg_blue);
-
-uch *readpng_get_image(double display_exponent, int *pChannels,
- ulg *pRowbytes);
-
-void readpng_cleanup(int free_image_data);
-*/
-
-// ========================================================
-
-static png_structp png_ptr = NULL;
-static png_infop info_ptr = NULL;
-
-static png_uint_32 width, height;
-static int bit_depth, color_type;
-static uch *image_data = NULL;
-
-static uch bg_red=0, bg_green=0, bg_blue=0;
-
-static double display_exponent;
-
-static ulg image_width, image_height, image_rowbytes;
-static int image_channels;
-static FILE *infile;
-
// ========================================================
-// return value = 0 for success, 1 for bad sig, 2 for bad IHDR, 4 for no mem
-static int readpng_init(FILE *infile, ulg *pWidth, ulg *pHeight)
+LC_IMAGE* OpenPNG(char* filename)
{
- uch sig[8];
+ unsigned char sig[8], red, green, blue;
+ unsigned char *image_data = NULL;
+ unsigned char *src, *dest;
+ unsigned char r, g, b, a;
+ unsigned long i, row;
+ unsigned long image_rowbytes;
+ png_color_16p pBackground;
+ png_structp png_ptr = NULL;
+ png_infop info_ptr = NULL;
+ png_uint_32 width, height;
+ png_bytepp row_pointers = NULL;
+ int bit_depth, color_type;
+ int image_channels;
+ double gamma;
+ FILE* f;
+
+ f = fopen(filename, "rb");
+ if (f == NULL)
+ return NULL;
- // first do a quick check that the file really is a PNG image; could
- // have used slightly more general png_sig_cmp() function instead
- fread(sig, 1, 8, infile);
+ fread(sig, 1, 8, f);
if (!png_check_sig(sig, 8))
- return 1; // bad signature
-
+ {
+ fclose(f);
+ return NULL; // bad signature
+ }
- // could pass pointers to user-defined error handlers instead of NULLs:
png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
if (!png_ptr)
- return 4; // out of memory
+ {
+ fclose(f);
+ return NULL; // out of memory
+ }
info_ptr = png_create_info_struct(png_ptr);
if (!info_ptr)
{
png_destroy_read_struct(&png_ptr, NULL, NULL);
- return 4; // out of memory
+ fclose(f);
+ return NULL; // out of memory
}
- // we could create a second info struct here (end_info), but it's only
- // useful if we want to keep pre- and post-IDAT chunk info separated
- // (mainly for PNG-aware image editors and converters)
-
-
- // setjmp() must be called in every function that calls a PNG-reading
- // libpng function
if (setjmp(png_ptr->jmpbuf))
{
png_destroy_read_struct(&png_ptr, &info_ptr, NULL);
- return 2;
+ fclose(f);
+ return NULL;
}
- png_init_io(png_ptr, infile);
+ png_init_io(png_ptr, f);
png_set_sig_bytes(png_ptr, 8); // we already read the 8 signature bytes
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, &width, &height, &bit_depth, &color_type,
- NULL, NULL, NULL);
- *pWidth = width;
- *pHeight = height;
+ NULL, NULL, NULL);
- // OK, that's all we need for now; return happy
- return 0;
-}
-
-// returns 0 if succeeds, 1 if fails due to no bKGD chunk, 2 if libpng error;
-// scales values to 8-bit if necessary
-static int readpng_get_bgcolor(uch *red, uch *green, uch *blue)
-{
- png_color_16p pBackground;
-
- // setjmp() must be called in every function that calls a PNG-reading
- // libpng function
if (setjmp(png_ptr->jmpbuf))
{
png_destroy_read_struct(&png_ptr, &info_ptr, NULL);
- return 2;
+ fclose(f);
+ return NULL;
}
- if (!png_get_valid(png_ptr, info_ptr, PNG_INFO_bKGD))
- return 1;
-
- // it is not obvious from the libpng documentation, but this function
- // takes a pointer to a pointer, and it always returns valid red, green
- // and blue values, regardless of color_type:
- png_get_bKGD(png_ptr, info_ptr, &pBackground);
-
- // however, it always returns the raw bKGD data, regardless of any
- // bit-depth transformations, so check depth and adjust if necessary
- if (bit_depth == 16)
+ if (png_get_valid(png_ptr, info_ptr, PNG_INFO_bKGD))
{
- *red = pBackground->red >> 8;
- *green = pBackground->green >> 8;
- *blue = pBackground->blue >> 8;
- }
- else if (color_type == PNG_COLOR_TYPE_GRAY && bit_depth < 8)
- {
- if (bit_depth == 1)
- *red = *green = *blue = pBackground->gray? 255 : 0;
- else if (bit_depth == 2)
- *red = *green = *blue = (255/3) * pBackground->gray;
- else // bit_depth == 4
- *red = *green = *blue = (255/15) * pBackground->gray;
- }
- else
- {
- *red = (uch)pBackground->red;
- *green = (uch)pBackground->green;
- *blue = (uch)pBackground->blue;
- }
-
- return 0;
-}
+ png_get_bKGD(png_ptr, info_ptr, &pBackground);
-// display_exponent == LUT_exponent * CRT_exponent
-static uch *readpng_get_image(double display_exponent, int *pChannels, ulg *pRowbytes)
-{
- double gamma;
- png_uint_32 i, rowbytes;
- png_bytepp row_pointers = NULL;
+ // however, it always returns the raw bKGD data, regardless of any
+ // bit-depth transformations, so check depth and adjust if necessary
+ if (bit_depth == 16)
+ {
+ red = pBackground->red >> 8;
+ green = pBackground->green >> 8;
+ blue = pBackground->blue >> 8;
+ }
+ else if (color_type == PNG_COLOR_TYPE_GRAY && bit_depth < 8)
+ {
+ if (bit_depth == 1)
+ red = green = blue = pBackground->gray? 255 : 0;
+ else if (bit_depth == 2)
+ red = green = blue = (255/3) * pBackground->gray;
+ else // bit_depth == 4
+ red = green = blue = (255/15) * pBackground->gray;
+ }
+ else
+ {
+ red = (unsigned char)pBackground->red;
+ green = (unsigned char)pBackground->green;
+ blue = (unsigned char)pBackground->blue;
+ }
+ }
- // setjmp() must be called in every function that calls a PNG-reading
- // libpng function
+ // decode the image, all at once
if (setjmp(png_ptr->jmpbuf))
{
png_destroy_read_struct(&png_ptr, &info_ptr, NULL);
+ fclose(f);
return NULL;
}
@@ -219,22 +128,20 @@ static uch *readpng_get_image(double display_exponent, int *pChannels, ulg *pRow
color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
png_set_gray_to_rgb(png_ptr);
- // unlike the example in the libpng documentation, we have *no* idea where
- // this file may have come from--so if it doesn't have a file gamma, don't
- // do any correction ("do no harm")
if (png_get_gAMA(png_ptr, info_ptr, &gamma))
- png_set_gamma(png_ptr, display_exponent, gamma);
+ png_set_gamma(png_ptr, 2.2, gamma);
// all transformations have been registered; now update info_ptr data,
// get rowbytes and channels, and allocate image memory
png_read_update_info(png_ptr, info_ptr);
- *pRowbytes = rowbytes = png_get_rowbytes(png_ptr, info_ptr);
- *pChannels = (int)png_get_channels(png_ptr, info_ptr);
+ image_rowbytes = png_get_rowbytes(png_ptr, info_ptr);
+ image_channels = (int)png_get_channels(png_ptr, info_ptr);
- if ((image_data = (uch *)malloc(rowbytes*height)) == NULL)
+ if ((image_data = (unsigned char*)malloc(image_rowbytes*height)) == NULL)
{
png_destroy_read_struct(&png_ptr, &info_ptr, NULL);
+ fclose(f);
return NULL;
}
@@ -242,13 +149,13 @@ static uch *readpng_get_image(double display_exponent, int *pChannels, ulg *pRow
{
png_destroy_read_struct(&png_ptr, &info_ptr, NULL);
free(image_data);
- image_data = NULL;
+ fclose(f);
return NULL;
}
// set the individual row_pointers to point at the correct offsets
for (i = 0; i < height; ++i)
- row_pointers[i] = image_data + i*rowbytes;
+ row_pointers[i] = image_data + i*image_rowbytes;
// now we can go ahead and just read the whole image
png_read_image(png_ptr, row_pointers);
@@ -260,177 +167,45 @@ static uch *readpng_get_image(double display_exponent, int *pChannels, ulg *pRow
png_read_end(png_ptr, NULL);
- return image_data;
-}
-
-static void readpng_cleanup(int free_image_data)
-{
- if (free_image_data && image_data)
- {
- free(image_data);
- image_data = NULL;
- }
-
- if (png_ptr && info_ptr)
- {
- png_destroy_read_struct(&png_ptr, &info_ptr, NULL);
- png_ptr = NULL;
- info_ptr = NULL;
- }
-}
-
-LC_IMAGE* OpenPNG(char* filename)
-{
- double LUT_exponent; // just the lookup table
- double CRT_exponent = 2.2; // just the monitor
- double default_display_exponent; // whole display system
-
- char *p;
- int rc;
- int error = 0;
-
-
- // First set the default value for our display-system exponent, i.e.,
- // the product of the CRT exponent and the exponent corresponding to
- // the frame-buffer's lookup table (LUT), if any. This is not an
- // exhaustive list of LUT values (e.g., OpenStep has a lot of weird
- // ones), but it should cover 99% of the current possibilities. And
- // yes, these ifdefs are completely wasted in a Windows program...
-#if defined(NeXT)
- LUT_exponent = 1.0 / 2.2;
- // if (some_next_function_that_returns_gamma(&next_gamma))
- // LUT_exponent = 1.0 / next_gamma;
-#elif defined(sgi)
- LUT_exponent = 1.0 / 1.7;
- // there doesn't seem to be any documented function to get the
- // "gamma" value, so we do it the hard way
- infile = fopen("/etc/config/system.glGammaVal", "r");
- if (infile)
- {
- double sgi_gamma;
-
- fgets(tmpline, 80, infile);
- fclose(infile);
- sgi_gamma = atof(tmpline);
- if (sgi_gamma > 0.0)
- LUT_exponent = 1.0 / sgi_gamma;
- }
-#elif defined(Macintosh)
- LUT_exponent = 1.8 / 2.61;
- // if (some_mac_function_that_returns_gamma(&mac_gamma))
- // LUT_exponent = mac_gamma / 2.61;
-#else
- LUT_exponent = 1.0; // assume no LUT: most PCs
-#endif
-
- // the defaults above give 1.0, 1.3, 1.5 and 2.2, respectively:
- default_display_exponent = LUT_exponent * CRT_exponent;
-
- // If the user has set the SCREEN_GAMMA environment variable as suggested
- // (somewhat imprecisely) in the libpng documentation, use that; otherwise
- // use the default value we just calculated. Either way, the user may
- // override this via a command-line option.
- if ((p = getenv("SCREEN_GAMMA")) != NULL)
- display_exponent = atof(p);
- else
- display_exponent = default_display_exponent;
-
- if (!(infile = fopen(filename, "rb")))
- {
- printf("can't open PNG file [%s]\n", filename);
- ++error;
- }
- else
- {
- if ((rc = readpng_init(infile, &image_width, &image_height)) != 0)
- {
- switch (rc)
- {
- case 1:
- printf("[%s] is not a PNG file: incorrect signature\n",filename);
- break;
- case 2:
- printf("[%s] has bad IHDR (libpng longjmp)\n", filename);
- break;
- case 4:
- printf("insufficient memory\n");
- break;
- default:
- printf("unknown readpng_init() error\n");
- break;
- }
- ++error;
- }
- if (error)
- fclose(infile);
- }
-
- if (error)
- return NULL;
-
- // if the user didn't specify a background color on the command line,
- // check for one in the PNG file--if not, the initialized values of 0
- // (black) will be used
-
- if (readpng_get_bgcolor(&bg_red, &bg_green, &bg_blue) > 1)
- {
- readpng_cleanup(TRUE);
- printf("libpng error while checking for background color\n");
- return NULL;
- }
-
- // decode the image, all at once
- image_data = readpng_get_image(display_exponent, &image_channels,
- &image_rowbytes);
-
- // done with PNG file, so clean up to minimize memory usage (but do NOT
- // nuke image_data!)
- readpng_cleanup(FALSE);
- fclose(infile);
+ // done with PNG file, so clean up to minimize memory usage
+ png_destroy_read_struct(&png_ptr, &info_ptr, NULL);
+ fclose(f);
if (!image_data)
- {
- printf("unable to decode PNG image\n");
return NULL;
- }
// get our buffer set to hold data
- LC_IMAGE* image = (LC_IMAGE*)malloc(image_width*image_height*3 + sizeof(LC_IMAGE));
+ LC_IMAGE* image = (LC_IMAGE*)malloc(width*height*3 + sizeof(LC_IMAGE));
if (image == NULL)
{
-// MessageBox(NULL, "Error", "Cannot allocate memory", MB_ICONSTOP);
free(image_data);
return NULL;
}
- image->width = (unsigned short)image_width;
- image->height = (unsigned short)image_height;
+ image->width = (unsigned short)width;
+ image->height = (unsigned short)height;
image->bits = (char*)image + sizeof(LC_IMAGE);
- uch *src, *dest;
- uch r, g, b, a;
- ulg i, row;
-
- for (row = 0; row < image_height; ++row)
+ for (row = 0; row < height; row++)
{
src = image_data + row*image_rowbytes;
dest = (unsigned char*)image->bits + row*image_rowbytes;
if (image_channels == 3)
{
- for (i = image_width; i > 0; --i)
+ for (i = width; i > 0; i--)
{
r = *src++;
g = *src++;
b = *src++;
- *dest++ = b;
- *dest++ = g; /* note reverse order */
*dest++ = r;
+ *dest++ = g;
+ *dest++ = b;
}
}
- else /* if (image_channels == 4) */
+ else // if (image_channels == 4)
{
- for (i = image_width; i > 0; --i)
+ for (i = width; i > 0; i--)
{
r = *src++;
g = *src++;
@@ -439,25 +214,25 @@ LC_IMAGE* OpenPNG(char* filename)
if (a == 255)
{
- *dest++ = b;
- *dest++ = g;
*dest++ = r;
+ *dest++ = g;
+ *dest++ = b;
}
else if (a == 0)
{
- *dest++ = bg_blue;
- *dest++ = bg_green;
- *dest++ = bg_red;
+ *dest++ = red;
+ *dest++ = green;
+ *dest++ = blue;
}
else
{
- /* this macro (copied from png.h) composites the
- * foreground and background values and puts the
- * result into the first argument; there are no
- * side effects with the first argument */
- alpha_composite(*dest++, b, a, bg_blue);
- alpha_composite(*dest++, g, a, bg_green);
- alpha_composite(*dest++, r, a, bg_red);
+ // this macro (copied from png.h) composites the
+ // foreground and background values and puts the
+ // result into the first argument; there are no
+ // side effects with the first argument
+ alpha_composite(*dest++, r, a, red);
+ alpha_composite(*dest++, g, a, green);
+ alpha_composite(*dest++, b, a, blue);
}
}
}
@@ -467,3 +242,113 @@ LC_IMAGE* OpenPNG(char* filename)
return image;
}
+// ========================================================
+
+bool SavePNG(char* filename, LC_IMAGE* image, bool transparent, bool interlaced, unsigned char* background)
+{
+ FILE *fp;
+ png_structp png_ptr;
+ png_infop info_ptr;
+ png_bytepp row_pointers = NULL;
+ png_color_8 sig_bit;
+ png_color_16 bg;
+ int i;
+
+ fp = fopen(filename, "wb");
+ if (!fp)
+ return false;
+
+ png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
+ if (!png_ptr)
+ {
+ fclose(fp);
+ return false;
+ }
+
+ info_ptr = png_create_info_struct(png_ptr);
+ if (!info_ptr)
+ {
+ png_destroy_write_struct(&png_ptr, NULL);
+ fclose(fp);
+ return false;
+ }
+
+ if (setjmp(png_ptr->jmpbuf))
+ {
+ png_destroy_write_struct(&png_ptr, (png_infopp)NULL);
+ fclose(fp);
+ return false;
+ }
+
+ png_init_io(png_ptr, fp);
+
+ png_set_IHDR(png_ptr, info_ptr, image->width, image->height, 8,
+ transparent ? PNG_COLOR_TYPE_RGB_ALPHA : PNG_COLOR_TYPE_RGB,
+ interlaced ? PNG_INTERLACE_ADAM7 : PNG_INTERLACE_NONE,
+ PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT);
+
+ bg.red = background[0];
+ bg.green = background[1];
+ bg.blue = background[2];
+ png_set_bKGD(png_ptr, info_ptr, &bg);
+
+ png_write_info(png_ptr, info_ptr);
+
+ // Set the true bit depth of the image data
+ sig_bit.red = 8;
+ sig_bit.green = 8;
+ sig_bit.blue = 8;
+ sig_bit.alpha = 8;
+
+ png_set_sBIT(png_ptr, info_ptr, &sig_bit);
+
+ if ((row_pointers = (png_bytepp)malloc(image->height*sizeof(png_bytep))) == NULL)
+ {
+ png_destroy_write_struct(&png_ptr, (png_infopp)NULL);
+ fclose(fp);
+ return false;
+ }
+
+ // set the individual row_pointers to point at the correct offsets
+ if (transparent)
+ {
+ unsigned char *buf, *src, *dst, alpha;
+ dst = buf = (unsigned char*)malloc(image->width*image->height*4);
+ src = (unsigned char*)image->bits;
+
+ for (i = 0; i < image->width*image->height; i++)
+ {
+ if ((src[0] == background[0]) &&
+ (src[1] == background[1]) &&
+ (src[2] == background[2]))
+ alpha = 0;
+ else
+ alpha = 255;
+ *dst++ = *src++;
+ *dst++ = *src++;
+ *dst++ = *src++;
+ *dst++ = alpha;
+ }
+
+ for (i = 0; i < image->height; i++)
+ row_pointers[i] = buf + i*image->width*4;
+ png_write_image(png_ptr, row_pointers);
+
+ free(buf);
+ }
+ else
+ {
+ for (i = 0; i < image->height; i++)
+ row_pointers[i] = (unsigned char*)image->bits + i*image->width*3;
+ png_write_image(png_ptr, row_pointers);
+ }
+
+ free(row_pointers);
+
+ png_write_end(png_ptr, info_ptr);
+ png_destroy_write_struct(&png_ptr, &info_ptr);
+ fclose(fp);
+
+ return true;
+}
+