Files
Yajbir Singh f1b860b25c
Some checks failed
check / markdownlint (push) Has been cancelled
check / spellchecker (push) Has been cancelled
updated
2025-12-11 19:03:17 +05:30

1311 lines
41 KiB
C++
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/*
* File: ximapsd.cpp
* Purpose: Platform Independent PSD Image Class Loader
* Dec/2010 Davide Pizzolato - www.xdp.it
* CxImage version 7.0.2 07/Feb/2011
*
* 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 16bit values as in Apples 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 16bit 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 16bit value from 0...100. The chromanance components
// are each 16bit 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 21: 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 16bit values as in Apples 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 16bit values as in Apples
// 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 16bit 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 16bit value from 0...10000. The chromanance components
// are each 16bit 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 25: 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 noninterleaved 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 CXIMAGE_SUPPORT_ALPHA
if (alpha)
AlphaCreate();
#endif
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