diff options
Diffstat (limited to 'plugins/AdvaImg/src/FreeImage/PSDParser.cpp')
-rw-r--r-- | plugins/AdvaImg/src/FreeImage/PSDParser.cpp | 1053 |
1 files changed, 1053 insertions, 0 deletions
diff --git a/plugins/AdvaImg/src/FreeImage/PSDParser.cpp b/plugins/AdvaImg/src/FreeImage/PSDParser.cpp new file mode 100644 index 0000000000..93573d01c0 --- /dev/null +++ b/plugins/AdvaImg/src/FreeImage/PSDParser.cpp @@ -0,0 +1,1053 @@ +// ========================================================== +// Photoshop Loader +// +// Design and implementation by +// - Hervé Drolon (drolon@infonie.fr) +// - Mihail Naydenov (mnaydenov@users.sourceforge.net) +// +// Based on LGPL code created and published by http://sourceforge.net/projects/elynx/ +// +// This file is part of FreeImage 3 +// +// COVERED CODE IS PROVIDED UNDER THIS LICENSE ON AN "AS IS" BASIS, WITHOUT WARRANTY +// OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, WITHOUT LIMITATION, WARRANTIES +// THAT THE COVERED CODE IS FREE OF DEFECTS, MERCHANTABLE, FIT FOR A PARTICULAR PURPOSE +// OR NON-INFRINGING. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE COVERED +// CODE IS WITH YOU. SHOULD ANY COVERED CODE PROVE DEFECTIVE IN ANY RESPECT, YOU (NOT +// THE INITIAL DEVELOPER OR ANY OTHER CONTRIBUTOR) ASSUME THE COST OF ANY NECESSARY +// SERVICING, REPAIR OR CORRECTION. THIS DISCLAIMER OF WARRANTY CONSTITUTES AN ESSENTIAL +// PART OF THIS LICENSE. NO USE OF ANY COVERED CODE IS AUTHORIZED HEREUNDER EXCEPT UNDER +// THIS DISCLAIMER. +// +// Use at your own risk! +// ========================================================== + +#include "FreeImage.h" +#include "Utilities.h" +#include "PSDParser.h" + +// -------------------------------------------------------------------------- + +// PSD signature (= '8BPS') +#define PSD_SIGNATURE 0x38425053 +// Image resource block signature (= '8BIM') +#define PSD_RESOURCE 0x3842494D + +// PSD color modes +#define PSDP_BITMAP 0 +#define PSDP_GRAYSCALE 1 +#define PSDP_INDEXED 2 +#define PSDP_RGB 3 +#define PSDP_CMYK 4 +#define PSDP_MULTICHANNEL 7 +#define PSDP_DUOTONE 8 +#define PSDP_LAB 9 + +// PSD compression schemes +#define PSDP_COMPRESSION_NONE 0 // Raw data +#define PSDP_COMPRESSION_RLE 1 // RLE compression (same as TIFF packed bits) + +#define SAFE_DELETE_ARRAY(_p_) { if (NULL != (_p_)) { delete [] (_p_); (_p_) = NULL; } } + +// -------------------------------------------------------------------------- + +static inline int +psdGetValue(const BYTE * iprBuffer, const int iBytes) { + int v = iprBuffer[0]; + for (int i=1; i<iBytes; ++i) { + v = (v << 8) | iprBuffer[i]; + } + return v; +} + +// -------------------------------------------------------------------------- + +psdHeaderInfo::psdHeaderInfo() : _Channels(-1), _Height(-1), _Width(-1), _BitsPerChannel(-1), _ColourMode(-1) { +} + +psdHeaderInfo::~psdHeaderInfo() { +} + +bool psdHeaderInfo::Read(FreeImageIO *io, fi_handle handle) { + psdHeader header; + + const int n = (int)io->read_proc(&header, sizeof(header), 1, handle); + if (!n) { + return false; + } + + // check the signature + int nSignature = psdGetValue(header.Signature, sizeof(header.Signature)); + if (PSD_SIGNATURE == nSignature) { + // check the version + int nVersion = psdGetValue( header.Version, sizeof(header.Version) ); + if (1 == nVersion) { + // header.Reserved must be zero + BYTE psd_reserved[] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; + if(memcmp(header.Reserved, psd_reserved, 6) != 0) { + FreeImage_OutputMessageProc(FIF_PSD, "Warning: file header reserved member is not equal to zero"); + } + // read the header + _Channels = (short)psdGetValue( header.Channels, sizeof(header.Channels) ); + _Height = psdGetValue( header.Rows, sizeof(header.Rows) ); + _Width = psdGetValue( header.Columns, sizeof(header.Columns) ); + _BitsPerChannel = (short)psdGetValue( header.Depth, sizeof(header.Depth) ); + _ColourMode = (short)psdGetValue( header.Mode, sizeof(header.Mode) ); + + return true; + } + } + + return false; +} + +// -------------------------------------------------------------------------- + +psdColourModeData::psdColourModeData() : _Length(-1), _plColourData(NULL) { +} + +psdColourModeData::~psdColourModeData() { + SAFE_DELETE_ARRAY(_plColourData); +} + +bool psdColourModeData::Read(FreeImageIO *io, fi_handle handle) { + if (0 < _Length) { + SAFE_DELETE_ARRAY(_plColourData); + } + + BYTE Length[4]; + io->read_proc(&Length, sizeof(Length), 1, handle); + + _Length = psdGetValue( Length, sizeof(_Length) ); + if (0 < _Length) { + _plColourData = new BYTE[_Length]; + io->read_proc(_plColourData, _Length, 1, handle); + } + + return true; +} + +bool psdColourModeData::FillPalette(FIBITMAP *dib) { + RGBQUAD *pal = FreeImage_GetPalette(dib); + if(pal) { + for (int i = 0; i < 256; i++) { + pal[i].rgbRed = _plColourData[i + 0*256]; + pal[i].rgbGreen = _plColourData[i + 1*256]; + pal[i].rgbBlue = _plColourData[i + 2*256]; + } + return true; + } + return false; +} + +// -------------------------------------------------------------------------- + +psdImageResource::psdImageResource() : _plName (0) { + Reset(); +} + +psdImageResource::~psdImageResource() { + SAFE_DELETE_ARRAY(_plName); +} + +void psdImageResource::Reset() { + _Length = -1; + memset( _OSType, '\0', sizeof(_OSType) ); + _ID = -1; + SAFE_DELETE_ARRAY(_plName); + _Size = -1; +} + +// -------------------------------------------------------------------------- + +psdResolutionInfo::psdResolutionInfo() : _widthUnit(-1), _heightUnit(-1), _hRes(-1), _vRes(-1), _hResUnit(-1), _vResUnit(-1) { +} + +psdResolutionInfo::~psdResolutionInfo() { +} + +int psdResolutionInfo::Read(FreeImageIO *io, fi_handle handle) { + BYTE IntValue[4], ShortValue[2]; + int nBytes=0, n; + + // Horizontal resolution in pixels per inch. + n = (int)io->read_proc(&ShortValue, sizeof(ShortValue), 1, handle); + nBytes += n * sizeof(ShortValue); + _hRes = (short)psdGetValue(ShortValue, sizeof(_hRes) ); + // 1=display horizontal resolution in pixels per inch; 2=display horizontal resolution in pixels per cm. + n = (int)io->read_proc(&IntValue, sizeof(IntValue), 1, handle); + nBytes += n * sizeof(IntValue); + _hResUnit = psdGetValue(IntValue, sizeof(_hResUnit) ); + // Display width as 1=inches; 2=cm; 3=points; 4=picas; 5=columns. + n = (int)io->read_proc(&ShortValue, sizeof(ShortValue), 1, handle); + nBytes += n * sizeof(ShortValue); + _widthUnit = (short)psdGetValue(ShortValue, sizeof(_widthUnit) ); + // Vertical resolution in pixels per inch. + n = (int)io->read_proc(&ShortValue, sizeof(ShortValue), 1, handle); + nBytes += n * sizeof(ShortValue); + _vRes = (short)psdGetValue(ShortValue, sizeof(_vRes) ); + // 1=display vertical resolution in pixels per inch; 2=display vertical resolution in pixels per cm. + n = (int)io->read_proc(&IntValue, sizeof(IntValue), 1, handle); + nBytes += n * sizeof(IntValue); + _vResUnit = psdGetValue(IntValue, sizeof(_vResUnit) ); + // Display height as 1=inches; 2=cm; 3=points; 4=picas; 5=columns. + n = (int)io->read_proc(&ShortValue, sizeof(ShortValue), 1, handle); + nBytes += n * sizeof(ShortValue); + _heightUnit = (short)psdGetValue(ShortValue, sizeof(_heightUnit) ); + + return nBytes; +} + +void psdResolutionInfo::GetResolutionInfo(unsigned &res_x, unsigned &res_y) { + if(_hResUnit == 1) { + // convert pixels / inch to pixel / m + res_x = (unsigned) (_hRes / 0.0254000 + 0.5); + } else if(_hResUnit == 2) { + // convert pixels / cm to pixel / m + res_x = (unsigned) (_hRes * 100.0 + 0.5); + } + if(_vResUnit == 1) { + // convert pixels / inch to pixel / m + res_y = (unsigned) (_vRes / 0.0254000 + 0.5); + } else if(_vResUnit == 2) { + // convert pixels / cm to pixel / m + res_y = (unsigned) (_vRes * 100.0 + 0.5); + } +} + +// -------------------------------------------------------------------------- + +psdResolutionInfo_v2::psdResolutionInfo_v2() { + _Channels = _Rows = _Columns = _Depth = _Mode = -1; +} + +psdResolutionInfo_v2::~psdResolutionInfo_v2() { +} + +int psdResolutionInfo_v2::Read(FreeImageIO *io, fi_handle handle) { + BYTE ShortValue[2]; + int nBytes=0, n; + + n = (int)io->read_proc(&ShortValue, sizeof(ShortValue), 1, handle); + nBytes += n * sizeof(ShortValue); + _Channels = (short)psdGetValue(ShortValue, sizeof(_Channels) ); + + n = (int)io->read_proc(&ShortValue, sizeof(ShortValue), 1, handle); + nBytes += n * sizeof(ShortValue); + _Rows = (short)psdGetValue(ShortValue, sizeof(_Rows) ); + + n = (int)io->read_proc(&ShortValue, sizeof(ShortValue), 1, handle); + nBytes += n * sizeof(ShortValue); + _Columns = (short)psdGetValue(ShortValue, sizeof(_Columns) ); + + n = (int)io->read_proc(&ShortValue, sizeof(ShortValue), 1, handle); + nBytes += n * sizeof(ShortValue); + _Depth = (short)psdGetValue(ShortValue, sizeof(_Depth) ); + + n = (int)io->read_proc(&ShortValue, sizeof(ShortValue), 1, handle); + nBytes += n * sizeof(ShortValue); + _Mode = (short)psdGetValue(ShortValue, sizeof(_Mode) ); + + return nBytes; +} + +// -------------------------------------------------------------------------- + +psdDisplayInfo::psdDisplayInfo() { + _Opacity = _ColourSpace = -1; + for (unsigned n = 0; n < 4; ++n) { + _Colour[n] = 0; + } + _Kind = 0; + _padding = '0'; +} + +psdDisplayInfo::~psdDisplayInfo() { +} + +int psdDisplayInfo::Read(FreeImageIO *io, fi_handle handle) { + BYTE ShortValue[2]; + int nBytes=0, n; + + n = (int)io->read_proc(&ShortValue, sizeof(ShortValue), 1, handle); + nBytes += n * sizeof(ShortValue); + _ColourSpace = (short)psdGetValue(ShortValue, sizeof(_ColourSpace) ); + + for (unsigned i = 0; i < 4; ++i) { + n = (int)io->read_proc(&ShortValue, sizeof(ShortValue), 1, handle); + nBytes += n * sizeof(ShortValue); + _Colour[i] = (short)psdGetValue(ShortValue, sizeof(_Colour[i]) ); + } + + n = (int)io->read_proc(&ShortValue, sizeof(ShortValue), 1, handle); + nBytes += n * sizeof(ShortValue); + _Opacity = (short)psdGetValue(ShortValue, sizeof(_Opacity) ); + if ((_Opacity < 0) || (_Opacity > 100)) { + throw "Invalid DisplayInfo::Opacity value"; + } + + BYTE c[1]; + n = (int)io->read_proc(&c, sizeof(c), 1, handle); + nBytes += n * sizeof(c); + _Kind = (BYTE)psdGetValue(c, sizeof(c)); + + n = (int)io->read_proc(&c, sizeof(c), 1, handle); + nBytes += n * sizeof(c); + _padding = (BYTE)psdGetValue(c, sizeof(c)); + if(_padding != 0) { + throw "Invalid DisplayInfo::Padding value"; + } + + return nBytes; +} + +// -------------------------------------------------------------------------- + +psdThumbnail::psdThumbnail() : +_Format(-1), _Width(-1), _Height(-1), _WidthBytes(-1), _Size(-1), _CompressedSize(-1), _BitPerPixel(-1), _Planes(-1), _dib(NULL) { +} + +psdThumbnail::~psdThumbnail() { + FreeImage_Unload(_dib); +} + +int psdThumbnail::Read(FreeImageIO *io, fi_handle handle, int iResourceSize, bool isBGR) { + BYTE ShortValue[2], IntValue[4]; + int nBytes=0, n; + + // remove the header size (28 bytes) from the total data size + int iTotalData = iResourceSize - 28; + + const long block_end = io->tell_proc(handle) + iTotalData; + + n = (int)io->read_proc(&IntValue, sizeof(IntValue), 1, handle); + nBytes += n * sizeof(IntValue); + _Format = psdGetValue(IntValue, sizeof(_Format) ); + + n = (int)io->read_proc(&IntValue, sizeof(IntValue), 1, handle); + nBytes += n * sizeof(IntValue); + _Width = psdGetValue(IntValue, sizeof(_Width) ); + + n = (int)io->read_proc(&IntValue, sizeof(IntValue), 1, handle); + nBytes += n * sizeof(IntValue); + _Height = psdGetValue(IntValue, sizeof(_Height) ); + + n = (int)io->read_proc(&IntValue, sizeof(IntValue), 1, handle); + nBytes += n * sizeof(IntValue); + _WidthBytes = psdGetValue(IntValue, sizeof(_WidthBytes) ); + + n = (int)io->read_proc(&IntValue, sizeof(IntValue), 1, handle); + nBytes += n * sizeof(IntValue); + _Size = psdGetValue(IntValue, sizeof(_Size) ); + + n = (int)io->read_proc(&IntValue, sizeof(IntValue), 1, handle); + nBytes += n * sizeof(IntValue); + _CompressedSize = psdGetValue(IntValue, sizeof(_CompressedSize) ); + + n = (int)io->read_proc(&ShortValue, sizeof(ShortValue), 1, handle); + nBytes += n * sizeof(ShortValue); + _BitPerPixel = (short)psdGetValue(ShortValue, sizeof(_BitPerPixel) ); + + n = (int)io->read_proc(&ShortValue, sizeof(ShortValue), 1, handle); + nBytes += n * sizeof(ShortValue); + _Planes = (short)psdGetValue(ShortValue, sizeof(_Planes) ); + + const long JFIF_startpos = io->tell_proc(handle); + + if(_dib) { + FreeImage_Unload(_dib); + } + + if(_Format == 1) { + // kJpegRGB thumbnail image + _dib = FreeImage_LoadFromHandle(FIF_JPEG, io, handle); + if(isBGR) { + SwapRedBlue32(_dib); + } + // HACK: manually go to end of thumbnail, because (for some reason) LoadFromHandle consumes more bytes then available! + io->seek_proc(handle, block_end, SEEK_SET); + } + else { + // kRawRGB thumbnail image + // ### Unimplemented (should be trivial) + + // skip the thumbnail part + io->seek_proc(handle, iTotalData, SEEK_CUR); + return iResourceSize; + } + + nBytes += (block_end - JFIF_startpos); + + return nBytes; +} + +//--------------------------------------------------------------------------- + +psdICCProfile::psdICCProfile() : _ProfileSize(0), _ProfileData(NULL) { +} + +psdICCProfile::~psdICCProfile() { + clear(); +} + +void psdICCProfile::clear() { SAFE_DELETE_ARRAY(_ProfileData); _ProfileSize = 0;} + +int psdICCProfile::Read(FreeImageIO *io, fi_handle handle, int size) { + int nBytes = 0, n; + + clear(); + + _ProfileData = new (std::nothrow) BYTE[size]; + if(NULL != _ProfileData) { + n = (int)io->read_proc(_ProfileData, 1, size, handle); + _ProfileSize = size; + nBytes += n * sizeof(BYTE); + } + + return nBytes; +} + +//--------------------------------------------------------------------------- + +/** +Invert only color components, skipping Alpha/Black +(Can be useful as public/utility function) +*/ +static +BOOL invertColor(FIBITMAP* dib) { + FREE_IMAGE_TYPE type = FreeImage_GetImageType(dib); + const unsigned Bpp = FreeImage_GetBPP(dib)/8; + + if ((type == FIT_BITMAP && Bpp == 4) || type == FIT_RGBA16) { + const unsigned width = FreeImage_GetWidth(dib); + const unsigned height = FreeImage_GetHeight(dib); + BYTE *line_start = FreeImage_GetScanLine(dib, 0); + const unsigned pitch = FreeImage_GetPitch(dib); + const unsigned triBpp = Bpp - (Bpp == 4 ? 1 : 2); + + for(unsigned y = 0; y < height; y++) { + BYTE *line = line_start; + + for(unsigned x = 0; x < width; x++) { + for(unsigned b=0; b < triBpp; ++b) { + line[b] = ~line[b]; + } + + line += Bpp; + } + line_start += pitch; + } + + return TRUE; + } + else { + return FreeImage_Invert(dib); + } +} + +//--------------------------------------------------------------------------- + +psdParser::psdParser() { + _bThumbnailFilled = false; + _bDisplayInfoFilled = false; + _bResolutionInfoFilled = false; + _bResolutionInfoFilled_v2 = false; + _bCopyright = false; + _GlobalAngle = 30; + _ColourCount = -1; + _TransparentIndex = -1; + _fi_flags = 0; + _fi_format_id = FIF_UNKNOWN; +} + +psdParser::~psdParser() { +} + +bool psdParser::ReadLayerAndMaskInfoSection(FreeImageIO *io, fi_handle handle) { + bool bSuccess = false; + + BYTE DataLength[4]; + int nBytes = 0; + int n = (int)io->read_proc(&DataLength, sizeof(DataLength), 1, handle); + int nTotalBytes = psdGetValue( DataLength, sizeof(DataLength) ); + + BYTE data[1]; + while( n && ( nBytes < nTotalBytes ) ) { + data[0] = '\0'; + n = (int)io->read_proc(&data, sizeof(data), 1, handle); + nBytes += n * sizeof(data); + } + + if ( nBytes == nTotalBytes ) { + bSuccess = true; + } + + return bSuccess; +} + +bool psdParser::ReadImageResources(FreeImageIO *io, fi_handle handle, LONG length) { + psdImageResource oResource; + bool bSuccess = false; + + if(length > 0) { + oResource._Length = length; + } else { + BYTE Length[4]; + int n = (int)io->read_proc(&Length, sizeof(Length), 1, handle); + + oResource._Length = psdGetValue( Length, sizeof(oResource._Length) ); + } + + int nBytes = 0; + int nTotalBytes = oResource._Length; + + while(nBytes < nTotalBytes) { + int n = 0; + oResource.Reset(); + + n = (int)io->read_proc(&oResource._OSType, sizeof(oResource._OSType), 1, handle); + nBytes += n * sizeof(oResource._OSType); + + if ( (nBytes % 2) != 0 ) { + return false; + } + + int nOSType = psdGetValue((BYTE*)&oResource._OSType, sizeof(oResource._OSType)); + + if ( PSD_RESOURCE == nOSType ) { + BYTE ID[2]; + n = (int)io->read_proc(&ID, sizeof(ID), 1, handle); + nBytes += n * sizeof(ID); + + oResource._ID = (short)psdGetValue( ID, sizeof(ID) ); + + BYTE SizeOfName; + n = (int)io->read_proc(&SizeOfName, sizeof(SizeOfName), 1, handle); + nBytes += n * sizeof(SizeOfName); + + int nSizeOfName = psdGetValue( &SizeOfName, sizeof(SizeOfName) ); + if ( 0 < nSizeOfName ) { + oResource._plName = new BYTE[nSizeOfName]; + n = (int)io->read_proc(oResource._plName, nSizeOfName, 1, handle); + nBytes += n * nSizeOfName; + } + + if ( 0 == (nSizeOfName % 2) ) { + n = (int)io->read_proc(&SizeOfName, sizeof(SizeOfName), 1, handle); + nBytes += n * sizeof(SizeOfName); + } + + BYTE Size[4]; + n = (int)io->read_proc(&Size, sizeof(Size), 1, handle); + nBytes += n * sizeof(Size); + + oResource._Size = psdGetValue( Size, sizeof(oResource._Size) ); + + if ( 0 != (oResource._Size % 2) ) { + // resource data must be even + oResource._Size++; + } + if ( 0 < oResource._Size ) { + BYTE IntValue[4]; + BYTE ShortValue[2]; + + switch( oResource._ID ) { + case 1000: + // Obsolete - Photoshop 2.0 + _bResolutionInfoFilled_v2 = true; + nBytes += _resolutionInfo_v2.Read(io, handle); + break; + + // ResolutionInfo structure + case 1005: + _bResolutionInfoFilled = true; + nBytes += _resolutionInfo.Read(io, handle); + break; + + // DisplayInfo structure + case 1007: + _bDisplayInfoFilled = true; + nBytes += _displayInfo.Read(io, handle); + break; + + // (Photoshop 4.0) Copyright flag + // Boolean indicating whether image is copyrighted. Can be set via Property suite or by user in File Info... + case 1034: + n = (int)io->read_proc(&ShortValue, sizeof(ShortValue), 1, handle); + nBytes += n * sizeof(ShortValue); + _bCopyright = (1 == psdGetValue(ShortValue, sizeof(ShortValue))); + break; + + // (Photoshop 4.0) Thumbnail resource for Photoshop 4.0 only + case 1033: + // (Photoshop 5.0) Thumbnail resource (supersedes resource 1033) + case 1036: + { + _bThumbnailFilled = true; + bool bBGR = (1033==oResource._ID); + nBytes += _thumbnail.Read(io, handle, oResource._Size, bBGR); + break; + } + + // (Photoshop 5.0) Global Angle + // 4 bytes that contain an integer between 0 and 359, which is the global + // lighting angle for effects layer. If not present, assumed to be 30. + case 1037: + n = (int)io->read_proc(&IntValue, sizeof(IntValue), 1, handle); + nBytes += n * sizeof(IntValue); + _GlobalAngle = psdGetValue(IntValue, sizeof(_GlobalAngle) ); + break; + + // ICC profile + case 1039: + nBytes += _iccProfile.Read(io, handle, oResource._Size); + break; + + // (Photoshop 6.0) Indexed Color Table Count + // 2 bytes for the number of colors in table that are actually defined + case 1046: + n = (int)io->read_proc(&ShortValue, sizeof(ShortValue), 1, handle); + nBytes += n * sizeof(ShortValue); + _ColourCount = (short)psdGetValue(ShortValue, sizeof(ShortValue) ); + break; + + // (Photoshop 6.0) Transparency Index. + // 2 bytes for the index of transparent color, if any. + case 1047: + n = (int)io->read_proc(&ShortValue, sizeof(ShortValue), 1, handle); + nBytes += n * sizeof(ShortValue); + _TransparentIndex = (short)psdGetValue(ShortValue, sizeof(ShortValue) ); + break; + + default: + { + // skip resource + unsigned skip_length = MIN(oResource._Size, nTotalBytes - nBytes); + io->seek_proc(handle, skip_length, SEEK_CUR); + nBytes += skip_length; + } + break; + } + } + } + } + + if (nBytes == nTotalBytes) { + bSuccess = true; + } + + return bSuccess; + +} + +FIBITMAP* psdParser::ReadImageData(FreeImageIO *io, fi_handle handle) { + if(handle == NULL) + return NULL; + + bool header_only = (_fi_flags & FIF_LOAD_NOPIXELS) == FIF_LOAD_NOPIXELS; + + WORD nCompression = 0; + io->read_proc(&nCompression, sizeof(nCompression), 1, handle); + +#ifndef FREEIMAGE_BIGENDIAN + SwapShort(&nCompression); +#endif + + if ((nCompression != PSDP_COMPRESSION_NONE && nCompression != PSDP_COMPRESSION_RLE)) { + FreeImage_OutputMessageProc(_fi_format_id, "Unsupported compression %d", nCompression); + return NULL; + } + + const unsigned nWidth = _headerInfo._Width; + const unsigned nHeight = _headerInfo._Height; + const unsigned nChannels = _headerInfo._Channels; + const unsigned depth = _headerInfo._BitsPerChannel; + const unsigned bytes = (depth == 1) ? 1 : depth / 8; + + // channel(plane) line (BYTE aligned) + const unsigned lineSize = (_headerInfo._BitsPerChannel == 1) ? (nWidth + 7) / 8 : nWidth * bytes; + + if(nCompression == PSDP_COMPRESSION_RLE && depth > 16) { + FreeImage_OutputMessageProc(_fi_format_id, "Unsupported RLE with depth %d", depth); + return NULL; + } + + // build output buffer + + FIBITMAP* bitmap = NULL; + unsigned dstCh = 0; + + short mode = _headerInfo._ColourMode; + + if(mode == PSDP_MULTICHANNEL && nChannels < 3) { + // CM + mode = PSDP_GRAYSCALE; // C as gray, M as extra channel + } + + bool needPalette = false; + switch (mode) { + case PSDP_BITMAP: + case PSDP_DUOTONE: + case PSDP_INDEXED: + case PSDP_GRAYSCALE: + dstCh = 1; + switch(depth) { + case 16: + bitmap = FreeImage_AllocateHeaderT(header_only, FIT_UINT16, nWidth, nHeight, depth*dstCh); + break; + case 32: + bitmap = FreeImage_AllocateHeaderT(header_only, FIT_FLOAT, nWidth, nHeight, depth*dstCh); + break; + default: // 1-, 8- + needPalette = true; + bitmap = FreeImage_AllocateHeader(header_only, nWidth, nHeight, depth*dstCh); + break; + } + break; + case PSDP_RGB: + case PSDP_LAB: + case PSDP_CMYK : + case PSDP_MULTICHANNEL : + // force PSDP_MULTICHANNEL CMY as CMYK + dstCh = (mode == PSDP_MULTICHANNEL && !header_only) ? 4 : MIN<unsigned>(nChannels, 4); + if(dstCh < 3) { + throw "Invalid number of channels"; + } + + switch(depth) { + case 16: + bitmap = FreeImage_AllocateHeaderT(header_only, dstCh < 4 ? FIT_RGB16 : FIT_RGBA16, nWidth, nHeight, depth*dstCh); + break; + case 32: + bitmap = FreeImage_AllocateHeaderT(header_only, dstCh < 4 ? FIT_RGBF : FIT_RGBAF, nWidth, nHeight, depth*dstCh); + break; + default: + bitmap = FreeImage_AllocateHeader(header_only, nWidth, nHeight, depth*dstCh); + break; + } + break; + default: + throw "Unsupported color mode"; + break; + } + if (!bitmap) { + throw FI_MSG_ERROR_DIB_MEMORY; + } + + // write thumbnail + FreeImage_SetThumbnail(bitmap, _thumbnail.getDib()); + + // @todo Add some metadata model + + if(header_only) { + return bitmap; + } + + // Load pixels data + + const unsigned dstChannels = dstCh; + + const unsigned dstBpp = (depth == 1) ? 1 : FreeImage_GetBPP(bitmap)/8; + const unsigned dstLineSize = FreeImage_GetPitch(bitmap); + BYTE* const dst_first_line = FreeImage_GetScanLine(bitmap, nHeight - 1);//<*** flipped + + BYTE* line_start = new BYTE[lineSize]; //< fileline cache + + switch ( nCompression ) { + case PSDP_COMPRESSION_NONE: // raw data + { + for(unsigned c = 0; c < nChannels; c++) { + if(c >= dstChannels) { + // @todo write extra channels + break; + } + + const unsigned channelOffset = c * bytes; + + BYTE* dst_line_start = dst_first_line; + for(unsigned h = 0; h < nHeight; ++h, dst_line_start -= dstLineSize) {//<*** flipped + + io->read_proc(line_start, lineSize, 1, handle); + + for (BYTE *line = line_start, *dst_line = dst_line_start; line < line_start + lineSize; + line += bytes, dst_line += dstBpp) { +#ifdef FREEIMAGE_BIGENDIAN + memcpy(dst_line + channelOffset, line, bytes); +#else + // reverse copy bytes + for (unsigned b = 0; b < bytes; ++b) { + dst_line[channelOffset + b] = line[(bytes-1) - b]; + } +#endif // FREEIMAGE_BIGENDIAN + } + } //< h + }//< ch + + SAFE_DELETE_ARRAY(line_start); + + } + break; + + case PSDP_COMPRESSION_RLE: // RLE compression + { + + // The RLE-compressed data is preceeded by a 2-byte line size for each row in the data, + // store an array of these + + // later use this array as WORD rleLineSizeList[nChannels][nHeight]; + WORD *rleLineSizeList = new (std::nothrow) WORD[nChannels*nHeight]; + + if (!rleLineSizeList) { + FreeImage_Unload(bitmap); + SAFE_DELETE_ARRAY(line_start); + throw std::bad_alloc(); + } + + io->read_proc(rleLineSizeList, 2, nChannels * nHeight, handle); + + WORD largestRLELine = 0; + for(unsigned ch = 0; ch < nChannels; ++ch) { + for(unsigned h = 0; h < nHeight; ++h) { + const unsigned index = ch * nHeight + h; + +#ifndef FREEIMAGE_BIGENDIAN + SwapShort(&rleLineSizeList[index]); +#endif + if(largestRLELine < rleLineSizeList[index]) { + largestRLELine = rleLineSizeList[index]; + } + } + } + + BYTE* rle_line_start = new (std::nothrow) BYTE[largestRLELine]; + if (!rle_line_start) { + FreeImage_Unload(bitmap); + SAFE_DELETE_ARRAY(line_start); + SAFE_DELETE_ARRAY(rleLineSizeList); + throw std::bad_alloc(); + } + + // Read the RLE data (assume 8-bit) + + const BYTE* const line_end = line_start + lineSize; + + for (unsigned ch = 0; ch < nChannels; ch++) { + const unsigned channelOffset = ch * bytes; + + BYTE* dst_line_start = dst_first_line; + for(unsigned h = 0; h < nHeight; ++h, dst_line_start -= dstLineSize) {//<*** flipped + const unsigned index = ch * nHeight + h; + + // - read and uncompress line - + + const WORD rleLineSize = rleLineSizeList[index]; + + io->read_proc(rle_line_start, rleLineSize, 1, handle); + + for (BYTE* rle_line = rle_line_start, *line = line_start; + rle_line < rle_line_start + rleLineSize, line < line_end;) { + + int len = *rle_line++; + + // NOTE len is signed byte in PackBits RLE + + if ( len < 128 ) { //<- MSB is not set + // uncompressed packet + + // (len + 1) bytes of data are copied + + ++len; + + // assert we don't write beyound eol + memcpy(line, rle_line, line + len > line_end ? line_end - line : len); + line += len; + rle_line += len; + } + else if ( len > 128 ) { //< MSB is set + + // RLE compressed packet + + // One byte of data is repeated (–len + 1) times + + len ^= 0xFF; // same as (-len + 1) & 0xFF + len += 2; // + + // assert we don't write beyound eol + memset(line, *rle_line++, line + len > line_end ? line_end - line : len); + line += len; + + } + else if ( 128 == len ) { + // Do nothing + } + }//< rle_line + + // - write line to destination - + + if(ch >= dstChannels) { + // @todo write to extra channels + break; + } + + // byte by byte copy a single channel to pixel + for (BYTE *line = line_start, *dst_line = dst_line_start; line < line_start + lineSize; + line += bytes, dst_line += dstBpp) { + +#ifdef FREEIMAGE_BIGENDIAN + memcpy(dst_line + channelOffset, line, bytes); +#else + // reverse copy bytes + for (unsigned b = 0; b < bytes; ++b) { + dst_line[channelOffset + b] = line[(bytes-1) - b]; + } +#endif // FREEIMAGE_BIGENDIAN + } + }//< h + }//< ch + + SAFE_DELETE_ARRAY(line_start); + SAFE_DELETE_ARRAY(rleLineSizeList); + SAFE_DELETE_ARRAY(rle_line_start); + } + break; + + case 2: // ZIP without prediction, no specification + break; + + case 3: // ZIP with prediction, no specification + break; + + default: // Unknown format + break; + + } + + // --- Further process the bitmap --- + + if ((mode == PSDP_CMYK || mode == PSDP_MULTICHANNEL)) { + // CMYK values are "inverted", invert them back + + if(mode == PSDP_MULTICHANNEL) { + invertColor(bitmap); + } else { + FreeImage_Invert(bitmap); + } + + if ((_fi_flags & PSD_CMYK) == PSD_CMYK) { + // keep as CMYK + + if(mode == PSDP_MULTICHANNEL) { + //### we force CMY to be CMYK, but CMY has no ICC. + // Create empty profile and add the flag. + FreeImage_CreateICCProfile(bitmap, NULL, 0); + FreeImage_GetICCProfile(bitmap)->flags |= FIICC_COLOR_IS_CMYK; + } + } + else { + // convert to RGB + + ConvertCMYKtoRGBA(bitmap); + + // The ICC Profile is no longer valid + _iccProfile.clear(); + + // remove the pending A if not present in source + if(nChannels == 4 || nChannels == 3 ) { + FIBITMAP* t = RemoveAlphaChannel(bitmap); + if(t) { + FreeImage_Unload(bitmap); + bitmap = t; + } // else: silently fail + } + } + } + else if ( mode == PSDP_LAB && !((_fi_flags & PSD_LAB) == PSD_LAB)) { + ConvertLABtoRGB(bitmap); + } + else { + if (needPalette && FreeImage_GetPalette(bitmap)) { + + if(mode == PSDP_BITMAP) { + CREATE_GREYSCALE_PALETTE_REVERSE(FreeImage_GetPalette(bitmap), 2); + } + else if(mode == PSDP_INDEXED) { + if (!_colourModeData._plColourData || _colourModeData._Length != 768 || _ColourCount < 0) { + FreeImage_OutputMessageProc(_fi_format_id, "Indexed image has no palette. Using the default grayscale one."); + } else { + _colourModeData.FillPalette(bitmap); + } + } + // GRAYSCALE, DUOTONE - use default grayscale palette + } + +#if FREEIMAGE_COLORORDER == FREEIMAGE_COLORORDER_BGR + if(FreeImage_GetImageType(bitmap) == FIT_BITMAP) { + SwapRedBlue32(bitmap); + } +#endif + } + + return bitmap; +} + +FIBITMAP* psdParser::Load(FreeImageIO *io, fi_handle handle, int s_format_id, int flags) { + FIBITMAP *Bitmap = NULL; + + _fi_flags = flags; + _fi_format_id = s_format_id; + + try { + if (NULL == handle) { + throw("Cannot open file"); + } + + if (!_headerInfo.Read(io, handle)) { + throw("Error in header"); + } + + if (!_colourModeData.Read(io, handle)) { + throw("Error in ColourMode Data"); + } + + if (!ReadImageResources(io, handle)) { + throw("Error in Image Resource"); + } + + if (!ReadLayerAndMaskInfoSection(io, handle)) { + throw("Error in Mask Info"); + } + + Bitmap = ReadImageData(io, handle); + if (NULL == Bitmap) { + throw("Error in Image Data"); + } + + // set resolution info + if(NULL != Bitmap) { + unsigned res_x = 2835; // 72 dpi + unsigned res_y = 2835; // 72 dpi + if (_bResolutionInfoFilled) { + _resolutionInfo.GetResolutionInfo(res_x, res_y); + } + FreeImage_SetDotsPerMeterX(Bitmap, res_x); + FreeImage_SetDotsPerMeterY(Bitmap, res_y); + } + + // set ICC profile + FreeImage_CreateICCProfile(Bitmap, _iccProfile._ProfileData, _iccProfile._ProfileSize); + if ((flags & PSD_CMYK) == PSD_CMYK) { + short mode = _headerInfo._ColourMode; + if ((mode == PSDP_CMYK) || (mode == PSDP_MULTICHANNEL)) { + FreeImage_GetICCProfile(Bitmap)->flags |= FIICC_COLOR_IS_CMYK; + } + } + + } catch(const char *text) { + FreeImage_OutputMessageProc(s_format_id, text); + } + catch(const std::exception& e) { + FreeImage_OutputMessageProc(s_format_id, "%s", e.what()); + } + + return Bitmap; +} |