-//
-// iffdecod.cc - image handler for IFF/ILBM images
-// parts of the source are based on xviff by Thomas Meyer
-// Permission for use in wxWindows has been gratefully given.
-//
-// (c) Steffen Gutmann, 2002
-//
-// Creation date: 08.01.2002
-// Last modified: 12.01.2002
-//
-
-#ifdef __GNUG__
-#pragma implementation "iffdecod.h"
-#endif
-
-// For compilers that support precompilation, includes "wx.h".
-#include "wx/wxprec.h"
-
-#ifdef __BORLANDC__
-# pragma hdrstop
-#endif
-
-#ifndef WX_PRECOMP
-# include "wx/defs.h"
-# include "wx/log.h"
-#endif
-
-#include <stdlib.h>
-#include <string.h>
-#include "wx/iffdecod.h"
-
-#if wxUSE_STREAMS && wxUSE_IFF
-
-//---------------------------------------------------------------------------
-// wxIFFDecoder constructor and destructor
-//---------------------------------------------------------------------------
-
-wxIFFDecoder::wxIFFDecoder(wxInputStream *s)
-{
- m_f = s;
- m_image = 0;
- databuf = 0;
- decomp_mem = 0;
-}
-
-void wxIFFDecoder::Destroy()
-{
- delete m_image;
- m_image = 0;
- delete [] databuf;
- databuf = 0;
- delete [] decomp_mem;
- decomp_mem = 0;
-}
-
-//---------------------------------------------------------------------------
-// Convert this image to a wxImage object
-//---------------------------------------------------------------------------
-
-// This function was designed by Vaclav Slavik
-
-bool wxIFFDecoder::ConvertToImage(wxImage *image) const
-{
- // just in case...
- image->Destroy();
-
- // create the image
- image->Create(GetWidth(), GetHeight());
-
- if (!image->Ok())
- return FALSE;
-
- unsigned char *pal = GetPalette();
- unsigned char *src = GetData();
- unsigned char *dst = image->GetData();
- int colors = GetNumColors();
- int transparent = GetTransparentColour();
- long i;
-
- // set transparent colour mask
- if (transparent != -1)
- {
- for (i = 0; i < colors; i++)
- {
- if ((pal[3 * i + 0] == 255) &&
- (pal[3 * i + 1] == 0) &&
- (pal[3 * i + 2] == 255))
- {
- pal[3 * i + 2] = 254;
- }
- }
-
- pal[3 * transparent + 0] = 255,
- pal[3 * transparent + 1] = 0,
- pal[3 * transparent + 2] = 255;
-
- image->SetMaskColour(255, 0, 255);
- }
- else
- image->SetMask(FALSE);
-
-#if wxUSE_PALETTE
- if (pal && colors > 0)
- {
- unsigned char* r = new unsigned char[colors];
- unsigned char* g = new unsigned char[colors];
- unsigned char* b = new unsigned char[colors];
-
- for (i = 0; i < colors; i++)
- {
- r[i] = pal[3*i + 0];
- g[i] = pal[3*i + 1];
- b[i] = pal[3*i + 2];
- }
-
- image->SetPalette(wxPalette(colors, r, g, b));
-
- delete [] r;
- delete [] g;
- delete [] b;
- }
-#endif // wxUSE_PALETTE
-
- // copy image data
- for (i = 0; i < (long)(GetWidth() * GetHeight()); i++, src += 3, dst += 3)
- {
- dst[0] = src[0];
- dst[1] = src[1];
- dst[2] = src[2];
- }
-
- return TRUE;
-}
-
-
-//---------------------------------------------------------------------------
-// Data accessors
-//---------------------------------------------------------------------------
-
-// Get data for current frame
-
-unsigned char* wxIFFDecoder::GetData() const { return (m_image->p); }
-unsigned char* wxIFFDecoder::GetPalette() const { return (m_image->pal); }
-int wxIFFDecoder::GetNumColors() const { return m_image->colors; }
-unsigned int wxIFFDecoder::GetWidth() const { return (m_image->w); }
-unsigned int wxIFFDecoder::GetHeight() const { return (m_image->h); }
-int wxIFFDecoder::GetTransparentColour() const { return m_image->transparent; }
-
-//---------------------------------------------------------------------------
-// IFF reading and decoding
-//---------------------------------------------------------------------------
-
-//
-// CanRead:
-// Returns TRUE if the file looks like a valid IFF, FALSE otherwise.
-//
-bool wxIFFDecoder::CanRead()
-{
- unsigned char buf[12] = "";
-
- m_f->Read(buf, 12);
- m_f->SeekI(-12, wxFromCurrent);
-
- return (memcmp(buf, "FORM", 4) == 0 && memcmp(buf+8, "ILBM", 4) == 0);
-}
-
-
-// ReadIFF:
-// Based on xv source code by Thomas Meyer
-// Permission for use in wxWindows has been gratefully given.
-
-typedef unsigned char byte;
-#define IFFDEBUG 0
-
-/*************************************************************************
- void decomprle(source, destination, source length, buffer size)
-
- Decompress run-length encoded data from source to destination. Terminates
- when source is decoded completely or destination buffer is full.
-
- The decruncher is as optimized as I could make it, without risking
- safety in case of corrupt BODY chunks.
-**************************************************************************/
-
-static void decomprle(const byte *sptr, byte *dptr, long slen, long dlen)
-{
- byte codeByte, dataByte;
-
- while ((slen > 0) && (dlen > 0)) {
- // read control byte
- codeByte = *sptr++;
-
- if (codeByte < 0x80) {
- codeByte++;
- if ((slen > (long) codeByte) && (dlen >= (long) codeByte)) {
- slen -= codeByte + 1;
- dlen -= codeByte;
- while (codeByte > 0) {
- *dptr++ = *sptr++;
- codeByte--;
- }
- }
- else slen = 0;
- }
-
- else if (codeByte > 0x80) {
- codeByte = 0x81 - (codeByte & 0x7f);
- if ((slen > (long) 0) && (dlen >= (long) codeByte)) {
- dataByte = *sptr++;
- slen -= 2;
- dlen -= codeByte;
- while (codeByte > 0) {
- *dptr++ = dataByte;
- codeByte--;
- }
- }
- else slen = 0;
- }
- }
-}
-
-/******************************************/
-static unsigned int iff_getword(const byte *ptr)
-{
- unsigned int v;
-
- v = *ptr++;
- v = (v << 8) + *ptr;
- return v;
-}
-
-/******************************************/
-static unsigned long iff_getlong(const byte *ptr)
-{
- unsigned long l;
-
- l = *ptr++;
- l = (l << 8) + *ptr++;
- l = (l << 8) + *ptr++;
- l = (l << 8) + *ptr;
- return l;
-}
-
-// Define internal ILBM types
-#define ILBM_NORMAL 0
-#define ILBM_EHB 1
-#define ILBM_HAM 2
-#define ILBM_HAM8 3
-#define ILBM_24BIT 4
-
-int wxIFFDecoder::ReadIFF()
-{
- Destroy();
-
- m_image = new IFFImage();
- if (m_image == 0) {
- Destroy();
- return wxIFF_MEMERR;
- }
-
- // compute file length
- off_t currentPos = m_f->TellI();
- m_f->SeekI(0, wxFromEnd);
- long filesize = m_f->TellI();
- m_f->SeekI(currentPos, wxFromStart);
-
- // allocate memory for complete file
- if ((databuf = new byte[filesize]) == 0) {
- Destroy();
- return wxIFF_MEMERR;
- }
-
- m_f->Read(databuf, filesize);
- const byte *dataend = databuf + filesize;
-
- // initialize work pointer. used to trace the buffer for IFF chunks
- const byte *dataptr = databuf;
-
- // check for minmal size
- if (dataptr + 12 > dataend) {
- Destroy();
- return wxIFF_INVFORMAT;
- }
-
- // check if we really got an IFF file
- if (strncmp((char *)dataptr, "FORM", 4) != 0) {
- Destroy();
- return wxIFF_INVFORMAT;
- }
-
- dataptr = dataptr + 8; // skip ID and length of FORM
-
- // check if the IFF file is an ILBM (picture) file
- if (strncmp((char *) dataptr, "ILBM", 4) != 0) {
- Destroy();
- return wxIFF_INVFORMAT;
- }
-
- wxLogTrace(_T("iff"), _T("IFF ILBM file recognized"));
-
- dataptr = dataptr + 4; // skip ID
-
- //
- // main decoding loop. searches IFF chunks and handles them.
- // terminates when BODY chunk was found or dataptr ran over end of file
- //
- bool BMHDok = false, CMAPok = false, CAMGok = false;
- int bmhd_width = 0, bmhd_height = 0, bmhd_bitplanes = 0, bmhd_transcol = -1;
- byte bmhd_masking = 0, bmhd_compression = 0;
- long camg_viewmode = 0;
- int colors = 0;
- while (dataptr + 8 <= dataend) {
- // get chunk length and make even
- size_t chunkLen = (iff_getlong(dataptr + 4) + 1) & 0xfffffffe;
-#ifdef __VMS
- // Silence compiler warning
- int chunkLen_;
- chunkLen_ = chunkLen;
- if (chunkLen_ < 0) { // format error?
-#else
- if (chunkLen < 0) { // format error?
-#endif
- break;
- }
- bool truncated = (dataptr + 8 + chunkLen > dataend);
-
- if (strncmp((char *)dataptr, "BMHD", 4) == 0) { // BMHD chunk?
- if (chunkLen < 12 + 2 || truncated) {
- break;
- }
- bmhd_width = iff_getword(dataptr + 8); // width of picture
- bmhd_height= iff_getword(dataptr + 8 + 2); // height of picture
- bmhd_bitplanes = *(dataptr + 8 + 8); // # of bitplanes
- bmhd_masking = *(dataptr + 8 + 9);
- bmhd_compression = *(dataptr + 8 + 10); // get compression
- bmhd_transcol = iff_getword(dataptr + 8 + 12);
- BMHDok = true; // got BMHD
- dataptr += 8 + chunkLen; // to next chunk
- }
- else if (strncmp((char *)dataptr, "CMAP", 4) == 0) { // CMAP ?
- if (truncated) {
- break;
- }
- const byte *cmapptr = dataptr + 8;
- colors = chunkLen / 3; // calc no of colors
-
- delete m_image->pal;
- m_image->pal = 0;
- m_image->colors = colors;
- if (colors > 0) {
- m_image->pal = new byte[3*colors];
- if (!m_image->pal) {
- Destroy();
- return wxIFF_MEMERR;
- }
-
- // copy colors to color map
- for (int i=0; i < colors; i++) {
- m_image->pal[3*i + 0] = *cmapptr++;
- m_image->pal[3*i + 1] = *cmapptr++;
- m_image->pal[3*i + 2] = *cmapptr++;
- }
- }
-
- wxLogTrace(_T("iff"), _T("Read %d colors from IFF file."),
- colors);
-
- CMAPok = true; // got CMAP
- dataptr += 8 + chunkLen; // to next chunk
- } else if (strncmp((char *)dataptr, "CAMG", 4) == 0) { // CAMG ?
- if (chunkLen < 4 || truncated) {
- break;
- }
- camg_viewmode = iff_getlong(dataptr + 8); // get viewmodes
- CAMGok = true; // got CAMG
- dataptr += 8 + chunkLen; // to next chunk
- }
- else if (strncmp((char *)dataptr, "BODY", 4) == 0) { // BODY ?
- if (!BMHDok) { // BMHD found?
- break;
- }
- const byte *bodyptr = dataptr + 8; // -> BODY data
-
- if (truncated) {
- chunkLen = dataend - dataptr;
- }
-
- //
- // if BODY is compressed, allocate buffer for decrunched BODY
- // and decompress it (run length encoding)
- //
- if (bmhd_compression == 1) {
- // calc size of decrunch buffer - (size of the actual pic.
- // decompressed in interleaved Amiga bitplane format)
-
- size_t decomp_bufsize = (((bmhd_width + 15) >> 4) << 1)
- * bmhd_height * bmhd_bitplanes;
-
- if ((decomp_mem = new byte[decomp_bufsize]) == 0) {
- Destroy();
- return wxIFF_MEMERR;
- }
-
- decomprle(bodyptr, decomp_mem, chunkLen, decomp_bufsize);
- bodyptr = decomp_mem; // -> uncompressed BODY
- chunkLen = decomp_bufsize;
- delete [] databuf;
- databuf = 0;
- }
-
- // the following determines the type of the ILBM file.
- // it's either NORMAL, EHB, HAM, HAM8 or 24BIT
-
- int fmt = ILBM_NORMAL; // assume normal ILBM
- if (bmhd_bitplanes == 24) {
- fmt = ILBM_24BIT;
- } else if (bmhd_bitplanes == 8) {
- if (CAMGok && (camg_viewmode & 0x800)) {
- fmt = ILBM_HAM8;
- }
- } else if ((bmhd_bitplanes > 5) && CAMGok) {
- if (camg_viewmode & 0x80) {
- fmt = ILBM_EHB;
- } else if (camg_viewmode & 0x800) {
- fmt = ILBM_HAM;
- }
- }
-
- wxLogTrace(_T("iff"),
- _T("LoadIFF: %s %dx%d, planes=%d (%d cols), comp=%d"),
- (fmt==ILBM_NORMAL) ? "Normal ILBM" :
- (fmt==ILBM_HAM) ? "HAM ILBM" :
- (fmt==ILBM_HAM8) ? "HAM8 ILBM" :
- (fmt==ILBM_EHB) ? "EHB ILBM" :
- (fmt==ILBM_24BIT) ? "24BIT ILBM" : "unknown ILBM",
- bmhd_width, bmhd_height, bmhd_bitplanes,
- 1<<bmhd_bitplanes, bmhd_compression);
-
- if ((fmt==ILBM_NORMAL) || (fmt==ILBM_EHB) || (fmt==ILBM_HAM)) {
- wxLogTrace(_T("iff"),
- _T("Converting CMAP from normal ILBM CMAP"));
-
- switch(fmt) {
- case ILBM_NORMAL: colors = 1 << bmhd_bitplanes; break;
- case ILBM_EHB: colors = 32*2; break;
- case ILBM_HAM: colors = 16; break;
- }
-
- if (colors > m_image->colors) {
- byte *pal = new byte[colors*3];
- if (!pal) {
- Destroy();
- return wxIFF_MEMERR;
- }
- int i;
- for (i = 0; i < m_image->colors; i++) {
- pal[3*i + 0] = m_image->pal[3*i + 0];
- pal[3*i + 1] = m_image->pal[3*i + 1];
- pal[3*i + 2] = m_image->pal[3*i + 2];
- }
- for (; i < colors; i++) {
- pal[3*i + 0] = 0;
- pal[3*i + 1] = 0;
- pal[3*i + 2] = 0;
- }
- delete m_image->pal;
- m_image->pal = pal;
- m_image->colors = colors;
- }
-
- for (int i=0; i < colors; i++) {
- m_image->pal[3*i + 0] = (m_image->pal[3*i + 0] >> 4) * 17;
- m_image->pal[3*i + 1] = (m_image->pal[3*i + 1] >> 4) * 17;
- m_image->pal[3*i + 2] = (m_image->pal[3*i + 2] >> 4) * 17;
- }
- }
-
- m_image->p = new byte[bmhd_width * bmhd_height * 3];
- byte *picptr = m_image->p;
- if (!picptr) {
- Destroy();
- return wxIFF_MEMERR;
- }
-
- byte *pal = m_image->pal;
- int lineskip = ((bmhd_width + 15) >> 4) << 1;
- int height = chunkLen / (lineskip * bmhd_bitplanes);
-
- if (bmhd_height < height) {
- height = bmhd_height;
- }
-
- if (fmt == ILBM_HAM || fmt == ILBM_HAM8 || fmt == ILBM_24BIT) {
- byte *pic = picptr;
- const byte *workptr = bodyptr;
-
- for (int i=0; i < height; i++) {
- byte bitmsk = 0x80;
- const byte *workptr2 = workptr;
-
- // at start of each line, init RGB values to background
- byte rval = pal[0];
- byte gval = pal[1];
- byte bval = pal[2];
-
- for (int j=0; j < bmhd_width; j++) {
- long col = 0;
- long colbit = 1;
- const byte *workptr3 = workptr2;
- for (int k=0; k < bmhd_bitplanes; k++) {
- if (*workptr3 & bitmsk) {
- col += colbit;
- }
- workptr3 += lineskip;
- colbit <<= 1;
- }
-
- if (fmt==ILBM_HAM) {
- int c = (col & 0x0f);
- switch (col & 0x30) {
- case 0x00: if (c >= 0 && c < colors) {
- rval = pal[3*c + 0];
- gval = pal[3*c + 1];
- bval = pal[3*c + 2];
- }
- break;
-
- case 0x10: bval = c * 17;
- break;
-
- case 0x20: rval = c * 17;
- break;
-
- case 0x30: gval = c * 17;
- break;
- }
- } else if (fmt == ILBM_HAM8) {
- int c = (col & 0x3f);
- switch(col & 0xc0) {
- case 0x00: if (c >= 0 && c < colors) {
- rval = pal[3*c + 0];
- gval = pal[3*c + 1];
- bval = pal[3*c + 2];
- }
- break;
-
- case 0x40: bval = (bval & 3) | (c << 2);
- break;
-
- case 0x80: rval = (rval & 3) | (c << 2);
- break;
-
- case 0xc0: gval = (rval & 3) | (c << 2);
- }
- } else {
- rval = col & 0xff;
- gval = (col >> 8) & 0xff;
- bval = (col >> 16) & 0xff;
- }
-
- *pic++ = rval;
- *pic++ = gval;
- *pic++ = bval;
-
- bitmsk = bitmsk >> 1;
- if (bitmsk == 0) {
- bitmsk = 0x80;
- workptr2++;
- }
- }
- workptr += lineskip * bmhd_bitplanes;
- }
- } else if ((fmt == ILBM_NORMAL) || (fmt == ILBM_EHB)) {
- if (fmt == ILBM_EHB) {
- wxLogTrace(_T("iff"), _T("Doubling CMAP for EHB mode"));
-
- for (int i=0; i<32; i++) {
- pal[3*(i + 32) + 0] = pal[3*i + 0] >> 1;
- pal[3*(i + 32) + 1] = pal[3*i + 1] >> 1;
- pal[3*(i + 32) + 2] = pal[3*i + 2] >> 1;
- }
- }
-
- byte *pic = picptr; // ptr to buffer
- const byte *workptr = bodyptr; // ptr to pic, planar format
-
- if (bmhd_height < height) {
- height = bmhd_height;
- }
-
- for (int i=0; i < height; i++) {
- byte bitmsk = 0x80; // left most bit (mask)
- const byte *workptr2 = workptr; // work ptr to source
- for (int j=0; j < bmhd_width; j++) {
- long col = 0;
- long colbit = 1;
- const byte *workptr3 = workptr2; // 1st byte in 1st pln
-
- for (int k=0; k < bmhd_bitplanes; k++) {
- if (*workptr3 & bitmsk) { // if bit set in this pln
- col = col + colbit; // add bit to chunky byte
- }
- workptr3 += lineskip; // go to next line
- colbit <<= 1; // shift color bit
- }
-
- if (col >= 0 && col < colors) {
- pic[0] = pal[3*col + 0];
- pic[1] = pal[3*col + 1];
- pic[2] = pal[3*col + 2];
- } else {
- pic[0] = pic[1] = pic[2] = 0;
- }
- pic += 3;
- bitmsk = bitmsk >> 1; // shift mask to next bit
- if (bitmsk == 0) { // if mask is zero
- bitmsk = 0x80; // reset mask
- workptr2++; // mv ptr to next byte
- }
- }
-
- workptr += lineskip * bmhd_bitplanes; // to next line
- }
- } else {
- break; // unknown format
- }
-
- m_image->w = bmhd_width;
- m_image->h = height;
- m_image->transparent = bmhd_transcol;
-
- wxLogTrace(_T("iff"), _T("Loaded IFF picture %s"),
- truncated? "truncated" : "completely");
-
- return (truncated? wxIFF_TRUNCATED : wxIFF_OK);
- } else {
- wxLogTrace(_T("iff"), _T("Skipping unknown chunk '%c%c%c%c'"),
- *dataptr, *(dataptr+1), *(dataptr+2), *(dataptr+3));
-
- dataptr = dataptr + 8 + chunkLen; // skip unknown chunk
- }
- }
-
- Destroy();
- return wxIFF_INVFORMAT;
-}
-
-#endif // wxUSE_STREAMS && wxUSE_IFF
-