povray/source/base/image/iff.cpp
2013-11-06 13:07:19 -05:00

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
}