Files
megasource/libs/DevIL-1.7.8/src-IL/src/il_psp.c
T
2013-09-26 00:20:08 +02:00

713 lines
14 KiB
C

//-----------------------------------------------------------------------------
//
// ImageLib Sources
// Copyright (C) 2000-2009 by Denton Woods
// Last modified: 03/07/2009
//
// Filename: src-IL/src/il_psp.c
//
// Description: Reads a Paint Shop Pro file.
//
//-----------------------------------------------------------------------------
#include "il_internal.h"
#include "il_psp.h"
#ifndef IL_NO_PSP
ILubyte PSPSignature[32] = {
0x50, 0x61, 0x69, 0x6E, 0x74, 0x20, 0x53, 0x68, 0x6F, 0x70, 0x20, 0x50, 0x72, 0x6F, 0x20, 0x49,
0x6D, 0x61, 0x67, 0x65, 0x20, 0x46, 0x69, 0x6C, 0x65, 0x0A, 0x1A, 0x00, 0x00, 0x00, 0x00, 0x00
};
ILubyte GenAttHead[4] = {
0x7E, 0x42, 0x4B, 0x00
};
// Make these global, since they contain most of the image information.
GENATT_CHUNK AttChunk;
PSPHEAD Header;
ILuint NumChannels;
ILubyte **Channels = NULL;
ILubyte *Alpha = NULL;
ILpal Pal;
//! Checks if the file specified in FileName is a valid Psp file.
ILboolean ilIsValidPsp(ILconst_string FileName)
{
ILHANDLE PspFile;
ILboolean bPsp = IL_FALSE;
if (!iCheckExtension(FileName, IL_TEXT("psp"))) {
ilSetError(IL_INVALID_EXTENSION);
return bPsp;
}
PspFile = iopenr(FileName);
if (PspFile == NULL) {
ilSetError(IL_COULD_NOT_OPEN_FILE);
return bPsp;
}
bPsp = ilIsValidPspF(PspFile);
icloser(PspFile);
return bPsp;
}
//! Checks if the ILHANDLE contains a valid Psp file at the current position.
ILboolean ilIsValidPspF(ILHANDLE File)
{
ILuint FirstPos;
ILboolean bRet;
iSetInputFile(File);
FirstPos = itell();
bRet = iIsValidPsp();
iseek(FirstPos, IL_SEEK_SET);
return bRet;
}
//! Checks if Lump is a valid Psp lump.
ILboolean ilIsValidPspL(const void *Lump, ILuint Size)
{
iSetInputLump(Lump, Size);
return iIsValidPsp();
}
// Internal function used to get the Psp header from the current file.
ILboolean iGetPspHead()
{
if (iread(Header.FileSig, 1, 32) != 32)
return IL_FALSE;
Header.MajorVersion = GetLittleUShort();
Header.MinorVersion = GetLittleUShort();
return IL_TRUE;
}
// Internal function to get the header and check it.
ILboolean iIsValidPsp()
{
if (!iGetPspHead())
return IL_FALSE;
iseek(-(ILint)sizeof(PSPHEAD), IL_SEEK_CUR);
return iCheckPsp();
}
// Internal function used to check if the HEADER is a valid Psp header.
ILboolean iCheckPsp()
{
if (stricmp(Header.FileSig, "Paint Shop Pro Image File\n\x1a"))
return IL_FALSE;
if (Header.MajorVersion < 3 || Header.MajorVersion > 5)
return IL_FALSE;
if (Header.MinorVersion != 0)
return IL_FALSE;
return IL_TRUE;
}
//! Reads a PSP file
ILboolean ilLoadPsp(ILconst_string FileName)
{
ILHANDLE PSPFile;
ILboolean bPsp = IL_FALSE;
PSPFile = iopenr(FileName);
if (PSPFile == NULL) {
ilSetError(IL_COULD_NOT_OPEN_FILE);
return bPsp;
}
bPsp = ilLoadPspF(PSPFile);
icloser(PSPFile);
return bPsp;
}
//! Reads an already-opened PSP file
ILboolean ilLoadPspF(ILHANDLE File)
{
ILuint FirstPos;
ILboolean bRet;
iSetInputFile(File);
FirstPos = itell();
bRet = iLoadPspInternal();
iseek(FirstPos, IL_SEEK_SET);
return bRet;
}
//! Reads from a memory "lump" that contains a PSP
ILboolean ilLoadPspL(const void *Lump, ILuint Size)
{
iSetInputLump(Lump, Size);
return iLoadPspInternal();
}
// Internal function used to load the PSP.
ILboolean iLoadPspInternal()
{
if (iCurImage == NULL) {
ilSetError(IL_ILLEGAL_OPERATION);
return IL_FALSE;
}
Channels = NULL;
Alpha = NULL;
Pal.Palette = NULL;
if (!iGetPspHead())
return IL_FALSE;
if (!iCheckPsp()) {
ilSetError(IL_INVALID_FILE_HEADER);
return IL_FALSE;
}
if (!ReadGenAttributes())
return IL_FALSE;
if (!ParseChunks())
return IL_FALSE;
if (!AssembleImage())
return IL_FALSE;
Cleanup();
return ilFixImage();
}
ILboolean ReadGenAttributes()
{
BLOCKHEAD AttHead;
ILint Padding;
ILuint ChunkLen;
if (iread(&AttHead, sizeof(AttHead), 1) != 1)
return IL_FALSE;
UShort(&AttHead.BlockID);
UInt(&AttHead.BlockLen);
if (AttHead.HeadID[0] != 0x7E || AttHead.HeadID[1] != 0x42 ||
AttHead.HeadID[2] != 0x4B || AttHead.HeadID[3] != 0x00) {
ilSetError(IL_INVALID_FILE_HEADER);
return IL_FALSE;
}
if (AttHead.BlockID != PSP_IMAGE_BLOCK) {
ilSetError(IL_INVALID_FILE_HEADER);
return IL_FALSE;
}
ChunkLen = GetLittleUInt();
if (Header.MajorVersion != 3)
ChunkLen -= 4;
if (iread(&AttChunk, IL_MIN(sizeof(AttChunk), ChunkLen), 1) != 1)
return IL_FALSE;
// Can have new entries in newer versions of the spec (4.0).
Padding = (ChunkLen) - sizeof(AttChunk);
if (Padding > 0)
iseek(Padding, IL_SEEK_CUR);
// @TODO: Anything but 24 not supported yet...
if (AttChunk.BitDepth != 24 && AttChunk.BitDepth != 8) {
ilSetError(IL_INVALID_FILE_HEADER);
return IL_FALSE;
}
// @TODO; Add support for compression...
if (AttChunk.Compression != PSP_COMP_NONE && AttChunk.Compression != PSP_COMP_RLE) {
ilSetError(IL_INVALID_FILE_HEADER);
return IL_FALSE;
}
// @TODO: Check more things in the general attributes chunk here.
return IL_TRUE;
}
ILboolean ParseChunks()
{
BLOCKHEAD Block;
ILuint Pos;
do {
if (iread(&Block, 1, sizeof(Block)) != sizeof(Block)) {
ilGetError(); // Get rid of the erroneous IL_FILE_READ_ERROR.
return IL_TRUE;
}
if (Header.MajorVersion == 3)
Block.BlockLen = GetLittleUInt();
else
UInt(&Block.BlockLen);
if (Block.HeadID[0] != 0x7E || Block.HeadID[1] != 0x42 ||
Block.HeadID[2] != 0x4B || Block.HeadID[3] != 0x00) {
return IL_TRUE;
}
UShort(&Block.BlockID);
UInt(&Block.BlockLen);
Pos = itell();
switch (Block.BlockID)
{
case PSP_LAYER_START_BLOCK:
if (!ReadLayerBlock(Block.BlockLen))
return IL_FALSE;
break;
case PSP_ALPHA_BANK_BLOCK:
if (!ReadAlphaBlock(Block.BlockLen))
return IL_FALSE;
break;
case PSP_COLOR_BLOCK:
if (!ReadPalette(Block.BlockLen))
return IL_FALSE;
break;
// Gets done in the next iseek, so this is now commented out.
//default:
//iseek(Block.BlockLen, IL_SEEK_CUR);
}
// Skip to next block just in case we didn't read the entire block.
iseek(Pos + Block.BlockLen, IL_SEEK_SET);
// @TODO: Do stuff here.
} while (1);
return IL_TRUE;
}
ILboolean ReadLayerBlock(ILuint BlockLen)
{
BLOCKHEAD Block;
LAYERINFO_CHUNK LayerInfo;
LAYERBITMAP_CHUNK Bitmap;
ILuint ChunkSize, Padding, i, j;
ILushort NumChars;
BlockLen;
// Layer sub-block header
if (iread(&Block, 1, sizeof(Block)) != sizeof(Block))
return IL_FALSE;
if (Header.MajorVersion == 3)
Block.BlockLen = GetLittleUInt();
else
UInt(&Block.BlockLen);
if (Block.HeadID[0] != 0x7E || Block.HeadID[1] != 0x42 ||
Block.HeadID[2] != 0x4B || Block.HeadID[3] != 0x00) {
return IL_FALSE;
}
if (Block.BlockID != PSP_LAYER_BLOCK)
return IL_FALSE;
if (Header.MajorVersion == 3) {
iseek(256, IL_SEEK_CUR); // We don't care about the name of the layer.
iread(&LayerInfo, sizeof(LayerInfo), 1);
if (iread(&Bitmap, sizeof(Bitmap), 1) != 1)
return IL_FALSE;
}
else { // Header.MajorVersion >= 4
ChunkSize = GetLittleUInt();
NumChars = GetLittleUShort();
iseek(NumChars, IL_SEEK_CUR); // We don't care about the layer's name.
ChunkSize -= (2 + 4 + NumChars);
if (iread(&LayerInfo, IL_MIN(sizeof(LayerInfo), ChunkSize), 1) != 1)
return IL_FALSE;
// Can have new entries in newer versions of the spec (5.0).
Padding = (ChunkSize) - sizeof(LayerInfo);
if (Padding > 0)
iseek(Padding, IL_SEEK_CUR);
ChunkSize = GetLittleUInt();
if (iread(&Bitmap, sizeof(Bitmap), 1) != 1)
return IL_FALSE;
Padding = (ChunkSize - 4) - sizeof(Bitmap);
if (Padding > 0)
iseek(Padding, IL_SEEK_CUR);
}
Channels = (ILubyte**)ialloc(sizeof(ILubyte*) * Bitmap.NumChannels);
if (Channels == NULL) {
return IL_FALSE;
}
NumChannels = Bitmap.NumChannels;
for (i = 0; i < NumChannels; i++) {
Channels[i] = GetChannel();
if (Channels[i] == NULL) {
for (j = 0; j < i; j++)
ifree(Channels[j]);
return IL_FALSE;
}
}
return IL_TRUE;
}
ILboolean ReadAlphaBlock(ILuint BlockLen)
{
BLOCKHEAD Block;
ALPHAINFO_CHUNK AlphaInfo;
ALPHA_CHUNK AlphaChunk;
ILushort NumAlpha, StringSize;
ILuint ChunkSize, Padding;
if (Header.MajorVersion == 3) {
NumAlpha = GetLittleUShort();
}
else {
ChunkSize = GetLittleUInt();
NumAlpha = GetLittleUShort();
Padding = (ChunkSize - 4 - 2);
if (Padding > 0)
iseek(Padding, IL_SEEK_CUR);
}
// Alpha channel header
if (iread(&Block, 1, sizeof(Block)) != sizeof(Block))
return IL_FALSE;
if (Header.MajorVersion == 3)
Block.BlockLen = GetLittleUInt();
else
UInt(&Block.BlockLen);
if (Block.HeadID[0] != 0x7E || Block.HeadID[1] != 0x42 ||
Block.HeadID[2] != 0x4B || Block.HeadID[3] != 0x00) {
return IL_FALSE;
}
if (Block.BlockID != PSP_ALPHA_CHANNEL_BLOCK)
return IL_FALSE;
if (Header.MajorVersion >= 4) {
ChunkSize = GetLittleUInt();
StringSize = GetLittleUShort();
iseek(StringSize, IL_SEEK_CUR);
if (iread(&AlphaInfo, sizeof(AlphaInfo), 1) != 1)
return IL_FALSE;
Padding = (ChunkSize - 4 - 2 - StringSize - sizeof(AlphaInfo));
if (Padding > 0)
iseek(Padding, IL_SEEK_CUR);
ChunkSize = GetLittleUInt();
if (iread(&AlphaChunk, sizeof(AlphaChunk), 1) != 1)
return IL_FALSE;
Padding = (ChunkSize - 4 - sizeof(AlphaChunk));
if (Padding > 0)
iseek(Padding, IL_SEEK_CUR);
}
else {
iseek(256, IL_SEEK_CUR);
iread(&AlphaInfo, sizeof(AlphaInfo), 1);
if (iread(&AlphaChunk, sizeof(AlphaChunk), 1) != 1)
return IL_FALSE;
}
/*Alpha = (ILubyte*)ialloc(AlphaInfo.AlphaRect.x2 * AlphaInfo.AlphaRect.y2);
if (Alpha == NULL) {
return IL_FALSE;
}*/
Alpha = GetChannel();
if (Alpha == NULL)
return IL_FALSE;
return IL_TRUE;
}
ILubyte *GetChannel()
{
BLOCKHEAD Block;
CHANNEL_CHUNK Channel;
ILubyte *CompData, *Data;
ILuint ChunkSize, Padding;
if (iread(&Block, 1, sizeof(Block)) != sizeof(Block))
return NULL;
if (Header.MajorVersion == 3)
Block.BlockLen = GetLittleUInt();
else
UInt(&Block.BlockLen);
if (Block.HeadID[0] != 0x7E || Block.HeadID[1] != 0x42 ||
Block.HeadID[2] != 0x4B || Block.HeadID[3] != 0x00) {
ilSetError(IL_ILLEGAL_FILE_VALUE);
return NULL;
}
if (Block.BlockID != PSP_CHANNEL_BLOCK) {
ilSetError(IL_ILLEGAL_FILE_VALUE);
return NULL;
}
if (Header.MajorVersion >= 4) {
ChunkSize = GetLittleUInt();
if (iread(&Channel, sizeof(Channel), 1) != 1)
return NULL;
Padding = (ChunkSize - 4) - sizeof(Channel);
if (Padding > 0)
iseek(Padding, IL_SEEK_CUR);
}
else {
if (iread(&Channel, sizeof(Channel), 1) != 1)
return NULL;
}
CompData = (ILubyte*)ialloc(Channel.CompLen);
Data = (ILubyte*)ialloc(AttChunk.Width * AttChunk.Height);
if (CompData == NULL || Data == NULL) {
ifree(Data);
ifree(CompData);
return NULL;
}
if (iread(CompData, 1, Channel.CompLen) != Channel.CompLen) {
ifree(CompData);
ifree(Data);
return NULL;
}
switch (AttChunk.Compression)
{
case PSP_COMP_NONE:
ifree(Data);
return CompData;
break;
case PSP_COMP_RLE:
if (!UncompRLE(CompData, Data, Channel.CompLen)) {
ifree(CompData);
ifree(Data);
return IL_FALSE;
}
break;
default:
ifree(CompData);
ifree(Data);
ilSetError(IL_INVALID_FILE_HEADER);
return NULL;
}
ifree(CompData);
return Data;
}
ILboolean UncompRLE(ILubyte *CompData, ILubyte *Data, ILuint CompLen)
{
ILubyte Run, Colour;
ILint i, /*x, y,*/ Count/*, Total = 0*/;
/*for (y = 0; y < AttChunk.Height; y++) {
for (x = 0, Count = 0; x < AttChunk.Width; ) {
Run = *CompData++;
if (Run > 128) {
Run -= 128;
Colour = *CompData++;
memset(Data, Colour, Run);
Data += Run;
Count += 2;
}
else {
memcpy(Data, CompData, Run);
CompData += Run;
Data += Run;
Count += Run;
}
x += Run;
}
Total += Count;
if (Count % 4) { // Has to be on a 4-byte boundary.
CompData += (4 - (Count % 4)) % 4;
Total += (4 - (Count % 4)) % 4;
}
if (Total >= CompLen)
return IL_FALSE;
}*/
for (i = 0, Count = 0; i < (ILint)CompLen; ) {
Run = *CompData++;
i++;
if (Run > 128) {
Run -= 128;
Colour = *CompData++;
i++;
memset(Data, Colour, Run);
}
else {
memcpy(Data, CompData, Run);
CompData += Run;
i += Run;
}
Data += Run;
Count += Run;
}
return IL_TRUE;
}
ILboolean ReadPalette(ILuint BlockLen)
{
ILuint ChunkSize, PalCount, Padding;
if (Header.MajorVersion >= 4) {
ChunkSize = GetLittleUInt();
PalCount = GetLittleUInt();
Padding = (ChunkSize - 4 - 4);
if (Padding > 0)
iseek(Padding, IL_SEEK_CUR);
}
else {
PalCount = GetLittleUInt();
}
Pal.PalSize = PalCount * 4;
Pal.PalType = IL_PAL_BGRA32;
Pal.Palette = (ILubyte*)ialloc(Pal.PalSize);
if (Pal.Palette == NULL)
return IL_FALSE;
if (iread(Pal.Palette, Pal.PalSize, 1) != 1) {
ifree(Pal.Palette);
return IL_FALSE;
}
return IL_TRUE;
}
ILboolean AssembleImage()
{
ILuint Size, i, j;
Size = AttChunk.Width * AttChunk.Height;
if (NumChannels == 1) {
ilTexImage(AttChunk.Width, AttChunk.Height, 1, 1, IL_LUMINANCE, IL_UNSIGNED_BYTE, NULL);
for (i = 0; i < Size; i++) {
iCurImage->Data[i] = Channels[0][i];
}
if (Pal.Palette) {
iCurImage->Format = IL_COLOUR_INDEX;
iCurImage->Pal.PalSize = Pal.PalSize;
iCurImage->Pal.PalType = Pal.PalType;
iCurImage->Pal.Palette = Pal.Palette;
}
}
else {
if (Alpha) {
ilTexImage(AttChunk.Width, AttChunk.Height, 1, 4, IL_RGBA, IL_UNSIGNED_BYTE, NULL);
for (i = 0, j = 0; i < Size; i++, j += 4) {
iCurImage->Data[j ] = Channels[0][i];
iCurImage->Data[j+1] = Channels[1][i];
iCurImage->Data[j+2] = Channels[2][i];
iCurImage->Data[j+3] = Alpha[i];
}
}
else if (NumChannels == 4) {
ilTexImage(AttChunk.Width, AttChunk.Height, 1, 4, IL_RGBA, IL_UNSIGNED_BYTE, NULL);
for (i = 0, j = 0; i < Size; i++, j += 4) {
iCurImage->Data[j ] = Channels[0][i];
iCurImage->Data[j+1] = Channels[1][i];
iCurImage->Data[j+2] = Channels[2][i];
iCurImage->Data[j+3] = Channels[3][i];
}
}
else if (NumChannels == 3) {
ilTexImage(AttChunk.Width, AttChunk.Height, 1, 3, IL_RGB, IL_UNSIGNED_BYTE, NULL);
for (i = 0, j = 0; i < Size; i++, j += 3) {
iCurImage->Data[j ] = Channels[0][i];
iCurImage->Data[j+1] = Channels[1][i];
iCurImage->Data[j+2] = Channels[2][i];
}
}
else
return IL_FALSE;
}
iCurImage->Origin = IL_ORIGIN_UPPER_LEFT;
return IL_TRUE;
}
ILboolean Cleanup()
{
ILuint i;
if (Channels) {
for (i = 0; i < NumChannels; i++) {
ifree(Channels[i]);
}
ifree(Channels);
}
if (Alpha) {
ifree(Alpha);
}
Channels = NULL;
Alpha = NULL;
Pal.Palette = NULL;
return IL_TRUE;
}
#endif//IL_NO_PSP