You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

307 lines
8.2 KiB

////////////////////////////////////////////////////////////////////
// ImagePNG.cpp
//
// Copyright 2007 cDc@seacave
// Distributed under the Boost Software License, Version 1.0
// (See http://www.boost.org/LICENSE_1_0.txt)
#include "Common.h"
#ifdef _IMAGE_PNG
#include "ImagePNG.h"
#include <png.h>
#include <zlib.h>
using namespace SEACAVE;
// D E F I N E S ///////////////////////////////////////////////////
// F U N C T I O N S ///////////////////////////////////////////////
void custom_png_read_file(png_structp png_ptr, png_bytep buffer, png_size_t size)
{
png_voidp const ptrIO = png_get_io_ptr(png_ptr);
((InputStream*)ptrIO)->read(buffer, size);
}
void custom_png_write_file(png_structp png_ptr, png_bytep buffer, png_size_t size)
{
png_voidp const ptrIO = png_get_io_ptr(png_ptr);
((IOStream*)ptrIO)->write(buffer, size);
}
void custom_png_flush_file(png_structp png_ptr)
{
png_voidp const ptrIO = png_get_io_ptr(png_ptr);
((IOStream*)ptrIO)->flush();
}
// S T R U C T S ///////////////////////////////////////////////////
CImagePNG::CImagePNG() : m_png_ptr(NULL), m_info_ptr(NULL)
{
} // Constructor
CImagePNG::~CImagePNG()
{
Close();
} // Destructor
/*----------------------------------------------------------------*/
void CImagePNG::Close()
{
if (m_png_ptr) {
png_structp png_ptr = (png_structp)m_png_ptr;
png_infop info_ptr = (png_infop)m_info_ptr;
if (bRead)
png_destroy_read_struct(&png_ptr, &info_ptr, (png_infopp)NULL);
else
png_destroy_write_struct(&png_ptr, &info_ptr);
m_png_ptr = m_info_ptr = NULL;
}
CImage::Close();
}
/*----------------------------------------------------------------*/
HRESULT CImagePNG::ReadHeader()
{
// initialize stuff
ASSERT(m_png_ptr == NULL);
m_png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
if (!m_png_ptr) {
LOG(LT_IMAGE, "error: invalid PNG image (png_create_read_struct)");
return _INVALIDFILE;
}
bRead = true;
png_structp png_ptr = (png_structp)m_png_ptr;
m_info_ptr = png_create_info_struct(png_ptr);
if (!m_info_ptr) {
LOG(LT_IMAGE, "error: invalid PNG image (png_create_info_struct)");
return _INVALIDFILE;
}
png_infop info_ptr = (png_infop)m_info_ptr;
if (setjmp(png_jmpbuf(png_ptr)))
return _INVALIDFILE;
((ISTREAM*)m_pStream)->setPos(0);
png_set_read_fn(png_ptr, m_pStream, custom_png_read_file);
png_read_info(png_ptr, info_ptr);
png_uint_32 width, height; int bitdepth, colortype;
png_get_IHDR(png_ptr, info_ptr, &width, &height, &bitdepth, &colortype, NULL, NULL, NULL);
if (png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS)) {
png_set_tRNS_to_alpha(png_ptr);
colortype = PNG_COLOR_TYPE_RGB_ALPHA;
}
#if 0
// if it doesn't have a file gamma, don't do any correction ("do no harm")
if (png_get_valid(png_ptr, info_ptr, PNG_INFO_gAMA)) {
double gamma = 0;
const double screen_gamma = 2.2;
if (png_get_gAMA(png_ptr, info_ptr, &gamma))
png_set_gamma(png_ptr, screen_gamma, gamma);
}
#endif
switch (colortype)
{
case PNG_COLOR_TYPE_GRAY:
if (bitdepth < 8)
png_set_expand_gray_1_2_4_to_8(png_ptr);
m_format = PF_GRAY8;
m_stride = 1;
break;
case PNG_COLOR_TYPE_PALETTE:
png_set_palette_to_rgb(png_ptr);
m_format = PF_B8G8R8;
m_stride = 3;
break;
case PNG_COLOR_TYPE_GRAY_ALPHA:
png_set_gray_to_rgb(png_ptr);
m_format = PF_B8G8R8;
m_stride = 3;
break;
case PNG_COLOR_TYPE_RGB:
m_format = PF_B8G8R8;
m_stride = 3;
break;
case PNG_COLOR_TYPE_RGB_ALPHA:
m_format = PF_B8G8R8A8;
m_stride = 4;
break;
default:
LOG(LT_IMAGE, "error: unsupported PNG image");
return _INVALIDFILE;
}
if (bitdepth == 16)
png_set_strip_16(png_ptr);
png_set_interlace_handling(png_ptr);
png_read_update_info(png_ptr, info_ptr);
m_dataWidth = m_width = (Size)width;
m_dataHeight= m_height = (Size)height;
m_numLevels = 0;
m_level = 0;
m_lineWidth = (Size)png_get_rowbytes(png_ptr, info_ptr);
return _OK;
} // ReadHeader
/*----------------------------------------------------------------*/
HRESULT CImagePNG::ReadData(void* pData, PIXELFORMAT dataFormat, Size nStride, Size lineWidth)
{
png_structp png_ptr = (png_structp)m_png_ptr;
// read data
if (nStride == m_stride && (m_format != PF_B8G8R8A8 || (dataFormat != PF_A8R8G8B8 && dataFormat != PF_A8B8G8R8))) {
if ((m_format == PF_B8G8R8 && dataFormat == PF_R8G8B8) ||
(m_format == PF_B8G8R8A8 && dataFormat == PF_R8G8B8A8)) {
// flip R and B from the PNG library
png_set_bgr(png_ptr);
} else {
ASSERT(m_format == dataFormat);
}
// read image directly to the data buffer
for (Size j=0; j<m_height; ++j, (BYTE*&)pData+=lineWidth)
png_read_row(png_ptr, (png_bytep)pData, NULL);
} else {
// read image to a buffer and convert it
CAutoPtrArr<png_byte> const buffer(new png_byte[m_lineWidth]);
for (Size j=0; j<m_height; ++j, pData=(uint8_t*)pData+lineWidth) {
png_read_row(png_ptr, buffer, NULL);
if (!FilterFormat(pData, dataFormat, nStride, buffer, m_format, m_stride, m_width))
return _FAIL;
}
}
return _OK;
} // Read
/*----------------------------------------------------------------*/
HRESULT CImagePNG::WriteHeader(PIXELFORMAT imageFormat, Size width, Size height, BYTE /*numLevels*/)
{
// initialize stuff
ASSERT(m_png_ptr == NULL);
m_png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
if (!m_png_ptr) {
LOG(LT_IMAGE, "error: can not create PNG image (png_create_write_struct)");
return _INVALIDFILE;
}
bRead = false;
png_structp png_ptr = (png_structp)m_png_ptr;
m_info_ptr = png_create_info_struct(png_ptr);
if (!m_info_ptr) {
LOG(LT_IMAGE, "error: invalid PNG image (png_create_info_struct)");
return _INVALIDFILE;
}
png_infop info_ptr = (png_infop)m_info_ptr;
if (setjmp(png_jmpbuf(png_ptr)))
return _INVALIDFILE;
// write header
switch (imageFormat)
{
case PF_A8:
case PF_GRAY8:
m_stride = 1;
m_format = PF_GRAY8;
break;
case PF_B8G8R8:
case PF_R8G8B8:
m_stride = 3;
m_format = PF_B8G8R8;
break;
case PF_R8G8B8A8:
case PF_A8R8G8B8:
case PF_B8G8R8A8:
case PF_A8B8G8R8:
m_stride = 4;
m_format = PF_B8G8R8A8;
break;
default:
LOG(LT_IMAGE, "error: unsupported PNG image format");
return _INVALIDFILE;
}
m_numLevels = 1;
m_level = 0;
m_width = width;
m_height = height;
m_lineWidth = GetDataSizes(0, m_dataWidth, m_dataHeight);
png_set_write_fn(png_ptr, m_pStream, custom_png_write_file, custom_png_flush_file);
int compression_level = 0;
int compression_strategy = Z_RLE;
if (compression_level > 0) {
png_set_compression_mem_level(png_ptr, compression_level);
}
else {
// tune parameters for speed
// (see http://wiki.linuxquestions.org/wiki/Libpng)
png_set_filter(png_ptr, PNG_FILTER_TYPE_BASE, PNG_FILTER_SUB);
png_set_compression_level(png_ptr, Z_BEST_SPEED);
}
png_set_compression_strategy(png_ptr, compression_strategy);
png_set_IHDR(png_ptr, info_ptr, m_width, m_height, 8/*depth*/,
m_stride == 1 ? PNG_COLOR_TYPE_GRAY :
m_stride == 3 ? PNG_COLOR_TYPE_RGB : PNG_COLOR_TYPE_RGBA,
PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_DEFAULT,
PNG_FILTER_TYPE_DEFAULT);
png_write_info(png_ptr, info_ptr);
return _OK;
} // WriteHeader
/*----------------------------------------------------------------*/
HRESULT CImagePNG::WriteData(void* pData, PIXELFORMAT dataFormat, Size nStride, Size lineWidth)
{
png_structp png_ptr = (png_structp)m_png_ptr;
// write data
if (nStride == m_stride && (m_format != PF_B8G8R8A8 || (dataFormat != PF_A8R8G8B8 && dataFormat != PF_A8B8G8R8))) {
if ((m_format == PF_B8G8R8 && dataFormat == PF_R8G8B8) ||
(m_format == PF_B8G8R8A8 && dataFormat == PF_R8G8B8A8)) {
// flip R and B from the PNG library
png_set_bgr(png_ptr);
} else {
ASSERT(m_format == dataFormat);
}
// write image directly to the data buffer
for (Size j=0; j<m_height; ++j, (BYTE*&)pData+=lineWidth)
png_write_row(png_ptr, (png_bytep)pData);
} else {
// read image to a buffer and convert it
CAutoPtrArr<png_byte> const buffer(new png_byte[m_lineWidth]);
for (Size j=0; j<m_height; ++j, pData=(uint8_t*)pData+lineWidth) {
if (!FilterFormat(buffer, m_format, m_stride, pData, dataFormat, nStride, m_width))
return _FAIL;
png_write_row(png_ptr, buffer);
}
}
png_write_end(png_ptr, NULL);
return _OK;
} // WriteData
/*----------------------------------------------------------------*/
#endif // _IMAGE_PNG