X-Git-Url: https://git.saurik.com/wxWidgets.git/blobdiff_plain/4b6b4dfcf48af89d910df5a21786fbcd02db8c97..6a62b2c6eddef047009cf2c14365b7012fc418ec:/src/common/imagiff.cpp diff --git a/src/common/imagiff.cpp b/src/common/imagiff.cpp index 72d9f91394..f71aaf3f7b 100644 --- a/src/common/imagiff.cpp +++ b/src/common/imagiff.cpp @@ -1,46 +1,732 @@ -// -// imgiff.cc - image handler for Amiga IFF images -// parts of the source taken by xv source code. -// -// (c) Steffen Gutmann, 2002 -// -// Creation date: 08.01.2002 -// Last modified: 08.01.2002 -// +///////////////////////////////////////////////////////////////////////////// +// Name: src/common/imagiff.cpp +// Purpose: wxImage handler for Amiga IFF images +// Author: Steffen Gutmann, Thomas Meyer +// RCS-ID: $Id$ +// Copyright: (c) Steffen Gutmann, 2002 +// Licence: wxWindows licence +///////////////////////////////////////////////////////////////////////////// -#ifdef __GNUG__ -#pragma implementation "imagiff.h" -#endif +// Parts of this source are based on the iff loading algorithm found +// in xviff.c. Permission by the original author, Thomas Meyer, and +// by the author of xv, John Bradley for using the iff loading part +// in wxWidgets has been gratefully given. // For compilers that support precompilation, includes "wx.h". #include "wx/wxprec.h" #ifdef __BORLANDC__ -# pragma hdrstop + #pragma hdrstop #endif +#if wxUSE_IMAGE && wxUSE_IFF + #ifndef WX_PRECOMP -# include "wx/defs.h" + #include "wx/log.h" + #include "wx/intl.h" #endif #include "wx/imagiff.h" -#include "wx/iffdecod.h" #include "wx/wfstream.h" -#include "wx/log.h" -#include "wx/intl.h" -#if wxUSE_IMAGE && wxUSE_IFF +#if wxUSE_PALETTE + #include "wx/palette.h" +#endif // wxUSE_PALETTE + +#include +#include + + +// -------------------------------------------------------------------------- +// Constants +// -------------------------------------------------------------------------- + +// Error codes: +// Note that the error code wxIFF_TRUNCATED means that the image itself +// is most probably OK, but the decoder didn't reach the end of the data +// stream; this means that if it was not reading directly from file, +// the stream will not be correctly positioned. +// + +enum +{ + wxIFF_OK = 0, /* everything was OK */ + wxIFF_INVFORMAT, /* error in iff header */ + wxIFF_MEMERR, /* error allocating memory */ + wxIFF_TRUNCATED /* file appears to be truncated */ +}; + +// -------------------------------------------------------------------------- +// wxIFFDecoder class +// -------------------------------------------------------------------------- + +// internal class for storing IFF image data +class IFFImage +{ +public: + unsigned int w; /* width */ + unsigned int h; /* height */ + int transparent; /* transparent color (-1 = none) */ + int colors; /* number of colors */ + unsigned char *p; /* bitmap */ + unsigned char *pal; /* palette */ + + IFFImage() : w(0), h(0), colors(0), p(0), pal(0) {} + ~IFFImage() { delete [] p; delete [] pal; } +}; + +class WXDLLEXPORT wxIFFDecoder +{ +private: + IFFImage *m_image; // image data + wxInputStream *m_f; // input stream + unsigned char *databuf; + unsigned char *picptr; + unsigned char *decomp_mem; + + void Destroy(); + +public: + // get data of current frame + unsigned char* GetData() const; + unsigned char* GetPalette() const; + int GetNumColors() const; + unsigned int GetWidth() const; + unsigned int GetHeight() const; + int GetTransparentColour() const; + + // constructor, destructor, etc. + wxIFFDecoder(wxInputStream *s); + ~wxIFFDecoder() { Destroy(); } + + // NOTE: this function modifies the current stream position + bool CanRead(); + + int ReadIFF(); + bool ConvertToImage(wxImage *image) const; +}; + + +//--------------------------------------------------------------------------- +// wxIFFDecoder constructor and destructor +//--------------------------------------------------------------------------- + +wxIFFDecoder::wxIFFDecoder(wxInputStream *s) +{ + m_f = s; + m_image = 0; + databuf = 0; + decomp_mem = 0; +} + +void wxIFFDecoder::Destroy() +{ + wxDELETE(m_image); + wxDELETEA(databuf); + wxDELETEA(decomp_mem); +} + +//--------------------------------------------------------------------------- +// 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->IsOk()) + 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]; + + if ( !m_f->Read(buf, WXSIZEOF(buf)) ) + return false; + + 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 wxWidgets 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 + wxFileOffset currentPos = m_f->TellI(); + if (m_f->SeekI(0, wxFromEnd) == wxInvalidOffset) { + Destroy(); + return wxIFF_MEMERR; + } + + long filesize = m_f->TellI(); + if (m_f->SeekI(currentPos, wxFromStart) == wxInvalidOffset) { + Destroy(); + return wxIFF_MEMERR; + } + + // 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(wxT("iff"), wxT("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, CAMGok = false; + int bmhd_width = 0, bmhd_height = 0, bmhd_bitplanes = 0, bmhd_transcol = -1; + byte bmhd_compression = 0; + long camg_viewmode = 0; + int colors = 0; + while (dataptr + 8 <= dataend) { + // get chunk length and make even + long 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); -- unused currently + 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 + + wxDELETE(m_image->pal); + 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(wxT("iff"), wxT("Read %d colors from IFF file."), + colors); + + 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; + wxDELETEA(databuf); + } + + // 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(wxT("iff"), + wxT("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< 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(wxT("iff"), wxT("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(wxT("iff"), wxT("Loaded IFF picture %s"), + truncated? "truncated" : "completely"); + + return (truncated? wxIFF_TRUNCATED : wxIFF_OK); + } else { + wxLogTrace(wxT("iff"), wxT("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; +} + -IMPLEMENT_DYNAMIC_CLASS(wxIFFHandler, wxImageHandler) //----------------------------------------------------------------------------- // wxIFFHandler //----------------------------------------------------------------------------- +IMPLEMENT_DYNAMIC_CLASS(wxIFFHandler, wxImageHandler) + #if wxUSE_STREAMS -bool wxIFFHandler::LoadFile(wxImage *image, wxInputStream& stream, - bool verbose, int WXUNUSED(index)) +bool wxIFFHandler::LoadFile(wxImage *image, wxInputStream& stream, + bool verbose, int WXUNUSED(index)) { wxIFFDecoder *decod; int error; @@ -67,7 +753,7 @@ bool wxIFFHandler::LoadFile(wxImage *image, wxInputStream& stream, } } delete decod; - return FALSE; + return false; } if ((error == wxIFF_TRUNCATED) && verbose) @@ -83,26 +769,24 @@ bool wxIFFHandler::LoadFile(wxImage *image, wxInputStream& stream, } bool wxIFFHandler::SaveFile(wxImage * WXUNUSED(image), - wxOutputStream& WXUNUSED(stream), bool verbose) + wxOutputStream& WXUNUSED(stream), bool verbose) { if (verbose) + { wxLogDebug(wxT("IFF: the handler is read-only!!")); + } - return FALSE; + return false; } bool wxIFFHandler::DoCanRead(wxInputStream& stream) { - wxIFFDecoder *decod; - bool ok; + wxIFFDecoder decod(&stream); - decod = new wxIFFDecoder(&stream); - ok = decod->CanRead(); - delete decod; - - return ok; + return decod.CanRead(); + // it's ok to modify the stream position here } -#endif -#endif +#endif // wxUSE_STREAMS +#endif // wxUSE_IFF