321 lines
9.5 KiB
C++
321 lines
9.5 KiB
C++
/*******************************************************************************
|
|
* iff.cpp
|
|
*
|
|
* This file implements a simple IFF format file reader.
|
|
*
|
|
* ---------------------------------------------------------------------------
|
|
* Persistence of Vision Ray Tracer ('POV-Ray') version 3.7.
|
|
* Copyright 1991-2013 Persistence of Vision Raytracer Pty. Ltd.
|
|
*
|
|
* POV-Ray is free software: you can redistribute it and/or modify
|
|
* it under the terms of the GNU Affero General Public License as
|
|
* published by the Free Software Foundation, either version 3 of the
|
|
* License, or (at your option) any later version.
|
|
*
|
|
* POV-Ray is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU Affero General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU Affero General Public License
|
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
* ---------------------------------------------------------------------------
|
|
* POV-Ray is based on the popular DKB raytracer version 2.12.
|
|
* DKBTrace was originally written by David K. Buck.
|
|
* DKBTrace Ver 2.0-2.12 were written by David K. Buck & Aaron A. Collins.
|
|
* ---------------------------------------------------------------------------
|
|
* $File: //depot/public/povray/3.x/source/base/image/iff.cpp $
|
|
* $Revision: #1 $
|
|
* $Change: 6069 $
|
|
* $DateTime: 2013/11/06 11:59:40 $
|
|
* $Author: chrisc $
|
|
*******************************************************************************/
|
|
|
|
#include <vector>
|
|
|
|
// configbase.h must always be the first POV file included within base *.cpp files
|
|
#include "base/configbase.h"
|
|
#include "base/image/image.h"
|
|
#include "base/image/iff.h"
|
|
|
|
#include <boost/scoped_array.hpp>
|
|
|
|
// this must be the last file included
|
|
#include "base/povdebug.h"
|
|
|
|
namespace pov_base
|
|
{
|
|
|
|
namespace Iff
|
|
{
|
|
|
|
/*****************************************************************************
|
|
* Local preprocessor defines
|
|
******************************************************************************/
|
|
|
|
#define FORM 0x464f524dL
|
|
#define ILBM 0x494c424dL
|
|
#define BMHD 0x424d4844L
|
|
#define CAMG 0x43414d47L
|
|
#define CMAP 0x434d4150L
|
|
#define BODY 0x424f4459L
|
|
#define CMPNONE 0
|
|
|
|
#define HAM 0x800
|
|
|
|
/*****************************************************************************
|
|
* Type definitions
|
|
******************************************************************************/
|
|
|
|
typedef struct Chunk_Header_Struct
|
|
{
|
|
int name;
|
|
int size;
|
|
} CHUNK_HEADER ;
|
|
|
|
/*****************************************************************************
|
|
* Static functions
|
|
******************************************************************************/
|
|
|
|
static int read_byte(IStream *file)
|
|
{
|
|
int c;
|
|
|
|
if ((c = file->Read_Byte()) == EOF)
|
|
throw POV_EXCEPTION(kFileDataErr, "Unexpected EOF while reading IFF file");
|
|
return (c);
|
|
}
|
|
|
|
static int read_word(IStream *file)
|
|
{
|
|
int result = read_byte(file) * 256;
|
|
result += read_byte(file);
|
|
return (result);
|
|
}
|
|
|
|
static long read_long(IStream *file)
|
|
{
|
|
long result = 0;
|
|
for (int i = 0; i < 4; i++)
|
|
result = (result << 8) + read_byte(file);
|
|
return (result);
|
|
}
|
|
|
|
Image *Read (IStream *file, const Image::ReadOptions& options)
|
|
{
|
|
int nPlanes = 0;
|
|
int compression = 0;
|
|
int mask;
|
|
int byte_index;
|
|
int count;
|
|
int viewmodes=0;
|
|
int Previous_Red=0;
|
|
int Previous_Green=0;
|
|
int Previous_Blue=0;
|
|
int colourmap_size = 0;
|
|
int width = -1;
|
|
int height = -1;
|
|
Image *image = NULL;
|
|
CHUNK_HEADER Chunk_Header;
|
|
unsigned int r;
|
|
unsigned int g;
|
|
unsigned int b;
|
|
unsigned long creg;
|
|
Image::RGBMapEntry entry;
|
|
vector<Image::RGBMapEntry> colormap;
|
|
|
|
while (true)
|
|
{
|
|
Chunk_Header.name = read_long(file);
|
|
Chunk_Header.size = read_long(file);
|
|
|
|
switch (IFF_SWITCH_CAST Chunk_Header.name)
|
|
{
|
|
case FORM:
|
|
if (read_long(file) != ILBM)
|
|
throw POV_EXCEPTION(kFileDataErr, "Expected ILBM while reading IFF file");
|
|
break;
|
|
|
|
case BMHD:
|
|
width = read_word(file);
|
|
height = read_word(file);
|
|
read_word(file); /* x position ignored */
|
|
read_word(file); /* y position ignored */
|
|
nPlanes = read_byte(file);
|
|
colourmap_size = 1 << nPlanes;
|
|
read_byte(file); /* masking ignored */
|
|
compression = read_byte(file); /* masking ignored */
|
|
read_byte(file); /* pad */
|
|
read_word(file); /* Transparent colour ignored */
|
|
read_word(file); /* Aspect ratio ignored */
|
|
read_word(file); /* page width ignored */
|
|
read_word(file); /* page height ignored */
|
|
break;
|
|
|
|
case CAMG:
|
|
viewmodes = (int)read_long(file); /* Viewmodes */
|
|
if (viewmodes & HAM)
|
|
colourmap_size = 16;
|
|
break;
|
|
|
|
case CMAP:
|
|
colourmap_size = (int) Chunk_Header.size / 3;
|
|
Previous_Red = read_byte(file);
|
|
Previous_Green = read_byte(file);
|
|
Previous_Blue = read_byte(file);
|
|
entry.red = Previous_Red / 255.0f;
|
|
entry.green = Previous_Green / 255.0f;
|
|
entry.blue = Previous_Blue / 255.0f;
|
|
colormap.push_back (entry);
|
|
for (int i = 1; i < colourmap_size; i++)
|
|
{
|
|
entry.red = read_byte(file) / 255.0f;
|
|
entry.green = read_byte(file) / 255.0f;
|
|
entry.blue = read_byte(file) / 255.0f;
|
|
colormap.push_back (entry);
|
|
// TODO FIXME - gamma!
|
|
}
|
|
|
|
for (int i = colourmap_size * 3; (long)i < Chunk_Header.size; i++)
|
|
read_byte(file);
|
|
|
|
break;
|
|
|
|
case BODY:
|
|
if (width > 0 && height > 0)
|
|
{
|
|
Image::ImageDataType imagetype = options.itype;
|
|
if (imagetype == Image::Undefined)
|
|
imagetype = ((viewmodes & HAM) != 0 || nPlanes == 24) ? Image::RGB_Int8 : Image::Colour_Map;
|
|
if ((viewmodes & HAM) != 0 || nPlanes == 24)
|
|
image = Image::Create (width, height, imagetype);
|
|
else
|
|
image = Image::Create (width, height, imagetype, colormap);
|
|
// NB: IFF files don't use alpha, so premultiplied vs. non-premultiplied is not an issue
|
|
|
|
int rowlen = ((width + 15) / 16) * 2 ;
|
|
boost::scoped_array<unsigned char> row_bytes (new unsigned char [nPlanes * rowlen]);
|
|
|
|
for (int row = 0; row < height; row++)
|
|
{
|
|
for (int plane = 0; plane < nPlanes; plane++)
|
|
{
|
|
if (compression != CMPNONE)
|
|
{
|
|
int nBytes = 0;
|
|
|
|
while (nBytes != ((width + 15) / 16) * 2)
|
|
{
|
|
unsigned char c = read_byte(file);
|
|
|
|
if ((c >= 0) && (c <= 127))
|
|
{
|
|
for (int k = 0; k <= c; k++)
|
|
row_bytes[plane * rowlen + nBytes++] = (unsigned char)read_byte(file);
|
|
}
|
|
else
|
|
{
|
|
if ((c >= 129) && (c <= 255))
|
|
{
|
|
count = 257 - c;
|
|
c = read_byte(file);
|
|
for (int k = 0; k < count; k++)
|
|
row_bytes[plane * rowlen + nBytes++] = (unsigned char)c;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else
|
|
for (int k = 0; k < (((width + 15) / 16) * 2); k++)
|
|
row_bytes[plane * rowlen + k] = (unsigned char)read_byte(file);
|
|
}
|
|
|
|
mask = 0x80;
|
|
byte_index = 0;
|
|
|
|
for (int col = 0; col < width; col++)
|
|
{
|
|
creg = 0;
|
|
|
|
for (int plane = nPlanes - 1; plane >= 0; plane--)
|
|
{
|
|
creg *= 2;
|
|
if (row_bytes[plane * rowlen + byte_index] & mask)
|
|
creg++;
|
|
}
|
|
|
|
if (viewmodes & HAM)
|
|
{
|
|
switch ((int)(creg >> 4))
|
|
{
|
|
case 0:
|
|
Previous_Red = r = (unsigned char)(colormap[creg].red*255.0);
|
|
Previous_Green = g = (unsigned char)(colormap[creg].green*255.0);
|
|
Previous_Blue = b = (unsigned char)(colormap[creg].blue*255.0);
|
|
break;
|
|
|
|
case 1:
|
|
r = (unsigned char)Previous_Red;
|
|
g = (unsigned char)Previous_Green;
|
|
Previous_Blue = b = (unsigned char)(((creg & 0xf) << 4) + (creg & 0xf));
|
|
break;
|
|
|
|
case 2:
|
|
Previous_Red = r = (unsigned char)(((creg & 0xf) << 4) + (creg & 0xf));
|
|
g = (unsigned char)Previous_Green;
|
|
b = (unsigned char)Previous_Blue;
|
|
break;
|
|
|
|
case 3:
|
|
r = (unsigned char)Previous_Red;
|
|
Previous_Green = g = (unsigned char)(((creg & 0xf) << 4) + (creg & 0xf));
|
|
b = (unsigned char)Previous_Blue;
|
|
break;
|
|
}
|
|
image->SetRGBValue (col, row, r, g, b); // TODO FIXME - gamma!
|
|
}
|
|
else
|
|
{
|
|
if (nPlanes == 24)
|
|
{
|
|
r = (unsigned char)((creg >> 16) & 0xFF);
|
|
g = (unsigned char)((creg >> 8) & 0xFF);
|
|
b = (unsigned char)(creg & 0xFF);
|
|
image->SetRGBValue (col, row, r, g, b); // TODO FIXME - gamma!
|
|
}
|
|
else
|
|
{
|
|
if (creg >= colormap.size())
|
|
throw POV_EXCEPTION(kFileDataErr, "IFF color out of range in image");
|
|
image->SetIndexedValue (col, row, creg);
|
|
}
|
|
}
|
|
|
|
mask >>= 1;
|
|
|
|
if (mask == 0)
|
|
{
|
|
mask = 0x80;
|
|
byte_index++;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else
|
|
throw POV_EXCEPTION(kFileDataErr, "Invalid IFF file");
|
|
break;
|
|
|
|
default:
|
|
for (int i = 0; (long)i < Chunk_Header.size; i++)
|
|
if (file->Read_Byte() == EOF)
|
|
throw POV_EXCEPTION(kFileDataErr, "Unexpected EOF while reading IFF file");
|
|
break;
|
|
}
|
|
}
|
|
return (image);
|
|
}
|
|
|
|
} // end of namespace Iff
|
|
|
|
}
|
|
|