#include "CImageLoaderPNG.h"
#ifdef _IRR_COMPILE_WITH_PNG_LOADER_
#ifdef _IRR_COMPILE_WITH_LIBPNG_
#ifndef _IRR_USE_NON_SYSTEM_LIB_PNG_
#include <png.h>
#else
#include "libpng/png.h"
#endif
#endif
#include "CImage.h"
#include "CReadFile.h"
#include "os.h"
namespace irr
{
namespace video
{
#ifdef _IRR_COMPILE_WITH_LIBPNG_
static void png_cpexcept_error(png_structp png_ptr, png_const_charp msg)
{
os::Printer::log("PNG fatal error", msg, ELL_ERROR);
longjmp(png_jmpbuf(png_ptr), 1);
}
static void png_cpexcept_warn(png_structp png_ptr, png_const_charp msg)
{
os::Printer::log("PNG warning", msg, ELL_WARNING);
}
void PNGAPI user_read_data_fcn(png_structp png_ptr, png_bytep data, png_size_t length)
{
png_size_t check;
io::IReadFile* file=(io::IReadFile*)png_get_io_ptr(png_ptr);
check=(png_size_t) file->read((void*)data,(u32)length);
if (check != length)
png_error(png_ptr, "Read Error");
}
#endif
bool CImageLoaderPng::isALoadableFileExtension(const io::path& filename) const
{
#ifdef _IRR_COMPILE_WITH_LIBPNG_
return core::hasFileExtension ( filename, "png" );
#else
return false;
#endif
}
bool CImageLoaderPng::isALoadableFileFormat(io::IReadFile* file) const
{
#ifdef _IRR_COMPILE_WITH_LIBPNG_
if (!file)
return false;
png_byte buffer[8];
if (file->read(buffer, 8) != 8)
return false;
return !png_sig_cmp(buffer, 0, 8);
#else
return false;
#endif
}
IImage* CImageLoaderPng::loadImage(io::IReadFile* file) const
{
#ifdef _IRR_COMPILE_WITH_LIBPNG_
if (!file)
return 0;
video::IImage* image = 0;
u8** RowPointers = 0;
png_byte buffer[8];
if( file->read(buffer, 8) != 8 )
{
os::Printer::log("LOAD PNG: can't read file\n", file->getFileName(), ELL_ERROR);
return 0;
}
if( png_sig_cmp(buffer, 0, 8) )
{
os::Printer::log("LOAD PNG: not really a png\n", file->getFileName(), ELL_ERROR);
return 0;
}
png_structp png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING,
NULL, (png_error_ptr)png_cpexcept_error, (png_error_ptr)png_cpexcept_warn);
if (!png_ptr)
{
os::Printer::log("LOAD PNG: Internal PNG create read struct failure\n", file->getFileName(), ELL_ERROR);
return 0;
}
png_infop info_ptr = png_create_info_struct(png_ptr);
if (!info_ptr)
{
os::Printer::log("LOAD PNG: Internal PNG create info struct failure\n", file->getFileName(), ELL_ERROR);
png_destroy_read_struct(&png_ptr, NULL, NULL);
return 0;
}
if (setjmp(png_jmpbuf(png_ptr)))
{
png_destroy_read_struct(&png_ptr, &info_ptr, NULL);
if (RowPointers)
delete [] RowPointers;
return 0;
}
png_set_read_fn(png_ptr, file, user_read_data_fcn);
png_set_sig_bytes(png_ptr, 8);
png_read_info(png_ptr, info_ptr);
u32 Width;
u32 Height;
s32 BitDepth;
s32 ColorType;
{
png_uint_32 w,h;
png_get_IHDR(png_ptr, info_ptr,
&w, &h,
&BitDepth, &ColorType, NULL, NULL, NULL);
Width=w;
Height=h;
}
if (ColorType==PNG_COLOR_TYPE_PALETTE)
png_set_palette_to_rgb(png_ptr);
if (BitDepth < 8)
{
if (ColorType==PNG_COLOR_TYPE_GRAY || ColorType==PNG_COLOR_TYPE_GRAY_ALPHA)
png_set_expand_gray_1_2_4_to_8(png_ptr);
else
png_set_packing(png_ptr);
}
if (png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS))
png_set_tRNS_to_alpha(png_ptr);
if (BitDepth == 16)
png_set_strip_16(png_ptr);
if (ColorType==PNG_COLOR_TYPE_GRAY || ColorType==PNG_COLOR_TYPE_GRAY_ALPHA)
png_set_gray_to_rgb(png_ptr);
png_read_update_info(png_ptr, info_ptr);
{
png_uint_32 w,h;
png_get_IHDR(png_ptr, info_ptr,
&w, &h,
&BitDepth, &ColorType, NULL, NULL, NULL);
Width=w;
Height=h;
}
if (ColorType==PNG_COLOR_TYPE_RGB_ALPHA)
{
#ifdef __BIG_ENDIAN__
png_set_swap_alpha(png_ptr);
#else
png_set_bgr(png_ptr);
#endif
}
{
png_uint_32 w,h;
png_get_IHDR(png_ptr, info_ptr,
&w, &h,
&BitDepth, &ColorType, NULL, NULL, NULL);
Width=w;
Height=h;
}
if (ColorType==PNG_COLOR_TYPE_RGB_ALPHA)
image = new CImage(ECF_A8R8G8B8, core::dimension2d<u32>(Width, Height));
else
image = new CImage(ECF_R8G8B8, core::dimension2d<u32>(Width, Height));
if (!image)
{
os::Printer::log("LOAD PNG: Internal PNG create image struct failure\n", file->getFileName(), ELL_ERROR);
png_destroy_read_struct(&png_ptr, NULL, NULL);
return 0;
}
RowPointers = new png_bytep[Height];
if (!RowPointers)
{
os::Printer::log("LOAD PNG: Internal PNG create row pointers failure\n", file->getFileName(), ELL_ERROR);
png_destroy_read_struct(&png_ptr, NULL, NULL);
delete image;
return 0;
}
unsigned char* data = (unsigned char*)image->lock();
for (u32 i=0; i<Height; ++i)
{
RowPointers[i]=data;
data += image->getPitch();
}
if (setjmp(png_jmpbuf(png_ptr)))
{
png_destroy_read_struct(&png_ptr, &info_ptr, NULL);
delete [] RowPointers;
image->unlock();
delete [] image;
return 0;
}
png_read_image(png_ptr, RowPointers);
png_read_end(png_ptr, NULL);
delete [] RowPointers;
image->unlock();
png_destroy_read_struct(&png_ptr,&info_ptr, 0);
return image;
#else
return 0;
#endif
}
IImageLoader* createImageLoaderPNG()
{
return new CImageLoaderPng();
}
}
}
#endif