// Copyright (C) 2002-2012 Nikolaus Gebhardt // This file is part of the "Irrlicht Engine". // For conditions of distribution and use, see copyright notice in irrlicht.h #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 { //! constructor CImageLoaderPSD::CImageLoaderPSD() { #ifdef _DEBUG setDebugName("CImageLoaderPSD"); #endif } //! returns true if the file maybe is able to be loaded by this class //! based on the file extension (e.g. ".tga") bool CImageLoaderPSD::isALoadableFileExtension(const io::path& filename) const { return core::hasFileExtension ( filename, "psd" ); } //! returns true if the file maybe is able to be loaded by this class bool CImageLoaderPSD::isALoadableFileFormat(io::IReadFile* file) const { if (!file) return false; u8 type[3]; file->read(&type, sizeof(u8)*3); return (type[2]==2); // we currently only handle tgas of type 2. } //! creates a surface from the file 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; } // skip color mode data 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; } // skip image resources 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; } // skip layer & mask 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; } // read image data 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; } // create image data block imageData = new u32[header.width * header.height]; bool res = false; if (compressionType == 0) res = readRawImageData(file, header, imageData); // RAW image data else res = readRLEImageData(file, header, imageData); // RLE compressed data video::IImage* image = 0; if (res) { // create surface image = new CImage(ECF_A8R8G8B8, core::dimension2d(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; channelread(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; xread(&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; // decompress packbit rle for (s32 channel=0; channel= 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