Move git root from Client/ to src/ to track all source code: - Client: Game client source (moved to Client/Client/) - Server: Game server source - GameTools: Development tools - CryptoSource: Encryption utilities - database: Database scripts - Script: Game scripts - rylCoder_16.02.2008_src: Legacy coder tools - GMFont, Game: Additional resources 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
936 lines
20 KiB
C
936 lines
20 KiB
C
|
|
// Information about the .psd format was taken from Adobe's PhotoShop SDK at
|
|
// http://partners.adobe.com/asn/developer/gapsdk/PhotoshopSDK.html
|
|
// Information about the Packbits compression scheme was found at
|
|
// http://partners.adobe.com/asn/developer/PDFS/TN/TIFF6.pdf
|
|
|
|
#include "il_internal.h"
|
|
#ifndef IL_NO_PSD
|
|
#include "il_psd.h"
|
|
|
|
|
|
//! Checks if the file specified in FileName is a valid Psd file.
|
|
ILboolean ilIsValidPsd(ILconst_string FileName)
|
|
{
|
|
ILHANDLE PsdFile;
|
|
ILboolean bPsd = IL_FALSE;
|
|
|
|
if (!iCheckExtension(FileName, IL_TEXT("psd")) &&
|
|
!iCheckExtension(FileName, IL_TEXT("pdd"))) {
|
|
ilSetError(IL_INVALID_EXTENSION);
|
|
return bPsd;
|
|
}
|
|
|
|
PsdFile = iopenr(FileName);
|
|
if (PsdFile == NULL) {
|
|
ilSetError(IL_COULD_NOT_OPEN_FILE);
|
|
return bPsd;
|
|
}
|
|
|
|
bPsd = ilIsValidPsdF(PsdFile);
|
|
icloser(PsdFile);
|
|
|
|
return bPsd;
|
|
}
|
|
|
|
|
|
//! Checks if the ILHANDLE contains a valid Psd file at the current position.
|
|
ILboolean ilIsValidPsdF(ILHANDLE File)
|
|
{
|
|
ILuint FirstPos;
|
|
ILboolean bRet;
|
|
|
|
iSetInputFile(File);
|
|
FirstPos = itell();
|
|
bRet = iIsValidPsd();
|
|
iseek(FirstPos, IL_SEEK_SET);
|
|
|
|
return bRet;
|
|
}
|
|
|
|
|
|
//! Checks if Lump is a valid Psd lump.
|
|
ILboolean ilIsValidPsdL(const ILvoid *Lump, ILuint Size)
|
|
{
|
|
iSetInputLump(Lump, Size);
|
|
return iIsValidPsd();
|
|
}
|
|
|
|
|
|
// Internal function used to get the Psd header from the current file.
|
|
ILboolean iGetPsdHead(PSDHEAD *Header)
|
|
{
|
|
iread(Header->Signature, 1, 4);
|
|
Header->Version = GetBigUShort();
|
|
iread(Header->Reserved, 1, 6);
|
|
Header->Channels = GetBigUShort();
|
|
Header->Height = GetBigUInt();
|
|
Header->Width = GetBigUInt();
|
|
Header->Depth = GetBigUShort();
|
|
Header->Mode = GetBigUShort();
|
|
|
|
return IL_TRUE;
|
|
}
|
|
|
|
|
|
// Internal function to get the header and check it.
|
|
ILboolean iIsValidPsd()
|
|
{
|
|
PSDHEAD Head;
|
|
|
|
iGetPsdHead(&Head);
|
|
iseek(-(ILint)sizeof(PSDHEAD), IL_SEEK_CUR);
|
|
|
|
return iCheckPsd(&Head);
|
|
}
|
|
|
|
|
|
// Internal function used to check if the HEADER is a valid Psd header.
|
|
ILboolean iCheckPsd(PSDHEAD *Header)
|
|
{
|
|
ILuint i;
|
|
|
|
if (strncmp((char*)Header->Signature, "8BPS", 4))
|
|
return IL_FALSE;
|
|
if (Header->Version != 1)
|
|
return IL_FALSE;
|
|
for (i = 0; i < 6; i++) {
|
|
if (Header->Reserved[i] != 0)
|
|
return IL_FALSE;
|
|
}
|
|
if (Header->Channels < 1 || Header->Channels > 24)
|
|
return IL_FALSE;
|
|
if (Header->Height < 1 || Header->Width < 1)
|
|
return IL_FALSE;
|
|
if (Header->Depth != 1 && Header->Depth != 8 && Header->Depth != 16)
|
|
return IL_FALSE;
|
|
|
|
return IL_TRUE;
|
|
}
|
|
|
|
|
|
//! Reads a Psd file
|
|
ILboolean ilLoadPsd(ILconst_string FileName)
|
|
{
|
|
ILHANDLE PsdFile;
|
|
ILboolean bPsd = IL_FALSE;
|
|
|
|
PsdFile = iopenr(FileName);
|
|
if (PsdFile == NULL) {
|
|
ilSetError(IL_COULD_NOT_OPEN_FILE);
|
|
return bPsd;
|
|
}
|
|
|
|
bPsd = ilLoadPsdF(PsdFile);
|
|
icloser(PsdFile);
|
|
|
|
return bPsd;
|
|
}
|
|
|
|
|
|
//! Reads an already-opened Psd file
|
|
ILboolean ilLoadPsdF(ILHANDLE File)
|
|
{
|
|
ILuint FirstPos;
|
|
ILboolean bRet;
|
|
|
|
iSetInputFile(File);
|
|
FirstPos = itell();
|
|
bRet = iLoadPsdInternal();
|
|
iseek(FirstPos, IL_SEEK_SET);
|
|
|
|
return bRet;
|
|
}
|
|
|
|
|
|
//! Reads from a memory "lump" that contains a Psd
|
|
ILboolean ilLoadPsdL(const ILvoid *Lump, ILuint Size)
|
|
{
|
|
iSetInputLump(Lump, Size);
|
|
return iLoadPsdInternal();
|
|
}
|
|
|
|
|
|
// Internal function used to load the Psd.
|
|
ILboolean iLoadPsdInternal()
|
|
{
|
|
PSDHEAD Header;
|
|
|
|
if (iCurImage == NULL) {
|
|
ilSetError(IL_ILLEGAL_OPERATION);
|
|
return IL_FALSE;
|
|
}
|
|
|
|
iGetPsdHead(&Header);
|
|
if (!iCheckPsd(&Header)) {
|
|
ilSetError(IL_INVALID_FILE_HEADER);
|
|
return IL_FALSE;
|
|
}
|
|
|
|
if (!ReadPsd(&Header))
|
|
return IL_FALSE;
|
|
iCurImage->Origin = IL_ORIGIN_UPPER_LEFT;
|
|
|
|
ilFixImage();
|
|
|
|
return IL_TRUE;
|
|
}
|
|
|
|
|
|
ILboolean ReadPsd(PSDHEAD *Head)
|
|
{
|
|
switch (Head->Mode)
|
|
{
|
|
case 1: // Greyscale
|
|
return ReadGrey(Head);
|
|
case 2: // Indexed
|
|
return ReadIndexed(Head);
|
|
case 3: // RGB
|
|
return ReadRGB(Head);
|
|
case 4: // CMYK
|
|
return ReadCMYK(Head);
|
|
}
|
|
|
|
ilSetError(IL_FORMAT_NOT_SUPPORTED);
|
|
return IL_FALSE;
|
|
}
|
|
|
|
|
|
ILboolean ReadGrey(PSDHEAD *Head)
|
|
{
|
|
ILuint ColorMode, ResourceSize, MiscInfo;
|
|
ILushort Compressed;
|
|
ILenum Type;
|
|
ILubyte *Resources = NULL;
|
|
|
|
ColorMode = GetBigUInt(); // Skip over the 'color mode data section'
|
|
iseek(ColorMode, IL_SEEK_CUR);
|
|
|
|
ResourceSize = GetBigUInt(); // Read the 'image resources section'
|
|
Resources = (ILubyte*)ialloc(ResourceSize);
|
|
if (Resources == NULL) {
|
|
return IL_FALSE;
|
|
}
|
|
if (iread(Resources, 1, ResourceSize) != ResourceSize)
|
|
goto cleanup_error;
|
|
|
|
MiscInfo = GetBigUInt();
|
|
iseek(MiscInfo, IL_SEEK_CUR);
|
|
|
|
Compressed = GetBigUShort();
|
|
|
|
ChannelNum = Head->Channels;
|
|
Head->Channels = 1; // Temporary to read only one channel...some greyscale .psd files have 2.
|
|
if (Head->Channels != 1) {
|
|
ilSetError(IL_FORMAT_NOT_SUPPORTED);
|
|
return IL_FALSE;
|
|
}
|
|
switch (Head->Depth)
|
|
{
|
|
case 8:
|
|
Type = IL_UNSIGNED_BYTE;
|
|
break;
|
|
case 16:
|
|
Type = IL_UNSIGNED_SHORT;
|
|
break;
|
|
default:
|
|
ilSetError(IL_FORMAT_NOT_SUPPORTED);
|
|
return IL_FALSE;
|
|
}
|
|
|
|
if (!ilTexImage(Head->Width, Head->Height, 1, 1, IL_LUMINANCE, Type, NULL))
|
|
goto cleanup_error;
|
|
if (!PsdGetData(Head, iCurImage->Data, (ILboolean)Compressed))
|
|
goto cleanup_error;
|
|
if (!ParseResources(ResourceSize, Resources))
|
|
goto cleanup_error;
|
|
ifree(Resources);
|
|
|
|
return IL_TRUE;
|
|
|
|
cleanup_error:
|
|
ifree(Resources);
|
|
return IL_FALSE;
|
|
}
|
|
|
|
|
|
ILboolean ReadIndexed(PSDHEAD *Head)
|
|
{
|
|
ILuint ColorMode, ResourceSize, MiscInfo, i, j, NumEnt;
|
|
ILushort Compressed;
|
|
ILubyte *Palette = NULL, *Resources = NULL;
|
|
|
|
ColorMode = GetBigUInt(); // Skip over the 'color mode data section'
|
|
if (ColorMode % 3 != 0) {
|
|
ilSetError(IL_INVALID_FILE_HEADER);
|
|
return IL_FALSE;
|
|
}
|
|
Palette = (ILubyte*)ialloc(ColorMode);
|
|
if (Palette == NULL)
|
|
return IL_FALSE;
|
|
if (iread(Palette, 1, ColorMode) != ColorMode)
|
|
goto cleanup_error;
|
|
|
|
ResourceSize = GetBigUInt(); // Read the 'image resources section'
|
|
Resources = (ILubyte*)ialloc(ResourceSize);
|
|
if (Resources == NULL) {
|
|
return IL_FALSE;
|
|
}
|
|
if (iread(Resources, 1, ResourceSize) != ResourceSize)
|
|
goto cleanup_error;
|
|
|
|
MiscInfo = GetBigUInt();
|
|
if (ieof())
|
|
goto cleanup_error;
|
|
iseek(MiscInfo, IL_SEEK_CUR);
|
|
|
|
Compressed = GetBigUShort();
|
|
if (ieof())
|
|
goto cleanup_error;
|
|
|
|
if (Head->Channels != 1 || Head->Depth != 8) {
|
|
ilSetError(IL_FORMAT_NOT_SUPPORTED);
|
|
goto cleanup_error;
|
|
}
|
|
ChannelNum = Head->Channels;
|
|
|
|
if (!ilTexImage(Head->Width, Head->Height, 1, 1, IL_COLOUR_INDEX, IL_UNSIGNED_BYTE, NULL))
|
|
goto cleanup_error;
|
|
|
|
iCurImage->Pal.Palette = (ILubyte*)ialloc(ColorMode);
|
|
if (iCurImage->Pal.Palette == NULL) {
|
|
goto cleanup_error;
|
|
}
|
|
iCurImage->Pal.PalSize = ColorMode;
|
|
iCurImage->Pal.PalType = IL_PAL_RGB24;
|
|
|
|
NumEnt = iCurImage->Pal.PalSize / 3;
|
|
for (i = 0, j = 0; i < iCurImage->Pal.PalSize; i += 3, j++) {
|
|
iCurImage->Pal.Palette[i ] = Palette[j];
|
|
iCurImage->Pal.Palette[i+1] = Palette[j+NumEnt];
|
|
iCurImage->Pal.Palette[i+2] = Palette[j+NumEnt*2];
|
|
}
|
|
ifree(Palette);
|
|
Palette = NULL;
|
|
|
|
if (!PsdGetData(Head, iCurImage->Data, (ILboolean)Compressed))
|
|
goto cleanup_error;
|
|
|
|
ParseResources(ResourceSize, Resources);
|
|
ifree(Resources);
|
|
Resources = NULL;
|
|
|
|
return IL_TRUE;
|
|
|
|
cleanup_error:
|
|
ifree(Palette);
|
|
ifree(Resources);
|
|
|
|
return IL_FALSE;
|
|
}
|
|
|
|
|
|
ILboolean ReadRGB(PSDHEAD *Head)
|
|
{
|
|
ILuint ColorMode, ResourceSize, MiscInfo;
|
|
ILushort Compressed;
|
|
ILenum Format, Type;
|
|
ILubyte *Resources = NULL;
|
|
|
|
ColorMode = GetBigUInt(); // Skip over the 'color mode data section'
|
|
iseek(ColorMode, IL_SEEK_CUR);
|
|
|
|
ResourceSize = GetBigUInt(); // Read the 'image resources section'
|
|
Resources = (ILubyte*)ialloc(ResourceSize);
|
|
if (Resources == NULL)
|
|
return IL_FALSE;
|
|
if (iread(Resources, 1, ResourceSize) != ResourceSize)
|
|
goto cleanup_error;
|
|
|
|
MiscInfo = GetBigUInt();
|
|
iseek(MiscInfo, IL_SEEK_CUR);
|
|
|
|
Compressed = GetBigUShort();
|
|
|
|
ChannelNum = Head->Channels;
|
|
switch (Head->Channels)
|
|
{
|
|
case 3:
|
|
Format = IL_RGB;
|
|
break;
|
|
case 4:
|
|
Format = IL_RGBA;
|
|
break;
|
|
default:
|
|
// ilSetError(IL_FORMAT_NOT_SUPPORTED);
|
|
// return IL_FALSE;
|
|
//drop additional channels. This is not the 100% correct
|
|
//way of loading the image, but it is better than loading
|
|
//the image not at all.
|
|
Format = IL_RGBA;
|
|
Head->Channels = 4;
|
|
}
|
|
switch (Head->Depth)
|
|
{
|
|
case 8:
|
|
Type = IL_UNSIGNED_BYTE;
|
|
break;
|
|
case 16:
|
|
Type = IL_UNSIGNED_SHORT;
|
|
break;
|
|
default:
|
|
ilSetError(IL_FORMAT_NOT_SUPPORTED);
|
|
return IL_FALSE;
|
|
}
|
|
if (!ilTexImage(Head->Width, Head->Height, 1, (ILubyte)Head->Channels, Format, Type, NULL))
|
|
goto cleanup_error;
|
|
if (!PsdGetData(Head, iCurImage->Data, (ILboolean)Compressed))
|
|
goto cleanup_error;
|
|
if (!ParseResources(ResourceSize, Resources))
|
|
goto cleanup_error;
|
|
ifree(Resources);
|
|
|
|
return IL_TRUE;
|
|
|
|
cleanup_error:
|
|
ifree(Resources);
|
|
return IL_FALSE;
|
|
}
|
|
|
|
|
|
ILboolean ReadCMYK(PSDHEAD *Head)
|
|
{
|
|
ILuint ColorMode, ResourceSize, MiscInfo, Size, i, j;
|
|
ILushort Compressed;
|
|
ILenum Format, Type;
|
|
ILubyte *Resources = NULL, *KChannel = NULL;
|
|
|
|
ColorMode = GetBigUInt(); // Skip over the 'color mode data section'
|
|
iseek(ColorMode, IL_SEEK_CUR);
|
|
|
|
ResourceSize = GetBigUInt(); // Read the 'image resources section'
|
|
Resources = (ILubyte*)ialloc(ResourceSize);
|
|
if (Resources == NULL) {
|
|
return IL_FALSE;
|
|
}
|
|
if (iread(Resources, 1, ResourceSize) != ResourceSize)
|
|
goto cleanup_error;
|
|
|
|
MiscInfo = GetBigUInt();
|
|
iseek(MiscInfo, IL_SEEK_CUR);
|
|
|
|
Compressed = GetBigUShort();
|
|
|
|
switch (Head->Channels)
|
|
{
|
|
case 4:
|
|
Format = IL_RGB;
|
|
ChannelNum = 4;
|
|
Head->Channels = 3;
|
|
break;
|
|
case 5:
|
|
Format = IL_RGBA;
|
|
ChannelNum = 5;
|
|
Head->Channels = 4;
|
|
break;
|
|
default:
|
|
ilSetError(IL_FORMAT_NOT_SUPPORTED);
|
|
return IL_FALSE;
|
|
}
|
|
switch (Head->Depth)
|
|
{
|
|
case 8:
|
|
Type = IL_UNSIGNED_BYTE;
|
|
break;
|
|
case 16:
|
|
Type = IL_UNSIGNED_SHORT;
|
|
break;
|
|
default:
|
|
ilSetError(IL_FORMAT_NOT_SUPPORTED);
|
|
return IL_FALSE;
|
|
}
|
|
if (!ilTexImage(Head->Width, Head->Height, 1, (ILubyte)Head->Channels, Format, Type, NULL))
|
|
goto cleanup_error;
|
|
if (!PsdGetData(Head, iCurImage->Data, (ILboolean)Compressed))
|
|
goto cleanup_error;
|
|
|
|
Size = iCurImage->Bpc * iCurImage->Width * iCurImage->Height;
|
|
KChannel = (ILubyte*)ialloc(Size);
|
|
if (KChannel == NULL)
|
|
goto cleanup_error;
|
|
if (!GetSingleChannel(Head, KChannel, (ILboolean)Compressed))
|
|
goto cleanup_error;
|
|
|
|
if (Format == IL_RGB) {
|
|
for (i = 0, j = 0; i < iCurImage->SizeOfData; i += 3, j++) {
|
|
iCurImage->Data[i ] = (iCurImage->Data[i ] * KChannel[j]) >> 8;
|
|
iCurImage->Data[i+1] = (iCurImage->Data[i+1] * KChannel[j]) >> 8;
|
|
iCurImage->Data[i+2] = (iCurImage->Data[i+2] * KChannel[j]) >> 8;
|
|
}
|
|
}
|
|
else { // IL_RGBA
|
|
// The KChannel array really holds the alpha channel on this one.
|
|
for (i = 0, j = 0; i < iCurImage->SizeOfData; i += 4, j++) {
|
|
iCurImage->Data[i ] = (iCurImage->Data[i ] * iCurImage->Data[i+3]) >> 8;
|
|
iCurImage->Data[i+1] = (iCurImage->Data[i+1] * iCurImage->Data[i+3]) >> 8;
|
|
iCurImage->Data[i+2] = (iCurImage->Data[i+2] * iCurImage->Data[i+3]) >> 8;
|
|
iCurImage->Data[i+3] = KChannel[j]; // Swap 'K' with alpha channel.
|
|
}
|
|
}
|
|
|
|
if (!ParseResources(ResourceSize, Resources))
|
|
goto cleanup_error;
|
|
|
|
ifree(Resources);
|
|
ifree(KChannel);
|
|
|
|
return IL_TRUE;
|
|
|
|
cleanup_error:
|
|
ifree(Resources);
|
|
ifree(KChannel);
|
|
return IL_FALSE;
|
|
}
|
|
|
|
|
|
ILuint *GetCompChanLen(PSDHEAD *Head)
|
|
{
|
|
ILushort *RleTable;
|
|
ILuint *ChanLen, c, i, j;
|
|
|
|
RleTable = (ILushort*)ialloc(Head->Height * ChannelNum * sizeof(ILushort));
|
|
ChanLen = (ILuint*)ialloc(ChannelNum * sizeof(ILuint));
|
|
if (RleTable == NULL || ChanLen == NULL) {
|
|
return NULL;
|
|
}
|
|
|
|
if (iread(RleTable, sizeof(ILushort), Head->Height * ChannelNum) != Head->Height * ChannelNum) {
|
|
ifree(RleTable);
|
|
ifree(ChanLen);
|
|
return NULL;
|
|
}
|
|
#ifdef __LITTLE_ENDIAN__
|
|
for (i = 0; i < Head->Height * ChannelNum; i++) {
|
|
iSwapUShort(&RleTable[i]);
|
|
}
|
|
#endif
|
|
|
|
imemclear(ChanLen, ChannelNum * sizeof(ILuint));
|
|
for (c = 0; c < ChannelNum; c++) {
|
|
j = c * Head->Height;
|
|
for (i = 0; i < Head->Height; i++) {
|
|
ChanLen[c] += RleTable[i + j];
|
|
}
|
|
}
|
|
|
|
ifree(RleTable);
|
|
|
|
return ChanLen;
|
|
}
|
|
|
|
|
|
ILboolean PsdGetData(PSDHEAD *Head, ILvoid *Buffer, ILboolean Compressed)
|
|
{
|
|
ILuint c, x, y, i, Size;
|
|
ILubyte *Channel = NULL;
|
|
ILushort *ShortPtr;
|
|
ILbyte HeadByte;
|
|
ILint Run;
|
|
ILuint *ChanLen = NULL;
|
|
ILboolean PreCache = IL_FALSE;
|
|
|
|
Channel = (ILubyte*)ialloc(Head->Width * Head->Height * iCurImage->Bpc);
|
|
if (Channel == NULL) {
|
|
return IL_FALSE;
|
|
}
|
|
ShortPtr = (ILushort*)Channel;
|
|
|
|
// @TODO: Add support for this in, though I have yet to run across a .psd
|
|
// file that uses this.
|
|
if (Compressed && iCurImage->Type == IL_UNSIGNED_SHORT) {
|
|
ilSetError(IL_FORMAT_NOT_SUPPORTED);
|
|
return IL_FALSE;
|
|
}
|
|
|
|
if (!Compressed) {
|
|
if (iCurImage->Bpc == 1) {
|
|
for (c = 0; c < Head->Channels; c++) {
|
|
i = 0;
|
|
if (iread(Channel, Head->Width * Head->Height, 1) != 1) {
|
|
ifree(Channel);
|
|
return IL_FALSE;
|
|
}
|
|
for (y = 0; y < Head->Height * iCurImage->Bps; y += iCurImage->Bps) {
|
|
for (x = 0; x < iCurImage->Bps; x += iCurImage->Bpp, i++) {
|
|
iCurImage->Data[y + x + c] = Channel[i];
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else { // iCurImage->Bpc == 2
|
|
for (c = 0; c < Head->Channels; c++) {
|
|
i = 0;
|
|
if (iread(Channel, Head->Width * Head->Height * 2, 1) != 1) {
|
|
ifree(Channel);
|
|
return IL_FALSE;
|
|
}
|
|
iCurImage->Bps /= 2;
|
|
for (y = 0; y < Head->Height * iCurImage->Bps; y += iCurImage->Bps) {
|
|
for (x = 0; x < iCurImage->Bps; x += iCurImage->Bpp, i++) {
|
|
#ifndef WORDS_BIGENDIAN
|
|
iSwapUShort(ShortPtr+i);
|
|
#endif
|
|
((ILushort*)iCurImage->Data)[y + x + c] = ShortPtr[i];
|
|
}
|
|
}
|
|
iCurImage->Bps *= 2;
|
|
}
|
|
}
|
|
}
|
|
else {
|
|
ChanLen = GetCompChanLen(Head);
|
|
if (iGetHint(IL_MEM_SPEED_HINT) == IL_FASTEST)
|
|
PreCache = IL_TRUE;
|
|
|
|
Size = Head->Width * Head->Height;
|
|
for (c = 0; c < Head->Channels; c++) {
|
|
if (PreCache)
|
|
iPreCache(ChanLen[c]);
|
|
for (i = 0; i < Size; ) {
|
|
HeadByte = igetc();
|
|
|
|
if (HeadByte >= 0) { // && HeadByte <= 127
|
|
if (i + HeadByte > Size)
|
|
goto file_corrupt;
|
|
if (iread(Channel + i, HeadByte + 1, 1) != 1)
|
|
goto file_read_error;
|
|
|
|
i += HeadByte + 1;
|
|
}
|
|
if (HeadByte >= -127 && HeadByte <= -1) {
|
|
Run = igetc();
|
|
if (Run == IL_EOF)
|
|
goto file_read_error;
|
|
if (i + (-HeadByte + 1) > Size)
|
|
goto file_corrupt;
|
|
|
|
memset(Channel + i, Run, -HeadByte + 1);
|
|
i += -HeadByte + 1;
|
|
}
|
|
if (HeadByte == -128)
|
|
{ } // Noop
|
|
}
|
|
if (PreCache)
|
|
iUnCache();
|
|
|
|
i = 0;
|
|
for (y = 0; y < Head->Height * iCurImage->Bps; y += iCurImage->Bps) {
|
|
for (x = 0; x < iCurImage->Bps; x += iCurImage->Bpp, i++) {
|
|
iCurImage->Data[y + x + c] = Channel[i];
|
|
}
|
|
}
|
|
}
|
|
|
|
ifree(ChanLen);
|
|
}
|
|
|
|
ifree(Channel);
|
|
|
|
return IL_TRUE;
|
|
|
|
file_corrupt:
|
|
ifree(ChanLen);
|
|
ifree(Channel);
|
|
if (PreCache)
|
|
iUnCache();
|
|
ilSetError(IL_ILLEGAL_FILE_VALUE);
|
|
return IL_FALSE;
|
|
|
|
file_read_error:
|
|
ifree(ChanLen);
|
|
ifree(Channel);
|
|
if (PreCache)
|
|
iUnCache();
|
|
return IL_FALSE;
|
|
}
|
|
|
|
|
|
ILboolean ParseResources(ILuint ResourceSize, ILubyte *Resources)
|
|
{
|
|
ILushort ID;
|
|
ILubyte NameLen;
|
|
ILuint Size;
|
|
|
|
if (Resources == NULL) {
|
|
ilSetError(IL_INTERNAL_ERROR);
|
|
return IL_FALSE;
|
|
}
|
|
|
|
while (ResourceSize) {
|
|
if (strncmp("8BIM", (const char*)Resources, 4)) {
|
|
//return IL_FALSE;
|
|
return IL_TRUE; // 05-30-2002: May not necessarily mean corrupt data...
|
|
}
|
|
Resources += 4;
|
|
|
|
ID = *((ILushort*)Resources);
|
|
BigUShort(&ID);
|
|
Resources += 2;
|
|
|
|
NameLen = *Resources++;
|
|
// NameLen + the byte it occupies must be padded to an even number, so NameLen must be odd.
|
|
NameLen = NameLen + (NameLen & 1 ? 0 : 1);
|
|
Resources += NameLen;
|
|
|
|
// Get the resource data size.
|
|
Size = *((ILuint*)Resources);
|
|
BigUInt(&Size);
|
|
Resources += 4;
|
|
|
|
ResourceSize -= (4 + 2 + 1 + NameLen);
|
|
|
|
switch (ID)
|
|
{
|
|
case 0x040F: // ICC Profile
|
|
iCurImage->Profile = (ILubyte*)ialloc(Size);
|
|
if (iCurImage->Profile == NULL) {
|
|
return IL_FALSE;
|
|
}
|
|
memcpy(iCurImage->Profile, Resources, Size);
|
|
iCurImage->ProfileSize = Size;
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
|
|
if (Size & 1) // Must be an even number.
|
|
Size++;
|
|
ResourceSize -= Size;
|
|
Resources += Size;
|
|
}
|
|
|
|
return IL_TRUE;
|
|
}
|
|
|
|
|
|
ILboolean GetSingleChannel(PSDHEAD *Head, ILubyte *Buffer, ILboolean Compressed)
|
|
{
|
|
ILuint i;
|
|
ILushort *ShortPtr;
|
|
ILbyte HeadByte;
|
|
ILint Run;
|
|
|
|
ShortPtr = (ILushort*)Buffer;
|
|
|
|
if (!Compressed) {
|
|
if (iCurImage->Bpc == 1) {
|
|
if (iread(Buffer, Head->Width * Head->Height, 1) != 1)
|
|
return IL_FALSE;
|
|
}
|
|
else { // iCurImage->Bpc == 2
|
|
if (iread(Buffer, Head->Width * Head->Height * 2, 1) != 1)
|
|
return IL_FALSE;
|
|
}
|
|
}
|
|
else {
|
|
for (i = 0; i < Head->Width * Head->Height; ) {
|
|
HeadByte = igetc();
|
|
|
|
if (HeadByte >= 0) { // && HeadByte <= 127
|
|
if (iread(Buffer + i, HeadByte + 1, 1) != 1)
|
|
return IL_FALSE;
|
|
i += HeadByte + 1;
|
|
}
|
|
if (HeadByte >= -127 && HeadByte <= -1) {
|
|
Run = igetc();
|
|
if (Run == IL_EOF)
|
|
return IL_FALSE;
|
|
memset(Buffer + i, Run, -HeadByte + 1);
|
|
i += -HeadByte + 1;
|
|
}
|
|
if (HeadByte == -128)
|
|
{ } // Noop
|
|
}
|
|
}
|
|
|
|
return IL_TRUE;
|
|
}
|
|
|
|
|
|
|
|
//! Writes a Psd file
|
|
ILboolean ilSavePsd(ILconst_string FileName)
|
|
{
|
|
ILHANDLE PsdFile;
|
|
ILboolean bPsd = IL_FALSE;
|
|
|
|
if (ilGetBoolean(IL_FILE_MODE) == IL_FALSE) {
|
|
if (iFileExists(FileName)) {
|
|
ilSetError(IL_FILE_ALREADY_EXISTS);
|
|
return IL_FALSE;
|
|
}
|
|
}
|
|
|
|
PsdFile = iopenw(FileName);
|
|
if (PsdFile == NULL) {
|
|
ilSetError(IL_COULD_NOT_OPEN_FILE);
|
|
return bPsd;
|
|
}
|
|
|
|
bPsd = ilSavePsdF(PsdFile);
|
|
iclosew(PsdFile);
|
|
|
|
return bPsd;
|
|
}
|
|
|
|
|
|
//! Writes a Psd to an already-opened file
|
|
ILboolean ilSavePsdF(ILHANDLE File)
|
|
{
|
|
iSetOutputFile(File);
|
|
return iSavePsdInternal();
|
|
}
|
|
|
|
|
|
//! Writes a Psd to a memory "lump"
|
|
ILboolean ilSavePsdL(ILvoid *Lump, ILuint Size)
|
|
{
|
|
iSetOutputLump(Lump, Size);
|
|
return iSavePsdInternal();
|
|
}
|
|
|
|
|
|
// Internal function used to save the Psd.
|
|
ILboolean iSavePsdInternal()
|
|
{
|
|
ILubyte *Signature = (ILubyte*)"8BPS";
|
|
ILimage *TempImage;
|
|
ILpal *TempPal;
|
|
ILuint c, i;
|
|
ILubyte *TempData;
|
|
ILushort *ShortPtr;
|
|
ILenum Format, Type;
|
|
|
|
if (iCurImage == NULL) {
|
|
ilSetError(IL_ILLEGAL_OPERATION);
|
|
return IL_FALSE;
|
|
}
|
|
|
|
Format = iCurImage->Format;
|
|
Type = iCurImage->Type;
|
|
|
|
// All of these comprise the actual signature.
|
|
iwrite(Signature, 1, 4);
|
|
SaveBigShort(1);
|
|
SaveBigInt(0);
|
|
SaveBigShort(0);
|
|
|
|
SaveBigShort(iCurImage->Bpp);
|
|
SaveBigInt(iCurImage->Height);
|
|
SaveBigInt(iCurImage->Width);
|
|
if (iCurImage->Bpc > 2)
|
|
Type = IL_UNSIGNED_SHORT;
|
|
|
|
if (iCurImage->Format == IL_BGR)
|
|
Format = IL_RGB;
|
|
else if (iCurImage->Format == IL_BGRA)
|
|
Format = IL_RGBA;
|
|
|
|
if (Format != iCurImage->Format || Type != iCurImage->Type) {
|
|
TempImage = iConvertImage(iCurImage, Format, Type);
|
|
if (TempImage == NULL)
|
|
return IL_FALSE;
|
|
}
|
|
else {
|
|
TempImage = iCurImage;
|
|
}
|
|
SaveBigShort((ILushort)(TempImage->Bpc * 8));
|
|
|
|
// @TODO: Put the other formats here.
|
|
switch (TempImage->Format)
|
|
{
|
|
case IL_COLOUR_INDEX:
|
|
SaveBigShort(2);
|
|
break;
|
|
case IL_LUMINANCE:
|
|
SaveBigShort(1);
|
|
break;
|
|
case IL_RGB:
|
|
case IL_RGBA:
|
|
SaveBigShort(3);
|
|
break;
|
|
default:
|
|
ilSetError(IL_INTERNAL_ERROR);
|
|
return IL_FALSE;
|
|
}
|
|
|
|
if (TempImage->Format == IL_COLOUR_INDEX) {
|
|
// @TODO: We're currently making a potentially fatal assumption that
|
|
// iConvertImage was not called if the format is IL_COLOUR_INDEX.
|
|
TempPal = iConvertPal(&TempImage->Pal, IL_PAL_RGB24);
|
|
if (TempPal == NULL)
|
|
return IL_FALSE;
|
|
SaveBigInt(768);
|
|
|
|
// Have to save the palette in a planar format.
|
|
for (c = 0; c < 3; c++) {
|
|
for (i = c; i < TempPal->PalSize; i += 3) {
|
|
iputc(TempPal->Palette[i]);
|
|
}
|
|
}
|
|
|
|
ifree(TempPal->Palette);
|
|
}
|
|
else {
|
|
SaveBigInt(0); // No colour mode data.
|
|
}
|
|
|
|
SaveBigInt(0); // No image resources.
|
|
SaveBigInt(0); // No layer information.
|
|
SaveBigShort(0); // Raw data, no compression.
|
|
|
|
// @TODO: Add RLE compression.
|
|
|
|
if (TempImage->Origin == IL_ORIGIN_LOWER_LEFT) {
|
|
TempData = iGetFlipped(TempImage);
|
|
if (TempData == NULL) {
|
|
ilCloseImage(TempImage);
|
|
return IL_FALSE;
|
|
}
|
|
}
|
|
else {
|
|
TempData = TempImage->Data;
|
|
}
|
|
|
|
if (TempImage->Bpc == 1) {
|
|
for (c = 0; c < TempImage->Bpp; c++) {
|
|
for (i = c; i < TempImage->SizeOfPlane; i += TempImage->Bpp) {
|
|
iputc(TempData[i]);
|
|
}
|
|
}
|
|
}
|
|
else { // TempImage->Bpc == 2
|
|
ShortPtr = (ILushort*)TempData;
|
|
TempImage->SizeOfPlane /= 2;
|
|
for (c = 0; c < TempImage->Bpp; c++) {
|
|
for (i = c; i < TempImage->SizeOfPlane; i += TempImage->Bpp) {
|
|
SaveBigUShort(ShortPtr[i]);
|
|
}
|
|
}
|
|
TempImage->SizeOfPlane *= 2;
|
|
}
|
|
|
|
if (TempData != TempImage->Data)
|
|
ifree(TempData);
|
|
|
|
if (TempImage != iCurImage)
|
|
ilCloseImage(TempImage);
|
|
|
|
|
|
return IL_TRUE;
|
|
}
|
|
|
|
|
|
#endif//IL_NO_PSD
|