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

699 lines
14 KiB
C

//-----------------------------------------------------------------------------
//
// ImageLib Sources
// Copyright (C) 2000-2009 by Denton Woods
// Last modified: 03/07/2009
//
// Filename: src-IL/src/il_pnm.c
//
// Description: Reads/writes to/from pbm/pgm/ppm formats (enough slashes? =)
//
//-----------------------------------------------------------------------------
#include "il_internal.h"
#ifndef IL_NO_PNM
#include "il_pnm.h"
#include <limits.h> // for maximum values
#include <ctype.h>
#include "il_manip.h"
#include "il_bits.h"
// According to the ppm specs, it's 70, but PSP
// likes to output longer lines.
#define MAX_BUFFER 180
static ILbyte LineBuffer[MAX_BUFFER];
static ILbyte SmallBuff[MAX_BUFFER];
// Can't read direct bits from a lump yet
ILboolean IsLump = IL_FALSE;
//! Checks if the file specified in FileName is a valid .pnm file.
ILboolean ilIsValidPnm(ILconst_string FileName)
{
ILHANDLE PnmFile;
ILboolean bPnm = IL_FALSE;
if ( !iCheckExtension(FileName, IL_TEXT("pbm"))
&& !iCheckExtension(FileName, IL_TEXT("pgm"))
&& !iCheckExtension(FileName, IL_TEXT("ppm"))
&& !iCheckExtension(FileName, IL_TEXT("pnm"))) {
ilSetError(IL_INVALID_EXTENSION);
return bPnm;
}
PnmFile = iopenr(FileName);
if (PnmFile == NULL) {
ilSetError(IL_COULD_NOT_OPEN_FILE);
return bPnm;
}
bPnm = ilIsValidPnmF(PnmFile);
icloser(PnmFile);
return bPnm;
}
//! Checks if the ILHANDLE contains a valid .pnm file at the current position.
ILboolean ilIsValidPnmF(ILHANDLE File)
{
ILuint FirstPos;
ILboolean bRet;
iSetInputFile(File);
FirstPos = itell();
bRet = iIsValidPnm();
iseek(FirstPos, IL_SEEK_SET);
return bRet;
}
//! Checks if Lump is a valid .pnm lump.
ILboolean ilIsValidPnmL(const void *Lump, ILuint Size)
{
iSetInputLump(Lump, Size);
return iIsValidPnm();
}
// Internal function to get the header and check it.
ILboolean iIsValidPnm()
{
char Head[2];
ILint Read;
Read = iread(Head, 1, 2);
iseek(-Read, IL_SEEK_CUR); // Go ahead and restore to previous state
if (Read != 2)
return IL_FALSE;
return iCheckPnm(Head);
}
// Internal function used to check if the HEADER is a valid .pnm header.
ILboolean iCheckPnm(char Header[2])
{
if (Header[0] != 'P')
return IL_FALSE;
switch (Header[1])
{
case '1':
case '2':
case '3':
case '4':
case '5':
case '6':
return IL_TRUE;
}
return IL_FALSE;
}
// Reads a file
ILboolean ilLoadPnm(ILconst_string FileName)
{
ILHANDLE PnmFile;
ILboolean bPnm = IL_FALSE;
PnmFile = iopenr(FileName);
if (PnmFile == NULL) {
ilSetError(IL_COULD_NOT_OPEN_FILE);
return bPnm;
}
bPnm = ilLoadPnmF(PnmFile);
icloser(PnmFile);
return bPnm;
}
// Reads an already-opened file
ILboolean ilLoadPnmF(ILHANDLE File)
{
ILuint FirstPos;
ILboolean bRet;
iSetInputFile(File);
FirstPos = itell();
bRet = iLoadPnmInternal();
iseek(FirstPos, IL_SEEK_SET);
return bRet;
}
// Reads from a memory "lump"
ILboolean ilLoadPnmL(const void *Lump, ILuint Size)
{
iSetInputLump(Lump, Size);
return iLoadPnmInternal();
}
// Load either a pgm or a ppm
ILboolean iLoadPnmInternal()
{
ILimage *PmImage = NULL;
PPMINFO Info;
// ILuint LineInc = 0, SmallInc = 0;
Info.Type = 0;
if (iCurImage == NULL) {
ilSetError(IL_ILLEGAL_OPERATION);
return IL_FALSE;
}
// Find out what type of pgm/ppm this is
if (iGetWord(IL_FALSE) == IL_FALSE)
return IL_FALSE;
if (SmallBuff[0] != 'P') {
ilSetError(IL_INVALID_FILE_HEADER);
return IL_FALSE;
}
switch( SmallBuff[1] ) {
case '1':
Info.Type = IL_PBM_ASCII;
break;
case '2':
Info.Type = IL_PGM_ASCII;
break;
case '3':
Info.Type = IL_PPM_ASCII;
break;
case '4':
Info.Type = IL_PBM_BINARY;
if (IsLump) {
ilSetError(IL_FORMAT_NOT_SUPPORTED);
return IL_FALSE;
}
break;
case '5':
Info.Type = IL_PGM_BINARY;
break;
case '6':
Info.Type = IL_PPM_BINARY;
break;
default:
ilSetError(IL_INVALID_FILE_HEADER);
return IL_FALSE;
}
// Retrieve the width and height
if (iGetWord(IL_FALSE) == IL_FALSE)
return IL_FALSE;
Info.Width = atoi((const char*)SmallBuff);
if (Info.Width == 0) {
ilSetError(IL_INVALID_FILE_HEADER);
return IL_FALSE;
}
if (iGetWord(IL_FALSE) == IL_FALSE)
return IL_FALSE;
Info.Height = atoi((const char*)SmallBuff);
if (Info.Height == 0) {
ilSetError(IL_INVALID_FILE_HEADER);
return IL_FALSE;
}
// Retrieve the maximum colour component value
if (Info.Type != IL_PBM_ASCII && Info.Type != IL_PBM_BINARY) {
if (iGetWord(IL_TRUE) == IL_FALSE)
return IL_FALSE;
if ((Info.MaxColour = atoi((const char*)SmallBuff)) == 0) {
ilSetError(IL_INVALID_FILE_HEADER);
return IL_FALSE;
}
} else {
Info.MaxColour = 1;
}
if (Info.Type == IL_PBM_ASCII || Info.Type == IL_PBM_BINARY ||
Info.Type == IL_PGM_ASCII || Info.Type == IL_PGM_BINARY) {
if (Info.Type == IL_PGM_ASCII) {
Info.Bpp = Info.MaxColour < 256 ? 1 : 2;
} else {
Info.Bpp = 1;
}
} else {
Info.Bpp = 3;
}
switch (Info.Type) {
case IL_PBM_ASCII:
case IL_PGM_ASCII:
case IL_PPM_ASCII:
PmImage = ilReadAsciiPpm(&Info);
break;
case IL_PBM_BINARY:
PmImage = ilReadBitPbm(&Info);
break;
case IL_PGM_BINARY:
case IL_PPM_BINARY:
PmImage = ilReadBinaryPpm(&Info);
break;
default:
return IL_FALSE;
}
if (PmImage == NULL) {
iCurImage->Format = ilGetFormatBpp(iCurImage->Bpp);
ilSetError(IL_FILE_READ_ERROR);
return IL_FALSE;
}
// Is this conversion needed? Just 0's and 1's shows up as all black
if (Info.Type == IL_PBM_ASCII) {
PbmMaximize(PmImage);
}
if (Info.MaxColour > 255)
PmImage->Type = IL_UNSIGNED_SHORT;
PmImage->Origin = IL_ORIGIN_UPPER_LEFT;
if (Info.Type == IL_PBM_ASCII || Info.Type == IL_PBM_BINARY ||
Info.Type == IL_PGM_ASCII || Info.Type == IL_PGM_BINARY)
PmImage->Format = IL_LUMINANCE;
else
PmImage->Format = IL_RGB;
PmImage->Origin = IL_ORIGIN_UPPER_LEFT;
if (PmImage == NULL)
return IL_FALSE;
return ilFixImage();
}
ILimage *ilReadAsciiPpm(PPMINFO *Info)
{
ILint LineInc = 0, SmallInc = 0, DataInc = 0, Size;
// ILint BytesRead = 0;
if (Info->MaxColour > 255)
Info->Bpp *= 2;
Size = Info->Width * Info->Height * Info->Bpp;
if (!ilTexImage(Info->Width, Info->Height, 1, (ILubyte)(Info->Bpp), 0, IL_UNSIGNED_BYTE, NULL)) {
return IL_FALSE;
}
iCurImage->Origin = IL_ORIGIN_UPPER_LEFT;
if (Info->MaxColour > 255)
iCurImage->Type = IL_UNSIGNED_SHORT;
while (DataInc < Size) { // && !feof(File)) {
LineInc = 0;
if (iFgets((char *)LineBuffer, MAX_BUFFER) == NULL) {
//ilSetError(IL_ILLEGAL_FILE_VALUE);
//return NULL;
//return iCurImage;
break;
}
if (LineBuffer[0] == '#') { // Comment
continue;
}
while ((LineBuffer[LineInc] != NUL) && (LineBuffer[LineInc] != '\n')) {
SmallInc = 0;
while (!isalnum(LineBuffer[LineInc])) { // Skip any whitespace
LineInc++;
}
while (isalnum(LineBuffer[LineInc])) {
SmallBuff[SmallInc] = LineBuffer[LineInc];
SmallInc++;
LineInc++;
}
SmallBuff[SmallInc] = NUL;
iCurImage->Data[DataInc] = atoi((const char*)SmallBuff); // Convert from string to colour
// PSP likes to put whitespace at the end of lines...figures. =/
while (!isalnum(LineBuffer[LineInc]) && LineBuffer[LineInc] != NUL) { // Skip any whitespace
LineInc++;
}
// We should set some kind of state flag that enables this
//Image->Data[DataInc] *= (ILubyte)(255 / Info->MaxColour); // Scales to 0-255
if (Info->MaxColour > 255)
DataInc++;
DataInc++;
}
}
// If we read less than what we should have...
if (DataInc < Size) {
//ilCloseImage(iCurImage);
//ilSetCurImage(NULL);
ilSetError(IL_ILLEGAL_FILE_VALUE);
return NULL;
}
return iCurImage;
}
ILimage *ilReadBinaryPpm(PPMINFO *Info)
{
ILuint Size;
Size = Info->Width * Info->Height * Info->Bpp;
if (!ilTexImage(Info->Width, Info->Height, 1, (ILubyte)(Info->Bpp), 0, IL_UNSIGNED_BYTE, NULL)) {
return IL_FALSE;
}
iCurImage->Origin = IL_ORIGIN_UPPER_LEFT;
/* 4/3/2007 Dario Meloni
Here it seems we have eaten too much bytes and it is needed to fix
the starting point
works well on various images
No more need of this workaround. fixed iGetWord
iseek(0,IL_SEEK_END);
ILuint size = itell();
iseek(size-Size,IL_SEEK_SET);
*/
if (iread(iCurImage->Data, 1, Size ) != Size) {
ilCloseImage(iCurImage);
return NULL;
}
return iCurImage;
}
ILimage *ilReadBitPbm(PPMINFO *Info)
{
ILuint m, j, x, CurrByte;
if (!ilTexImage(Info->Width, Info->Height, 1, (ILubyte)(Info->Bpp), 0, IL_UNSIGNED_BYTE, NULL)) {
return IL_FALSE;
}
iCurImage->Origin = IL_ORIGIN_UPPER_LEFT;
x = 0;
for (j = 0; j < iCurImage->SizeOfData;) {
CurrByte = igetc();
for (m = 128; m > 0 && x < Info->Width; m >>= 1, ++x, ++j) {
iCurImage->Data[j] = (CurrByte & m)?255:0;
}
if (x == Info->Width)
x = 0;
}
return iCurImage;
}
ILboolean iGetWord(ILboolean final)
{
ILint WordPos = 0;
ILint Current = 0;
ILboolean Started = IL_FALSE;
ILboolean Looping = IL_TRUE;
if (ieof())
return IL_FALSE;
while (Looping) {
while ((Current = igetc()) != IL_EOF && Current != '\n' && Current != '#' && Current != ' ') {
if (WordPos >= MAX_BUFFER) // We have hit the maximum line length.
return IL_FALSE;
if (!isalnum(Current)) {
if (Started) {
Looping = IL_FALSE;
break;
}
continue;
}
if (Looping)
SmallBuff[WordPos++] = Current;
}
if (Current == IL_EOF)
return IL_FALSE;
SmallBuff[WordPos] = 0; // 08-17-2008 - was NULL, changed to avoid warning
if (final == IL_TRUE)
break;
if (!Looping)
break;
if (Current == '#') { // '#' is a comment...read until end of line
while ((Current = igetc()) != IL_EOF && Current != '\n');
}
// Get rid of any erroneous spaces
while ((Current = igetc()) != IL_EOF) {
if (Current != ' ')
break;
}
iseek(-1, IL_SEEK_CUR);
if (WordPos > 0)
break;
}
if (Current == -1 || WordPos == 0) {
ilSetError(IL_INVALID_FILE_HEADER);
return IL_FALSE;
}
return IL_TRUE;
}
ILstring FName = NULL;
//! Writes a Pnm file
ILboolean ilSavePnm(const ILstring FileName)
{
ILHANDLE PnmFile;
ILuint PnmSize;
if (ilGetBoolean(IL_FILE_MODE) == IL_FALSE) {
if (iFileExists(FileName)) {
ilSetError(IL_FILE_ALREADY_EXISTS);
return IL_FALSE;
}
}
PnmFile = iopenw(FileName);
if (PnmFile == NULL) {
ilSetError(IL_COULD_NOT_OPEN_FILE);
return IL_FALSE;
}
PnmSize = ilSavePnmF(PnmFile);
iclosew(PnmFile);
if (PnmSize == 0)
return IL_FALSE;
return IL_TRUE;
}
//! Writes a Pnm to an already-opened file
ILuint ilSavePnmF(ILHANDLE File)
{
ILuint Pos;
iSetOutputFile(File);
Pos = itellw();
if (iSavePnmInternal() == IL_FALSE)
return 0; // Error occurred
return itellw() - Pos; // Return the number of bytes written.
}
//! Writes a Pnm to a memory "lump"
ILuint ilSavePnmL(void *Lump, ILuint Size)
{
ILuint Pos;
FName = NULL;
iSetOutputLump(Lump, Size);
Pos = itellw();
if (iSavePnmInternal() == IL_FALSE)
return 0; // Error occurred
return itellw() - Pos; // Return the number of bytes written.
}
// Internal function used to save the Pnm.
ILboolean iSavePnmInternal()
{
ILuint Bpp, MaxVal = UCHAR_MAX, i = 0, j, k;
ILenum Type = 0;
ILuint LinePos = 0; // Cannot exceed 70 for pnm's!
ILboolean Binary;
ILimage *TempImage;
ILubyte *TempData;
if (iCurImage == NULL) {
ilSetError(IL_ILLEGAL_OPERATION);
return IL_FALSE;
}
if (iCheckExtension(FName, IL_TEXT("pbm")))
Type = IL_PBM_ASCII;
else if (iCheckExtension(FName, IL_TEXT("pgm")))
Type = IL_PGM_ASCII;
else if (iCheckExtension(FName, IL_TEXT("ppm")))
Type = IL_PPM_ASCII;
else
Type = IL_PPM_ASCII;
/*if (!Type) {
ilSetError(IL_INVALID_EXTENSION);
return IL_FALSE;
}*/
if (iGetHint(IL_COMPRESSION_HINT) == IL_USE_COMPRESSION) {
Type += 3;
Binary = IL_TRUE;
}
else {
Binary = IL_FALSE;
}
if (iCurImage->Type == IL_UNSIGNED_BYTE) {
MaxVal = UCHAR_MAX;
}
else if (iCurImage->Type == IL_UNSIGNED_SHORT) {
MaxVal = USHRT_MAX;
}
else {
ilSetError(IL_FORMAT_NOT_SUPPORTED);
return IL_FALSE;
}
if (MaxVal > UCHAR_MAX && Type >= IL_PBM_BINARY) { // binary cannot be higher than 255
ilSetError(IL_FORMAT_NOT_SUPPORTED);
return IL_FALSE;
}
switch (Type)
{
case IL_PBM_ASCII:
Bpp = 1;
ilprintf("P1\n");
TempImage = iConvertImage(iCurImage, IL_LUMINANCE, IL_UNSIGNED_BYTE);
break;
//case IL_PBM_BINARY: // Don't want to mess with saving bits just yet...
//Bpp = 1;
//ilprintf("P4\n");
//break;
case IL_PBM_BINARY:
ilSetError(IL_FORMAT_NOT_SUPPORTED);
return IL_FALSE;
case IL_PGM_ASCII:
Bpp = 1;
ilprintf("P2\n");
TempImage = iConvertImage(iCurImage, IL_COLOUR_INDEX, IL_UNSIGNED_BYTE);
break;
case IL_PGM_BINARY:
Bpp = 1;
ilprintf("P5\n");
TempImage = iConvertImage(iCurImage, IL_COLOUR_INDEX, IL_UNSIGNED_BYTE);
break;
case IL_PPM_ASCII:
Bpp = 3;
ilprintf("P3\n");
TempImage = iConvertImage(iCurImage, IL_RGB, IL_UNSIGNED_BYTE);
break;
case IL_PPM_BINARY:
Bpp = 3;
ilprintf("P6\n");
TempImage = iConvertImage(iCurImage, IL_RGB, IL_UNSIGNED_BYTE);
break;
default:
ilSetError(IL_INTERNAL_ERROR);
return IL_FALSE;
}
if (TempImage == NULL)
return IL_FALSE;
if (Bpp != TempImage->Bpp) {
ilSetError(IL_INVALID_CONVERSION);
return IL_FALSE;
}
if (TempImage->Origin != IL_ORIGIN_UPPER_LEFT) {
TempData = iGetFlipped(TempImage);
if (TempData == NULL) {
ilCloseImage(TempImage);
return IL_FALSE;
}
}
else {
TempData = TempImage->Data;
}
ilprintf("%d %d\n", TempImage->Width, TempImage->Height);
if (Type != IL_PBM_BINARY && Type != IL_PBM_ASCII) // not needed for .pbm's (only 0 and 1)
ilprintf("%d\n", MaxVal);
while (i < TempImage->SizeOfPlane) {
for (j = 0; j < Bpp; j++) {
if (Binary) {
if (Type == IL_PBM_BINARY) {
iputc((ILubyte)(TempData[i] > 127 ? 1 : 0));
}
else {
iputc(TempData[i]);
}
}
else {
if (TempImage->Type == IL_UNSIGNED_BYTE)
k = TempData[i];
else // IL_UNSIGNED_SHORT
k = *((ILushort*)TempData + i);
if (Type == IL_PBM_ASCII) {
LinePos += ilprintf("%d ", TempData[i] > 127 ? 1 : 0);
}
else {
LinePos += ilprintf("%d ", TempData[i]);
}
}
if (TempImage->Type == IL_UNSIGNED_SHORT)
i++;
i++;
}
if (LinePos > 65) { // Just a good number =]
ilprintf("\n");
LinePos = 0;
}
}
if (TempImage->Origin != IL_ORIGIN_UPPER_LEFT)
ifree(TempData);
ilCloseImage(TempImage);
return IL_TRUE;
}
// Converts a .pbm to something viewable.
void PbmMaximize(ILimage *Image)
{
ILuint i = 0;
for (i = 0; i < Image->SizeOfPlane; i++)
if (Image->Data[i] == 1)
Image->Data[i] = 0xFF;
return;
}
#endif//IL_NO_PNM