/*
 * File:	ximapsd.cpp
 * Purpose:	Platform Independent PSD Image Class Loader
 * Dec/2010 Davide Pizzolato - www.xdp.it
 * CxImage version 7.0.0 31/Dec/2010
 *
 * libpsd (c) 2004-2007 Graphest Software
 *
 * Based on MyPSD class by Iosif Hamlatzis
 * Details: http://www.codeproject.com/KB/graphics/MyPSD.aspx
 * Cleaned up a bit and ported to CxImage by Vitaly Ovchinnikov
 * Send feedback to vitaly(dot)ovchinnikov(at)gmail.com
 */

#include "ximapsd.h"

#if CXIMAGE_SUPPORT_PSD

enum {
	PSD_FILE_HEADER,
	PSD_COLOR_MODE_DATA,
	PSD_IMAGE_RESOURCE,
	PSD_LAYER_AND_MASK_INFORMATION,
	PSD_IMAGE_DATA,
	PSD_DONE
};

////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////
#if CXIMAGE_USE_LIBPSD == 0
// MyPSD.h /////////////////////////////////////////////////////////////////////

#ifndef __MyPSD_H__
#define __MyPSD_H__

namespace MyPSD
{

	class CPSD
	{
		struct HEADER_INFO
		{
			//Table 2-12: HeaderInfo Color spaces
			//	Color-ID	Name	Description
			//-------------------------------------------
			//		0		Bitmap			// Probably means black & white
			//		1		Grayscale		The first value in the color data is the gray value, from 0...10000.
			//		2		Indexed
			//		3		RGB				The first three values in the color data are red, green, and blue.
			//								They are full unsigned 16–bit values as in Apple’s RGBColor data
			//								structure. Pure red=65535,0,0.
			//		4		CMYK			The four values in the color data are cyan, magenta, yellow, and
			//								black. They are full unsigned 16–bit values. 0=100% ink. Pure
			//								cyan=0,65535,65535,65535.
			//		7		Multichannel	// Have no idea
			//		8		Duotone
			//		9		Lab				The first three values in the color data are lightness, a chrominance,
			//								and b chrominance.
			//								Lightness is a 16–bit value from 0...100. The chromanance components
			//								are each 16–bit values from –128...127. Gray values
			//								are represented by chrominance components of 0. Pure
			//								white=100,0,0.
			short nChannels;
			int nHeight;
			int nWidth;
			short nBitsPerPixel;
			short nColourMode;
			HEADER_INFO();
		};

		struct COLOUR_MODE_DATA
		{
			int nLength;
			unsigned char* ColourData;
			COLOUR_MODE_DATA();
		};


		struct IMAGE_RESOURCE
		{
			// Table 2–1: Image resource block
			//	Type		Name	Description
			//-------------------------------------------
			//	OSType		Type	Photoshop always uses its signature, 8BIM
			//	int16		ID		Unique identifier
			//	PString		Name	A pascal string, padded to make size even (a null name consists of two bytes of 0)
			//						Pascal style string where the first byte gives the length of the
			//						string and the content bytes follow.
			//	int32		Size	Actual size of resource data. This does not include the
			//						Type, ID, Name, or Size fields.
			//	Variable	Data	Resource data, padded to make size even
			int nLength;
			char OSType[4];
			short nID;
			unsigned char* Name;
			int	nSize;
			IMAGE_RESOURCE();
			void Reset();
		};

		struct RESOLUTION_INFO
		{
			// Table A-6: ResolutionInfo structure
			//	Type		Name	Description
			//-------------------------------------------
			//	Fixed		hRes		Horizontal resolution in pixels per inch.
			//	int			hResUnit	1=display horizontal resolution in pixels per inch;
			//							2=display horizontal resolution in pixels per cm.
			//	short		widthUnit	Display width as 1=inches; 2=cm; 3=points; 4=picas; 5=columns.
			//	Fixed		vRes		Vertical resolution in pixels per inch.
			//	int			vResUnit	1=display vertical resolution in pixels per inch;
			//							2=display vertical resolution in pixels per cm.
			//	short		heightUnit	Display height as 1=inches; 2=cm; 3=points; 4=picas; 5=columns.
			short hRes;
			int hResUnit;
			short widthUnit;

			short vRes;
			int vResUnit;
			short heightUnit;
			RESOLUTION_INFO();
		};

		struct RESOLUTION_INFO_v2	// Obsolete - Photoshop 2.0
		{
			short nChannels;
			short nRows;
			short nColumns;
			short nDepth;
			short nMode;
			RESOLUTION_INFO_v2();
		};

		struct DISPLAY_INFO
		{
			// This structure contains display information about each channel.
			//Table A-7: DisplayInfo Color spaces
			//	Color-ID	Name	Description
			//-------------------------------------------
			//		0		RGB			The first three values in the color data are red, green, and blue.
			//							They are full unsigned 16–bit values as in Apple’s RGBColor data
			//							structure. Pure red=65535,0,0.
			//		1		HSB			The first three values in the color data are hue, saturation, and
			//							brightness. They are full unsigned 16–bit values as in Apple’s
			//							HSVColor data structure. Pure red=0,65535, 65535.
			//		2		CMYK		The four values in the color data are cyan, magenta, yellow, and
			//							black. They are full unsigned 16–bit values. 0=100% ink. Pure
			//							cyan=0,65535,65535,65535.
			//		7		Lab			The first three values in the color data are lightness, a chrominance,
			//							and b chrominance.
			//							Lightness is a 16–bit value from 0...10000. The chromanance components
			//							are each 16–bit values from –12800...12700. Gray values
			//							are represented by chrominance components of 0. Pure
			//							white=10000,0,0.
			//		8		grayscale	The first value in the color data is the gray value, from 0...10000.
			short ColourSpace;
			short Colour[4];
			short Opacity;			// 0..100
			bool kind;				// selected = 0, protected = 1
			unsigned char padding;	// should be zero
			DISPLAY_INFO();
		};
		struct THUMBNAIL
		{
			// Adobe Photoshop 5.0 and later stores thumbnail information for preview
			// display in an image resource block. These resource blocks consist of an
			// 28 byte header, followed by a JFIF thumbnail in RGB (red, green, blue)
			// for both Macintosh and Windows. Adobe Photoshop 4.0 stored the
			// thumbnail information in the same format except the data section is
			// (blue, green, red). The Adobe Photoshop 4.0 format is at resource ID
			// and the Adobe Photoshop 5.0 format is at resource ID 1036.
			// Table 2–5: Thumnail resource header
			//	Type		Name		Description
			//-------------------------------------------
			//	4 bytes		format			= 1 (kJpegRGB). Also supports kRawRGB (0).
			//	4 bytes		width			Width of thumbnail in pixels.
			//	4 bytes		height			Height of thumbnail in pixels.
			//	4 bytes		widthbytes		Padded row bytes as (width * bitspixel + 31) / 32 * 4.
			//	4 bytes		size			Total size as widthbytes * height * planes
			//	4 bytes		compressedsize	Size after compression. Used for consistentcy check.
			//	2 bytes		bitspixel		= 24. Bits per pixel.
			//	2 bytes		planes			= 1. Number of planes.
			//	Variable	Data			JFIF data in RGB format.
			//								Note: For resource ID 1033 the data is in BGR format.
			int		nFormat;
			int		nWidth;
			int		nHeight;
			int		nWidthBytes;
			int		nSize;
			int		nCompressedSize;
			short	nBitPerPixel;
			short	nPlanes;
			unsigned char* Data;
			THUMBNAIL();
		};


