/******************************************************************************/ #include "stdafx.h" namespace EE{ /******************************************************************************/ #if SUPPORT_PSD struct PSD { struct HEADER { Byte Signature[4], // always equal 8BPS, do not read file if not Version [2], // always equal 1, do not read file if not Reserved [6], // must be zero Channels [2], // numer of channels including any alpha channels, supported range 1 to 24 Rows [4], // height in PIXELS, supported range 1 to 30000 Columns [4], // width in PIXELS, supported range 1 to 30000 Depth [2], // number of bpp Mode [2]; // colour mode of the file, Bitmap=0, Grayscale=1, Indexed=2, RGB=3, CMYK=4, Multichannel=7, Duotone=8, Lab=9 }; 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. I16 nChannels; Int nHeight; Int nWidth; I16 nBitsPerPixel; I16 nColourMode; HEADER_INFO() { nChannels=-1; nHeight=-1; nWidth=-1; nBitsPerPixel=-1; nColourMode=-1; } }; struct COLOUR_MODE_DATA { Int nLength; Byte *ColourData; ~COLOUR_MODE_DATA() { DeleteN(ColourData); } COLOUR_MODE_DATA() { nLength=-1; ColourData=null; } }; 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]; I16 nID; Byte *Name; Int nSize; IMAGE_RESOURCE() { Name=null; Reset(); } ~IMAGE_RESOURCE() { DeleteN(Name); } void Reset() { nLength=-1; REPAO(OSType)=0; nID=-1; DeleteN(Name); nSize=-1; } }; 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. // I16 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. // I16 heightUnit Display height as 1=inches; 2=cm; 3=points; 4=picas; 5=columns. I16 hRes; Int hResUnit; I16 widthUnit; I16 vRes; Int vResUnit; I16 heightUnit; RESOLUTION_INFO() { hRes=-1; hResUnit=-1; widthUnit=-1; vRes=-1; vResUnit=-1; heightUnit=-1; } }; struct RESOLUTION_INFO_v2 // Obsolete - Photoshop 2.0 { I16 nChannels; I16 nRows; I16 nColumns; I16 nDepth; I16 nMode; RESOLUTION_INFO_v2() { nChannels=-1; nRows=-1; nColumns=-1; nDepth=-1; nMode=-1; } }; 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. I16 ColourSpace; I16 Colour[4]; I16 Opacity; // 0..100 Bool kind; // selected=0, protected=1 Byte padding; // should be zero DISPLAY_INFO() { ColourSpace=-1; REPAO(Colour)=0; Opacity=-1; kind=false; padding='0'; } }; 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; I16 nBitPerPixel; I16 nPlanes; Byte *Data; THUMBNAIL() { nFormat=-1; nWidth=-1; nHeight=-1; nWidthBytes=-1; nSize=-1; nCompressedSize=-1; nBitPerPixel=-1; nPlanes=-1; Data=null; } }; Bool ReadHeader(File &f, HEADER_INFO& header_info); Bool ReadColourModeData(File &f, COLOUR_MODE_DATA& colour_mode_data); Bool ReadImageResource(File &f, IMAGE_RESOURCE& image_resource); Bool ReadLayerAndMaskInfoSection(File &f); Bool ReadImageData(File &f, Image &dest); Bool ProccessBuffer(Byte *pData, Image &dest); HEADER_INFO header_info; COLOUR_MODE_DATA colour_mode_data; I16 mnColourCount; I16 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; Bool merged_alpha; PSD() { mbThumbNailFilled=false; mbDisplayInfoFilled=false; mbResolutionInfoFilled=false; mbResolutionInfoFilled_v2=false; mnGlobalAngle=30; mbCopyright=false; mnColourCount=-1; mnTransparentIndex=-1; merged_alpha=false; } }; /******************************************************************************/ static UInt Calculate(Byte *b, Int digs) { UInt v=0; FREP(digs)v=(v<<8)|b[i]; return v; } static UInt GetUInt(File &f) {Byte b[4]; f>>b; return Calculate(b, Elms(b));} static UInt GetU16 (File &f) {Byte b[2]; f>>b; return Calculate(b, Elms(b));} static Int GetI16 (File &f) {Byte b[2]; f>>b; return (I16)Calculate(b, Elms(b));} static void XYZToRGB(Dbl x, Dbl y, Dbl 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 Dbl ref_X= 95.047, ref_Y=100.000, ref_Z=108.883; Dbl var_X=x/100.0, var_Y=y/100.0, var_Z=z/100.0; Dbl var_R=var_X * 3.2406 + var_Y * -1.5372 + var_Z * -0.4986, var_G=var_X * -0.9689 + var_Y * 1.8758 + var_Z * 0.0415, 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=FltToByte(var_R); g=FltToByte(var_G); b=FltToByte(var_B); } static void LABToRGB(Dbl L, Dbl A, Dbl 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 Dbl ref_X= 95.047, ref_Y=100.000, ref_Z=108.883; Dbl var_Y=(L + 16.0)/116.0, var_X= A /500.0 + var_Y, var_Z=var_Y - 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; Dbl x=ref_X*var_X, y=ref_Y*var_Y, z=ref_Z*var_Z; XYZToRGB(x, y, z, r, g, b); } static void CMYKToRGB(Dbl c, Dbl m, Dbl y, Dbl k, Int &r, Int &g, Int &b) { //r=RoundPos(Sat(1-(c*(1-k)+k))*255.0f); //g=RoundPos(Sat(1-(m*(1-k)+k))*255.0f); //b=RoundPos(Sat(1-(y*(1-k)+k))*255.0f); Dbl colors=1-k; r=RoundPos(Sat(colors*(1-c))*255); g=RoundPos(Sat(colors*(1-m))*255); b=RoundPos(Sat(colors*(1-y))*255); /*r=RoundPos(Sat(1-Min(1.0, c+k))*255); g=RoundPos(Sat(1-Min(1.0, m+k))*255); b=RoundPos(Sat(1-Min(1.0, y+k))*255);*/ /*r=RoundPos(255*Sat(1-Min(1.0, c*(1-k)+k))); g=RoundPos(255*Sat(1-Min(1.0, m*(1-k)+k))); b=RoundPos(255*Sat(1-Min(1.0, y*(1-k)+k)));*/ } /******************************************************************************/ Bool PSD::ReadHeader(File &f, HEADER_INFO& header_info) { HEADER header; f>>header; if(header.Signature[0]=='8' && header.Signature[1]=='B' && header.Signature[2]=='P' && header.Signature[3]=='S') { Int ver=Calculate(header.Version, SIZE(header.Version)); if( ver==1) { REPA(header.Reserved)if(header.Reserved[i])return false; header_info.nChannels =Calculate(header.Channels, SIZE(header.Channels)); header_info.nHeight =Calculate(header.Rows , SIZE(header.Rows )); header_info.nWidth =Calculate(header.Columns , SIZE(header.Columns )); header_info.nBitsPerPixel=Calculate(header.Depth , SIZE(header.Depth )); header_info.nColourMode =Calculate(header.Mode , SIZE(header.Mode )); return f.ok(); } } return false; } /******************************************************************************/ Bool PSD::ReadColourModeData(File &f, 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 DeleteN(colour_mode_data.ColourData); colour_mode_data.nLength=GetUInt(f); if(colour_mode_data.nLength>0) { colour_mode_data.ColourData=new Byte[colour_mode_data.nLength]; f.get(colour_mode_data.ColourData, colour_mode_data.nLength); } return f.ok(); } /******************************************************************************/ Bool PSD::ReadLayerAndMaskInfoSection(File &f) { UInt size=GetUInt(f); Long pos=f.pos()+size; if(size) if(UInt layer_len=GetUInt(f)) { Int layers=GetI16(f); if( layers<0) { merged_alpha=true; CHS(layers); } } f.pos(pos); return f.ok(); } /******************************************************************************/ Bool PSD::ReadImageResource(File &f, IMAGE_RESOURCE& image_resource) { image_resource.nLength=GetUInt(f); #if 1 f.skip(image_resource.nLength); return f.ok(); #else Int nBytesRead=0, nTotalBytes=image_resource.nLength; for(; !f.end() && nBytesRead= display_info.Opacity); Byte c[1]; nItemsRead=fread(&c, SIZE(c), 1, pFile); nBytesRead += nItemsRead * SIZE(c); (1 == Calculate(c, SIZE(c))) ? display_info.kind=true : display_info.kind=false; nItemsRead=fread(&c, SIZE(c), 1, pFile); nBytesRead += nItemsRead * SIZE(c); display_info.padding=(UInt)Calculate(c, SIZE(c)); assert (0 == display_info.padding); }break; case 1034: { nItemsRead=fread(&ShortValue, SIZE(ShortValue), 1, pFile); nBytesRead += nItemsRead * SIZE(ShortValue); (1 == Calculate(ShortValue, SIZE(ShortValue))) ? mbCopyright=true : mbCopyright=false; }break; case 1033: case 1036: { mbThumbNailFilled=true; nItemsRead=fread(&IntValue, SIZE(IntValue), 1, pFile); nBytesRead += nItemsRead * SIZE(IntValue); thumbnail.nFormat=Calculate(IntValue, SIZE(thumbnail.nFormat)); nItemsRead=fread(&IntValue, SIZE(IntValue), 1, pFile); nBytesRead += nItemsRead * SIZE(IntValue); thumbnail.nWidth=Calculate(IntValue, SIZE(thumbnail.nWidth)); nItemsRead=fread(&IntValue, SIZE(IntValue), 1, pFile); nBytesRead += nItemsRead * SIZE(IntValue); thumbnail.nHeight=Calculate(IntValue, SIZE(thumbnail.nHeight)); nItemsRead=fread(&IntValue, SIZE(IntValue), 1, pFile); nBytesRead += nItemsRead * SIZE(IntValue); thumbnail.nWidthBytes=Calculate(IntValue, SIZE(thumbnail.nWidthBytes)); nItemsRead=fread(&IntValue, SIZE(IntValue), 1, pFile); nBytesRead += nItemsRead * SIZE(IntValue); thumbnail.nSize=Calculate(IntValue, SIZE(thumbnail.nSize)); nItemsRead=fread(&IntValue, SIZE(IntValue), 1, pFile); nBytesRead += nItemsRead * SIZE(IntValue); thumbnail.nCompressedSize=Calculate(IntValue, SIZE(thumbnail.nCompressedSize)); nItemsRead=fread(&ShortValue, SIZE(ShortValue), 1, pFile); nBytesRead += nItemsRead * SIZE(ShortValue); thumbnail.nBitPerPixel=Calculate(ShortValue, SIZE(thumbnail.nBitPerPixel)); nItemsRead=fread(&ShortValue, SIZE(ShortValue), 1, pFile); nBytesRead += nItemsRead * SIZE(ShortValue); thumbnail.nPlanes=Calculate(ShortValue, SIZE(thumbnail.nPlanes)); Int nTotalData=image_resource.nSize - 28; // header Byte *buffer=new Byte[nTotalData]; Byte c[1]; if(1033 == image_resource.nID) { // In BGR format for(Int n=0; n=nWidth){x=0; y++;} } return true; } }break; case 2: // Indexed { // pData holds the indices of loop through the palette and set the correct RGB, 8bpp are supported if(colour_mode_data.ColourData && colour_mode_data.nLength==768 && mnColourCount>0) if(dest.createSoftTry(nWidth, nHeight, 1, IMAGE_R8G8B8)) { for(; nCounter=nWidth){x=0; y++;} } return true; } }break; case 3: // RGB(A), there can be more than 4 channels !! { if(dest.createSoftTry(nWidth, nHeight, 1, (header_info.nChannels>=4) ? IMAGE_R8G8B8A8 : IMAGE_R8G8B8)) { for(; nCounter=4) ? Calculate(pData+nCounter+3*bytesPerPixelPerChannel, bytesPerPixelPerChannel) : 0); if(bytesPerPixelPerChannel==4) // Flt { red =RoundU(Pow(Sat((Flt&)red ), 1/2.12f)*255); green=RoundU(Pow(Sat((Flt&)green), 1/2.12f)*255); blue =RoundU(Pow(Sat((Flt&)blue ), 1/2.12f)*255); }else REP(bytesPerPixelPerChannel-1){red>>=8; green>>=8; blue>>=8; alpha>>=8;} dest.color(x, y, Color(red, green, blue, alpha)); x++; if(x>=nWidth){x=0; y++;} } return true; } }break; case 4: // CMYK { if(dest.createSoftTry(nWidth, nHeight, 1, IMAGE_R8G8B8)) { for(; nCounter=nWidth){x=0; y++;} } return true; } }break; case 7: // Multichannel { if(header_info.nChannels==1) // for now support just one channel if(dest.createSoftTry(nWidth, nHeight, 1, IMAGE_L8)) { for(; nCounter=nWidth){x=0; y++;} } return true; } }break; case 9: // LAB { if(dest.createSoftTry(nWidth, nHeight, 1, (header_info.nChannels==4) ? IMAGE_R8G8B8A8 : IMAGE_R8G8B8)) { Dbl L_coef=max_value/100.0, a_coef=max_value/256.0, b_coef=max_value/256.0; for(; nCounter>=8; dest.color(x, y, Color(red, green, blue, alpha)); x++; if(x>=nWidth){x=0; y++;} } return true; } }break; } } return false; } /******************************************************************************/ Bool PSD::ReadImageData(File &f, Image &dest) { Bool ok=false; switch(GetU16(f)) { case 0: // raw data { Int nWidth=header_info.nWidth; Int nHeight=header_info.nHeight; Int bytesPerPixelPerChannel=header_info.nBitsPerPixel/8; Int nPixels=nWidth*nHeight; Int nTotalBytes=nPixels*bytesPerPixelPerChannel*header_info.nChannels; Int nBytesRead=0; Byte *pData=null; switch(header_info.nColourMode) { case 1: // Grayscale case 7: // Multichannel case 8: // Duotone { pData=new Byte[nTotalBytes]; nBytesRead=f.getReturnSize(pData, nTotalBytes); }break; case 2: // Indexed { if(mnColourCount>0 && colour_mode_data.ColourData!=0) { pData=new Byte[nTotalBytes]; nBytesRead=f.getReturnSize(pData, nTotalBytes); } }break; case 3: // RGB case 4: // CMYK case 9: // LAB { pData=new Byte[nTotalBytes]; for(Int nColour=0; nColour128) { // Next -len+1 bytes in the dest are replicated from next source byte, Interpret len as a negative 8-bit Int len^=0xFF; len+=2; Byte value=f.getByte(); count+=len; while(len) { *p++=value; len--; } }/*else if(len==128) { // Do nothing }*/ } } Byte *pSrc =pData, *pDest=new Byte[nTotalBytes]; Int nPixelCounter=0; for(Int nColour=0; nColour