// ========================================================== // 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; iread_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); if(n != 1) { FreeImage_OutputMessageProc(_fi_format_id, "This file contains damaged data causing an unexpected end-of-file - stop reading resources"); return false; } 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(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; }