+//
+// 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[colors];
+ unsigned char g[colors];
+ unsigned char b[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));
+ }
+#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;
+ if (chunkLen < 0) { // format error?
+ 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
+