mirror of
https://github.com/1dot13/source.git
synced 2026-07-22 13:40:22 +02:00
scripted commit:
find . -not -path '*/.*' -and -not -path '*/ext*' -type d -exec coan source -R -E {} ';'
https://coan2.sourceforge.net/coan_man_1.html
982 lines
28 KiB
C++
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;
|
|
}
|
|
|