		CxImage	&m_image;

		HEADER_INFO header_info;

		COLOUR_MODE_DATA colour_mode_data;
		short mnColourCount;
		short mnTransparentIndex;

		IMAGE_RESOURCE image_resource;

		int		mnGlobalAngle;

		RESOLUTION_INFO resolution_info;
		bool	mbResolutionInfoFilled;

		RESOLUTION_INFO_v2 resolution_info_v2;
		bool	mbResolutionInfoFilled_v2;

		DISPLAY_INFO display_info;
		bool	mbDisplayInfoFilled;

		THUMBNAIL thumbnail;
		bool	mbThumbNailFilled;

		bool	mbCopyright;

		int Calculate(unsigned char* c, int nDigits);
		void XYZToRGB(const double X, const double Y, const double Z, int &R, int &G, int &B);
		void LabToRGB(const int L, const int a, const int b, int &R, int &G, int &B );
		void CMYKToRGB(const double C, const double M, const double Y, const double K, int &R, int &G, int &B);

		bool ReadHeader(CxFile &f, HEADER_INFO& header_info);
		bool ReadColourModeData(CxFile &f, COLOUR_MODE_DATA& colour_mode_data);
		bool ReadImageResource(CxFile &f, IMAGE_RESOURCE& image_resource);
		bool ReadLayerAndMaskInfoSection(CxFile &f); // Actually ignore it
		int ReadImageData(CxFile &f);

		int DecodeRawData(CxFile &pFile);
		int DecodeRLEData(CxFile &pFile);

		void ProccessBuffer(unsigned char* pData = 0);

	public:
		CPSD(CxImage &image);
		~CPSD();

		int Load(LPCTSTR szPathName);
		int Load(CxFile &file);

		bool ThumbNailIncluded() const { return mbThumbNailFilled; }
		void DPI(int &x, int &y) const { x = resolution_info.hRes; y = resolution_info.vRes; }
		void Dimensions(int &cx, int &cy) const { cx = header_info.nWidth; cy = header_info.nHeight; }
		int BitsPerPixel() const { return header_info.nBitsPerPixel; }
		int GlobalAngle() const { return mnGlobalAngle; }
		bool IsCopyright() const { return mbCopyright; }
		HBITMAP Detach();
	};
}

#endif // __MyPSD_H__

// MyPSD.cpp ///////////////////////////////////////////////////////////////////


inline int dti(double value) { return (int)floor(value+.5f); }

#define assert(a) 

#define mypsd_fread(a, b, c, d) d.Read(a, b, c)
#define mypsd_fseek(a, b, c) a.Seek(b, c)
#define mypsd_feof(a) a.Eof()

namespace MyPSD
{
	CPSD::CPSD(CxImage &image) : m_image(image)
	{
		mbThumbNailFilled = false;
		mbDisplayInfoFilled = false;
		mbResolutionInfoFilled = false;
		mbResolutionInfoFilled_v2 = false;
		mnGlobalAngle = 30;
		mbCopyright = false;
		mnColourCount = -1;
		mnTransparentIndex = -1;
	}
	CPSD::~CPSD()
	{
		// free memory
		if ( 0 < colour_mode_data.nLength )
			delete[] colour_mode_data.ColourData;
		colour_mode_data.ColourData = 0;

		if ( image_resource.Name )
			delete[] image_resource.Name;
		image_resource.Name = 0;
	}

	int CPSD::Calculate(unsigned char* c, int nDigits)
	{
		int nValue = 0;

		for(int n = 0; n < nDigits; ++n)
			nValue = ( nValue << 8 ) | *(c+n);

		return nValue;
	};

	void CPSD::XYZToRGB(const double X, const double Y, const double Z, int &R, int &G, int &B)
	{
		// Standards used Observer = 2, Illuminant = D65
		// ref_X = 95.047, ref_Y = 100.000, ref_Z = 108.883
		const double ref_X = 95.047;
		const double ref_Y = 100.000;
		const double ref_Z = 108.883;

		double var_X = X / 100.0;
		double var_Y = Y / 100.0;
		double var_Z = Z / 100.0;

		double var_R = var_X * 3.2406 + var_Y * (-1.5372) + var_Z * (-0.4986);
		double var_G = var_X * (-0.9689) + var_Y * 1.8758 + var_Z * 0.0415;
		double var_B = var_X * 0.0557 + var_Y * (-0.2040) + var_Z * 1.0570;

		if ( var_R > 0.0031308 )
			var_R = 1.055 * ( pow(var_R, 1/2.4) ) - 0.055;
		else
			var_R = 12.92 * var_R;

		if ( var_G > 0.0031308 )
			var_G = 1.055 * ( pow(var_G, 1/2.4) ) - 0.055;
		else
			var_G = 12.92 * var_G;

		if ( var_B > 0.0031308 )
			var_B = 1.055 * ( pow(var_B, 1/2.4) )- 0.055;
		else
			var_B = 12.92 * var_B;

		R = (int)(var_R * 256.0);
		G = (int)(var_G * 256.0);
		B = (int)(var_B * 256.0);
	};

