summaryrefslogtreecommitdiff
path: root/src/im_format_png.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/im_format_png.cpp')
-rw-r--r--src/im_format_png.cpp910
1 files changed, 910 insertions, 0 deletions
diff --git a/src/im_format_png.cpp b/src/im_format_png.cpp
new file mode 100644
index 0000000..1e7f8ba
--- /dev/null
+++ b/src/im_format_png.cpp
@@ -0,0 +1,910 @@
+/** \file
+ * \brief PNG - Portable Network Graphic Format
+ *
+ * See Copyright Notice in im_lib.h
+ * See libPNG Copyright Notice in png.h
+ * $Id: im_format_png.cpp,v 1.1 2008/10/17 06:10:16 scuri Exp $
+ */
+
+#include "im_format.h"
+#include "im_format_all.h"
+#include "im_util.h"
+#include "im_counter.h"
+
+#include "im_binfile.h"
+
+#include <stdlib.h>
+#include <string.h>
+
+#include "png.h"
+
+static void png_user_read_fn(png_structp png_ptr, png_bytep buffer, png_size_t size)
+{
+ imBinFileRead((imBinFile*)png_ptr->io_ptr, buffer, size, 1);
+ if (imBinFileError((imBinFile*)png_ptr->io_ptr))
+ png_error(png_ptr, "Read Error");
+}
+
+static void png_user_write_fn(png_structp png_ptr, png_bytep buffer, png_size_t size)
+{
+ imBinFileWrite((imBinFile*)png_ptr->io_ptr, buffer, size, 1);
+ if (imBinFileError((imBinFile*)png_ptr->io_ptr))
+ png_error(png_ptr, "Write Error");
+}
+
+static void png_user_flush_fn(png_structp png_ptr)
+{
+ (void)png_ptr;
+}
+
+static const char* iPNGCompTable[1] =
+{
+ "DEFLATE"
+};
+
+class imFormatPNG: public imFormat
+{
+ png_structp png_ptr;
+ png_infop info_ptr;
+
+ imBinFile* handle;
+ int interlace_steps, fixbits;
+
+ void iReadAttrib(imAttribTable* attrib_table);
+ void iWriteAttrib(imAttribTable* attrib_table);
+
+public:
+ imFormatPNG()
+ :imFormat("PNG",
+ "Portable Network Graphic Format",
+ "*.png;",
+ iPNGCompTable,
+ 1,
+ 0)
+ {}
+ ~imFormatPNG() {}
+
+ int Open(const char* file_name);
+ int New(const char* file_name);
+ void Close();
+ void* Handle(int index);
+ int ReadImageInfo(int index);
+ int ReadImageData(void* data);
+ int WriteImageInfo();
+ int WriteImageData(void* data);
+ int CanWrite(const char* compression, int color_mode, int data_type) const;
+};
+
+void imFormatRegisterPNG(void)
+{
+ imFormatRegister(new imFormatPNG());
+}
+
+int imFormatPNG::Open(const char* file_name)
+{
+ this->handle = imBinFileOpen(file_name);
+ if (this->handle == NULL)
+ return IM_ERR_OPEN;
+
+ unsigned char sig[8];
+ if (!imBinFileRead(this->handle, sig, 8, 1))
+ {
+ imBinFileClose(this->handle);
+ return IM_ERR_ACCESS;
+ }
+
+ if (png_sig_cmp(sig, 0, 8) != 0)
+ {
+ imBinFileClose(this->handle);
+ return IM_ERR_FORMAT;
+ }
+
+ imBinFileSeekTo(this->handle, 0);
+
+ strcpy(this->compression, "DEFLATE");
+ this->image_count = 1;
+
+ /* Create and initialize the png_struct with the default error handler functions. */
+ this->png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, (png_error_ptr)NULL, (png_error_ptr)NULL);
+ if (!this->png_ptr)
+ {
+ imBinFileClose(this->handle);
+ return IM_ERR_FORMAT;
+ }
+
+ return IM_ERR_NONE;
+}
+
+int imFormatPNG::New(const char* file_name)
+{
+ this->handle = imBinFileNew(file_name);
+ if (this->handle == NULL)
+ return IM_ERR_OPEN;
+
+ this->png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
+ if (this->png_ptr == NULL)
+ {
+ imBinFileClose(this->handle);
+ return IM_ERR_ACCESS;
+ }
+
+ strcpy(this->compression, "DEFLATE");
+ this->image_count = 1;
+
+ return IM_ERR_NONE;
+}
+
+void imFormatPNG::Close()
+{
+ if (this->is_new)
+ png_destroy_write_struct(&this->png_ptr, &this->info_ptr);
+ else
+ png_destroy_read_struct(&this->png_ptr, &this->info_ptr, (png_infopp)NULL);
+
+ imBinFileClose(this->handle);
+}
+
+void* imFormatPNG::Handle(int index)
+{
+ if (index == 0)
+ return (void*)this->handle;
+ else if (index == 1)
+ return (void*)this->png_ptr;
+ else
+ return 0;
+}
+
+void imFormatPNG::iReadAttrib(imAttribTable* attrib_table)
+{
+ double gamma;
+ if (png_get_gAMA(png_ptr, info_ptr, &gamma))
+ {
+ float fvalue = (float)gamma;
+ attrib_table->Set("Gamma", IM_FLOAT, 1, &fvalue);
+ }
+
+ png_uint_32 xr, yr;
+ int res_unit_type = PNG_RESOLUTION_UNKNOWN;
+ if (png_get_pHYs(png_ptr, info_ptr, &xr, &yr, &res_unit_type))
+ {
+ if (res_unit_type == PNG_RESOLUTION_METER)
+ {
+ float xres = xr / 100.0f;
+ float yres = yr / 100.0f;
+ attrib_table->Set("XResolution", IM_FLOAT, 1, &xres);
+ attrib_table->Set("YResolution", IM_FLOAT, 1, &yres);
+ attrib_table->Set("ResolutionUnit", IM_BYTE, 4, "DPC");
+ }
+ }
+
+ png_int_32 x, y;
+ int unit_type;
+ if (png_get_oFFs(png_ptr, info_ptr, &x, &y, &unit_type))
+ {
+ float xpos, ypos;
+
+ if (res_unit_type == PNG_RESOLUTION_UNKNOWN)
+ {
+ if (unit_type == PNG_OFFSET_PIXEL)
+ {
+ xpos = (float)x;
+ ypos = (float)y;
+ }
+ else
+ {
+ xpos = 0; // can not calculate position
+ ypos = 0;
+ }
+ }
+ else
+ {
+ if (unit_type == PNG_OFFSET_PIXEL)
+ {
+ // pixels to centimeters
+ xpos = ((float)x / (float)xr) * 100.0f;
+ ypos = ((float)y / (float)yr) * 100.0f;
+ }
+ else
+ {
+ // micrometers to centimeters
+ xpos = (float)x / 100.0f;
+ ypos = (float)y / 100.0f;
+ }
+ }
+
+ if (xpos && ypos)
+ {
+ // Position is in ResolutionUnits
+ attrib_table->Set("YPosition", IM_FLOAT, 1, &ypos);
+ attrib_table->Set("XPosition", IM_FLOAT, 1, &xpos);
+ }
+ }
+
+ int intent;
+ if (png_get_sRGB(png_ptr, info_ptr, &intent))
+ {
+ if (intent)
+ attrib_table->Set("sRGBIntent", IM_INT, 1, &intent);
+ }
+
+ double chroma[8];
+ if (png_get_cHRM(png_ptr,info_ptr, &chroma[0], &chroma[1], &chroma[2], &chroma[3], &chroma[4], &chroma[5], &chroma[6], &chroma[7]))
+ {
+ float white[2] = {(float)chroma[0], (float)chroma[1]};
+ float primchroma[6] = {(float)chroma[2], (float)chroma[3],
+ (float)chroma[4], (float)chroma[5],
+ (float)chroma[6], (float)chroma[7]};
+ attrib_table->Set("WhitePoint", IM_FLOAT, 2, white);
+ attrib_table->Set("PrimaryChromaticities", IM_FLOAT, 6, primchroma);
+ }
+
+ png_charp pcal_purpose;
+ int pcal_type, pcal_nparams;
+ png_int_32 pcal_limits[2];
+ png_charp pcal_units;
+ png_charpp pcal_params;
+ if (png_get_pCAL(png_ptr, info_ptr, &pcal_purpose, &pcal_limits[0], &pcal_limits[1], &pcal_type, &pcal_nparams, &pcal_units, &pcal_params))
+ {
+ char param_buf[255*100], *param_ptr;
+ int p, size, total_size = 0;
+
+ attrib_table->Set("CalibrationName", IM_BYTE, strlen(pcal_purpose)+1, pcal_purpose);
+ attrib_table->Set("CalibrationLimits", IM_INT, 2, pcal_limits);
+ attrib_table->Set("CalibrationUnits", IM_BYTE, strlen(pcal_units)+1, pcal_units);
+ attrib_table->Set("CalibrationEquation", IM_BYTE, 1, &pcal_type);
+
+ param_ptr = &param_buf[0];
+ for (p = 0; p < pcal_nparams; p++)
+ {
+ size = strlen(pcal_params[p]);
+ memcpy(param_ptr, pcal_params[p], size);
+ param_ptr += size;
+ *param_ptr = '\n';
+ param_ptr++;
+ total_size += size+1;
+ }
+ *param_ptr = '0';
+
+ attrib_table->Set("CalibrationParam", IM_BYTE, total_size+1, param_buf);
+ }
+
+ int num_trans;
+ png_bytep trans;
+ png_color_16p trans_values;
+ if (png_get_tRNS(png_ptr, info_ptr, &trans, &num_trans, &trans_values))
+ {
+ if (imColorModeSpace(file_color_mode) == IM_MAP)
+ {
+ attrib_table->Set("TransparencyIndex", IM_BYTE, num_trans, trans);
+ }
+ else if (imColorModeSpace(file_color_mode) == IM_RGB)
+ {
+ imbyte transp_color[3];
+ transp_color[0] = (imbyte)(trans_values->red >> 8);
+ transp_color[1] = (imbyte)(trans_values->green >> 8);
+ transp_color[2] = (imbyte)(trans_values->blue >> 8);
+ attrib_table->Set("TransparentColor", IM_BYTE, 3, transp_color);
+ }
+ else
+ {
+ imbyte bvalue = (imbyte)(trans_values->gray >> 8);
+ attrib_table->Set("TransparencyIndex", IM_BYTE, 1, &bvalue);
+ }
+ }
+
+ int num_text;
+ png_textp text_ptr;
+ if (png_get_text(png_ptr, info_ptr, &text_ptr, &num_text))
+ {
+ int t;
+ for (t = 0; t < num_text; t++)
+ {
+ png_textp png_text = &text_ptr[t];
+ if (png_text->text_length)
+ {
+ if (imStrEqual(png_text->key, "Creation Time"))
+ attrib_table->Set("DateTime", IM_BYTE, png_text->text_length+1, png_text->text);
+ else
+ attrib_table->Set(png_text->key, IM_BYTE, png_text->text_length+1, png_text->text);
+ }
+ }
+ }
+
+ png_timep time;
+ if (png_get_tIME(png_ptr, info_ptr, &time))
+ {
+ char* stime = png_convert_to_rfc1123(png_ptr, time);
+ attrib_table->Set("DateTimeModified", IM_BYTE, strlen(stime)+1, stime);
+ }
+
+ png_charp name;
+ int compression_type;
+ png_charp profile;
+ png_uint_32 proflen;
+ if (png_get_iCCP(png_ptr, info_ptr, &name, &compression_type, &profile, &proflen))
+ attrib_table->Set("ICCProfile", IM_BYTE, proflen, profile);
+
+ int scale_unit;
+ double scale_width, scale_height;
+ if (png_get_sCAL(png_ptr, info_ptr, &scale_unit, &scale_width, &scale_height))
+ {
+ if (scale_unit == PNG_SCALE_METER || scale_unit == PNG_SCALE_RADIAN)
+ {
+ float xscale = (float)scale_width;
+ float yscale = (float)scale_height;
+ attrib_table->Set("XScale", IM_FLOAT, 1, &xscale);
+ attrib_table->Set("YScale", IM_FLOAT, 1, &yscale);
+ if (scale_unit == PNG_SCALE_METER)
+ attrib_table->Set("ScaleUnit", IM_BYTE, 7, "meters");
+ else
+ attrib_table->Set("ScaleUnit", IM_BYTE, 8, "radians");
+ }
+ }
+}
+
+static int iAttribStringCount = 0;
+
+static int iFindAttribString(void* user_data, int index, const char* name, int data_type, int count, const void* data)
+{
+ png_textp text_ptr = (png_textp)user_data;
+ (void)index;
+
+ if (data_type == IM_BYTE && count > 3 && ((imbyte*)data)[count-1] == 0)
+ {
+ if (imStrEqual(name, "ResolutionUnit") ||
+ imStrEqual(name, "InkNames") ||
+ imStrEqual(name, "CalibrationUnits") ||
+ imStrEqual(name, "CalibrationName") ||
+ imStrEqual(name, "CalibrationParam") ||
+ imStrEqual(name, "ICCProfile") ||
+ imStrEqual(name, "ScaleUnit"))
+ return 1;
+
+ png_textp png_text = &text_ptr[iAttribStringCount];
+
+ png_text->key = (char*)name;
+ png_text->text = (char*)data;
+ png_text->text_length = count-1;
+
+ if (count < 1000)
+ png_text->compression = PNG_TEXT_COMPRESSION_NONE;
+ else
+ png_text->compression = PNG_TEXT_COMPRESSION_zTXt;
+
+ iAttribStringCount++;
+ }
+
+ return 1;
+}
+
+void imFormatPNG::iWriteAttrib(imAttribTable* attrib_table)
+{
+ const void* attrib_data = attrib_table->Get("Gamma");
+ if (attrib_data)
+ png_set_gAMA(png_ptr, info_ptr, *(float*)attrib_data);
+
+ int offset_res = PNG_OFFSET_PIXEL;
+ attrib_data = attrib_table->Get("ResolutionUnit");
+ if (attrib_data)
+ {
+ char* res_unit = (char*)attrib_data;
+
+ float* xres = (float*)attrib_table->Get("XResolution");
+ float* yres = (float*)attrib_table->Get("YResolution");
+
+ if (xres && yres)
+ {
+ png_uint_32 ixres, iyres;
+
+ if (imStrEqual(res_unit, "DPI"))
+ {
+ ixres = (png_uint_32)(*xres * 100. / 2.54);
+ iyres = (png_uint_32)(*yres * 100. / 2.54);
+ offset_res = -1;
+ }
+ else
+ {
+ ixres = (png_uint_32)(*xres * 100.);
+ iyres = (png_uint_32)(*yres * 100.);
+ offset_res = PNG_OFFSET_MICROMETER;
+ }
+
+ png_set_pHYs(png_ptr, info_ptr, ixres, iyres, PNG_RESOLUTION_METER);
+ }
+ }
+
+ attrib_data = attrib_table->Get("XPosition");
+ if (attrib_data)
+ {
+ float xpos = *(float*)attrib_data;
+
+ attrib_data = attrib_table->Get("YPosition");
+ if (attrib_data)
+ {
+ float ypos = *(float*)attrib_data;
+
+ if (offset_res == -1)
+ {
+ // inches to micrometer
+ offset_res = PNG_OFFSET_MICROMETER;
+ xpos *= 25400.0f;
+ ypos *= 25400.0f;
+ }
+ else if (offset_res == PNG_OFFSET_MICROMETER)
+ {
+ // centimeter to micrometer
+ xpos *= 100.0f;
+ ypos *= 100.0f;
+ }
+
+ png_set_oFFs(png_ptr, info_ptr, (png_int_32)xpos, (png_int_32)ypos, offset_res);
+ }
+ }
+
+ attrib_data = attrib_table->Get("sRGBIntent");
+ if (attrib_data)
+ png_set_sRGB(png_ptr, info_ptr, *(int*)attrib_data);
+
+ attrib_data = attrib_table->Get("PrimaryChromaticities");
+ if (attrib_data)
+ {
+ float *primchroma = (float*)attrib_data;
+
+ attrib_data = attrib_table->Get("WhitePoint");
+ if (attrib_data)
+ {
+ float* white = (float*)attrib_data;
+
+ png_set_cHRM(png_ptr,info_ptr, white[0], white[1],
+ primchroma[0], primchroma[1], primchroma[2],
+ primchroma[3], primchroma[4], primchroma[5]);
+ }
+ }
+
+ attrib_data = attrib_table->Get("CalibrationName");
+ if (attrib_data)
+ {
+ char params[255][100], *pparams[255], *new_param_ptr;
+ int nparams = 0, size;
+
+ char* name = (char*)attrib_data;
+ int* limits = (int*)attrib_table->Get("CalibrationLimits");
+ char* units = (char*)attrib_table->Get("CalibrationUnits");
+ char* equation = (char*)attrib_table->Get("CalibrationEquation");
+ char* param_ptr = (char*)attrib_table->Get("CalibrationParam");
+
+ do
+ {
+ new_param_ptr = (char*)strstr(param_ptr, "\n");
+ if (new_param_ptr)
+ {
+ size = new_param_ptr - param_ptr;
+ memcpy(params[nparams], param_ptr, size);
+ params[nparams][size] = 0;
+ param_ptr = new_param_ptr+1;
+ pparams[nparams] = params[nparams];
+ nparams++;
+ }
+ } while (new_param_ptr && *param_ptr != 0);
+
+ png_set_pCAL(png_ptr, info_ptr, name, limits[0], limits[1], *equation, nparams, units, pparams);
+ }
+
+ int transp_count;
+ attrib_data = attrib_table->Get("TransparencyIndex", NULL, &transp_count);
+ if (attrib_data)
+ {
+ png_color_16 trans_values;
+ if (imColorModeSpace(file_color_mode) == IM_MAP)
+ {
+ png_set_tRNS(png_ptr, info_ptr, (imbyte*)attrib_data, transp_count, NULL);
+ }
+ else if (imColorModeSpace(file_color_mode) == IM_GRAY)
+ {
+ imbyte *transp_color = (imbyte*)attrib_data;
+ trans_values.gray = (png_uint_16)(transp_color[0] << 8);
+ png_set_tRNS(png_ptr, info_ptr, NULL, 1, &trans_values);
+ }
+ }
+
+ attrib_data = attrib_table->Get("TransparentColor");
+ if (attrib_data)
+ {
+ if (imColorModeSpace(file_color_mode) == IM_RGB)
+ {
+ png_color_16 trans_values;
+ imbyte *transp_color = (imbyte*)attrib_data;
+ trans_values.red = (png_uint_16)(transp_color[0] << 8);
+ trans_values.green = (png_uint_16)(transp_color[1] << 8);
+ trans_values.blue = (png_uint_16)(transp_color[2] << 8);
+ png_set_tRNS(png_ptr, info_ptr, NULL, 1, &trans_values);
+ }
+ }
+
+ iAttribStringCount = 0;
+ png_text text_ptr[512];
+ attrib_table->ForEach(text_ptr, iFindAttribString);
+ if (iAttribStringCount)
+ png_set_text(png_ptr, info_ptr, text_ptr, iAttribStringCount);
+
+ attrib_data = attrib_table->Get("DateTimeModified");
+ if (attrib_data)
+ {
+ png_time ptime;
+ time_t cur_time;
+ time(&cur_time);
+ png_convert_from_time_t(&ptime, cur_time);
+ png_set_tIME(png_ptr, info_ptr, &ptime);
+ }
+
+ int proflen;
+ attrib_data = attrib_table->Get("ICCProfile", NULL, &proflen);
+ if (attrib_data)
+ {
+ png_charp profile = (png_charp)attrib_data;
+ png_set_iCCP(png_ptr, info_ptr, "ICC Profile", 0, profile, proflen);
+ }
+
+ attrib_data = attrib_table->Get("ScaleUnit");
+ if (attrib_data)
+ {
+ char* scale_unit = (char*)attrib_data;
+
+ float* xscale = (float*)attrib_table->Get("XScale");
+ float* yscale = (float*)attrib_table->Get("YScale");
+
+ if (xscale && yscale)
+ {
+ if (imStrEqual(scale_unit, "meters"))
+ png_set_sCAL(png_ptr, info_ptr, PNG_SCALE_METER, *xscale, *yscale);
+ else if (imStrEqual(scale_unit, "radians"))
+ png_set_sCAL(png_ptr, info_ptr, PNG_SCALE_RADIAN, *xscale, *yscale);
+ }
+ }
+}
+
+int imFormatPNG::ReadImageInfo(int index)
+{
+ (void)index;
+
+ /* Allocate/initialize the memory for image information. REQUIRED. */
+ info_ptr = png_create_info_struct(png_ptr);
+ if (info_ptr == NULL)
+ return IM_ERR_ACCESS;
+
+ /* Set error handling */
+ if (setjmp(png_ptr->jmpbuf))
+ return IM_ERR_ACCESS;
+
+ png_set_read_fn(png_ptr, (void*)this->handle, (png_rw_ptr)png_user_read_fn);
+
+ png_read_info(png_ptr, info_ptr);
+
+ png_uint_32 Width, Height;
+ int bit_depth, color_type, interlace_type;
+ png_get_IHDR(png_ptr, info_ptr, &Width, &Height, &bit_depth, &color_type, &interlace_type, NULL, NULL);
+
+ this->width = Width;
+ this->height = Height;
+
+ switch(color_type)
+ {
+ case PNG_COLOR_TYPE_GRAY:
+ this->file_color_mode = IM_GRAY;
+ break;
+ case PNG_COLOR_TYPE_GRAY_ALPHA:
+ this->file_color_mode = IM_GRAY | IM_ALPHA;
+ break;
+ case PNG_COLOR_TYPE_RGB:
+ this->file_color_mode = IM_RGB;
+ break;
+ case PNG_COLOR_TYPE_RGB_ALPHA:
+ this->file_color_mode = IM_RGB | IM_ALPHA;
+ break;
+ case PNG_COLOR_TYPE_PALETTE:
+ this->file_color_mode = IM_MAP;
+ break;
+ default:
+ return IM_ERR_DATA;
+ }
+
+ if (bit_depth == 16)
+ {
+ this->file_data_type = IM_USHORT;
+
+ if (imBinCPUByteOrder() == IM_LITTLEENDIAN) // Intel
+ png_set_swap(png_ptr);
+ }
+ else if (bit_depth == 1)
+ {
+ if (this->file_color_mode == IM_RGB)
+ return IM_ERR_DATA;
+
+ this->file_color_mode = IM_BINARY;
+ this->file_data_type = IM_BYTE;
+ }
+ else
+ this->file_data_type = IM_BYTE;
+
+ this->file_color_mode |= IM_TOPDOWN;
+
+ if (imColorModeDepth(this->file_color_mode) > 1)
+ this->file_color_mode |= IM_PACKED;
+
+ this->fixbits = 0;
+ if (bit_depth < 8)
+ {
+ png_set_packing(png_ptr);
+ if (bit_depth > 1 &&
+ (imColorModeSpace(this->file_color_mode) == IM_GRAY || imColorModeSpace(this->file_color_mode) == IM_RGB))
+ this->fixbits = bit_depth;
+ }
+
+ if (imColorModeSpace(this->file_color_mode) == IM_MAP)
+ {
+ png_colorp pal;
+ int count;
+ if (png_get_PLTE(png_ptr, info_ptr, &pal, &count))
+ {
+ long palette[256];
+
+ for (int c = 0; c < count; c++)
+ {
+ palette[c] = imColorEncode(pal[c].red,
+ pal[c].green,
+ pal[c].blue);
+ }
+
+ imFileSetPalette(this, palette, count);
+ }
+ else
+ return IM_ERR_FORMAT;
+ }
+
+ imAttribTable* attrib_table = AttribTable();
+
+ this->interlace_steps = 1; // Not interlaced.
+ if (interlace_type)
+ {
+ attrib_table->Set("Interlaced", IM_INT, 1, &interlace_type);
+ /* Turn on interlace handling. */
+ this->interlace_steps = png_set_interlace_handling(png_ptr);
+ }
+
+ png_read_update_info(png_ptr, info_ptr);
+
+ iReadAttrib(attrib_table);
+
+ return IM_ERR_NONE;
+}
+
+int imFormatPNG::WriteImageInfo()
+{
+ this->file_color_mode = imColorModeSpace(this->user_color_mode);
+ this->file_color_mode |= IM_TOPDOWN;
+
+ this->file_data_type = this->user_data_type;
+
+ int bit_depth = 8;
+ if (this->file_data_type == IM_USHORT)
+ bit_depth = 16;
+
+ int color_type;
+ switch (imColorModeSpace(this->user_color_mode))
+ {
+ case IM_BINARY:
+ bit_depth = 1;
+ this->convert_bpp = 1;
+ case IM_GRAY:
+ color_type = PNG_COLOR_TYPE_GRAY;
+ break;
+ case IM_RGB:
+ color_type = PNG_COLOR_TYPE_RGB;
+ break;
+ case IM_MAP:
+ color_type = PNG_COLOR_TYPE_PALETTE;
+ break;
+ default:
+ return IM_ERR_DATA;
+ }
+
+ if (imColorModeHasAlpha(this->user_color_mode))
+ {
+ color_type |= PNG_COLOR_MASK_ALPHA;
+ this->file_color_mode |= IM_ALPHA;
+ }
+
+ if (imColorModeDepth(this->file_color_mode) > 1)
+ this->file_color_mode |= IM_PACKED;
+
+ /* Allocate/initialize the image information data. REQUIRED */
+ info_ptr = png_create_info_struct(png_ptr);
+ if (info_ptr == NULL)
+ return IM_ERR_ACCESS;
+
+ /* Set error handling. REQUIRED if you aren't supplying your own
+ * error hadnling functions in the png_create_write_struct() call. */
+ if (setjmp(png_ptr->jmpbuf))
+ return IM_ERR_ACCESS;
+
+ png_set_write_fn(png_ptr, this->handle, (png_rw_ptr)png_user_write_fn, (png_flush_ptr)png_user_flush_fn);
+
+ imAttribTable* attrib_table = AttribTable();
+
+ int interlace = 0;
+ int* interlaced = (int*)attrib_table->Get("Interlaced");
+ if (interlaced && *interlaced)
+ interlace = 1;
+
+ /* write image header */
+ png_set_IHDR(png_ptr, info_ptr, this->width, this->height, bit_depth, color_type, interlace, PNG_COMPRESSION_TYPE_BASE, PNG_FILTER_TYPE_BASE);
+
+ if (imColorModeSpace(this->user_color_mode) == IM_MAP)
+ {
+ png_color pal[256];
+ unsigned char r, g, b;
+ for (int c = 0; c < this->palette_count; c++)
+ {
+ imColorDecode(&r, &g, &b, this->palette[c]);
+ pal[c].red = r;
+ pal[c].green = g;
+ pal[c].blue = b;
+ }
+
+ png_set_PLTE(png_ptr, info_ptr, pal, this->palette_count);
+ }
+
+ int* quality = (int*)attrib_table->Get("ZIPQuality");
+ if (quality)
+ png_set_compression_level(png_ptr, *quality);
+
+ iWriteAttrib(attrib_table);
+
+ /* write image attribs */
+ png_write_info(png_ptr, info_ptr);
+
+ if (this->file_data_type == IM_USHORT)
+ {
+ if (imBinCPUByteOrder() == IM_LITTLEENDIAN) // Intel
+ png_set_swap(png_ptr);
+ }
+
+ this->interlace_steps = 1;
+ if (interlace)
+ this->interlace_steps = png_set_interlace_handling(png_ptr);
+
+ return IM_ERR_NONE;
+}
+
+static int iInterlaceRowCheck(int row_step, int pass)
+{
+ switch(row_step)
+ {
+ case 0:
+ if (pass == 1 || pass == 2 || pass == 4 || pass == 6)
+ return 1;
+ break;
+ case 4:
+ if (pass == 3 || pass == 4 || pass == 6)
+ return 1;
+ break;
+ case 2:
+ case 6:
+ if (pass == 5 || pass == 6)
+ return 1;
+ break;
+ case 1:
+ case 3:
+ case 5:
+ case 7:
+ if (pass == 7)
+ return 1;
+ break;
+ }
+
+ return 0;
+}
+
+int imFormatPNG::ReadImageData(void* data)
+{
+ if (setjmp(this->png_ptr->jmpbuf))
+ return IM_ERR_ACCESS;
+
+ int count = this->height*this->interlace_steps;
+ imCounterTotal(this->counter, count, "Reading PNG...");
+
+ int row = 0;
+ for (int i = 0; i < count; i++)
+ {
+ if (this->interlace_steps > 1 && ((row % 8) % 2 == 0)) // only when interlaced and in the 2,4,6 row steps.
+ imFileLineBufferWrite(this, data, row, 0);
+
+ png_read_row(this->png_ptr, (imbyte*)this->line_buffer, NULL);
+
+ if (this->interlace_steps == 1 || iInterlaceRowCheck(row % 8, png_ptr->pass+1))
+ {
+ if (this->fixbits)
+ {
+ unsigned char* buf = (unsigned char*)this->line_buffer;
+ for (int b = 0; b < this->line_buffer_size; b++)
+ {
+ if (this->fixbits == 4)
+ *buf *= 17;
+ else
+ *buf *= 85;
+
+ buf++;
+ }
+ }
+
+ imFileLineBufferRead(this, data, row, 0);
+ }
+
+ if (!imCounterInc(this->counter))
+ {
+ png_read_end(this->png_ptr, NULL);
+ return IM_ERR_COUNTER;
+ }
+
+ row++;
+ if (row == this->height)
+ row = 0;
+ }
+
+ png_read_end(this->png_ptr, NULL);
+
+ return IM_ERR_NONE;
+}
+
+int imFormatPNG::WriteImageData(void* data)
+{
+ if (setjmp(this->png_ptr->jmpbuf))
+ return IM_ERR_ACCESS;
+
+ int count = this->height*this->interlace_steps;
+ imCounterTotal(this->counter, count, "Writing PNG...");
+
+ int row = 0;
+ for (int i = 0; i < count; i++)
+ {
+ imFileLineBufferWrite(this, data, row, 0);
+
+ png_write_row(this->png_ptr, (imbyte*)this->line_buffer);
+
+ if (!imCounterInc(this->counter))
+ {
+ png_write_end(this->png_ptr, this->info_ptr);
+ return IM_ERR_COUNTER;
+ }
+
+ row++;
+ if (row == this->height)
+ row = 0;
+ }
+
+ png_write_end(this->png_ptr, this->info_ptr);
+
+ return IM_ERR_NONE;
+}
+
+int imFormatPNG::CanWrite(const char* compression, int color_mode, int data_type) const
+{
+ int color_space = imColorModeSpace(color_mode);
+
+ if (color_space == IM_YCBCR || color_space == IM_LAB ||
+ color_space == IM_LUV || color_space == IM_XYZ ||
+ color_space == IM_CMYK)
+ return IM_ERR_DATA;
+
+ if (data_type != IM_BYTE && data_type != IM_USHORT)
+ return IM_ERR_DATA;
+
+ if (!compression || compression[0] == 0)
+ return IM_ERR_NONE;
+
+ if (!imStrEqual(compression, "DEFLATE"))
+ return IM_ERR_COMPRESS;
+
+ return IM_ERR_NONE;
+}
+