/* * 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; xwidth; 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