	void CPSD::LabToRGB(const int L, const int a, const int b, int &R, int &G, int &B )
	{
		// For the conversion we first convert values to XYZ and then to RGB
		// Standards used Observer = 2, Illuminant = D65
		// ref_X = 95.047, ref_Y = 100.000, ref_Z = 108.883
		const double ref_X = 95.047;
		const double ref_Y = 100.000;
		const double ref_Z = 108.883;

		double var_Y = ( (double)L + 16.0 ) / 116.0;
		double var_X = (double)a / 500.0 + var_Y;
		double var_Z = var_Y - (double)b / 200.0;

		if ( pow(var_Y, 3) > 0.008856 )
			var_Y = pow(var_Y, 3);
		else
			var_Y = ( var_Y - 16 / 116 ) / 7.787;

		if ( pow(var_X, 3) > 0.008856 )
			var_X = pow(var_X, 3);
		else
			var_X = ( var_X - 16 / 116 ) / 7.787;

		if ( pow(var_Z, 3) > 0.008856 )
			var_Z = pow(var_Z, 3);
		else
			var_Z = ( var_Z - 16 / 116 ) / 7.787;

		double X = ref_X * var_X;
		double Y = ref_Y * var_Y;
		double Z = ref_Z * var_Z;

		XYZToRGB(X, Y, Z, R, G, B);
	};

	void CPSD::CMYKToRGB(const double C, const double M, const double Y, const double K, int &R, int &G, int &B )
	{
		R = dti( ( 1.0f - ( C *( 1.0f - K ) + K ) ) * 255.0f );
		G = dti( ( 1.0f - ( M *( 1.0f - K ) + K ) ) * 255.0f );
		B = dti( ( 1.0f - ( Y *( 1.0f - K ) + K ) ) * 255.0f );
	};
	
