diff options
Diffstat (limited to 'plugins/FreeImage/Source/FreeImage/PSDParser.cpp')
| -rw-r--r-- | plugins/FreeImage/Source/FreeImage/PSDParser.cpp | 1052 | 
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;
 +} 
  | 
