summaryrefslogtreecommitdiff
path: root/plugins/FreeImage/Source/FreeImage/PSDParser.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'plugins/FreeImage/Source/FreeImage/PSDParser.cpp')
-rw-r--r--plugins/FreeImage/Source/FreeImage/PSDParser.cpp1052
1 files changed, 1052 insertions, 0 deletions
diff --git a/plugins/FreeImage/Source/FreeImage/PSDParser.cpp b/plugins/FreeImage/Source/FreeImage/PSDParser.cpp
new file mode 100644
index 0000000000..7d210330b0
--- /dev/null
+++ b/plugins/FreeImage/Source/FreeImage/PSDParser.cpp
@@ -0,0 +1,1052 @@
+// ==========================================================
+// 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
+ if(NULL != _iccProfile._ProfileData) {
+ FreeImage_CreateICCProfile(Bitmap, _iccProfile._ProfileData, _iccProfile._ProfileSize);
+ if ((flags & PSD_CMYK) == PSD_CMYK) {
+ 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;
+}