	bool CPSD::ReadLayerAndMaskInfoSection(CxFile &pFile)	// Actually ignore it
	{
		bool bSuccess = false;

		unsigned char DataLength[4];
		int nBytesRead = 0;
		int nItemsRead = (int)(int)mypsd_fread(&DataLength, sizeof(DataLength), 1, pFile);

		int nTotalBytes = Calculate( DataLength, sizeof(DataLength) );

		unsigned char data[1];
		while( !mypsd_feof( pFile ) && ( nBytesRead < nTotalBytes ) )
		{
			data[0] = '\0';
			nItemsRead = (int)(int)mypsd_fread(&data, sizeof(data), 1, pFile);
			nBytesRead += nItemsRead * sizeof(data);
		}

		assert ( nBytesRead == nTotalBytes );
		if ( nBytesRead == nTotalBytes )
			bSuccess = true;

		return bSuccess;
	}
	bool CPSD::ReadImageResource(CxFile &pFile, IMAGE_RESOURCE& image_resource)
	{
		bool bSuccess = false;

		unsigned char Length[4];
		int nItemsRead = (int)(int)mypsd_fread(&Length, sizeof(Length), 1, pFile);

		image_resource.nLength = Calculate( Length, sizeof(image_resource.nLength) );

		int nBytesRead = 0;
		int nTotalBytes = image_resource.nLength;

		while( !mypsd_feof( pFile ) && ( nBytesRead < nTotalBytes ) )
		{
			nItemsRead = 0;
			image_resource.Reset();

			nItemsRead = (int)(int)mypsd_fread(&image_resource.OSType, sizeof(image_resource.OSType), 1, pFile);
			nBytesRead += nItemsRead * sizeof(image_resource.OSType);

			assert ( 0 == (nBytesRead % 2) );
			if (::memcmp(image_resource.OSType, "8BIM", 4) == 0)
			{
				unsigned char ID[2];
				nItemsRead = (int)(int)mypsd_fread(&ID, sizeof(ID), 1, pFile);
				nBytesRead += nItemsRead * sizeof(ID);

				image_resource.nID = (short)Calculate( ID, sizeof(ID) );

				unsigned char SizeOfName;
				nItemsRead = (int)(int)mypsd_fread(&SizeOfName, sizeof(SizeOfName), 1, pFile);
				nBytesRead += nItemsRead * sizeof(SizeOfName);

				int nSizeOfName = Calculate( &SizeOfName, sizeof(SizeOfName) );
				if ( 0 < nSizeOfName )
				{
					image_resource.Name = new unsigned char[nSizeOfName];
					nItemsRead = (int)(int)mypsd_fread(image_resource.Name, nSizeOfName, 1, pFile);
					nBytesRead += nItemsRead * nSizeOfName;
				}

				if ( 0 == (nSizeOfName % 2) )
				{
					nItemsRead = (int)(int)mypsd_fread(&SizeOfName, sizeof(SizeOfName), 1, pFile);
					nBytesRead += nItemsRead * sizeof(SizeOfName);
				}

				unsigned char Size[4];
				nItemsRead = (int)(int)mypsd_fread(&Size, sizeof(Size), 1, pFile);
				nBytesRead += nItemsRead * sizeof(Size);

				image_resource.nSize = Calculate( Size, sizeof(image_resource.nSize) );

				if ( 0 != (image_resource.nSize % 2) )	// resource data must be even
					image_resource.nSize++;
				if ( 0 < image_resource.nSize )
				{
					unsigned char IntValue[4];
					unsigned char ShortValue[2];

					switch( image_resource.nID )
					{
					case 1000:
						{
							// Obsolete - Photoshop 2.0
							mbResolutionInfoFilled_v2 = true;

							nItemsRead = (int)(int)mypsd_fread(&ShortValue, sizeof(ShortValue), 1, pFile);
							nBytesRead += nItemsRead * sizeof(ShortValue);
							resolution_info_v2.nChannels = (short)Calculate(ShortValue, sizeof(resolution_info_v2.nChannels) );
							nItemsRead = (int)(int)mypsd_fread(&ShortValue, sizeof(ShortValue), 1, pFile);
							nBytesRead += nItemsRead * sizeof(ShortValue);
							resolution_info_v2.nRows = (short)Calculate(ShortValue, sizeof(resolution_info_v2.nRows) );
							nItemsRead = (int)(int)mypsd_fread(&ShortValue, sizeof(ShortValue), 1, pFile);
							nBytesRead += nItemsRead * sizeof(ShortValue);
							resolution_info_v2.nColumns = (short)Calculate(ShortValue, sizeof(resolution_info_v2.nColumns) );
							nItemsRead = (int)(int)mypsd_fread(&ShortValue, sizeof(ShortValue), 1, pFile);
							nBytesRead += nItemsRead * sizeof(ShortValue);
							resolution_info_v2.nDepth = (short)Calculate(ShortValue, sizeof(resolution_info_v2.nDepth) );
							nItemsRead = (int)(int)mypsd_fread(&ShortValue, sizeof(ShortValue), 1, pFile);
							nBytesRead += nItemsRead * sizeof(ShortValue);
							resolution_info_v2.nMode = (short)Calculate(ShortValue, sizeof(resolution_info_v2.nMode) );
						}
						break;
					case 1005:
						{
							mbResolutionInfoFilled = true;

							nItemsRead = (int)(int)mypsd_fread(&ShortValue, sizeof(ShortValue), 1, pFile);
							nBytesRead += nItemsRead * sizeof(ShortValue);
							resolution_info.hRes = (short)Calculate(ShortValue, sizeof(resolution_info.hRes) );
							nItemsRead = (int)(int)mypsd_fread(&IntValue, sizeof(IntValue), 1, pFile);
							nBytesRead += nItemsRead * sizeof(IntValue);
							resolution_info.hResUnit = Calculate(IntValue, sizeof(resolution_info.hResUnit) );
							nItemsRead = (int)(int)mypsd_fread(&ShortValue, sizeof(ShortValue), 1, pFile);
							nBytesRead += nItemsRead * sizeof(ShortValue);
							resolution_info.widthUnit = (short)Calculate(ShortValue, sizeof(resolution_info.widthUnit) );

							nItemsRead = (int)(int)mypsd_fread(&ShortValue, sizeof(ShortValue), 1, pFile);
							nBytesRead += nItemsRead * sizeof(ShortValue);
							resolution_info.vRes = (short)Calculate(ShortValue, sizeof(resolution_info.vRes) );
							nItemsRead = (int)(int)mypsd_fread(&IntValue, sizeof(IntValue), 1, pFile);
							nBytesRead += nItemsRead * sizeof(IntValue);
							resolution_info.vResUnit = Calculate(IntValue, sizeof(resolution_info.vResUnit) );
							nItemsRead = (int)mypsd_fread(&ShortValue, sizeof(ShortValue), 1, pFile);
							nBytesRead += nItemsRead * sizeof(ShortValue);
							resolution_info.heightUnit = (short)Calculate(ShortValue, sizeof(resolution_info.heightUnit) );
						}
						break;
					case 1007:
						{
							mbDisplayInfoFilled = true;

							nItemsRead = (int)mypsd_fread(&ShortValue, sizeof(ShortValue), 1, pFile);
							nBytesRead += nItemsRead * sizeof(ShortValue);
							display_info.ColourSpace = (short)Calculate(ShortValue, sizeof(display_info.ColourSpace) );

							for ( unsigned int n = 0; n < 4; ++n )
							{
								nItemsRead = (int)mypsd_fread(&ShortValue, sizeof(ShortValue), 1, pFile);
								nBytesRead += nItemsRead * sizeof(ShortValue);
								display_info.Colour[n] = (short)Calculate(ShortValue, sizeof(display_info.Colour[n]) );
							}

							nItemsRead = (int)mypsd_fread(&ShortValue, sizeof(ShortValue), 1, pFile);
							nBytesRead += nItemsRead * sizeof(ShortValue);
							display_info.Opacity = (short)Calculate(ShortValue, sizeof(display_info.Opacity) );
							assert ( 0 <= display_info.Opacity );
							assert ( 100 >= display_info.Opacity );

							unsigned char c[1];
							nItemsRead = (int)mypsd_fread(&c, sizeof(c), 1, pFile);
							nBytesRead += nItemsRead * sizeof(c);
							( 1 == Calculate(c, sizeof(c) ) ) ? display_info.kind = true : display_info.kind = false;

							nItemsRead = (int)mypsd_fread(&c, sizeof(c), 1, pFile);
							nBytesRead += nItemsRead * sizeof(c);
							display_info.padding = (unsigned int)Calculate(c, sizeof(c) );
							assert ( 0 == display_info.padding );
						}
						break;
					case 1034:
						{
							nItemsRead = (int)mypsd_fread(&ShortValue, sizeof(ShortValue), 1, pFile);
							nBytesRead += nItemsRead * sizeof(ShortValue);
							( 1 == Calculate(ShortValue, sizeof(ShortValue) ) ) ? mbCopyright = true : mbCopyright = false;
						}
						break;
					case 1033:
					case 1036:
						{
							mbThumbNailFilled = true;

							nItemsRead = (int)mypsd_fread(&IntValue, sizeof(IntValue), 1, pFile);
							nBytesRead += nItemsRead * sizeof(IntValue);
							thumbnail.nFormat = Calculate(IntValue, sizeof(thumbnail.nFormat) );

							nItemsRead = (int)mypsd_fread(&IntValue, sizeof(IntValue), 1, pFile);
							nBytesRead += nItemsRead * sizeof(IntValue);
							thumbnail.nWidth = Calculate(IntValue, sizeof(thumbnail.nWidth) );

							nItemsRead = (int)mypsd_fread(&IntValue, sizeof(IntValue), 1, pFile);
							nBytesRead += nItemsRead * sizeof(IntValue);
							thumbnail.nHeight = Calculate(IntValue, sizeof(thumbnail.nHeight) );

							nItemsRead = (int)mypsd_fread(&IntValue, sizeof(IntValue), 1, pFile);
							nBytesRead += nItemsRead * sizeof(IntValue);
							thumbnail.nWidthBytes = Calculate(IntValue, sizeof(thumbnail.nWidthBytes) );

							nItemsRead = (int)mypsd_fread(&IntValue, sizeof(IntValue), 1, pFile);
							nBytesRead += nItemsRead * sizeof(IntValue);
							thumbnail.nSize = Calculate(IntValue, sizeof(thumbnail.nSize) );

							nItemsRead = (int)mypsd_fread(&IntValue, sizeof(IntValue), 1, pFile);
							nBytesRead += nItemsRead * sizeof(IntValue);
							thumbnail.nCompressedSize = Calculate(IntValue, sizeof(thumbnail.nCompressedSize) );

							nItemsRead = (int)mypsd_fread(&ShortValue, sizeof(ShortValue), 1, pFile);
							nBytesRead += nItemsRead * sizeof(ShortValue);
							thumbnail.nBitPerPixel = (short)Calculate(ShortValue, sizeof(thumbnail.nBitPerPixel) );

							nItemsRead = (int)mypsd_fread(&ShortValue, sizeof(ShortValue), 1, pFile);
							nBytesRead += nItemsRead * sizeof(ShortValue);
							thumbnail.nPlanes = (short)Calculate(ShortValue, sizeof(thumbnail.nPlanes) );

							int nTotalData = image_resource.nSize - 28; // header
							unsigned char* buffer = new unsigned char[nTotalData];
							unsigned char c[1];
							if ( 1033 == image_resource.nID )
							{
								// In BGR format
								for (int n = 0; n < nTotalData; n = n +3 )
								{
									nItemsRead = (int)mypsd_fread(&c, sizeof(unsigned char), 1, pFile);
									nBytesRead += nItemsRead * sizeof(unsigned char);
									buffer[n+2] = (unsigned char)Calculate(c, sizeof(unsigned char) );
									nItemsRead = (int)mypsd_fread(&c, sizeof(unsigned char), 1, pFile);
									nBytesRead += nItemsRead * sizeof(unsigned char);
									buffer[n+1] = (unsigned char)Calculate(c, sizeof(BYTE) );
									nItemsRead = (int)mypsd_fread(&c, sizeof(unsigned char), 1, pFile);
									nBytesRead += nItemsRead * sizeof(unsigned char);
									buffer[n] = (unsigned char)Calculate(c, sizeof(unsigned char) );
								}
							}
							else if ( 1036 == image_resource.nID )
							{
								// In RGB format										
								for (int n = 0; n < nTotalData; ++n )
								{
									nItemsRead = (int)mypsd_fread(&c, sizeof(BYTE), 1, pFile);
									nBytesRead += nItemsRead * sizeof(BYTE);
									buffer[n] = (BYTE)Calculate(c, sizeof(BYTE) );
								}
							}

							delete[] buffer;
							buffer = 0;
						}
						break;
					case 1037:
						{
							nItemsRead = (int)mypsd_fread(&IntValue, sizeof(IntValue), 1, pFile);
							nBytesRead += nItemsRead * sizeof(IntValue);
							mnGlobalAngle = Calculate(IntValue, sizeof(mnGlobalAngle) );
						}
						break;
					case 1046:
						{
							nItemsRead = (int)mypsd_fread(&ShortValue, sizeof(ShortValue), 1, pFile);
							nBytesRead += nItemsRead * sizeof(ShortValue);
							mnColourCount = (short)Calculate(ShortValue, sizeof(ShortValue) );
						}
						break;
					case 1047:
						{
							nItemsRead = (int)mypsd_fread(&ShortValue, sizeof(ShortValue), 1, pFile);
							nBytesRead += nItemsRead * sizeof(ShortValue);
							mnTransparentIndex = (short)Calculate(ShortValue, sizeof(ShortValue) );
						}
						break;

					default:
						pFile.Seek(image_resource.nSize, SEEK_CUR);
						nBytesRead += image_resource.nSize;
						break;
					}
				}
			}
		}

		assert ( nBytesRead == nTotalBytes );
		if ( nBytesRead == nTotalBytes )
			bSuccess = true;

		return bSuccess;
	}
	bool CPSD::ReadColourModeData(CxFile &pFile, COLOUR_MODE_DATA& colour_mode_data)
	{
		// Only indexed colour and duotone have colour mode data,
		// for all other modes this section is 4 bytes length, the length field is set to zero

		// For indexed color images, the length will be equal to 768, and the color
		// will contain the color table for the image, in non–interleaved order.

		// For duotone images, the color data will contain the duotone specification,
		// the format of which is not documented. Other applications that read
		// Photoshop files can treat a duotone image as a grayscale image, and just
		// preserve the contents of the duotone information when reading and writing
		// the file.

		// free memory
		if ( 0 < colour_mode_data.nLength )
			delete[] colour_mode_data.ColourData;
		colour_mode_data.ColourData = 0;

		unsigned char Length[4];
		int nItemsRead = (int)mypsd_fread(&Length, sizeof(Length), 1, pFile);

		colour_mode_data.nLength = Calculate( Length, sizeof(colour_mode_data.nLength) );
		if ( 0 < colour_mode_data.nLength )
		{
			colour_mode_data.ColourData = new unsigned char[colour_mode_data.nLength];
			nItemsRead = 0;
			memset(colour_mode_data.ColourData, 254, colour_mode_data.nLength);

			nItemsRead += (int)mypsd_fread( colour_mode_data.ColourData, colour_mode_data.nLength, 1, pFile);

		}

		return true;
	}

