- Main Page
- Data Structures
- Files

- File List

# E:/DevIL/src-IL/src/il_psd.c

    00001 //-----------------------------------------------------------------------------
    00002 //
    00003 // ImageLib Sources
    00004 // Copyright (C) 2000-2009 by Denton Woods
    00005 // Last modified: 01/07/2009
    00006 //
    00007 // Filename: src-IL/src/il_psd.c
    00008 //
    00009 // Description: Reads and writes Photoshop (.psd) files.
    00010 //
    00011 //-----------------------------------------------------------------------------
    00012
    00013
    00014 // Information about the .psd format was taken from Adobe's PhotoShop SDK at
    00015 //  http://partners.adobe.com/asn/developer/gapsdk/PhotoshopSDK.html
    00016 //  Information about the Packbits compression scheme was found at
    00017 //      http://partners.adobe.com/asn/developer/PDFS/TN/TIFF6.pdf
    00018
    00019 #include "il_internal.h"
    00020 #ifndef IL_NO_PSD
    00021 #include "il_psd.h"
    00022
    00023 static float ubyte_to_float(ILubyte val)
    00024 {
    00025         return ((float)val) / 255.0f;
    00026 }
    00027 static float ushort_to_float(ILushort val)
    00028 {
    00029         return ((float)val) / 65535.0f;
    00030 }
    00031
    00032 static ILubyte float_to_ubyte(float val)
    00033 {
    00034         return (ILubyte)(val * 255.0f);
    00035 }
    00036 static ILushort float_to_ushort(float val)
    00037 {
    00038         return (ILushort)(val * 65535.0f);
    00039 }
    00040
    00041
    00043 ILboolean ilIsValidPsd(ILconst_string FileName)
    00044 {
    00045         ILHANDLE        PsdFile;
    00046         ILboolean       bPsd = IL_FALSE;
    00047
    00048         if (!iCheckExtension(FileName, IL_TEXT("psd")) &&
    00049                 !iCheckExtension(FileName, IL_TEXT("pdd"))) {
    00050                 ilSetError(IL_INVALID_EXTENSION);
    00051                 return bPsd;
    00052         }
    00053
    00054         PsdFile = iopenr(FileName);
    00055         if (PsdFile == NULL) {
    00056                 ilSetError(IL_COULD_NOT_OPEN_FILE);
    00057                 return bPsd;
    00058         }
    00059
    00060         bPsd = ilIsValidPsdF(PsdFile);
    00061         icloser(PsdFile);
    00062
    00063         return bPsd;
    00064 }
    00065
    00066
    00068 ILboolean ilIsValidPsdF(ILHANDLE File)
    00069 {
    00070         ILuint          FirstPos;
    00071         ILboolean       bRet;
    00072
    00073         iSetInputFile(File);
    00074         FirstPos = itell();
    00075         bRet = iIsValidPsd();
    00076         iseek(FirstPos, IL_SEEK_SET);
    00077
    00078         return bRet;
    00079 }
    00080
    00081
    00083 ILboolean ilIsValidPsdL(const void *Lump, ILuint Size)
    00084 {
    00085         iSetInputLump(Lump, Size);
    00086         return iIsValidPsd();
    00087 }
    00088
    00089
    00090 // Internal function used to get the Psd header from the current file.
    00091 ILboolean iGetPsdHead(PSDHEAD *Header)
    00092 {
    00093         iread(Header->Signature, 1, 4);
    00094         Header->Version = GetBigUShort();
    00095         iread(Header->Reserved, 1, 6);
    00096         Header->Channels = GetBigUShort();
    00097         Header->Height = GetBigUInt();
    00098         Header->Width = GetBigUInt();
    00099         Header->Depth = GetBigUShort();
    00100         Header->Mode = GetBigUShort();
    00101
    00102         return IL_TRUE;
    00103 }
    00104
    00105
    00106 // Internal function to get the header and check it.
    00107 ILboolean iIsValidPsd()
    00108 {
    00109         PSDHEAD Head;
    00110
    00111         iGetPsdHead(&Head);
    00112         iseek(-(ILint)sizeof(PSDHEAD), IL_SEEK_CUR);
    00113
    00114         return iCheckPsd(&Head);
    00115 }
    00116
    00117
    00118 // Internal function used to check if the HEADER is a valid Psd header.
    00119 ILboolean iCheckPsd(PSDHEAD *Header)
    00120 {
    00121         ILuint i;
    00122
    00123         if (strncmp((char*)Header->Signature, "8BPS", 4))
    00124                 return IL_FALSE;
    00125         if (Header->Version != 1)
    00126                 return IL_FALSE;
    00127         for (i = 0; i < 6; i++) {
    00128                 if (Header->Reserved[i] != 0)
    00129                         return IL_FALSE;
    00130         }
    00131         if (Header->Channels < 1 || Header->Channels > 24)
    00132                 return IL_FALSE;
    00133         if (Header->Height < 1 || Header->Width < 1)
    00134                 return IL_FALSE;
    00135         if (Header->Depth != 1 && Header->Depth != 8 && Header->Depth != 16)
    00136                 return IL_FALSE;
    00137
    00138         return IL_TRUE;
    00139 }
    00140
    00141
    00143 ILboolean ilLoadPsd(ILconst_string FileName)
    00144 {
    00145         ILHANDLE        PsdFile;
    00146         ILboolean       bPsd = IL_FALSE;
    00147
    00148         PsdFile = iopenr(FileName);
    00149         if (PsdFile == NULL) {
    00150                 ilSetError(IL_COULD_NOT_OPEN_FILE);
    00151                 return bPsd;
    00152         }
    00153
    00154         bPsd = ilLoadPsdF(PsdFile);
    00155         icloser(PsdFile);
    00156
    00157         return bPsd;
    00158 }
    00159
    00160
    00162 ILboolean ilLoadPsdF(ILHANDLE File)
    00163 {
    00164         ILuint          FirstPos;
    00165         ILboolean       bRet;
    00166
    00167         iSetInputFile(File);
    00168         FirstPos = itell();
    00169         bRet = iLoadPsdInternal();
    00170         iseek(FirstPos, IL_SEEK_SET);
    00171
    00172         return bRet;
    00173 }
    00174
    00175
    00177 ILboolean ilLoadPsdL(const void *Lump, ILuint Size)
    00178 {
    00179         iSetInputLump(Lump, Size);
    00180         return iLoadPsdInternal();
    00181 }
    00182
    00183
    00184 // Internal function used to load the Psd.
    00185 ILboolean iLoadPsdInternal()
    00186 {
    00187         PSDHEAD Header;
    00188
    00189         if (iCurImage == NULL) {
    00190                 ilSetError(IL_ILLEGAL_OPERATION);
    00191                 return IL_FALSE;
    00192         }
    00193
    00194         iGetPsdHead(&Header);
    00195         if (!iCheckPsd(&Header)) {
    00196                 ilSetError(IL_INVALID_FILE_HEADER);
    00197                 return IL_FALSE;
    00198         }
    00199
    00200         if (!ReadPsd(&Header))
    00201                 return IL_FALSE;
    00202         iCurImage->Origin = IL_ORIGIN_UPPER_LEFT;
    00203
    00204         ilFixImage();
    00205
    00206         return IL_TRUE;
    00207 }
    00208
    00209
    00210 ILboolean ReadPsd(PSDHEAD *Head)
    00211 {
    00212         switch (Head->Mode)
    00213         {
    00214                 case 1:  // Greyscale
    00215                         return ReadGrey(Head);
    00216                 case 2:  // Indexed
    00217                         return ReadIndexed(Head);
    00218                 case 3:  // RGB
    00219                         return ReadRGB(Head);
    00220                 case 4:  // CMYK
    00221                         return ReadCMYK(Head);
    00222         }
    00223
    00224         ilSetError(IL_FORMAT_NOT_SUPPORTED);
    00225         return IL_FALSE;
    00226 }
    00227
    00228
    00229 ILboolean ReadGrey(PSDHEAD *Head)
    00230 {
    00231         ILuint          ColorMode, ResourceSize, MiscInfo;
    00232         ILushort        Compressed;
    00233         ILenum          Type;
    00234         ILubyte         *Resources = NULL;
    00235
    00236         ColorMode = GetBigUInt();  // Skip over the 'color mode data section'
    00237         iseek(ColorMode, IL_SEEK_CUR);
    00238
    00239         ResourceSize = GetBigUInt();  // Read the 'image resources section'
    00240         Resources = (ILubyte*)ialloc(ResourceSize);
    00241         if (Resources == NULL) {
    00242                 return IL_FALSE;
    00243         }
    00244         if (iread(Resources, 1, ResourceSize) != ResourceSize)
    00245                 goto cleanup_error;
    00246
    00247         MiscInfo = GetBigUInt();
    00248         iseek(MiscInfo, IL_SEEK_CUR);
    00249
    00250         Compressed = GetBigUShort();
    00251
    00252         ChannelNum = Head->Channels;
    00253         Head->Channels = 1;  // Temporary to read only one channel...some greyscale .psd files have 2.
    00254         if (Head->Channels != 1) {
    00255                 ilSetError(IL_FORMAT_NOT_SUPPORTED);
    00256                 return IL_FALSE;
    00257         }
    00258         switch (Head->Depth)
    00259         {
    00260                 case 8:
    00261                         Type = IL_UNSIGNED_BYTE;
    00262                         break;
    00263                 case 16:
    00264                         Type = IL_UNSIGNED_SHORT;
    00265                         break;
    00266                 default:
    00267                         ilSetError(IL_FORMAT_NOT_SUPPORTED);
    00268                         return IL_FALSE;
    00269         }
    00270
    00271         if (!ilTexImage(Head->Width, Head->Height, 1, 1, IL_LUMINANCE, Type, NULL))
    00272                 goto cleanup_error;
    00273         if (!PsdGetData(Head, iCurImage->Data, (ILboolean)Compressed))
    00274                 goto cleanup_error;
    00275         if (!ParseResources(ResourceSize, Resources))
    00276                 goto cleanup_error;
    00277         ifree(Resources);
    00278
    00279         return IL_TRUE;
    00280
    00281 cleanup_error:
    00282         ifree(Resources);
    00283         return IL_FALSE;
    00284 }
    00285
    00286
    00287 ILboolean ReadIndexed(PSDHEAD *Head)
    00288 {
    00289         ILuint          ColorMode, ResourceSize, MiscInfo, i, j, NumEnt;
    00290         ILushort        Compressed;
    00291         ILubyte         *Palette = NULL, *Resources = NULL;
    00292
    00293         ColorMode = GetBigUInt();  // Skip over the 'color mode data section'
    00294         if (ColorMode % 3 != 0) {
    00295                 ilSetError(IL_INVALID_FILE_HEADER);
    00296                 return IL_FALSE;
    00297         }
    00298         Palette = (ILubyte*)ialloc(ColorMode);
    00299         if (Palette == NULL)
    00300                 return IL_FALSE;
    00301         if (iread(Palette, 1, ColorMode) != ColorMode)
    00302                 goto cleanup_error;
    00303
    00304         ResourceSize = GetBigUInt();  // Read the 'image resources section'
    00305         Resources = (ILubyte*)ialloc(ResourceSize);
    00306         if (Resources == NULL) {
    00307                 return IL_FALSE;
    00308         }
    00309         if (iread(Resources, 1, ResourceSize) != ResourceSize)
    00310                 goto cleanup_error;
    00311
    00312         MiscInfo = GetBigUInt();
    00313         if (ieof())
    00314                 goto cleanup_error;
    00315         iseek(MiscInfo, IL_SEEK_CUR);
    00316
    00317         Compressed = GetBigUShort();
    00318         if (ieof())
    00319                 goto cleanup_error;
    00320
    00321         if (Head->Channels != 1 || Head->Depth != 8) {
    00322                 ilSetError(IL_FORMAT_NOT_SUPPORTED);
    00323                 goto cleanup_error;
    00324         }
    00325         ChannelNum = Head->Channels;
    00326
    00327         if (!ilTexImage(Head->Width, Head->Height, 1, 1, IL_COLOUR_INDEX, IL_UNSIGNED_BYTE, NULL))
    00328                 goto cleanup_error;
    00329
    00330         iCurImage->Pal.Palette = (ILubyte*)ialloc(ColorMode);
    00331         if (iCurImage->Pal.Palette == NULL) {
    00332                 goto cleanup_error;
    00333         }
    00334         iCurImage->Pal.PalSize = ColorMode;
    00335         iCurImage->Pal.PalType = IL_PAL_RGB24;
    00336
    00337         NumEnt = iCurImage->Pal.PalSize / 3;
    00338         for (i = 0, j = 0; i < iCurImage->Pal.PalSize; i += 3, j++) {
    00339                 iCurImage->Pal.Palette[i  ] = Palette[j];
    00340                 iCurImage->Pal.Palette[i+1] = Palette[j+NumEnt];
    00341                 iCurImage->Pal.Palette[i+2] = Palette[j+NumEnt*2];
    00342         }
    00343         ifree(Palette);
    00344         Palette = NULL;
    00345
    00346         if (!PsdGetData(Head, iCurImage->Data, (ILboolean)Compressed))
    00347                 goto cleanup_error;
    00348
    00349         ParseResources(ResourceSize, Resources);
    00350         ifree(Resources);
    00351         Resources = NULL;
    00352
    00353         return IL_TRUE;
    00354
    00355 cleanup_error:
    00356         ifree(Palette);
    00357         ifree(Resources);
    00358
    00359         return IL_FALSE;
    00360 }
    00361
    00362
    00363 ILboolean ReadRGB(PSDHEAD *Head)
    00364 {
    00365         ILuint          ColorMode, ResourceSize, MiscInfo;
    00366         ILushort        Compressed;
    00367         ILenum          Format, Type;
    00368         ILubyte         *Resources = NULL;
    00369
    00370         ColorMode = GetBigUInt();  // Skip over the 'color mode data section'
    00371         iseek(ColorMode, IL_SEEK_CUR);
    00372
    00373         ResourceSize = GetBigUInt();  // Read the 'image resources section'
    00374         Resources = (ILubyte*)ialloc(ResourceSize);
    00375         if (Resources == NULL)
    00376                 return IL_FALSE;
    00377         if (iread(Resources, 1, ResourceSize) != ResourceSize)
    00378                 goto cleanup_error;
    00379
    00380         MiscInfo = GetBigUInt();
    00381         iseek(MiscInfo, IL_SEEK_CUR);
    00382
    00383         Compressed = GetBigUShort();
    00384
    00385         ChannelNum = Head->Channels;
    00386         if (Head->Channels == 3)
    00387         {
    00388                 Format = IL_RGB;
    00389         }
    00390         else if (Head->Channels == 4)
    00391         {
    00392                 Format = IL_RGBA;
    00393         }
    00394         else if (Head->Channels >= 5)
    00395         {
    00396                 // Additional channels are accumulated as a single alpha channel, since
    00397                 // if an image does not have a layer set as the "background", but also
    00398                 // has a real alpha channel, there will be 5 channels (or more).
    00399                 Format = IL_RGBA;
    00400         }
    00401         else
    00402         {
    00403                 ilSetError(IL_FORMAT_NOT_SUPPORTED);
    00404                 return IL_FALSE;
    00405         }
    00406
    00407         switch (Head->Depth)
    00408         {
    00409                 case 8:
    00410                         Type = IL_UNSIGNED_BYTE;
    00411                         break;
    00412                 case 16:
    00413                         Type = IL_UNSIGNED_SHORT;
    00414                         break;
    00415                 default:
    00416                         ilSetError(IL_FORMAT_NOT_SUPPORTED);
    00417                         return IL_FALSE;
    00418         }
    00419         if (!ilTexImage(Head->Width, Head->Height, 1, (Format==IL_RGB) ? 3 : 4, Format, Type, NULL))
    00420                 goto cleanup_error;
    00421         if (!PsdGetData(Head, iCurImage->Data, (ILboolean)Compressed))
    00422                 goto cleanup_error;
    00423         if (!ParseResources(ResourceSize, Resources))
    00424                 goto cleanup_error;
    00425         ifree(Resources);
    00426
    00427         return IL_TRUE;
    00428
    00429 cleanup_error:
    00430         ifree(Resources);
    00431         return IL_FALSE;
    00432 }
    00433
    00434
    00435 ILboolean ReadCMYK(PSDHEAD *Head)
    00436 {
    00437         ILuint          ColorMode, ResourceSize, MiscInfo, Size, i, j;
    00438         ILushort        Compressed;
    00439         ILenum          Format, Type;
    00440         ILubyte         *Resources = NULL, *KChannel = NULL;
    00441
    00442         ColorMode = GetBigUInt();  // Skip over the 'color mode data section'
    00443         iseek(ColorMode, IL_SEEK_CUR);
    00444
    00445         ResourceSize = GetBigUInt();  // Read the 'image resources section'
    00446         Resources = (ILubyte*)ialloc(ResourceSize);
    00447         if (Resources == NULL) {
    00448                 return IL_FALSE;
    00449         }
    00450         if (iread(Resources, 1, ResourceSize) != ResourceSize)
    00451                 goto cleanup_error;
    00452
    00453         MiscInfo = GetBigUInt();
    00454         iseek(MiscInfo, IL_SEEK_CUR);
    00455
    00456         Compressed = GetBigUShort();
    00457
    00458         switch (Head->Channels)
    00459         {
    00460                 case 4:
    00461                         Format = IL_RGB;
    00462                         ChannelNum = 4;
    00463                         Head->Channels = 3;
    00464                         break;
    00465                 case 5:
    00466                         Format = IL_RGBA;
    00467                         ChannelNum = 5;
    00468                         Head->Channels = 4;
    00469                         break;
    00470                 default:
    00471                         ilSetError(IL_FORMAT_NOT_SUPPORTED);
    00472                         return IL_FALSE;
    00473         }
    00474         switch (Head->Depth)
    00475         {
    00476                 case 8:
    00477                         Type = IL_UNSIGNED_BYTE;
    00478                         break;
    00479                 case 16:
    00480                         Type = IL_UNSIGNED_SHORT;
    00481                         break;
    00482                 default:
    00483                         ilSetError(IL_FORMAT_NOT_SUPPORTED);
    00484                         return IL_FALSE;
    00485         }
    00486         if (!ilTexImage(Head->Width, Head->Height, 1, (ILubyte)Head->Channels, Format, Type, NULL))
    00487                 goto cleanup_error;
    00488         if (!PsdGetData(Head, iCurImage->Data, (ILboolean)Compressed))
    00489                 goto cleanup_error;
    00490
    00491         Size = iCurImage->Bpc * iCurImage->Width * iCurImage->Height;
    00492         KChannel = (ILubyte*)ialloc(Size);
    00493         if (KChannel == NULL)
    00494                 goto cleanup_error;
    00495         if (!GetSingleChannel(Head, KChannel, (ILboolean)Compressed))
    00496                 goto cleanup_error;
    00497
    00498         if (Format == IL_RGB) {
    00499                 for (i = 0, j = 0; i < iCurImage->SizeOfData; i += 3, j++) {
    00500                         iCurImage->Data[i  ] = (iCurImage->Data[i  ] * KChannel[j]) >> 8;
    00501                         iCurImage->Data[i+1] = (iCurImage->Data[i+1] * KChannel[j]) >> 8;
    00502                         iCurImage->Data[i+2] = (iCurImage->Data[i+2] * KChannel[j]) >> 8;
    00503                 }
    00504         }
    00505         else {  // IL_RGBA
    00506                 // The KChannel array really holds the alpha channel on this one.
    00507                 for (i = 0, j = 0; i < iCurImage->SizeOfData; i += 4, j++) {
    00508                         iCurImage->Data[i  ] = (iCurImage->Data[i  ] * iCurImage->Data[i+3]) >> 8;
    00509                         iCurImage->Data[i+1] = (iCurImage->Data[i+1] * iCurImage->Data[i+3]) >> 8;
    00510                         iCurImage->Data[i+2] = (iCurImage->Data[i+2] * iCurImage->Data[i+3]) >> 8;
    00511                         iCurImage->Data[i+3] = KChannel[j];  // Swap 'K' with alpha channel.
    00512                 }
    00513         }
    00514
    00515         if (!ParseResources(ResourceSize, Resources))
    00516                 goto cleanup_error;
    00517
    00518         ifree(Resources);
    00519         ifree(KChannel);
    00520
    00521         return IL_TRUE;
    00522
    00523 cleanup_error:
    00524         ifree(Resources);
    00525         ifree(KChannel);
    00526         return IL_FALSE;
    00527 }
    00528
    00529
    00530 ILuint *GetCompChanLen(PSDHEAD *Head)
    00531 {
    00532         ILushort        *RleTable;
    00533         ILuint          *ChanLen, c, i, j;
    00534
    00535         RleTable = (ILushort*)ialloc(Head->Height * ChannelNum * sizeof(ILushort));
    00536         ChanLen = (ILuint*)ialloc(ChannelNum * sizeof(ILuint));
    00537         if (RleTable == NULL || ChanLen == NULL) {
    00538                 return NULL;
    00539         }
    00540
    00541         if (iread(RleTable, sizeof(ILushort), Head->Height * ChannelNum) != Head->Height * ChannelNum) {
    00542                 ifree(RleTable);
    00543                 ifree(ChanLen);
    00544                 return NULL;
    00545         }
    00546 #ifdef __LITTLE_ENDIAN__
    00547         for (i = 0; i < Head->Height * ChannelNum; i++) {
    00548                 iSwapUShort(&RleTable[i]);
    00549         }
    00550 #endif
    00551
    00552         imemclear(ChanLen, ChannelNum * sizeof(ILuint));
    00553         for (c = 0; c < ChannelNum; c++) {
    00554                 j = c * Head->Height;
    00555                 for (i = 0; i < Head->Height; i++) {
    00556                         ChanLen[c] += RleTable[i + j];
    00557                 }
    00558         }
    00559
    00560         ifree(RleTable);
    00561
    00562         return ChanLen;
    00563 }
    00564
    00565
    00566
    00567 static const ILuint READ_COMPRESSED_SUCCESS                                     = 0;
    00568 static const ILuint READ_COMPRESSED_ERROR_FILE_CORRUPT          = 1;
    00569 static const ILuint READ_COMPRESSED_ERROR_FILE_READ_ERROR       = 2;
    00570
    00571 static ILuint ReadCompressedChannel(const ILuint ChanLen, ILuint Size, ILubyte* Channel)
    00572 {
    00573         ILuint          i;
    00574         ILint           Run;
    00575         ILboolean       PreCache = IL_FALSE;
    00576         ILbyte          HeadByte;
    00577
    00578         if (iGetHint(IL_MEM_SPEED_HINT) == IL_FASTEST)
    00579                 PreCache = IL_TRUE;
    00580
    00581         if (PreCache)
    00582                 iPreCache(ChanLen);
    00583         for (i = 0; i < Size; ) {
    00584                 HeadByte = igetc();
    00585
    00586                 if (HeadByte >= 0) {  //  && HeadByte <= 127
    00587                         if (i + HeadByte > Size)
    00588                         {
    00589                                 if (PreCache)
    00590                                         iUnCache();
    00591                                 return READ_COMPRESSED_ERROR_FILE_CORRUPT;
    00592                         }
    00593                         if (iread(Channel + i, HeadByte + 1, 1) != 1)
    00594                         {
    00595                                 if (PreCache)
    00596                                         iUnCache();
    00597                                 return READ_COMPRESSED_ERROR_FILE_READ_ERROR;
    00598                         }
    00599
    00600                         i += HeadByte + 1;
    00601                 }
    00602                 if (HeadByte >= -127 && HeadByte <= -1) {
    00603                         Run = igetc();
    00604                         if (Run == IL_EOF)
    00605                         {
    00606                                 if (PreCache)
    00607                                         iUnCache();
    00608                                 return READ_COMPRESSED_ERROR_FILE_READ_ERROR;
    00609                         }
    00610                         if (i + (-HeadByte + 1) > Size)
    00611                         {
    00612                                 if (PreCache)
    00613                                         iUnCache();
    00614                                 return READ_COMPRESSED_ERROR_FILE_CORRUPT;
    00615                         }
    00616
    00617                         memset(Channel + i, Run, -HeadByte + 1);
    00618                         i += -HeadByte + 1;
    00619                 }
    00620                 if (HeadByte == -128)
    00621                 { }  // Noop
    00622         }
    00623         if (PreCache)
    00624                 iUnCache();
    00625
    00626         return READ_COMPRESSED_SUCCESS;
    00627 }
    00628
    00629
    00630 ILboolean PsdGetData(PSDHEAD *Head, void *Buffer, ILboolean Compressed)
    00631 {
    00632         ILuint          c, x, y, i, Size, ReadResult, NumChan;
    00633         ILubyte         *Channel = NULL;
    00634         ILushort        *ShortPtr;
    00635         ILuint          *ChanLen = NULL;
    00636
    00637         // Added 01-07-2009: This is needed to correctly load greyscale and
    00638         //  paletted images.
    00639         switch (Head->Mode)
    00640         {
    00641                 case 1:
    00642                 case 2:
    00643                         NumChan = 1;
    00644                         break;
    00645                 default:
    00646                         NumChan = 3;
    00647         }
    00648
    00649         Channel = (ILubyte*)ialloc(Head->Width * Head->Height * iCurImage->Bpc);
    00650         if (Channel == NULL) {
    00651                 return IL_FALSE;
    00652         }
    00653         ShortPtr = (ILushort*)Channel;
    00654
    00655         // @TODO: Add support for this in, though I have yet to run across a .psd
    00656         //      file that uses this.
    00657         if (Compressed && iCurImage->Type == IL_UNSIGNED_SHORT) {
    00658                 ilSetError(IL_FORMAT_NOT_SUPPORTED);
    00659                 return IL_FALSE;
    00660         }
    00661
    00662         if (!Compressed) {
    00663                 if (iCurImage->Bpc == 1) {
    00664                         for (c = 0; c < NumChan; c++) {
    00665                                 i = 0;
    00666                                 if (iread(Channel, Head->Width * Head->Height, 1) != 1) {
    00667                                         ifree(Channel);
    00668                                         return IL_FALSE;
    00669                                 }
    00670                                 for (y = 0; y < Head->Height * iCurImage->Bps; y += iCurImage->Bps) {
    00671                                         for (x = 0; x < iCurImage->Bps; x += iCurImage->Bpp, i++) {
    00672                                                 iCurImage->Data[y + x + c] = Channel[i];
    00673                                         }
    00674                                 }
    00675                         }
    00676                         // Accumulate any remaining channels into a single alpha channel
    00677                         //@TODO: This needs to be changed for greyscale images.
    00678                         for (; c < Head->Channels; c++) {
    00679                                 i = 0;
    00680                                 if (iread(Channel, Head->Width * Head->Height, 1) != 1) {
    00681                                         ifree(Channel);
    00682                                         return IL_FALSE;
    00683                                 }
    00684                                 for (y = 0; y < Head->Height * iCurImage->Bps; y += iCurImage->Bps) {
    00685                                         for (x = 0; x < iCurImage->Bps; x += iCurImage->Bpp, i++) {
    00686                                                 float curVal = ubyte_to_float(iCurImage->Data[y + x + 3]);
    00687                                                 float newVal = ubyte_to_float(Channel[i]);
    00688                                                 iCurImage->Data[y + x + 3] = float_to_ubyte(curVal * newVal);
    00689                                         }
    00690                                 }
    00691                         }
    00692                 }
    00693                 else {  // iCurImage->Bpc == 2
    00694                         for (c = 0; c < NumChan; c++) {
    00695                                 i = 0;
    00696                                 if (iread(Channel, Head->Width * Head->Height * 2, 1) != 1) {
    00697                                         ifree(Channel);
    00698                                         return IL_FALSE;
    00699                                 }
    00700                                 iCurImage->Bps /= 2;
    00701                                 for (y = 0; y < Head->Height * iCurImage->Bps; y += iCurImage->Bps) {
    00702                                         for (x = 0; x < iCurImage->Bps; x += iCurImage->Bpp, i++) {
    00703                                          #ifndef WORDS_BIGENDIAN
    00704                                                 iSwapUShort(ShortPtr+i);
    00705                                          #endif
    00706                                                 ((ILushort*)iCurImage->Data)[y + x + c] = ShortPtr[i];
    00707                                         }
    00708                                 }
    00709                                 iCurImage->Bps *= 2;
    00710                         }
    00711                         // Accumulate any remaining channels into a single alpha channel
    00712                         //@TODO: This needs to be changed for greyscale images.
    00713                         for (; c < Head->Channels; c++) {
    00714                                 i = 0;
    00715                                 if (iread(Channel, Head->Width * Head->Height * 2, 1) != 1) {
    00716                                         ifree(Channel);
    00717                                         return IL_FALSE;
    00718                                 }
    00719                                 iCurImage->Bps /= 2;
    00720                                 for (y = 0; y < Head->Height * iCurImage->Bps; y += iCurImage->Bps) {
    00721                                         for (x = 0; x < iCurImage->Bps; x += iCurImage->Bpp, i++) {
    00722                                                 float curVal = ushort_to_float(((ILushort*)iCurImage->Data)[y + x + 3]);
    00723                                                 float newVal = ushort_to_float(ShortPtr[i]);
    00724                                                 ((ILushort*)iCurImage->Data)[y + x + 3] = float_to_ushort(curVal * newVal);
    00725                                         }
    00726                                 }
    00727                                 iCurImage->Bps *= 2;
    00728                         }
    00729                 }
    00730         }
    00731         else {
    00732                 ChanLen = GetCompChanLen(Head);
    00733
    00734                 Size = Head->Width * Head->Height;
    00735                 for (c = 0; c < NumChan; c++) {
    00736                         ReadResult = ReadCompressedChannel(ChanLen[c], Size, Channel);
    00737                         if (ReadResult == READ_COMPRESSED_ERROR_FILE_CORRUPT)
    00738                                 goto file_corrupt;
    00739                         else if (ReadResult == READ_COMPRESSED_ERROR_FILE_READ_ERROR)
    00740                                 goto file_read_error;
    00741
    00742                         i = 0;
    00743                         for (y = 0; y < Head->Height * iCurImage->Bps; y += iCurImage->Bps) {
    00744                                 for (x = 0; x < iCurImage->Bps; x += iCurImage->Bpp, i++) {
    00745                                         iCurImage->Data[y + x + c] = Channel[i];
    00746                                 }
    00747                         }
    00748                 }
    00749
    00750                 // Initialize the alpha channel to solid
    00751                 //@TODO: This needs to be changed for greyscale images.
    00752                 if (Head->Channels >= 4) {
    00753                         for (y = 0; y < Head->Height * iCurImage->Bps; y += iCurImage->Bps) {
    00754                                 for (x = 0; x < iCurImage->Bps; x += iCurImage->Bpp) {
    00755                                         iCurImage->Data[y + x + 3] = 255;
    00756                                 }
    00757                         }
    00758
    00759                         for (; c < Head->Channels; c++) {
    00760                                 ReadResult = ReadCompressedChannel(ChanLen[c], Size, Channel);
    00761                                 if (ReadResult == READ_COMPRESSED_ERROR_FILE_CORRUPT)
    00762                                         goto file_corrupt;
    00763                                 else if (ReadResult == READ_COMPRESSED_ERROR_FILE_READ_ERROR)
    00764                                         goto file_read_error;
    00765
    00766                                 i = 0;
    00767                                 for (y = 0; y < Head->Height * iCurImage->Bps; y += iCurImage->Bps) {
    00768                                         for (x = 0; x < iCurImage->Bps; x += iCurImage->Bpp, i++) {
    00769                                                 float curVal = ubyte_to_float(iCurImage->Data[y + x + 3]);
    00770                                                 float newVal = ubyte_to_float(Channel[i]);
    00771                                                 iCurImage->Data[y + x + 3] = float_to_ubyte(curVal * newVal);
    00772                                         }
    00773                                 }
    00774                         }
    00775                 }
    00776
    00777                 ifree(ChanLen);
    00778         }
    00779
    00780         ifree(Channel);
    00781
    00782         return IL_TRUE;
    00783
    00784 file_corrupt:
    00785         ifree(ChanLen);
    00786         ifree(Channel);
    00787         ilSetError(IL_ILLEGAL_FILE_VALUE);
    00788         return IL_FALSE;
    00789
    00790 file_read_error:
    00791         ifree(ChanLen);
    00792         ifree(Channel);
    00793         return IL_FALSE;
    00794 }
    00795
    00796
    00797 ILboolean ParseResources(ILuint ResourceSize, ILubyte *Resources)
    00798 {
    00799         ILushort        ID;
    00800         ILubyte         NameLen;
    00801         ILuint          Size;
    00802
    00803         if (Resources == NULL) {
    00804                 ilSetError(IL_INTERNAL_ERROR);
    00805                 return IL_FALSE;
    00806         }
    00807
    00808         while (ResourceSize > 13) {  // Absolutely has to be larger than this.
    00809                 if (strncmp("8BIM", (const char*)Resources, 4)) {
    00810                         //return IL_FALSE;
    00811                         return IL_TRUE;  // 05-30-2002: May not necessarily mean corrupt data...
    00812                 }
    00813                 Resources += 4;
    00814
    00815                 ID = *((ILushort*)Resources);
    00816                 BigUShort(&ID);
    00817                 Resources += 2;
    00818
    00819                 NameLen = *Resources++;
    00820                 // NameLen + the byte it occupies must be padded to an even number, so NameLen must be odd.
    00821                 NameLen = NameLen + (NameLen & 1 ? 0 : 1);
    00822                 Resources += NameLen;
    00823
    00824                 // Get the resource data size.
    00825                 Size = *((ILuint*)Resources);
    00826                 BigUInt(&Size);
    00827                 Resources += 4;
    00828
    00829                 ResourceSize -= (4 + 2 + 1 + NameLen + 4);
    00830
    00831                 switch (ID)
    00832                 {
    00833                         case 0x040F:  // ICC Profile
    00834                                 if (Size > ResourceSize) {  // Check to make sure we are not going past the end of Resources.
    00835                                         ilSetError(IL_ILLEGAL_FILE_VALUE);
    00836                                         return IL_FALSE;
    00837                                 }
    00838                                 iCurImage->Profile = (ILubyte*)ialloc(Size);
    00839                                 if (iCurImage->Profile == NULL) {
    00840                                         return IL_FALSE;
    00841                                 }
    00842                                 memcpy(iCurImage->Profile, Resources, Size);
    00843                                 iCurImage->ProfileSize = Size;
    00844                                 break;
    00845
    00846                         default:
    00847                                 break;
    00848                 }
    00849
    00850                 if (Size & 1)  // Must be an even number.
    00851                         Size++;
    00852                 ResourceSize -= Size;
    00853                 Resources += Size;
    00854         }
    00855
    00856         return IL_TRUE;
    00857 }
    00858
    00859
    00860 ILboolean GetSingleChannel(PSDHEAD *Head, ILubyte *Buffer, ILboolean Compressed)
    00861 {
    00862         ILuint          i;
    00863         ILushort        *ShortPtr;
    00864         ILbyte          HeadByte;
    00865         ILint           Run;
    00866
    00867         ShortPtr = (ILushort*)Buffer;
    00868
    00869         if (!Compressed) {
    00870                 if (iCurImage->Bpc == 1) {
    00871                         if (iread(Buffer, Head->Width * Head->Height, 1) != 1)
    00872                                 return IL_FALSE;
    00873                 }
    00874                 else {  // iCurImage->Bpc == 2
    00875                         if (iread(Buffer, Head->Width * Head->Height * 2, 1) != 1)
    00876                                 return IL_FALSE;
    00877                 }
    00878         }
    00879         else {
    00880                 for (i = 0; i < Head->Width * Head->Height; ) {
    00881                         HeadByte = igetc();
    00882
    00883                         if (HeadByte >= 0) {  //  && HeadByte <= 127
    00884                                 if (iread(Buffer + i, HeadByte + 1, 1) != 1)
    00885                                         return IL_FALSE;
    00886                                 i += HeadByte + 1;
    00887                         }
    00888                         if (HeadByte >= -127 && HeadByte <= -1) {
    00889                                 Run = igetc();
    00890                                 if (Run == IL_EOF)
    00891                                         return IL_FALSE;
    00892                                 memset(Buffer + i, Run, -HeadByte + 1);
    00893                                 i += -HeadByte + 1;
    00894                         }
    00895                         if (HeadByte == -128)
    00896                         { }  // Noop
    00897                 }
    00898         }
    00899
    00900         return IL_TRUE;
    00901 }
    00902
    00903
    00904
    00906 ILboolean ilSavePsd(const ILstring FileName)
    00907 {
    00908         ILHANDLE        PsdFile;
    00909         ILuint          PsdSize;
    00910
    00911         if (ilGetBoolean(IL_FILE_MODE) == IL_FALSE) {
    00912                 if (iFileExists(FileName)) {
    00913                         ilSetError(IL_FILE_ALREADY_EXISTS);
    00914                         return IL_FALSE;
    00915                 }
    00916         }
    00917
    00918         PsdFile = iopenw(FileName);
    00919         if (PsdFile == NULL) {
    00920                 ilSetError(IL_COULD_NOT_OPEN_FILE);
    00921                 return IL_FALSE;
    00922         }
    00923
    00924         PsdSize = ilSavePsdF(PsdFile);
    00925         iclosew(PsdFile);
    00926
    00927         if (PsdSize == 0)
    00928                 return IL_FALSE;
    00929         return IL_TRUE;
    00930 }
    00931
    00932
    00934 ILuint ilSavePsdF(ILHANDLE File)
    00935 {
    00936         ILuint Pos;
    00937         iSetOutputFile(File);
    00938         Pos = itellw();
    00939         if (iSavePsdInternal() == IL_FALSE)
    00940                 return 0;  // Error occurred
    00941         return itellw() - Pos;  // Return the number of bytes written.
    00942 }
    00943
    00944
    00946 ILuint ilSavePsdL(void *Lump, ILuint Size)
    00947 {
    00948         ILuint Pos = itellw();
    00949         iSetOutputLump(Lump, Size);
    00950         if (iSavePsdInternal() == IL_FALSE)
    00951                 return 0;  // Error occurred
    00952         return itellw() - Pos;  // Return the number of bytes written.
    00953 }
    00954
    00955
    00956 // Internal function used to save the Psd.
    00957 ILboolean iSavePsdInternal()
    00958 {
    00959         ILubyte         *Signature = (ILubyte*)"8BPS";
    00960         ILimage         *TempImage;
    00961         ILpal           *TempPal;
    00962         ILuint          c, i;
    00963         ILubyte         *TempData;
    00964         ILushort        *ShortPtr;
    00965         ILenum          Format, Type;
    00966
    00967         if (iCurImage == NULL) {
    00968                 ilSetError(IL_ILLEGAL_OPERATION);
    00969                 return IL_FALSE;
    00970         }
    00971
    00972         Format = iCurImage->Format;
    00973         Type = iCurImage->Type;
    00974
    00975         // All of these comprise the actual signature.
    00976         iwrite(Signature, 1, 4);
    00977         SaveBigShort(1);
    00978         SaveBigInt(0);
    00979         SaveBigShort(0);
    00980
    00981         SaveBigShort(iCurImage->Bpp);
    00982         SaveBigInt(iCurImage->Height);
    00983         SaveBigInt(iCurImage->Width);
    00984         if (iCurImage->Bpc > 2)
    00985                 Type = IL_UNSIGNED_SHORT;
    00986
    00987         if (iCurImage->Format == IL_BGR)
    00988                 Format = IL_RGB;
    00989         else if (iCurImage->Format == IL_BGRA)
    00990                 Format = IL_RGBA;
    00991
    00992         if (Format != iCurImage->Format || Type != iCurImage->Type) {
    00993                 TempImage = iConvertImage(iCurImage, Format, Type);
    00994                 if (TempImage == NULL)
    00995                         return IL_FALSE;
    00996         }
    00997         else {
    00998                 TempImage = iCurImage;
    00999         }
    01000         SaveBigShort((ILushort)(TempImage->Bpc * 8));
    01001
    01002         // @TODO:  Put the other formats here.
    01003         switch (TempImage->Format)
    01004         {
    01005                 case IL_COLOUR_INDEX:
    01006                         SaveBigShort(2);
    01007                         break;
    01008                 case IL_LUMINANCE:
    01009                         SaveBigShort(1);
    01010                         break;
    01011                 case IL_RGB:
    01012                 case IL_RGBA:
    01013                         SaveBigShort(3);
    01014                         break;
    01015                 default:
    01016                         ilSetError(IL_INTERNAL_ERROR);
    01017                         return IL_FALSE;
    01018         }
    01019
    01020         if (TempImage->Format == IL_COLOUR_INDEX) {
    01021                 // @TODO: We're currently making a potentially fatal assumption that
    01022                 //      iConvertImage was not called if the format is IL_COLOUR_INDEX.
    01023                 TempPal = iConvertPal(&TempImage->Pal, IL_PAL_RGB24);
    01024                 if (TempPal == NULL)
    01025                         return IL_FALSE;
    01026                 SaveBigInt(768);
    01027
    01028                 // Have to save the palette in a planar format.
    01029                 for (c = 0; c < 3; c++) {
    01030                         for (i = c; i < TempPal->PalSize; i += 3) {
    01031                                 iputc(TempPal->Palette[i]);
    01032                         }
    01033                 }
    01034
    01035                 ifree(TempPal->Palette);
    01036         }
    01037         else {
    01038                 SaveBigInt(0);  // No colour mode data.
    01039         }
    01040
    01041         SaveBigInt(0);  // No image resources.
    01042         SaveBigInt(0);  // No layer information.
    01043         SaveBigShort(0);  // Psd data, no compression.
    01044
    01045         // @TODO:  Add RLE compression.
    01046
    01047         if (TempImage->Origin == IL_ORIGIN_LOWER_LEFT) {
    01048                 TempData = iGetFlipped(TempImage);
    01049                 if (TempData == NULL) {
    01050                         ilCloseImage(TempImage);
    01051                         return IL_FALSE;
    01052                 }
    01053         }
    01054         else {
    01055                 TempData = TempImage->Data;
    01056         }
    01057
    01058         if (TempImage->Bpc == 1) {
    01059                 for (c = 0; c < TempImage->Bpp; c++) {
    01060                         for (i = c; i < TempImage->SizeOfPlane; i += TempImage->Bpp) {
    01061                                 iputc(TempData[i]);
    01062                         }
    01063                 }
    01064         }
    01065         else {  // TempImage->Bpc == 2
    01066                 ShortPtr = (ILushort*)TempData;
    01067                 TempImage->SizeOfPlane /= 2;
    01068                 for (c = 0; c < TempImage->Bpp; c++) {
    01069                         for (i = c; i < TempImage->SizeOfPlane; i += TempImage->Bpp) {
    01070                                 SaveBigUShort(ShortPtr[i]);
    01071                         }
    01072                 }
    01073                 TempImage->SizeOfPlane *= 2;
    01074         }
    01075
    01076         if (TempData != TempImage->Data)
    01077                 ifree(TempData);
    01078
    01079         if (TempImage != iCurImage)
    01080                 ilCloseImage(TempImage);
    01081
    01082
    01083         return IL_TRUE;
    01084 }
    01085
    01086
    01087 #endif//IL_NO_PSD