#include "CImageLoaderPSD.h"
#ifdef _IRR_COMPILE_WITH_PSD_LOADER_
#include "IReadFile.h"
#include "os.h"
#include "CImage.h"
#include "irrString.h"
namespace irr
{
namespace video
{
CImageLoaderPSD::CImageLoaderPSD()
{
#ifdef _DEBUG
setDebugName("CImageLoaderPSD");
#endif
}
bool CImageLoaderPSD::isALoadableFileExtension(const io::path& filename) const
{
return core::hasFileExtension ( filename, "psd" );
}
bool CImageLoaderPSD::isALoadableFileFormat(io::IReadFile* file) const
{
if (!file)
return false;
u8 type[3];
file->read(&type, sizeof(u8)*3);
return (type[2]==2);
}
IImage* CImageLoaderPSD::loadImage(io::IReadFile* file) const
{
u32* imageData = 0;
PsdHeader header;
file->read(&header, sizeof(PsdHeader));
#ifndef __BIG_ENDIAN__
header.version = os::Byteswap::byteswap(header.version);
header.channels = os::Byteswap::byteswap(header.channels);
header.height = os::Byteswap::byteswap(header.height);
header.width = os::Byteswap::byteswap(header.width);
header.depth = os::Byteswap::byteswap(header.depth);
header.mode = os::Byteswap::byteswap(header.mode);
#endif
if (header.signature[0] != '8' ||
header.signature[1] != 'B' ||
header.signature[2] != 'P' ||
header.signature[3] != 'S')
return 0;
if (header.version != 1)
{
os::Printer::log("Unsupported PSD file version", file->getFileName(), ELL_ERROR);
return 0;
}
if (header.mode != 3 || header.depth != 8)
{
os::Printer::log("Unsupported PSD color mode or depth.\n", file->getFileName(), ELL_ERROR);
return 0;
}
u32 l;
file->read(&l, sizeof(u32));
#ifndef __BIG_ENDIAN__
l = os::Byteswap::byteswap(l);
#endif
if (!file->seek(l, true))
{
os::Printer::log("Error seeking file pos to image resources.\n", file->getFileName(), ELL_ERROR);
return 0;
}
file->read(&l, sizeof(u32));
#ifndef __BIG_ENDIAN__
l = os::Byteswap::byteswap(l);
#endif
if (!file->seek(l, true))
{
os::Printer::log("Error seeking file pos to layer and mask.\n", file->getFileName(), ELL_ERROR);
return 0;
}
file->read(&l, sizeof(u32));
#ifndef __BIG_ENDIAN__
l = os::Byteswap::byteswap(l);
#endif
if (!file->seek(l, true))
{
os::Printer::log("Error seeking file pos to image data section.\n", file->getFileName(), ELL_ERROR);
return 0;
}
u16 compressionType;
file->read(&compressionType, sizeof(u16));
#ifndef __BIG_ENDIAN__
compressionType = os::Byteswap::byteswap(compressionType);
#endif
if (compressionType != 1 && compressionType != 0)
{
os::Printer::log("Unsupported psd compression mode.\n", file->getFileName(), ELL_ERROR);
return 0;
}
imageData = new u32[header.width * header.height];
bool res = false;
if (compressionType == 0)
res = readRawImageData(file, header, imageData);
else
res = readRLEImageData(file, header, imageData);
video::IImage* image = 0;
if (res)
{
image = new CImage(ECF_A8R8G8B8,
core::dimension2d<u32>(header.width, header.height), imageData);
}
if (!image)
delete [] imageData;
imageData = 0;
return image;
}
bool CImageLoaderPSD::readRawImageData(io::IReadFile* file, const PsdHeader& header, u32* imageData) const
{
u8* tmpData = new u8[header.width * header.height];
for (s32 channel=0; channel<header.channels && channel < 3; ++channel)
{
if (!file->read(tmpData, sizeof(c8) * header.width * header.height))
{
os::Printer::log("Error reading color channel\n", file->getFileName(), ELL_ERROR);
break;
}
s16 shift = getShiftFromChannel((c8)channel, header);
if (shift != -1)
{
u32 mask = 0xff << shift;
for (u32 x=0; x<header.width; ++x)
{
for (u32 y=0; y<header.height; ++y)
{
s32 index = x + y*header.width;
imageData[index] = ~(~imageData[index] | mask);
imageData[index] |= tmpData[index] << shift;
}
}
}
}
delete [] tmpData;
return true;
}
bool CImageLoaderPSD::readRLEImageData(io::IReadFile* file, const PsdHeader& header, u32* imageData) const
{
u8* tmpData = new u8[header.width * header.height];
u16 *rleCount= new u16 [header.height * header.channels];
s32 size=0;
for (u32 y=0; y<header.height * header.channels; ++y)
{
if (!file->read(&rleCount[y], sizeof(u16)))
{
delete [] tmpData;
delete [] rleCount;
os::Printer::log("Error reading rle rows\n", file->getFileName(), ELL_ERROR);
return false;
}
#ifndef __BIG_ENDIAN__
rleCount[y] = os::Byteswap::byteswap(rleCount[y]);
#endif
size += rleCount[y];
}
s8 *buf = new s8[size];
if (!file->read(buf, size))
{
delete [] rleCount;
delete [] buf;
delete [] tmpData;
os::Printer::log("Error reading rle rows\n", file->getFileName(), ELL_ERROR);
return false;
}
u16 *rcount=rleCount;
s8 rh;
u16 bytesRead;
u8 *dest;
s8 *pBuf = buf;
for (s32 channel=0; channel<header.channels; channel++)
{
for (u32 y=0; y<header.height; ++y, ++rcount)
{
bytesRead=0;
dest = &tmpData[y*header.width];
while (bytesRead < *rcount)
{
rh = *pBuf++;
++bytesRead;
if (rh >= 0)
{
++rh;
while (rh--)
{
*dest = *pBuf++;
++bytesRead;
++dest;
}
}
else
if (rh > -128)
{
rh = -rh +1;
while (rh--)
{
*dest = *pBuf;
++dest;
}
++pBuf;
++bytesRead;
}
}
}
s16 shift = getShiftFromChannel((c8)channel, header);
if (shift != -1)
{
u32 mask = 0xff << shift;
for (u32 x=0; x<header.width; ++x)
for (u32 y=0; y<header.height; ++y)
{
s32 index = x + y*header.width;
imageData[index] = ~(~imageData[index] | mask);
imageData[index] |= tmpData[index] << shift;
}
}
}
delete [] rleCount;
delete [] buf;
delete [] tmpData;
return true;
}
s16 CImageLoaderPSD::getShiftFromChannel(c8 channelNr, const PsdHeader& header) const
{
switch(channelNr)
{
case 0:
return 16;
case 1:
return 8;
case 2:
return 0;
case 3:
return header.channels == 4 ? 24 : -1;
case 4:
return 24;
default:
return -1;
}
}
IImageLoader* createImageLoaderPSD()
{
return new CImageLoaderPSD();
}
}
}
#endif