	bool CPSD::ReadHeader(CxFile &pFile, HEADER_INFO& header_info)
	{
		bool bSuccess = false;

		struct HEADER
		{
			char Signature[4];	// always equal 8BPS, do not read file if not
			unsigned char Version[2];	// always equal 1, do not read file if not
			char Reserved[6];	// must be zero
			unsigned char Channels[2];	// numer of channels including any alpha channels, supported range 1 to 24
			unsigned char Rows[4];		// height in PIXELS, supported range 1 to 30000
			unsigned char Columns[4];	// width in PIXELS, supported range 1 to 30000
			unsigned char Depth[2];		// number of bpp
			unsigned char Mode[2];		// colour mode of the file,
			// Btmap=0, Grayscale=1, Indexed=2, RGB=3,
			// CMYK=4, Multichannel=7, Duotone=8, Lab=9
		};

		HEADER header;
		int nItemsRead = (int)mypsd_fread(&header, sizeof(HEADER), 1, pFile);
		if ( nItemsRead )
		{
			if ( 0 == ::memcmp(header.Signature, "8BPS", 4))
			{
				int nVersion = Calculate( header.Version, sizeof(header.Version) );

				if ( 1 == nVersion )
				{
					unsigned int n = 0;
					bool bOK = true;
					while ( (n < 6) && bOK )
					{
						if ( '\0' != header.Reserved[n] )
							bOK = false;
						n++;
					}
					bSuccess = bOK;

					if ( bSuccess )
					{
						header_info.nChannels = (short)Calculate( header.Channels, sizeof(header.Channels) );
						header_info.nHeight = Calculate( header.Rows, sizeof(header.Rows) );
						header_info.nWidth = Calculate( header.Columns, sizeof(header.Columns) );
						header_info.nBitsPerPixel = (short)Calculate( header.Depth, sizeof(header.Depth) );
						header_info.nColourMode = (short)Calculate( header.Mode, sizeof(header.Mode) );
					}
				}
			}
		}

		return bSuccess;
	}


