//////////////////////////////////////////////////////////////////// // 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 #include 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 const buffer(new png_byte[m_lineWidth]); for (Size j=0; j 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 const buffer(new png_byte[m_lineWidth]); for (Size j=0; j