Page MenuHomePhorge

PNGLoader.cpp
No OneTemporary

Size
10 KB
Referenced Files
None
Subscribers
None

PNGLoader.cpp

#include <echo/Resource/PNGLoader.h>
#include <echo/FileSystem/File.h>
#include <iostream>
#include <bitset>
//#define PNGLOADER_DEBUG_OUTPUT
#define PNG_SETJMP_NOT_SUPPORTED
#include <png.h>
// png.h should #define the libpng version numbers. Test for oldest supported version:
BOOST_STATIC_ASSERT(PNG_LIBPNG_VER_MAJOR == 1);
BOOST_STATIC_ASSERT(PNG_LIBPNG_VER_MINOR >= 2);
// TODO: figure out the exact version number where where the interface changed
#define NEWER_INTERFACE_LIBPNG_VER_MINOR 6
namespace Echo
{
class PNGLoader::Implementation
{
public:
Implementation()
:
mWidth(0),
mHeight(0),
mBytesPerPixel(),
mFormat(Texture::Formats::UNKNOWN),
mCurrentRow(),
#if PNG_LIBPNG_VER_MINOR < NEWER_INTERFACE_LIBPNG_VER_MINOR
mRowPointers(),
mPNGPtr(),
mInfoPtr(),
#endif
mFile(nullptr)
{}
u32 mWidth;
u32 mHeight;
u32 mBytesPerPixel;
Texture::Format mFormat;
u32 mCurrentRow;
#if PNG_LIBPNG_VER_MINOR < NEWER_INTERFACE_LIBPNG_VER_MINOR
png_bytepp mRowPointers;
png_structp mPNGPtr;
png_infop mInfoPtr;
#else
// TODO: Add new interface code here
#endif
File* mFile;
};
#if PNG_LIBPNG_VER_MINOR < NEWER_INTERFACE_LIBPNG_VER_MINOR
png_voidp PngAlloc(png_structp p, png_size_t s)
{
png_voidp d = (png_voidp) (new u8[s]);
return d;
}
void PngFree(png_structp p, png_voidp s)
{
u8* u8p = reinterpret_cast<u8*> (s);
delete [] u8p;
}
void PngRead(png_structp png_ptr, png_bytep data, png_size_t length)
{
voidp read_io_ptr = png_get_io_ptr(png_ptr);
if(read_io_ptr)
{
File& file = *reinterpret_cast<File*> (read_io_ptr);
file.Read(data, 1, (u32) length);
}
}
#else
// TODO: Add new interface code here
#endif
PNGLoader::PNGLoader()
:
mImplementation(new PNGLoader::Implementation())
{}
PNGLoader::~PNGLoader()
{}
bool PNGLoader::ProcessFile(File& textureFile)
{
mImplementation->mFile = nullptr;
mImplementation->mCurrentRow = 0;
if(!textureFile.IsOpen())
{
std::cout << "PNGLoader: Error input file is not open: " << textureFile.GetActualFileName() << std::endl;
return false;
}
std::cout << "LoadPNG: " << textureFile.GetActualFileName() << std::endl;
u8 header[8];
//Read first 8 bytes - if we couldn't then that really sucks
if(textureFile.Read((void*) (&header), 1, 8) < 8)
{
std::cout << "PNGLoader: Failed to read in first 8 bytes: " << textureFile.GetActualFileName() << std::endl;
return 0;
}
//Check the file is a PNG
const bool is_png =
#if PNG_LIBPNG_VER_MINOR < NEWER_INTERFACE_LIBPNG_VER_MINOR
!png_sig_cmp(header, 0, 8);
#else
false; // TODO: Add new interface code here
std::cout << "PNGLoader: libpng " PNG_LIBPNG_VER_STRING " not implemented yet!" << std::endl;
#endif
if (!is_png)
{
std::cout << "PNGLoader: File is not a PNG file: " << textureFile.GetActualFileName() << std::endl;
return 0;
}
//Allocate memory for the png stuct and the info struct
mImplementation->mPNGPtr =
#if PNG_LIBPNG_VER_MINOR < NEWER_INTERFACE_LIBPNG_VER_MINOR
png_create_read_struct_2(PNG_LIBPNG_VER_STRING, 0, 0, 0, 0, PngAlloc, PngFree);
#else
nullptr; // TODO: Add new interface code here
#endif
if (!mImplementation->mPNGPtr)
{
std::cout << "PNGLoader: png_create_read_struct: " << textureFile.GetActualFileName() << std::endl;
return 0;
}
mImplementation->mInfoPtr =
#if PNG_LIBPNG_VER_MINOR < NEWER_INTERFACE_LIBPNG_VER_MINOR
png_create_info_struct(mImplementation->mPNGPtr);
#else
nullptr; // TODO: Add new interface code here
#endif
if (!mImplementation->mInfoPtr)
{
#if PNG_LIBPNG_VER_MINOR < NEWER_INTERFACE_LIBPNG_VER_MINOR
png_destroy_read_struct(&mImplementation->mPNGPtr, (png_infopp) 0, (png_infopp) 0);
#else
// TODO: Add new interface code here
#endif
std::cout << "PNGLoader: png_create_info_struct:1: " << textureFile.GetActualFileName() << std::endl;
return 0;
}
mImplementation->mFile = &textureFile;
#if PNG_LIBPNG_VER_MINOR < NEWER_INTERFACE_LIBPNG_VER_MINOR
png_set_read_fn(mImplementation->mPNGPtr, mImplementation->mFile, PngRead);
png_set_sig_bytes(mImplementation->mPNGPtr, 8);
png_read_png(mImplementation->mPNGPtr, mImplementation->mInfoPtr, PNG_TRANSFORM_IDENTITY, NULL);
png_byte colourType = png_get_color_type(mImplementation->mPNGPtr, mImplementation->mInfoPtr);
if (colourType == PNG_COLOR_TYPE_RGB_ALPHA)
{
png_set_swap_alpha(mImplementation->mPNGPtr);
}
//We only support 8 and channels at the moment
u32 bitDepth = png_get_bit_depth(mImplementation->mPNGPtr, mImplementation->mInfoPtr);
if(bitDepth == 16)
{
png_set_strip_16(mImplementation->mPNGPtr);
}
mImplementation->mRowPointers = png_get_rows(mImplementation->mPNGPtr, mImplementation->mInfoPtr);
mImplementation->mWidth = png_get_image_width(mImplementation->mPNGPtr, mImplementation->mInfoPtr);
mImplementation->mHeight = png_get_image_height(mImplementation->mPNGPtr, mImplementation->mInfoPtr);
mImplementation->mBytesPerPixel = png_get_channels(mImplementation->mPNGPtr, mImplementation->mInfoPtr);
// PNG only supports 8 bit and 16bit channels. There is a chunk called sBIT which
// can define which bits you consider significant. These can be used to shift the
// bits to make them relevant. libpng will do this for you if you enable it.
// I created a R4G4B4A4 image and ImageMagick was able to detect 4bits per channel
// however when I load the image here I cannot work out a way to find this out.
// I've inspected all of the info structure.
// To test the loading of the R4G4B4A4 file I forced the bitDepth to 8. This doesn't
// perform the shifts automatically for you but the loader code here will deal with
// the 8 bit to 4 bit conversion.
if(bitDepth == 8)
{
if(colourType == PNG_COLOR_TYPE_RGB_ALPHA)
{
mImplementation->mFormat = Texture::Formats::R8G8B8A8;
}else
if(colourType == PNG_COLOR_TYPE_RGB)
{
if(mImplementation->mBytesPerPixel==3)
{
mImplementation->mFormat = Texture::Formats::R8G8B8;
}else
{
mImplementation->mFormat = Texture::Formats::R8G8B8X8;
}
}else
{
std::cout << "PNGLoader Error: Unsupported PNG Format" << std::endl;
return false;
}
}else
if(bitDepth == 4 && (colourType == PNG_COLOR_TYPE_RGB_ALPHA))
{
mImplementation->mFormat = Texture::Formats::R4G4B4A4;
}else
{
//PNG_COLOR_TYPE_GRAY_ALPHA
//PNG_COLOR_TYPE_GRAY
//PNG_COLOR_TYPE_PALETTE
std::cout << "PNGLoader Error: Unsupported PNG Format" << std::endl;
return false;
}
#ifdef PNGLOADER_DEBUG_OUTPUT
png_infop info = mImplementation->mInfoPtr;
std::cout << "\twidth: " << info->width << std::endl;
std::cout << "\theight: " << info->height << std::endl;
std::cout << "\tvalid: " << info->valid << std::endl;
std::cout << "\trowbytes: " << info->rowbytes << std::endl;
std::cout << "\tnum_palette: " << info->num_palette << std::endl;
std::cout << "\tnum_trans: " << info->num_trans << std::endl;
std::cout << "\tbit_depth: " << (int)info->bit_depth << std::endl;
std::cout << "\tcolor_type: " << (int)info->color_type << std::endl;
std::cout << "\tcompression_type: " << (int)info->compression_type << std::endl;
std::cout << "\tfilter_type: " << (int)info->filter_type << std::endl;
std::cout << "\tinterlace_type: " << (int)info->interlace_type << std::endl;
std::cout << "\tchannels: " << (int)info->channels << std::endl;
std::cout << "\tpixel_depth: " << (int)info->pixel_depth << std::endl;
std::cout << "\tspare_byte: " << (int)info->spare_byte << std::endl;
std::cout << "\tsig_bit R:" << (int)info->sig_bit.red << std::endl;
std::cout << "\tsig_bit G:" << (int)info->sig_bit.green << std::endl;
std::cout << "\tsig_bit B:" << (int)info->sig_bit.blue << std::endl;
std::cout << "\tsig_bit A:" << (int)info->sig_bit.alpha << std::endl;
std::cout << "\tFormat:" << mImplementation->mFormat << std::endl;
#endif
return true;
#else
// TODO: Add new interface code here
return false;
#endif
}
std::string PNGLoader::GetFileExtension() const
{
return "png";
}
u32 PNGLoader::GetWidth() const
{
return mImplementation->mWidth;
}
u32 PNGLoader::GetHeight() const
{
return mImplementation->mHeight;
}
Texture::Format PNGLoader::GetFormat() const
{
return mImplementation->mFormat;
}
bool PNGLoader::GetLoadInverted() const
{
return false;
}
void PNGLoader::ReadLine(u8* lineStart, u32 maxLength)
{
if (!mImplementation->mFile || !lineStart)
{
return;
}
if (mImplementation->mFormat == Texture::Formats::R4G4B4A4)
{
u32 widthBytes = std::min(mImplementation->mWidth * mImplementation->mBytesPerPixel, maxLength);
for(u32 c = 0, di=0; di < widthBytes; c+=4,di+=2)
{
//Convert to 4bits per channel
#ifdef ECHO_BIG_ENDIAN
// We have to deal with big endian byte difference here because File can't do it automatically
// since PNG files are compressed. Byte order
lineStart[di] = (mImplementation->mRowPointers[mImplementation->mCurrentRow][c]&0xF0) | (mImplementation->mRowPointers[mImplementation->mCurrentRow][c+1]>>4);
lineStart[di+1] = (mImplementation->mRowPointers[mImplementation->mCurrentRow][c+2]&0xF0) | (mImplementation->mRowPointers[mImplementation->mCurrentRow][c+3]>>4);
#else
lineStart[di] = (mImplementation->mRowPointers[mImplementation->mCurrentRow][c+2]&0xF0) | (mImplementation->mRowPointers[mImplementation->mCurrentRow][c+3]>>4);
lineStart[di+1] = (mImplementation->mRowPointers[mImplementation->mCurrentRow][c]&0xF0) | (mImplementation->mRowPointers[mImplementation->mCurrentRow][c+1]>>4);
#endif
}
}else
{
//It is just an 8bit RGB or 8bit RGBA.
u32 widthBytes = std::min(mImplementation->mWidth * mImplementation->mBytesPerPixel, maxLength);
for(u32 c = 0; c < widthBytes; ++c)
{
lineStart[c] = mImplementation->mRowPointers[mImplementation->mCurrentRow][c];
}
}
mImplementation->mCurrentRow++;
}
void PNGLoader::CleanUp()
{
#if PNG_LIBPNG_VER_MINOR < NEWER_INTERFACE_LIBPNG_VER_MINOR
png_destroy_read_struct(&mImplementation->mPNGPtr, &mImplementation->mInfoPtr, (png_infopp) 0);
#else
// TODO: Add new interface code here
#endif
}
}

File Metadata

Mime Type
text/x-c++
Expires
Wed, Jan 15, 11:12 PM (11 h, 20 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
72087
Default Alt Text
PNGLoader.cpp (10 KB)

Event Timeline