	void CPSD::ProccessBuffer(unsigned char* pData )
	{
		if (!pData) return;

		switch ( header_info.nColourMode )
		{
		case 1:		// Grayscale
		case 8:		// Duotone
			{
				bool bAlpha = header_info.nChannels > 1;

				int nPixels = header_info.nWidth * header_info.nHeight;
				byte *pRGBA = new byte[nPixels * (bAlpha ? 4 : 3)];
				byte *pSrc = pData, *pDst = pRGBA;
				for (int i = 0; i < nPixels; i++, pSrc += header_info.nChannels, pDst += bAlpha ? 4 : 3)
				{
					pDst[0] = pDst[1] = pDst[2] = pSrc[0];
					if (bAlpha) pDst[3] = pSrc[1];
				}
				
				m_image.CreateFromArray(pRGBA, header_info.nWidth, header_info.nHeight, bAlpha ? 32 : 24, header_info.nWidth * (bAlpha ? 4 : 3), true);

				delete [] pRGBA;
			}
			break;
		case 2:		// Indexed
			{
				if (!colour_mode_data.ColourData) break;
				if (colour_mode_data.nLength != 768) break;
				if (mnColourCount == 0) break;

				int nPixels = header_info.nWidth * header_info.nHeight;
				byte *pRGB = new byte[nPixels * 3];
				::memset(pRGB, 0, nPixels * 3);
				byte *pSrc = pData, *pDst = pRGB;
				for (int i = 0; i < nPixels; i++, pSrc += header_info.nChannels, pDst += 3)
				{
					int nIndex = *pSrc;
					pDst[2] = colour_mode_data.ColourData[nIndex + 0 * 256];
					pDst[1] = colour_mode_data.ColourData[nIndex + 1 * 256];
					pDst[0] = colour_mode_data.ColourData[nIndex + 2 * 256];
				}

				m_image.CreateFromArray(pRGB, header_info.nWidth, header_info.nHeight, 24, header_info.nWidth * 3, true);
				delete [] pRGB;
			}
			break;
		case 3:	// RGB
			{
				m_image.CreateFromArray(pData, header_info.nWidth, header_info.nHeight, header_info.nChannels == 3 ? 24 : 32, header_info.nWidth * header_info.nChannels, true);
				m_image.SwapRGB2BGR();
			}
			break;
		case 4:	// CMYK
			{
				bool bAlpha = header_info.nChannels > 4;

				int nPixels = header_info.nWidth * header_info.nHeight;
				byte *pRGBA = new byte[nPixels * (bAlpha ? 4 : 3)];
				byte *pSrc = pData, *pDst = pRGBA;
				double C, M, Y, K;
				int	nRed, nGreen, nBlue;
				for (int i = 0; i < nPixels; i++, pSrc += header_info.nChannels, pDst += bAlpha ? 4 : 3)
				{
					C = (1.0 - (double)pSrc[0] / 256);
					M = (1.0 - (double)pSrc[1] / 256);
					Y = (1.0 - (double)pSrc[2] / 256);
					K = (1.0 - (double)pSrc[3] / 256);
					
					CMYKToRGB(C, M, Y, K, nRed, nGreen, nBlue);

					if (0 > nRed) nRed = 0;		else if (255 < nRed) nRed = 255;
					if (0 > nGreen) nGreen = 0;	else if (255 < nGreen) nGreen = 255;
					if (0 > nBlue) nBlue = 0;	else if (255 < nBlue) nBlue = 255;

					pDst[0] = nBlue; pDst[1] = nGreen; pDst[2] = nRed;
					if (bAlpha) pDst[3] = pSrc[4];
				}

				m_image.CreateFromArray(pRGBA, header_info.nWidth, header_info.nHeight, bAlpha ? 32 : 24, header_info.nWidth * (bAlpha ? 4 : 3), true);

				delete [] pRGBA;
			}
			break;
		case 7:		// Multichannel
			{
				if (header_info.nChannels == 0 || header_info.nChannels > 4) break; // ???

				int nPixels = header_info.nWidth * header_info.nHeight;
				byte *pRGB = new byte[nPixels * 3];
				byte *pSrc = pData, *pDst = pRGB;
				double C, M, Y, K;
				int	nRed, nGreen, nBlue;
				for (int i = 0; i < nPixels; i++, pSrc += header_info.nChannels, pDst += 3)
				{
					C = M = Y = K = 0;
					C = (1.0 - (double)pSrc[0] / 256);
					if (header_info.nChannels > 1) M = (1.0 - (double)pSrc[1] / 256);
					if (header_info.nChannels > 2) Y = (1.0 - (double)pSrc[2] / 256);
					if (header_info.nChannels > 3) K = (1.0 - (double)pSrc[3] / 256);

					CMYKToRGB(C, M, Y, K, nRed, nGreen, nBlue);

					if (0 > nRed) nRed = 0;		else if (255 < nRed) nRed = 255;
					if (0 > nGreen) nGreen = 0;	else if (255 < nGreen) nGreen = 255;
					if (0 > nBlue) nBlue = 0;	else if (255 < nBlue) nBlue = 255;

					pDst[0] = nBlue; pDst[1] = nGreen; pDst[2] = nRed;
				}

				m_image.CreateFromArray(pRGB, header_info.nWidth, header_info.nHeight, 24, header_info.nWidth * 3, true);

				delete [] pRGB;
			}
			break;
		case 9:	// Lab
			{
				bool bAlpha = header_info.nChannels > 3;

				int nPixels = header_info.nWidth * header_info.nHeight;
				byte *pRGBA = new byte[nPixels * (bAlpha ? 4 : 3)];
				byte *pSrc = pData, *pDst = pRGBA;

				double L_coef = 256.f / 100.f, a_coef = 256.f / 256.f, b_coef = 256.f / 256.f;
				int L, a, b;
				int	nRed, nGreen, nBlue;
				for (int i = 0; i < nPixels; i++, pSrc += header_info.nChannels, pDst += bAlpha ? 4 : 3)
				{
					L = (int)((float)pSrc[0] / L_coef);
					a = (int)((float)pSrc[1] / a_coef - 128.0);
					b = (int)((float)pSrc[2] / b_coef - 128.0);

					LabToRGB(L, a, b, nRed, nGreen, nBlue );

					if (0 > nRed) nRed = 0;		else if (255 < nRed) nRed = 255;
					if (0 > nGreen) nGreen = 0;	else if (255 < nGreen) nGreen = 255;
					if (0 > nBlue) nBlue = 0;	else if (255 < nBlue) nBlue = 255;

					pDst[0] = nBlue; pDst[1] = nGreen; pDst[2] = nRed;
					if (bAlpha) pDst[3] = pSrc[3];
				}

				m_image.CreateFromArray(pRGBA, header_info.nWidth, header_info.nHeight, bAlpha ? 32 : 24, header_info.nWidth * (bAlpha ? 4 : 3), true);

				delete [] pRGBA;
			}
			break;
		}
	}

