482 lines
12 KiB
C++
482 lines
12 KiB
C++
/* ScummVM - Scumm Interpreter
|
|
* Copyright (C) 2005-2006 The ScummVM project
|
|
*
|
|
* This program is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU General Public License
|
|
* as published by the Free Software Foundation; either version 2
|
|
* of the License, or (at your option) any later version.
|
|
*
|
|
* This program 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 General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program; if not, write to the Free Software
|
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
|
*
|
|
* $URL$
|
|
* $Id$
|
|
*/
|
|
|
|
#include "common/stdafx.h"
|
|
#include "common/endian.h"
|
|
#include "common/stream.h"
|
|
#include "graphics/surface.h"
|
|
#include "graphics/ilbm.h"
|
|
|
|
namespace Graphics {
|
|
|
|
static char * ID2string(IFF_ID id) {
|
|
static char str[] = "abcd";
|
|
|
|
str[0] = (char)(id >> 24 & 0xff);
|
|
str[1] = (char)(id >> 16 & 0xff);
|
|
str[2] = (char)(id >> 8 & 0xff);
|
|
str[3] = (char)(id >> 0 & 0xff);
|
|
|
|
return str;
|
|
}
|
|
|
|
#define ID_FORM MKID_BE('FORM')
|
|
/* EA IFF 85 group identifier */
|
|
#define ID_CAT MKID_BE('CAT ')
|
|
/* EA IFF 85 group identifier */
|
|
#define ID_LIST MKID_BE('LIST')
|
|
/* EA IFF 85 group identifier */
|
|
#define ID_PROP MKID_BE('PROP')
|
|
/* EA IFF 85 group identifier */
|
|
#define ID_END MKID_BE('END ')
|
|
/* unofficial END-of-FORM identifier (see Amiga RKM Devices Ed.3
|
|
page 376) */
|
|
#define ID_ILBM MKID_BE('ILBM')
|
|
/* EA IFF 85 raster bitmap form */
|
|
#define ID_DEEP MKID_BE('DEEP')
|
|
/* Chunky pixel image files (Used in TV Paint) */
|
|
#define ID_RGB8 MKID_BE('RGB8')
|
|
/* RGB image forms, Turbo Silver (Impulse) */
|
|
#define ID_RGBN MKID_BE('RGBN')
|
|
/* RGB image forms, Turbo Silver (Impulse) */
|
|
#define ID_PBM MKID_BE('PBM ')
|
|
/* 256-color chunky format (DPaint 2 ?) */
|
|
#define ID_ACBM MKID_BE('ACBM')
|
|
/* Amiga Contiguous Bitmap (AmigaBasic) */
|
|
|
|
/* generic */
|
|
|
|
#define ID_FVER MKID_BE('FVER')
|
|
/* AmigaOS version string */
|
|
#define ID_JUNK MKID_BE('JUNK')
|
|
/* always ignore this chunk */
|
|
#define ID_ANNO MKID_BE('ANNO')
|
|
/* EA IFF 85 Generic Annotation chunk */
|
|
#define ID_AUTH MKID_BE('AUTH')
|
|
/* EA IFF 85 Generic Author chunk */
|
|
#define ID_CHRS MKID_BE('CHRS')
|
|
/* EA IFF 85 Generic character string chunk */
|
|
#define ID_NAME MKID_BE('NAME')
|
|
/* EA IFF 85 Generic Name of art, music, etc. chunk */
|
|
#define ID_TEXT MKID_BE('TEXT')
|
|
/* EA IFF 85 Generic unformatted ASCII text chunk */
|
|
#define ID_copy MKID_BE('(c) ')
|
|
/* EA IFF 85 Generic Copyright text chunk */
|
|
|
|
/* ILBM chunks */
|
|
|
|
#define ID_BMHD MKID_BE('BMHD')
|
|
/* ILBM BitmapHeader */
|
|
#define ID_CMAP MKID_BE('CMAP')
|
|
/* ILBM 8bit RGB colormap */
|
|
#define ID_GRAB MKID_BE('GRAB')
|
|
/* ILBM "hotspot" coordiantes */
|
|
#define ID_DEST MKID_BE('DEST')
|
|
/* ILBM destination image info */
|
|
#define ID_SPRT MKID_BE('SPRT')
|
|
/* ILBM sprite identifier */
|
|
#define ID_CAMG MKID_BE('CAMG')
|
|
/* Amiga viewportmodes */
|
|
#define ID_BODY MKID_BE('BODY')
|
|
/* ILBM image data */
|
|
#define ID_CRNG MKID_BE('CRNG')
|
|
/* color cycling */
|
|
#define ID_CCRT MKID_BE('CCRT')
|
|
/* color cycling */
|
|
#define ID_CLUT MKID_BE('CLUT')
|
|
/* Color Lookup Table chunk */
|
|
#define ID_DPI MKID_BE('DPI ')
|
|
/* Dots per inch chunk */
|
|
#define ID_DPPV MKID_BE('DPPV')
|
|
/* DPaint perspective chunk (EA) */
|
|
#define ID_DRNG MKID_BE('DRNG')
|
|
/* DPaint IV enhanced color cycle chunk (EA) */
|
|
#define ID_EPSF MKID_BE('EPSF')
|
|
/* Encapsulated Postscript chunk */
|
|
#define ID_CMYK MKID_BE('CMYK')
|
|
/* Cyan, Magenta, Yellow, & Black color map (Soft-Logik) */
|
|
#define ID_CNAM MKID_BE('CNAM')
|
|
/* Color naming chunk (Soft-Logik) */
|
|
#define ID_PCHG MKID_BE('PCHG')
|
|
/* Line by line palette control information (Sebastiano Vigna) */
|
|
#define ID_PRVW MKID_BE('PRVW')
|
|
/* A mini duplicate ILBM used for preview (Gary Bonham) */
|
|
#define ID_XBMI MKID_BE('XBMI')
|
|
/* eXtended BitMap Information (Soft-Logik) */
|
|
#define ID_CTBL MKID_BE('CTBL')
|
|
/* Newtek Dynamic Ham color chunk */
|
|
#define ID_DYCP MKID_BE('DYCP')
|
|
/* Newtek Dynamic Ham chunk */
|
|
#define ID_SHAM MKID_BE('SHAM')
|
|
/* Sliced HAM color chunk */
|
|
#define ID_ABIT MKID_BE('ABIT')
|
|
/* ACBM body chunk */
|
|
#define ID_DCOL MKID_BE('DCOL')
|
|
/* unofficial direct color */
|
|
#define ID_DPPS MKID_BE('DPPS')
|
|
/* ? */
|
|
#define ID_TINY MKID_BE('TINY')
|
|
/* ? */
|
|
#define ID_DPPV MKID_BE('DPPV')
|
|
/* ? */
|
|
|
|
void IFFDecoder::readBMHD() {
|
|
|
|
_bitmapHeader.width = _chunk.readUint16();
|
|
_bitmapHeader.height = _chunk.readUint16();
|
|
_bitmapHeader.x = _chunk.readUint16();
|
|
_bitmapHeader.y = _chunk.readUint16();
|
|
|
|
_bitmapHeader.depth = _chunk.readByte();
|
|
_bitmapHeader.masking = _chunk.readByte();
|
|
_bitmapHeader.pack = _chunk.readByte();
|
|
_bitmapHeader.flags = _chunk.readByte();
|
|
_bitmapHeader.transparentColor = _chunk.readUint16();
|
|
_bitmapHeader.xAspect = _chunk.readByte();
|
|
_bitmapHeader.yAspect = _chunk.readByte();
|
|
_bitmapHeader.pageWidth = _chunk.readUint16();
|
|
_bitmapHeader.pageHeight = _chunk.readUint16();
|
|
|
|
|
|
_colorCount = 1 << _bitmapHeader.depth;
|
|
*_colors = (byte*)malloc(sizeof(**_colors) * _colorCount * 3);
|
|
_surface->create(_bitmapHeader.width, _bitmapHeader.height, 1);
|
|
|
|
}
|
|
|
|
void IFFDecoder::readCRNG() {
|
|
}
|
|
|
|
void IFFDecoder::readCMAP() {
|
|
if (*_colors == NULL) {
|
|
error("wrong input chunk sequence");
|
|
}
|
|
for (uint32 i = 0; i < _colorCount; i++) {
|
|
(*_colors)[i * 3 + 0] = _chunk.readByte();
|
|
(*_colors)[i * 3 + 1] = _chunk.readByte();
|
|
(*_colors)[i * 3 + 2] = _chunk.readByte();
|
|
}
|
|
}
|
|
|
|
IFFDecoder::IFFDecoder(Common::ReadStream &input) : _formChunk(&input), _chunk(&input), _colorCount(0) {
|
|
_formChunk.readHeader();
|
|
if (_formChunk.id != ID_FORM) {
|
|
error("IFFDecoder input is not a FORM type IFF file");
|
|
}
|
|
}
|
|
|
|
void IFFDecoder::decode(Surface &surface, byte *&colors) {
|
|
_surface = &surface;
|
|
_colors = &colors;
|
|
*_colors = 0;
|
|
|
|
if (!isTypeSupported(_formChunk.readUint32())) {
|
|
error( "IFFDecoder input is not a valid subtype");
|
|
}
|
|
|
|
while (!_formChunk.eos()) {
|
|
_formChunk.incBytesRead(8);
|
|
_chunk.readHeader();
|
|
|
|
switch (_chunk.id) {
|
|
case ID_BMHD:
|
|
readBMHD();
|
|
break;
|
|
|
|
case ID_CMAP:
|
|
readCMAP();
|
|
break;
|
|
|
|
case ID_BODY:
|
|
readBODY();
|
|
break;
|
|
|
|
case ID_CRNG:
|
|
readCRNG();
|
|
break;
|
|
|
|
case ID_GRAB: case ID_TINY: case ID_DPPS: case ID_DPPV: case ID_CAMG:
|
|
break;
|
|
|
|
default:
|
|
error("unknown chunk : %s\n", ID2string(_chunk.id));
|
|
}
|
|
|
|
_chunk.feed();
|
|
_formChunk.incBytesRead(_chunk.size);
|
|
}
|
|
}
|
|
|
|
bool PBMDecoder::isTypeSupported(IFF_ID type) {
|
|
return type == ID_PBM;
|
|
}
|
|
|
|
void PBMDecoder::readBODY() {
|
|
byte byteRun;
|
|
byte idx;
|
|
uint32 si = 0, i, j;
|
|
|
|
if (_bitmapHeader.depth > 8) {
|
|
error("PBMDecoder depth > 8");
|
|
}
|
|
|
|
if ((_bitmapHeader.pack != 0) && (_bitmapHeader.pack != 1)) {
|
|
error("PBMDecoder unsupported pack");
|
|
}
|
|
|
|
switch (_bitmapHeader.pack) {
|
|
case 0:
|
|
while (!_chunk.eos()) {
|
|
idx = _chunk.readByte();
|
|
((byte*)_surface->pixels)[si++] = idx;
|
|
}
|
|
break;
|
|
case 1:
|
|
while (!_chunk.eos()) {
|
|
byteRun = _chunk.readByte();
|
|
if (byteRun <= 127) {
|
|
i = byteRun + 1;
|
|
for (j = 0; j < i; j++){
|
|
idx = _chunk.readByte();
|
|
((byte*)_surface->pixels)[si++] = idx;
|
|
}
|
|
} else if (byteRun != 128) {
|
|
i = (256 - byteRun) + 1;
|
|
idx = _chunk.readByte();
|
|
for (j = 0; j < i; j++) {
|
|
((byte*)_surface->pixels)[si++] = idx;
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
|
|
}
|
|
|
|
|
|
bool ILBMDecoder::isTypeSupported(IFF_ID type) {
|
|
return type == ID_ILBM;
|
|
}
|
|
|
|
void ILBMDecoder::expandLine(byte *buf, uint32 width) {
|
|
|
|
byte byteRun;
|
|
byte idx;
|
|
|
|
uint32 si = 0, i, j;
|
|
|
|
while (si != width) {
|
|
byteRun = _chunk.readByte();
|
|
if (byteRun <= 127) {
|
|
i = byteRun + 1;
|
|
for (j = 0; j < i; j++){
|
|
idx = _chunk.readByte();
|
|
buf[si++] = idx;
|
|
}
|
|
} else if (byteRun != 128) {
|
|
i = (256 - byteRun) + 1;
|
|
idx = _chunk.readByte();
|
|
for (j = 0; j < i; j++) {
|
|
buf[si++] = idx;
|
|
}
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
void ILBMDecoder::fillPlane(byte *out, byte* buf, uint32 width, uint32 plane) {
|
|
|
|
byte src, idx, set;
|
|
byte mask = 1 << plane;
|
|
|
|
for (uint32 j = 0; j < _bitmapHeader.width; j++) {
|
|
src = buf[j >> 3];
|
|
idx = 7 - (j & 7);
|
|
set = src & (1 << idx);
|
|
|
|
if (set)
|
|
out[j] |= mask;
|
|
}
|
|
|
|
}
|
|
|
|
void ILBMDecoder::readBODY() {
|
|
|
|
if (_bitmapHeader.depth > 8) {
|
|
error("ILBMDecoder depth > 8");
|
|
}
|
|
|
|
if (_bitmapHeader.pack != 1) {
|
|
error("ILBMDecoder unsupported pack");
|
|
}
|
|
|
|
if (_bitmapHeader.masking == 1) {
|
|
error("ILBMDecoder mask not supported");
|
|
}
|
|
|
|
uint32 scanWidth = _bitmapHeader.width >> 3;
|
|
byte *scan = (byte*)malloc(scanWidth);
|
|
byte *out = (byte*)_surface->pixels;
|
|
|
|
switch (_bitmapHeader.pack) {
|
|
// case 0:
|
|
// while (!_chunk.eos()) {
|
|
// idx = _chunk.readByte();
|
|
// ((byte*)_surface->pixels)[si++] = idx;
|
|
// }
|
|
// break;
|
|
case 1:
|
|
for (uint32 line = 0; line < _bitmapHeader.height; line++) {
|
|
|
|
for (uint32 plane = 0; plane < _bitmapHeader.depth; plane++) {
|
|
expandLine(scan, scanWidth);
|
|
fillPlane(out, scan, scanWidth, plane);
|
|
}
|
|
|
|
out += _bitmapHeader.width;
|
|
}
|
|
break;
|
|
}
|
|
|
|
free(scan);
|
|
|
|
}
|
|
|
|
void ILBMDecoder::readCRNG() {
|
|
// TODO: implement this. May require changing decode(), too, or adding
|
|
// another parameter to ILBMDecoder constructor
|
|
}
|
|
|
|
ILBMDecoder::ILBMDecoder(Common::ReadStream &input) : IFFDecoder(input) {
|
|
|
|
}
|
|
|
|
ILBMDecoder::~ILBMDecoder() {
|
|
|
|
}
|
|
|
|
|
|
|
|
void decodeILBM(Common::ReadStream &input, Surface &surface, byte *&colors) {
|
|
IFF_ID typeId;
|
|
BMHD bitmapHeader;
|
|
Chunk formChunk(&input);
|
|
Chunk chunk(&input);
|
|
uint32 colorCount = 0, i, j, si;
|
|
byte byteRun;
|
|
byte idx;
|
|
colors = 0;
|
|
si = 0;
|
|
|
|
formChunk.readHeader();
|
|
if (formChunk.id != ID_FORM) {
|
|
error("decodeILBM() input is not a FORM type IFF file");
|
|
}
|
|
|
|
typeId = formChunk.readUint32();
|
|
if (typeId != ID_PBM) {
|
|
error( "decodeILBM() input is not an PBM ");
|
|
}
|
|
|
|
while (!formChunk.eos()) {
|
|
formChunk.incBytesRead(8);
|
|
chunk.readHeader();
|
|
|
|
switch (chunk.id) {
|
|
case ID_BMHD:
|
|
bitmapHeader.width = chunk.readUint16();
|
|
bitmapHeader.height = chunk.readUint16();
|
|
bitmapHeader.x = chunk.readUint16();
|
|
bitmapHeader.y = chunk.readUint16();
|
|
|
|
bitmapHeader.depth = chunk.readByte();
|
|
if (bitmapHeader.depth > 8) {
|
|
error("decodeILBM() depth > 8");
|
|
}
|
|
bitmapHeader.masking = chunk.readByte();
|
|
bitmapHeader.pack = chunk.readByte();
|
|
if ((bitmapHeader.pack != 0) && (bitmapHeader.pack != 1)) {
|
|
error("decodeILBM() unsupported pack");
|
|
}
|
|
bitmapHeader.flags = chunk.readByte();
|
|
bitmapHeader.transparentColor = chunk.readUint16();
|
|
bitmapHeader.xAspect = chunk.readByte();
|
|
bitmapHeader.yAspect = chunk.readByte();
|
|
bitmapHeader.pageWidth = chunk.readUint16();
|
|
bitmapHeader.pageHeight = chunk.readUint16();
|
|
|
|
|
|
colorCount = 1 << bitmapHeader.depth;
|
|
colors = (byte*)malloc(sizeof(*colors) * colorCount * 3);
|
|
surface.create(bitmapHeader.width, bitmapHeader.height, 1);
|
|
break;
|
|
case ID_CMAP:
|
|
if (colors == NULL) {
|
|
error("wrong input chunk sequence");
|
|
}
|
|
for (i = 0; i < colorCount; i++) {
|
|
colors[i * 3 + 0] = chunk.readByte();
|
|
colors[i * 3 + 1] = chunk.readByte();
|
|
colors[i * 3 + 2] = chunk.readByte();
|
|
}
|
|
break;
|
|
|
|
case ID_BODY:
|
|
switch (bitmapHeader.pack) {
|
|
case 0:
|
|
while (!chunk.eos()) {
|
|
idx = chunk.readByte();
|
|
((byte*)surface.pixels)[si++] = idx;
|
|
}
|
|
break;
|
|
case 1:
|
|
while (!chunk.eos()) {
|
|
byteRun = chunk.readByte();
|
|
if (byteRun <= 127) {
|
|
i = byteRun + 1;
|
|
for (j = 0; j < i; j++){
|
|
idx = chunk.readByte();
|
|
((byte*)surface.pixels)[si++] = idx;
|
|
}
|
|
} else if (byteRun != 128) {
|
|
i = (256 - byteRun) + 1;
|
|
idx = chunk.readByte();
|
|
for (j = 0; j < i; j++) {
|
|
((byte*)surface.pixels)[si++] = idx;
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
break;
|
|
case ID_GRAB: case ID_CRNG: case ID_TINY: case ID_DPPS:
|
|
break;
|
|
default:
|
|
error("unknown chunk : %s\n", ID2string(chunk.id));
|
|
}
|
|
|
|
chunk.feed();
|
|
formChunk.incBytesRead(chunk.size);
|
|
}
|
|
}
|
|
|
|
} // End of namespace Graphics
|
|
|