/ExtLibs/wxWidgets/src/common/imagiff.cpp
C++ | 792 lines | 561 code | 137 blank | 94 comment | 145 complexity | 43788bfee99e3692a36cbbd01cf3fe4e MD5 | raw file
- /////////////////////////////////////////////////////////////////////////////
- // 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
- /////////////////////////////////////////////////////////////////////////////
- // 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
- #endif
- #if wxUSE_IMAGE && wxUSE_IFF
- #ifndef WX_PRECOMP
- #include "wx/log.h"
- #include "wx/intl.h"
- #endif
- #include "wx/imagiff.h"
- #include "wx/wfstream.h"
- #if wxUSE_PALETTE
- #include "wx/palette.h"
- #endif // wxUSE_PALETTE
- #include <stdlib.h>
- #include <string.h>
- // --------------------------------------------------------------------------
- // 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<<bmhd_bitplanes, bmhd_compression);
- if ((fmt==ILBM_NORMAL) || (fmt==ILBM_EHB) || (fmt==ILBM_HAM)) {
- wxLogTrace(wxT("iff"),
- wxT("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(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;
- }
- //-----------------------------------------------------------------------------
- // wxIFFHandler
- //-----------------------------------------------------------------------------
- IMPLEMENT_DYNAMIC_CLASS(wxIFFHandler, wxImageHandler)
- #if wxUSE_STREAMS
- bool wxIFFHandler::LoadFile(wxImage *image, wxInputStream& stream,
- bool verbose, int WXUNUSED(index))
- {
- wxIFFDecoder *decod;
- int error;
- bool ok;
- decod = new wxIFFDecoder(&stream);
- error = decod->ReadIFF();
- if ((error != wxIFF_OK) && (error != wxIFF_TRUNCATED))
- {
- if (verbose)
- {
- switch (error)
- {
- case wxIFF_INVFORMAT:
- wxLogError(_("IFF: error in IFF image format."));
- break;
- case wxIFF_MEMERR:
- wxLogError(_("IFF: not enough memory."));
- break;
- default:
- wxLogError(_("IFF: unknown error!!!"));
- break;
- }
- }
- delete decod;
- return false;
- }
- if ((error == wxIFF_TRUNCATED) && verbose)
- {
- wxLogError(_("IFF: data stream seems to be truncated."));
- /* go on; image data is OK */
- }
- ok = decod->ConvertToImage(image);
- delete decod;
- return ok;
- }
- bool wxIFFHandler::SaveFile(wxImage * WXUNUSED(image),
- wxOutputStream& WXUNUSED(stream), bool verbose)
- {
- if (verbose)
- {
- wxLogDebug(wxT("IFF: the handler is read-only!!"));
- }
- return false;
- }
- bool wxIFFHandler::DoCanRead(wxInputStream& stream)
- {
- wxIFFDecoder decod(&stream);
- return decod.CanRead();
- // it's ok to modify the stream position here
- }
- #endif // wxUSE_STREAMS
- #endif // wxUSE_IFF