	int CPSD::Load(LPCTSTR szPathName)
	{
		CxIOFile	f;
		if (!f.Open(szPathName, _T("rb"))) return -1;
		return Load(f);
	}

	int CPSD::Load(CxFile &f)	
	{
		if (!ReadHeader(f, header_info)) return -2; // Error in header
		if (!ReadColourModeData(f, colour_mode_data)) return -3; // Error in ColourMode Data
		if (!ReadImageResource(f, image_resource)) return -4; // Error in Image Resource
		if (!ReadLayerAndMaskInfoSection(f)) return -5; // Error in Mask Info
		if (ReadImageData(f) != 0) return -6; // Error in Image Data
		return 0; // all right
	}

	int CPSD::DecodeRawData( CxFile &pFile)
	{
		if (header_info.nBitsPerPixel != 8 && header_info.nBitsPerPixel != 16) return -7; // can't read this

		int nWidth = header_info.nWidth;
		int nHeight = header_info.nHeight;
		int bytesPerPixelPerChannel = header_info.nBitsPerPixel / 8;

		int nPixels = nWidth * nHeight;
		int nTotalBytes = 0;

		byte* pData = NULL;

		switch ( header_info.nColourMode )
		{
		case 1:	// Grayscale
		case 2:	// Indexed
		case 3:	// RGB
		case 4:	// CMYK
		case 8:	// Duotone
		case 9:	// Lab
			{
				// read RRRRRRRGGGGGGGBBBBBBAAAAAA data
				int	nAllDataSize = nPixels * bytesPerPixelPerChannel * header_info.nChannels;
				byte *pFileData = new byte[nAllDataSize];
				::memset(pFileData, 0, nAllDataSize);
				if (pFile.Read(pFileData, nAllDataSize, 1) != 1)
				{
					delete [] pFileData;
					return -1; // bad data
				}

				// and convert them to RGBARGBARGBA data (depends on number of channels)
				nTotalBytes = nPixels * header_info.nChannels;
				pData = new byte[nTotalBytes];
				byte *pSource = pFileData;
				for (int nChannel = 0; nChannel < header_info.nChannels; nChannel++)
				{
					byte *pDest = pData + nChannel;
					for (int pos = 0; pos < nPixels; pos++, pDest += header_info.nChannels, pSource += bytesPerPixelPerChannel) *pDest = *pSource;
				}
				delete [] pFileData;
			}
			break;
		default:
			return -1; // unsupported format
		}

		ProccessBuffer(pData);
		delete [] pData;

		// dpi related things
		int ppm_x = 3780;	// 96 dpi
		int ppm_y = 3780;	// 96 dpi
		if (mbResolutionInfoFilled)
		{
			int nHorResolution = (int)resolution_info.hRes;
			int nVertResolution = (int)resolution_info.vRes;
			ppm_x = (nHorResolution * 10000) / 254;
			ppm_y = (nVertResolution * 10000) / 254;
		}
		m_image.SetXDPI(ppm_x);
		m_image.SetYDPI(ppm_y);

		return 0;
	}


	int CPSD::DecodeRLEData(CxFile & pFile)
	{
		if (header_info.nBitsPerPixel != 8) return -7; // can't read this

		int nWidth = header_info.nWidth;
		int nHeight = header_info.nHeight;
		int nPixels = nWidth * nHeight;

		// The RLE-compressed data is preceeded by a 2-byte data count for each row in the data
		// read them and compute size of RLE data
		int nLengthDataSize = nHeight * header_info.nChannels * 2;
		byte *pLengthData = new byte[nLengthDataSize];
		if (pFile.Read(pLengthData, nLengthDataSize, 1) != 1)
		{
			delete [] pLengthData;
			return -1; // error while reading
		}
		int nRLEDataSize = 0;
		for (int i = 0; i < nHeight * header_info.nChannels * 2; i += 2)
			nRLEDataSize += Calculate(pLengthData + i, 2);
		delete [] pLengthData;

		// now read RLE data to the buffer for fast access
		byte *pRLEData = new byte[nRLEDataSize];
		if (pFile.Read(pRLEData, nRLEDataSize, 1) != 1)
		{
			delete [] pRLEData;
			return -1;
		}

		// allocate buffer for raw data (RRRRRRR...RRRGGGGG...GGGGGGBBBBB...BBBBBAAAAA....AAAAA) it has the same size as the final buffer
		// and the perform RLE-decoding
		int nTotalBytes = nPixels * header_info.nChannels;
		byte* pRawData = new byte[nTotalBytes];
		byte *pRLESource = pRLEData, *pRLEDest = pRawData;
		for (int channel = 0; channel < header_info.nChannels; channel++)
		{
			int nCount = 0;
			while (nCount < nPixels)
			{
				int len = *pRLESource++;
				if ( 128 > len )
				{ // copy next (len + 1) bytes as is
					len++;
					nCount += len;
					::memcpy(pRLEDest, pRLESource, len);
					pRLEDest += len; pRLESource += len;
				}
				else if ( 128 < len )
				{
					// Next -len+1 bytes in the dest are replicated from next source byte.
					// (Interpret len as a negative 8-bit int.)
					len ^= 0x0FF;
					len += 2;
					nCount += len;
					::memset(pRLEDest, *pRLESource++, len);
					pRLEDest += len;
				}
				else if ( 128 == len ) { /* Do nothing */ }
			}
		}
		delete [] pRLEData;

		// transform raw data to the good one (RGBARGBARGBA...RGBA)
		byte *pRawSource = pRawData;
		byte *pData = new byte[nTotalBytes];
		int nPixelCounter = 0;
		for( int nColour = 0; nColour < header_info.nChannels; ++nColour )
		{
			nPixelCounter = nColour;
			for (int nPos = 0; nPos < nPixels; nPos++, pRawSource++)
			{
				pData[nPixelCounter] = *pRawSource;
				nPixelCounter += header_info.nChannels;
			}
		}
		delete[] pRawData;

		// create image
		ProccessBuffer(pData);
		delete [] pData;

		// dpi related things
		int ppm_x = 3780;	// 96 dpi
		int ppm_y = 3780;	// 96 dpi
		if (mbResolutionInfoFilled)
		{
			int nHorResolution = (int)resolution_info.hRes;
			int nVertResolution = (int)resolution_info.vRes;
			ppm_x = (nHorResolution * 10000) / 254;
			ppm_y = (nVertResolution * 10000) / 254;
		}
		m_image.SetXDPI(ppm_x);
		m_image.SetYDPI(ppm_y);

		return 0;
	}

