Files
source/sgp/PngLoader.cpp
T
majcostaandGitHub 381ab936eb Rename 'Standard Gaming Platform' to 'sgp' (#246)
* rename 'Standard Gaming Platform' to 'sgp'

* Adapt CMakeLists.txt files to renamed SGP directory

* Lowercase it
2023-10-23 10:18:20 -03:00

982 lines
28 KiB
C++

#include "PngLoader.h"
#include "FileMan.h"
#include "DEBUG.H"
#include "vobject.h"
#include "STIConvert.h"
#include "MemMan.h"
#include <vfs/Core/vfs.h>
#include <vfs/Core/vfs_file_raii.h>
#include <vfs/Ext/7z/vfs_7z_library.h>
#include <vfs/Core/Interface/vfs_location_interface.h>
#include <vfs/Core/File/vfs_buffer_file.h>
#include <vfs/Core/File/vfs_lib_file.h>
#include "XML_Parser.h"
namespace png
{
# include <png.h>
}
#include "sgp_auto_memory.h"
const vfs::String::str_t CONST_DOTPNG(L".png");
const vfs::String::str_t CONST_DOTXML(L".xml");
void Load24bppPNGImage(HIMAGE hImage, png::png_bytepp rows, png::png_infop info);
void Load32bppPNGImage(HIMAGE hImage, png::png_bytepp rows, png::png_infop info);
void LoadPalettedPNGImage(HIMAGE hImage, png::png_bytepp rows, png::png_infop info);
void user_read_data(png::png_structp png_ptr, png::png_bytep data, png::png_size_t length)
{
SGP_TRYCATCH_RETHROW( static_cast<vfs::tReadableFile*>(png_ptr->io_ptr)->read((vfs::Byte*)data,length),
L"error during png file reading");
}
/*******************************************************************************/
/*******************************************************************************/
class IndexedSTIImage
{
public:
IndexedSTIImage();
~IndexedSTIImage();
bool setPalette(SGPPaletteEntry *pPal, int iSize);
bool setPalette(png::png_colorp pPal, int iSize);
bool addImage(UINT8 *data, UINT32 data_size, UINT32 image_width, UINT32 image_height, INT32 image_offset_x, INT32 image_offset_y, UINT8 *original_compressed=NULL, UINT32 original_compressed_size=0);
bool addCompressedImage(UINT8 *data, UINT32 data_size, UINT32 image_width, UINT32 image_height, INT32 image_offset_x, INT32 image_offset_y);
bool readAppDataFromXMLFile(HIMAGE hImage, vfs::tReadableFile* pFile);
bool writeImage(vfs::tWritableFile* pFile);
bool writeToHIMAGE(HIMAGE pImage);
private:
STCIHeader _header;
STCIPaletteElement* _palette;
int _pal_size;
std::vector<STCISubImage> _images;
std::vector<std::vector<UINT8> > _compressed_images;
};
IndexedSTIImage::IndexedSTIImage()
: _palette(NULL)
{
memset(&_header, 0, sizeof(STCIHeader));
}
IndexedSTIImage::~IndexedSTIImage()
{
if(_palette)
{
delete[] _palette;
}
for(unsigned int i=0; i < _compressed_images.size(); ++i)
{
//delete[] _compressed_images[i];
}
}
bool IndexedSTIImage::setPalette(SGPPaletteEntry *pPal, int iSize)
{
if(iSize < 0 ||iSize > 1024)
{
return false;
}
_pal_size = iSize;
_palette = new STCIPaletteElement[iSize];
for(int i=0; i<iSize;++i)
{
_palette[i].ubBlue = pPal[i].peBlue;
_palette[i].ubGreen = pPal[i].peGreen;
_palette[i].ubRed = pPal[i].peRed;
}
return true;
}
bool IndexedSTIImage::setPalette(png::png_colorp pPal, int iSize)
{
if(iSize < 0 ||iSize > 1024)
{
return false;
}
_pal_size = iSize;
_palette = new STCIPaletteElement[iSize];
for(int i=0; i<iSize;++i)
{
_palette[i].ubBlue = pPal[i].blue;
_palette[i].ubGreen = pPal[i].green;
_palette[i].ubRed = pPal[i].red;
}
return true;
}
bool IndexedSTIImage::addCompressedImage(UINT8 *data, UINT32 data_size, UINT32 image_width, UINT32 image_height, INT32 image_offset_x, INT32 image_offset_y)
{
if(!data || (data_size < 0) )
{
return false;
}
STCISubImage subimage;
subimage.sOffsetX = image_offset_x;
subimage.sOffsetY = image_offset_y;
subimage.usWidth = image_width;
subimage.usHeight = image_height;
UINT8 *compressed = new UINT8[data_size];
memcpy(compressed,data,data_size);
subimage.uiDataLength = data_size;
_header.uiStoredSize += data_size;
_images.push_back(subimage);
// _compressed_images.push_back(compressed);
_header.uiOriginalSize += image_width * image_height;
_header.Indexed.usNumberOfSubImages += 1;
return true;
}
bool IndexedSTIImage::addImage(UINT8 *data, UINT32 data_size, UINT32 image_width, UINT32 image_height, INT32 image_offset_x, INT32 image_offset_y, UINT8 *original_compressed, UINT32 original_compressed_size)
{
if(!data || (data_size != image_width*image_height) )
{
return false;
}
STCISubImage subimage;
memset(&subimage,0,sizeof(STCISubImage));
UINT32 RESIZE = 3*data_size;
int comprSize = _compressed_images.size();
_compressed_images.resize(comprSize+1);
_compressed_images[comprSize].resize(RESIZE);
// UINT8 *compressed = new UINT8[2*data_size];
UINT8 *compressed = &_compressed_images[comprSize][0];
UINT8 *compressed_start = compressed;
// memset(compressed,0,RESIZE);
UINT32 compressed_size = 0;
subimage.usWidth = image_width;
subimage.usHeight = image_height;
INT32 etrle_size = ETRLECompressSubImage( compressed, RESIZE, data, image_width, image_height, &subimage);
SGP_THROW_IFFALSE( etrle_size > 0, L"not enough space reserved for ETRLE compression");
subimage.sOffsetX = image_offset_x;
subimage.sOffsetY = image_offset_y;
//subimage.uiDataLength = compressed_size;
//_header.uiStoredSize += compressed_size;
subimage.uiDataLength = etrle_size;
_header.uiStoredSize += etrle_size;
_images.push_back(subimage);
// _compressed_images.push_back(compressed_start);
_header.uiOriginalSize += data_size;
_header.Indexed.usNumberOfSubImages += 1;
return true;
}
static inline void setFlag(UINT8 &flags, char const* sFlag)
{
/*0x01*/ if (strcmp(sFlag, "AUX_FULL_TILE") == 0) flags |= AUX_FULL_TILE;
/*0x02*/ else if(strcmp(sFlag, "AUX_ANIMATED_TILE") == 0) flags |= AUX_ANIMATED_TILE;
/*0x04*/ else if(strcmp(sFlag, "AUX_DYNAMIC_TILE") == 0) flags |= AUX_DYNAMIC_TILE;
/*0x08*/ else if(strcmp(sFlag, "AUX_INTERACTIVE_TILE") == 0) flags |= AUX_INTERACTIVE_TILE;
/*0x10*/ else if(strcmp(sFlag, "AUX_IGNORES_HEIGHT") == 0) flags |= AUX_IGNORES_HEIGHT;
/*0x20*/ else if(strcmp(sFlag, "AUX_USES_LAND_Z") == 0) flags |= AUX_USES_LAND_Z;
else
{
std::wstringstream wss;
wss << L"Unknown flag [" << vfs::String::as_utf16(sFlag) << L"]";
SGP_THROW(wss.str().c_str());
}
}
class CAppDataParser : public IXMLParser
{
enum DOM_OBJECT
{
DO_ELEMENT_NONE,
DO_ELEMENT_ImageData,
DO_ELEMENT_SubImage,
DO_ELEMENT_offset,
DO_ELEMENT_AuxData,
DO_ELEMENT_flags,
DO_ELEMENT_properties,
};
public:
CAppDataParser(XML_Parser &parser, IXMLParser* caller=NULL)
: IXMLParser("ImageData", &parser, caller),
current_state(DO_ELEMENT_NONE),
m_hImage(NULL),
m_iCurrentIndex(-1)
{
}
virtual ~CAppDataParser() {};
virtual void onStartElement(const XML_Char* name, const XML_Char** atts);
virtual void onEndElement(const XML_Char* name);
virtual void onTextElement(const XML_Char *str, int len);
void setImage(HIMAGE hImage)
{
m_hImage = hImage;
if(m_hImage)
{
m_vAppData.resize(m_hImage->usNumberOfObjects);
}
}
private:
DOM_OBJECT current_state;
HIMAGE m_hImage;
struct SubImage{
SubImage()
{
memset(&aux,0,sizeof(AuxObjectData));
offset._override = false;
offset.x = 0;
offset.y = 0;
};
AuxObjectData aux;
struct{
int x,y;
bool _override;
} offset;
};
std::vector<SubImage> m_vAppData;
int m_iCurrentIndex;
};
void CAppDataParser::onStartElement(const XML_Char* name, const XML_Char** atts)
{
if( current_state == DO_ELEMENT_NONE && strcmp(name, this->ElementName) == 0 )
{
current_state = DO_ELEMENT_ImageData;
}
else if(current_state == DO_ELEMENT_ImageData && strcmp(name, "SubImage") == 0)
{
int index = -1;
SGP_THROW_IFFALSE(getAttributeAsInt("index",atts,index), L"could not read attribute \"index\"");
m_iCurrentIndex = index;
if(index >= (int)m_vAppData.size())
{
m_vAppData.resize(index+1);
}
current_state = DO_ELEMENT_SubImage;
}
else if(current_state == DO_ELEMENT_SubImage && strcmp(name, "offset") == 0)
{
int offset_x = 0, offset_y = 0;
if(getAttributeAsInt("x",atts,offset_x))
{
m_vAppData[m_iCurrentIndex].offset.x = offset_x;
m_vAppData[m_iCurrentIndex].offset._override = true;
}
if(getAttributeAsInt("y",atts,offset_y))
{
m_vAppData[m_iCurrentIndex].offset.y = offset_y;
m_vAppData[m_iCurrentIndex].offset._override = true;
}
current_state = DO_ELEMENT_offset;
}
else if(current_state == DO_ELEMENT_SubImage && strcmp(name, "AuxObjectData") == 0)
{
current_state = DO_ELEMENT_AuxData;
}
else if(current_state == DO_ELEMENT_AuxData && strcmp(name, "flags") == 0)
{
current_state = DO_ELEMENT_flags;
}
else if(current_state == DO_ELEMENT_flags)
{
setFlag(m_vAppData[m_iCurrentIndex].aux.fFlags, name);
}
else if(current_state == DO_ELEMENT_AuxData &&
( strcmp(name, "ubCurrentFrame") == 0 ||
strcmp(name, "ubNumberOfFrames") == 0 ||
strcmp(name, "ubNumberOfTiles") == 0 ||
strcmp(name, "ubWallOrientation") == 0 ||
strcmp(name, "usTileLocIndex") == 0 )
)
{
current_state = DO_ELEMENT_properties;
}
sCharData = "";
}
void CAppDataParser::onEndElement(const XML_Char* name)
{
char *p;
if( strcmp(name, "usTileLocIndex") == 0 && current_state == DO_ELEMENT_properties)
{
m_vAppData[m_iCurrentIndex].aux.usTileLocIndex = (UINT16)strtoul( sCharData.c_str(),&p,10);
current_state = DO_ELEMENT_AuxData;
}
else if( strcmp(name, "ubWallOrientation") == 0 && current_state == DO_ELEMENT_properties)
{
m_vAppData[m_iCurrentIndex].aux.ubWallOrientation = (UINT8)strtoul(sCharData.c_str(),&p,10);
current_state = DO_ELEMENT_AuxData;
}
else if( strcmp(name, "ubNumberOfTiles") == 0 && current_state == DO_ELEMENT_properties)
{
m_vAppData[m_iCurrentIndex].aux.ubNumberOfTiles = (UINT8)strtoul(sCharData.c_str(),&p,10);
current_state = DO_ELEMENT_AuxData;
}
else if( strcmp(name, "ubNumberOfFrames") == 0 && current_state == DO_ELEMENT_properties)
{
m_vAppData[m_iCurrentIndex].aux.ubNumberOfFrames = (UINT)strtoul(sCharData.c_str(),&p,10);
current_state = DO_ELEMENT_AuxData;
}
else if( strcmp(name, "ubCurrentFrame") == 0 && current_state == DO_ELEMENT_properties)
{
m_vAppData[m_iCurrentIndex].aux.ubCurrentFrame = (UINT8)strtoul(sCharData.c_str(),&p,10);
current_state = DO_ELEMENT_AuxData;
}
else if( strcmp(name, "flags") == 0 && current_state == DO_ELEMENT_flags)
{
current_state = DO_ELEMENT_AuxData;
}
else if( strcmp(name, "offset") == 0 && current_state == DO_ELEMENT_offset)
{
current_state = DO_ELEMENT_SubImage;
}
else if( strcmp(name, "AuxObjectData") == 0 && current_state == DO_ELEMENT_AuxData)
{
current_state = DO_ELEMENT_SubImage;
}
else if( strcmp(name, "SubImage") == 0 && current_state == DO_ELEMENT_SubImage)
{
current_state = DO_ELEMENT_ImageData;
}
else if( strcmp(name, this->ElementName) == 0 && current_state == DO_ELEMENT_ImageData)
{
unsigned int sum_frames = 0;
for(unsigned int i = 0; i < m_vAppData.size(); ++i)
{
sum_frames += m_vAppData[i].aux.ubNumberOfFrames;
}
SGP_THROW_IFFALSE(sum_frames <= m_vAppData.size(), L"Sum of animation frames doesn't match the total number of frames");
int iAppDataSize = m_vAppData.size()*sizeof(AuxObjectData);
m_hImage->pAppData = (UINT8*) MemAlloc( iAppDataSize );
for(unsigned int i = 0; i < m_vAppData.size(); ++i)
{
memcpy(&((AuxObjectData*)m_hImage->pAppData)[i],&m_vAppData[i],sizeof(AuxObjectData));
}
m_hImage->uiAppDataSize = iAppDataSize;
unsigned int aaa = std::max<int>(m_hImage->usNumberOfObjects,m_vAppData.size());
for(unsigned int i = 0; i < aaa; ++i)
{
if(m_vAppData[i].offset._override)
{
m_hImage->pETRLEObject[i].sOffsetX = m_vAppData[i].offset.x;
m_hImage->pETRLEObject[i].sOffsetY = m_vAppData[i].offset.y;
}
}
m_hImage->fFlags |= IMAGE_APPDATA;
current_state = DO_ELEMENT_NONE;
}
}
void CAppDataParser::onTextElement(const XML_Char *str, int len)
{
// handle only this special case; everything else does not matter for now
if(current_state == DO_ELEMENT_properties)
{
sCharData.append(str,len);
}
}
bool IndexedSTIImage::readAppDataFromXMLFile(HIMAGE hImage, vfs::tReadableFile* pFile)
{
if(!pFile)
{
return false;
}
std::vector<char> vBuffer;
UINT32 uiSize = 0;
try
{
vfs::COpenReadFile oFile(pFile);
uiSize = oFile->getSize();
vBuffer.resize(uiSize+1);
SGP_THROW_IFFALSE(uiSize == oFile->read(&vBuffer[0],uiSize), L"Could not read XML file");
vBuffer[uiSize] = 0;
oFile->close();
}
catch(std::exception& ex)
{
SGP_RETHROW(L"", ex);
}
XML_Parser parser = XML_ParserCreate(NULL);
CAppDataParser adp(parser);
adp.grabParser();
adp.setImage(hImage);
try
{
if(!XML_Parse(parser, &vBuffer[0], uiSize, TRUE))
{
std::wstringstream wss;
wss << L"XML Parser Error in Groups.xml: "
<< vfs::String(XML_ErrorString(XML_GetErrorCode(parser))).c_wcs()
<< L" at line "
<< XML_GetCurrentLineNumber(parser);
SGP_THROW(wss.str().c_str());
}
}
catch(std::exception &ex)
{
SGP_RETHROW(L"Could not load xml file", ex);
}
return true;
}
bool IndexedSTIImage::writeToHIMAGE(HIMAGE pImage)
{
if(!pImage)
{
return false;
}
pImage->usNumberOfObjects = _compressed_images.size();
pImage->usWidth = 640;
pImage->usHeight = 480;
pImage->ubBitDepth = 8;
// pImage->fFlags = STCI_ETRLE_COMPRESSED | STCI_INDEXED | STCI_TRANSPARENT;
pImage->fFlags |= IMAGE_TRLECOMPRESSED;
pImage->fFlags |= IMAGE_BITMAPDATA;
//strncat(pImage->ImageFile,"test",4);
pImage->iFileLoader = -1;
pImage->pui16BPPPalette = NULL;
pImage->pPalette = new SGPPaletteEntry[256];
for(int j=0; j<256; ++j)
{
pImage->pPalette[j].peRed = _palette[j].ubRed;
pImage->pPalette[j].peGreen = _palette[j].ubGreen;
pImage->pPalette[j].peBlue = _palette[j].ubBlue;
pImage->pPalette[j].peFlags = 0;
}
pImage->pETRLEObject = new ETRLEObject[pImage->usNumberOfObjects];
UINT32 offset=0; // == size
for(unsigned int i=0; i<_images.size(); ++i)
{
pImage->pETRLEObject[i].sOffsetX = _images[i].sOffsetX;
pImage->pETRLEObject[i].sOffsetY = _images[i].sOffsetY;
pImage->pETRLEObject[i].usHeight = _images[i].usHeight;
pImage->pETRLEObject[i].usWidth = _images[i].usWidth;
pImage->pETRLEObject[i].uiDataOffset = offset;
pImage->pETRLEObject[i].uiDataLength = _images[i].uiDataLength;
offset += _images[i].uiDataLength;
}
pImage->uiSizePixData = offset;
pImage->pPixData8 = new UINT8[offset];
// write sub-images
for(unsigned int i=0; i<_compressed_images.size(); ++i)
{
memcpy(
&pImage->pPixData8[pImage->pETRLEObject[i].uiDataOffset],
&_compressed_images[i][0],
pImage->pETRLEObject[i].uiDataLength);
}
//pImage->uiAppDataSize = pImage->usNumberOfObjects * sizeof(AuxObjectData);
//pImage->pAppData = (UINT8*)(new AuxObjectData[pImage->usNumberOfObjects]);
//memset(pImage->pAppData,0,pImage->uiAppDataSize);
//// write application data
//int frames = _compressed_images.size()/8; // 8 = number of directions
//for(unsigned int i=0; i<pImage->usNumberOfObjects; ++i)
//{
// if(i % frames == 0)
// {
// AuxObjectData &aod = *((AuxObjectData*)(&pImage->pAppData[i*sizeof(AuxObjectData)]));
// aod.ubNumberOfFrames = frames;
// aod.fFlags = AUX_ANIMATED_TILE;
// }
//}
return true;
}
/*******************************************************************************/
/*******************************************************************************/
class LoadPngFile
{
public:
LoadPngFile(vfs::Path const& sFile)
: _file(NULL), _struct(NULL), _info(NULL), _row_ptr(NULL)
{
// Open and read in the file
vfs::COpenReadFile oFile(sFile);
_file = &oFile.file();
oFile.release();
}
LoadPngFile(vfs::tReadableFile* pFile)
: _file(pFile), _struct(NULL), _info(NULL), _row_ptr(NULL)
{
SGP_THROW_IFFALSE(_file, L"file pointer is NULL");
}
bool Load()
{
png::png_error_ptr user_error_ptr = NULL;
png::png_error_ptr user_error_fn = NULL;
png::png_error_ptr user_warning_fn = NULL;
_struct = png_create_read_struct(PNG_LIBPNG_VER_STRING, (png::png_voidp)user_error_ptr, user_error_fn, user_warning_fn);
if (!_struct)
{
return false;
}
_info = png_create_info_struct(_struct);
if (!_info)
{
png_destroy_read_struct(&_struct, (png::png_infopp)NULL, (png::png_infopp)NULL);
return false;
}
png::png_infop end_info = png_create_info_struct(_struct);
if (!end_info)
{
png_destroy_read_struct(&_struct, &_info, (png::png_infopp)NULL);
return false;
}
// push file handle
png_set_read_fn(_struct, _file, user_read_data);
int png_transforms = PNG_TRANSFORM_IDENTITY;
png_read_png(_struct, _info, png_transforms, NULL);
// png_read_end(_struct,_info);
_row_ptr = png_get_rows(_struct, _info);
return _row_ptr != NULL;
}
~LoadPngFile()
{
if(_file)
{
_file->close();
}
if(_struct && _info)
{
png_destroy_info_struct(_struct, &_info);
_info = NULL;
}
if(_struct)
{
png_destroy_read_struct(&_struct, (png::png_infopp)_info, (png::png_infopp)NULL);
}
//if(_row_ptr)
//{
// int i=0;
//}
}
png::png_bytepp Rows()
{
return _row_ptr;
}
png::png_infop Info()
{
return _info;
}
private:
vfs::tReadableFile* _file;
png::png_structp _struct;
png::png_infop _info;
png::png_bytepp _row_ptr;
png::png_bytep _data;
};
bool LoadPNGFileToImage(HIMAGE hImage, UINT16 fContents)
{
LoadPngFile fpng(hImage->ImageFile);
if(!fpng.Load())
{
return false;
}
if(fpng.Info()->channels == 4 && fpng.Info()->bit_depth == 8)
{
Load32bppPNGImage(hImage, fpng.Rows(), fpng.Info());
}
else if(fpng.Info()->channels == 3 && fpng.Info()->bit_depth == 8)
{
Load24bppPNGImage(hImage, fpng.Rows(), fpng.Info());
}
else if(fpng.Info()->channels == 1 && fpng.Info()->bit_depth == 8)
{
LoadPalettedPNGImage(hImage, fpng.Rows(), fpng.Info());
}
else
{
std::wstringstream wss;
wss << L"Unknown image datatype : channels = " << (int)(fpng.Info()->channels)
<< L", bit depth = " << (int)(fpng.Info()->bit_depth);
SGP_THROW(wss.str().c_str());
}
// data was copied to hImage, need to clean up here
// ...
return true;
}
bool LoadJPCFileToImage(HIMAGE hImage, UINT16 fContents)
{
if(!getVFS()->fileExists(hImage->ImageFile))
{
return false;
}
vfs::CBufferFile oBuffer("");
vfs::COpenReadFile oFile(hImage->ImageFile);
SGP_TRYCATCH_RETHROW(oBuffer.copyToBuffer(oFile.file()), L"Could not copy file to buffer");
oBuffer.close();
//vfs::CUncompressed7zLibrary oLib(&oFile.file(),"");
vfs::ObjBlockAllocator<vfs::CLibFile> allocator(128);
vfs::CUncompressed7zLibrary oLib(vfs::tReadableFile::cast(&oBuffer),"",false, &allocator);
if(!oLib.init())
{
return false;
}
IndexedSTIImage image;
std::vector<vfs::IBaseFile*> vFiles;
int count_files = 0;
vfs::CUncompressed7zLibrary::Iterator it = oLib.begin();
for(; !it.end(); it.next())
{
count_files++;
}
vFiles.resize(count_files);
it = oLib.begin();
vfs::tReadableFile* appdata_file = NULL;
for(; !it.end(); it.next())
{
// check extension
vfs::String::str_t const& fname = it.value()->getName().c_wcs();
vfs::String::size_t dot = fname.find_last_of(vfs::Const::DOT());
if(dot != vfs::String::str_t::npos)
{
if( vfs::StrCmp::Equal(fname.substr(dot,fname.length()-dot), CONST_DOTPNG) )
{
std::wstringstream bss;
bss.str( fname.substr(0,dot) );
int index;
bss >> index;
if(index >= 0 && index < count_files)
{
vFiles[index] = it.value();
}
else
{
SGP_THROW(L"invalid index");
}
}
else if (vfs::StrCmp::Equal(fname.substr(dot,fname.length()-dot), CONST_DOTXML) )
{
appdata_file = vfs::tReadableFile::cast(it.value());
}
}
}
bool bHasPalette = false;
std::vector<vfs::IBaseFile*>::iterator fit = vFiles.begin();
if(vFiles.size() == 1)
{
try
{
// VR r2348 fix - loading .jpc.7z with a single image would cause exception as file was not considered open (by anv)
//LoadPngFile lpng(vfs::tReadableFile::cast(vFiles[0]));
vfs::CBufferFile oTempFile("");
oTempFile.copyToBuffer(*vfs::tReadableFile::cast(vFiles[0]));
LoadPngFile lpng(vfs::tReadableFile::cast(&oTempFile));
bool bLoadS = lpng.Load();
if(bLoadS)
{
if(lpng.Info()->channels == 4 && lpng.Info()->bit_depth == 8)
{
Load32bppPNGImage(hImage, lpng.Rows(), lpng.Info());
}
else if(lpng.Info()->channels == 3 && lpng.Info()->bit_depth == 8)
{
Load24bppPNGImage(hImage, lpng.Rows(), lpng.Info());
}
else if(lpng.Info()->channels == 1 && lpng.Info()->bit_depth == 8)
{
LoadPalettedPNGImage(hImage, lpng.Rows(), lpng.Info());
}
else
{
std::wstringstream wss;
wss << L"Unknown image datatype : channels = " << (int)(lpng.Info()->channels)
<< L", bit depth = " << (int)(lpng.Info()->bit_depth);
SGP_THROW(wss.str().c_str());
}
return true;
}
}
catch(std::exception &ex)
{
std::wstringstream wss;
wss << L"Loading PNG image from file '"
<< oFile->getPath().c_wcs()
<< L"' failed";
SGP_RETHROW(wss.str().c_str(), ex);
}
return false;
}
else
{
for(int findex = 0; fit != vFiles.end(); ++fit, ++findex)
{
if(*fit == NULL)
{
// probably not a PNG file
continue;
}
try
{
vfs::CBufferFile oTempFile("");
oTempFile.copyToBuffer( *vfs::tReadableFile::cast(*fit) );
// LoadPngFile lpng( vfs::tReadableFile::cast(*fit) );
LoadPngFile lpng( vfs::tReadableFile::cast(&oTempFile) );
bool bLoadS = lpng.Load();
if(bLoadS)
{
if(lpng.Info()->channels == 1 && lpng.Info()->bit_depth == 8)
{
if(!bHasPalette)
{
SGP_THROW_IFFALSE(lpng.Info()->num_palette == 256, L"size of palette is not 256");
image.setPalette(lpng.Info()->palette, lpng.Info()->num_palette);
bHasPalette = true;
}
UINT32 SIZE = lpng.Info()->height * lpng.Info()->width;
std::vector<UINT8> data(SIZE,0);
for(unsigned int i = 0; i < lpng.Info()->height; ++i)
{
memcpy(&data[i*lpng.Info()->width],lpng.Rows()[i],lpng.Info()->width);
}
image.addImage(&data[0], SIZE, lpng.Info()->width, lpng.Info()->height, lpng.Info()->x_offset, lpng.Info()->y_offset);
}
else
{
std::wstringstream wss;
wss << L"PNG file '" << (*fit)->getName()() << L" @ " << oFile->getPath()() << L"' is not a paletted image";
SGP_THROW(wss.str().c_str());
}
}
}
catch(std::exception& ex)
{
std::wstringstream wss;
wss << L"Loading PNG image [" << findex << L"] from file '" << oFile->getPath().c_wcs() << L"' failed";
SGP_RETHROW(wss.str().c_str(), ex);
}
}
}
bool success = image.writeToHIMAGE(hImage);
if(appdata_file)
{
success &= image.readAppDataFromXMLFile(hImage, appdata_file);
}
return success;
}
void Load32bppPNGImage(HIMAGE hImage, png::png_bytepp rows, png::png_infop info)
{
hImage->pETRLEObject = (ETRLEObject*)MemAlloc(1 * sizeof(ETRLEObject));
if(!hImage->pETRLEObject)
{
SGP_THROW(L"bad alloc");
}
memset(hImage->pETRLEObject, 0, sizeof(ETRLEObject));
hImage->usNumberOfObjects = 1;
hImage->pETRLEObject[0].usHeight = (UINT16)info->height;
hImage->pETRLEObject[0].usWidth = (UINT16)info->width;
hImage->pETRLEObject[0].sOffsetX = (INT16)info->x_offset;
hImage->pETRLEObject[0].sOffsetY = (INT16)info->y_offset;
hImage->usHeight = (UINT16)info->height;
hImage->usWidth = (UINT16)info->width;
hImage->ubBitDepth = 32;
UINT32 SIZE = info->height * info->width * sizeof(UINT32);
hImage->p32BPPData = (UINT32*)MemAlloc(SIZE);
if(!hImage->p32BPPData)
{
MemFree(hImage->pETRLEObject);
SGP_THROW(L"bad alloc");
}
memset(hImage->p32BPPData, 0, SIZE);
UINT32* dest_row = NULL;
UINT32 rgbcolor = 0;
for(unsigned int i=0; i<info->height; ++i)
{
dest_row = &(hImage->p32BPPData[i*info->width]);
png::png_bytep row_i = rows[i];
memcpy(dest_row,row_i,info->width * sizeof(UINT32));
}
hImage->fFlags |= IMAGE_BITMAPDATA;
}
void Load24bppPNGImage(HIMAGE hImage, png::png_bytepp rows, png::png_infop info)
{
hImage->pETRLEObject = (ETRLEObject*)MemAlloc(1 * sizeof(ETRLEObject));
if(!hImage->pETRLEObject)
{
SGP_THROW(L"bad alloc");
}
memset(hImage->pETRLEObject, 0, sizeof(ETRLEObject));
hImage->usNumberOfObjects = 1;
hImage->pETRLEObject[0].usHeight = (UINT16)info->height;
hImage->pETRLEObject[0].usWidth = (UINT16)info->width;
hImage->pETRLEObject[0].sOffsetX = (UINT16)info->x_offset;
hImage->pETRLEObject[0].sOffsetY = (UINT16)info->y_offset;
hImage->usHeight = (UINT16)info->height;
hImage->usWidth = (UINT16)info->width;
hImage->ubBitDepth = 16;
UINT32 SIZE = info->height * info->width * sizeof(UINT16);
hImage->p16BPPData = (UINT16*)MemAlloc(SIZE);
if(!hImage->p16BPPData)
{
MemFree(hImage->pETRLEObject);
SGP_THROW(L"bad alloc");
}
memset(hImage->p16BPPData, 0, SIZE);
UINT16* dest_row = NULL;
UINT32 rgbcolor = 0;
for(unsigned int i=0; i<info->height; ++i)
{
dest_row = &(hImage->p16BPPData[i*info->width]);
png::png_bytep row_i = rows[i];
for(unsigned int sx = 0, dx = 0; sx < 3*info->width; sx+=3, dx+=1)
{
dest_row[dx] = Get16BPPColor( FROMRGB(row_i[sx], row_i[sx+1], row_i[sx+2]) );
}
}
hImage->fFlags |= IMAGE_BITMAPDATA;
}
void LoadPalettedPNGImage(HIMAGE hImage, png::png_bytepp rows, png::png_infop info)
{
UINT32 SIZE = info->height * info->width * sizeof(UINT8);
UINT8 *data = (UINT8*)MemAlloc(SIZE);
if(!data)
{
SGP_THROW(L"bad alloc");
}
memset(data,0,SIZE);
for(unsigned int i = 0; i < info->height; ++i)
{
memcpy(&data[i*info->width],rows[i],info->width);
}
hImage->usNumberOfObjects = 1;
hImage->pETRLEObject = (ETRLEObject*)MemAlloc(hImage->usNumberOfObjects * sizeof(ETRLEObject));
if(!hImage->pETRLEObject)
{
MemFree(data);
SGP_THROW(L"bad alloc");
}
memset(hImage->pETRLEObject,0, hImage->usNumberOfObjects * sizeof(ETRLEObject));
ETRLEObject &subimage = hImage->pETRLEObject[0];
subimage.sOffsetX = (INT16) info->x_offset;;
subimage.sOffsetY = (INT16) info->y_offset;
subimage.usWidth = (UINT16) info->width;
subimage.usHeight = (UINT16) info->height;
// why allocate so much? because in in the worst case we need one TAG and one DATAVALUE per pixel
UINT32 RESIZE = 3*SIZE;
hImage->p8BPPData = (UINT8*)MemAlloc( RESIZE );
if(!hImage->p8BPPData)
{
MemFree(hImage->pETRLEObject);
MemFree(data);
SGP_THROW(L"bad alloc");
}
UINT8 *compressed = hImage->p8BPPData;
memset(hImage->p8BPPData,0,RESIZE);
UINT32 compressed_size = 0;
STCISubImage subim;
memset(&subim,0,sizeof(STCISubImage));
subim.uiDataLength = subimage.uiDataLength;
subim.uiDataOffset = subimage.uiDataOffset;
subim.usHeight = subimage.usHeight;
subim.usWidth = subimage.usWidth;
UINT32 etrle_size = ETRLECompressSubImage( hImage->p8BPPData, RESIZE, data, (UINT16)info->width, (UINT16)info->height, &subim);
SGP_THROW_IFFALSE( etrle_size > 0, L"ETRLE compression of PNG image failed" );
subim.sOffsetX = subimage.sOffsetX;
subim.sOffsetY = subimage.sOffsetY;
//subimage.uiDataLength = compressed_size;
subimage.uiDataLength = etrle_size;
subimage.uiDataOffset = 0;
hImage->ubBitDepth = 8;
//hImage->uiSizePixData = compressed_size;
hImage->uiSizePixData = etrle_size;
hImage->usHeight = (UINT16)info->height;
hImage->usWidth = (UINT16)info->width;
hImage->fFlags |= IMAGE_TRLECOMPRESSED;
hImage->fFlags |= IMAGE_BITMAPDATA;
// palette
SGP_THROW_IFFALSE(info->num_palette == 256, L"palette has size != 256");
hImage->pPalette = (SGPPaletteEntry*)MemAlloc(sizeof(SGPPaletteEntry) *256);
for(int i=0; i<256; ++i)
{
hImage->pPalette[i].peRed = info->palette[i].red;
hImage->pPalette[i].peGreen = info->palette[i].green;
hImage->pPalette[i].peBlue = info->palette[i].blue;
hImage->pPalette[i].peFlags = 0;
}
hImage->fFlags |= IMAGE_PALETTE;
}