	int CPSD::ReadImageData(CxFile &pFile)
	{
		int nErrorCode = 0;	// No Errors

 		if ( !mypsd_feof(pFile) )
		{
			unsigned char ShortValue[2];
			int nBytesRead = 0;
			int nItemsRead = (int)mypsd_fread(&ShortValue, sizeof(ShortValue), 1, pFile);
			short nCompression = (short)Calculate( ShortValue, sizeof(ShortValue) );

			switch ( nCompression )
			{
			case 0:	// raw data
				nErrorCode = DecodeRawData(pFile);
				break;
			case 1:	// RLE compression
				nErrorCode = DecodeRLEData(pFile);
				break;
			case 2:	// ZIP without prediction
				nErrorCode = -10;	// ZIP without prediction, no specification
				break;
			case 3:	// ZIP with prediction
				nErrorCode = -11;	// ZIP with prediction, no specification
				break;
			default:
				nErrorCode = -12;	// Unknown format
			}
		}
		return nErrorCode;
	}

	//////////////////////////////////////////////////////////////////////////
	//////////////////////////////////////////////////////////////////////////
	CPSD::HEADER_INFO::HEADER_INFO()
	{
		nChannels = -1;
		nHeight = -1;
		nWidth = -1;
		nBitsPerPixel = -1;
		nColourMode = -1;
	}

	CPSD::COLOUR_MODE_DATA::COLOUR_MODE_DATA()
	{
		nLength = -1;
		ColourData = 0;
	}

	CPSD::IMAGE_RESOURCE::IMAGE_RESOURCE()
	{
		Name = 0;
		Reset();
	}

	void CPSD::IMAGE_RESOURCE::Reset()
	{
		nLength = -1;
		memset( OSType, '\0', sizeof(OSType) );
		nID = -1;
		if ( Name )
			delete[] Name;
		Name = 0;
		nSize = -1;
	}

	CPSD::RESOLUTION_INFO::RESOLUTION_INFO()
	{
		hRes = -1;
		hResUnit = -1;
		widthUnit = -1;
		vRes = -1;
		vResUnit = -1;
		heightUnit = -1;
	}

	CPSD::RESOLUTION_INFO_v2::RESOLUTION_INFO_v2()
	{
		nChannels = -1;
		nRows = -1;
		nColumns = -1;
		nDepth = -1;
		nMode = -1;
	}

	CPSD::DISPLAY_INFO::DISPLAY_INFO()
	{
		ColourSpace = -1;
		for ( unsigned int n = 0; n < 4; ++n)
			Colour[n] = 0;
		Opacity = -1;
		kind = false;
		padding = '0';
	}

	CPSD::THUMBNAIL::THUMBNAIL()
	{
		nFormat = -1;
		nWidth = -1;
		nHeight = -1;
		nWidthBytes = -1;
		nSize = -1;
		nCompressedSize = -1;
		nBitPerPixel = -1;
		nPlanes = -1;
		Data = 0;
	}
}	// MyPSD

////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////
#endif //CXIMAGE_USE_LIBPSD

////////////////////////////////////////////////////////////////////////////////
#if CXIMAGE_SUPPORT_DECODE
////////////////////////////////////////////////////////////////////////////////
bool CxImagePSD::Decode(CxFile *hFile)
{
	if (hFile==NULL)
		return false;

#if CXIMAGE_USE_LIBPSD
	psd_context* context = NULL;
#endif

  cx_try
  {
#if CXIMAGE_USE_LIBPSD

	psd_status status;

	context = (psd_context *)malloc(sizeof(psd_context));
	if(context == NULL){
		cx_throw("CxImagePSD: psd_status_malloc_failed");
	}
	memset(context, 0, sizeof(psd_context));

	// install file manager
	CxFilePsd src(hFile,context);

	context->state = PSD_FILE_HEADER;
	context->stream.file_length = hFile->Size();
	context->load_tag = psd_load_tag_all;
	status = psd_main_loop(context);
	
	if(status != psd_status_done){
		cx_throw("CxImagePSD: psd_main_loop failed");
	}

	Create(context->width,context->height,24,CXIMAGE_FORMAT_PSD);

	uint8_t* rgba = (uint8_t*)context->merged_image_data;
	uint8_t* alpha = NULL;
	if (context->alpha_channel_info)
		alpha = (uint8_t*)context->alpha_channel_info->channel_data;
	if (alpha)
		AlphaCreate();

	int32_t x,y;
	RGBQUAD c;
	c.rgbReserved = 0;
	if (rgba){
		for(y =context->height-1; y--;){
			for (x=0; x<context->width; x++){
				c.rgbBlue  = *rgba++;
				c.rgbGreen = *rgba++;
				c.rgbRed   = *rgba++;
				rgba++;
				SetPixelColor(x,y,c);
#if CXIMAGE_SUPPORT_ALPHA
				if (alpha) AlphaSet(x,y,*alpha++);
#endif //CXIMAGE_SUPPORT_ALPHA
			}
		}
	}

	psd_image_free(context);
	free(context);

#else //CXIMAGE_USE_LIBPSD == 0

	MyPSD::CPSD psd(*this);
	int nErrorCode = psd.Load(*hFile);
	if (nErrorCode != 0) cx_throw("error loading PSD file");

#endif //CXIMAGE_USE_LIBPSD

  } cx_catch {

#if CXIMAGE_USE_LIBPSD
	psd_image_free(context);
	if (context) free(context);
#endif //CXIMAGE_USE_LIBPSD

	if (strcmp(message,"")) strncpy(info.szLastError,message,255);
	if (info.nEscape == -1 && info.dwType == CXIMAGE_FORMAT_PSD) return true;
	return false;
  }
	/* that's it */
	return true;
}

////////////////////////////////////////////////////////////////////////////////
#endif //CXIMAGE_SUPPORT_DECODE
////////////////////////////////////////////////////////////////////////////////
#if CXIMAGE_SUPPORT_ENCODE
////////////////////////////////////////////////////////////////////////////////
bool CxImagePSD::Encode(CxFile * hFile)
{
	if (hFile == NULL) return false;
	strcpy(info.szLastError, "Save PSD not supported");
	return false;
}
////////////////////////////////////////////////////////////////////////////////
#endif // CXIMAGE_SUPPORT_ENCODE
////////////////////////////////////////////////////////////////////////////////
#endif // CXIMAGE_SUPPORT_PSD

