1 /////////////////////////////////////////////////////////////////////////////
3 // Purpose: wxImage handler for Amiga IFF images
4 // Author: Steffen Gutmann, Thomas Meyer
6 // Copyright: (c) Steffen Gutmann, 2002
7 // Licence: wxWindows licence
8 /////////////////////////////////////////////////////////////////////////////
10 // Parts of this source are based on the iff loading algorithm found
11 // in xviff.c. Permission by the original author, Thomas Meyer, and
12 // by the author of xv, John Bradley for using the iff loading part
13 // in wxWidgets has been gratefully given.
15 #if defined(__GNUG__) && !defined(NO_GCC_PRAGMA)
16 #pragma implementation "imagiff.h"
19 // For compilers that support precompilation, includes "wx.h".
20 #include "wx/wxprec.h"
30 #if wxUSE_IMAGE && wxUSE_IFF
32 #include "wx/imagiff.h"
33 #include "wx/wfstream.h"
38 #include "wx/palette.h"
39 #endif // wxUSE_PALETTE
45 // --------------------------------------------------------------------------
47 // --------------------------------------------------------------------------
50 // Note that the error code wxIFF_TRUNCATED means that the image itself
51 // is most probably OK, but the decoder didn't reach the end of the data
52 // stream; this means that if it was not reading directly from file,
53 // the stream will not be correctly positioned.
58 wxIFF_OK
= 0, /* everything was OK */
59 wxIFF_INVFORMAT
, /* error in iff header */
60 wxIFF_MEMERR
, /* error allocating memory */
61 wxIFF_TRUNCATED
/* file appears to be truncated */
64 // --------------------------------------------------------------------------
66 // --------------------------------------------------------------------------
68 // internal class for storing IFF image data
72 unsigned int w
; /* width */
73 unsigned int h
; /* height */
74 int transparent
; /* transparent color (-1 = none) */
75 int colors
; /* number of colors */
76 unsigned char *p
; /* bitmap */
77 unsigned char *pal
; /* palette */
79 IFFImage() : w(0), h(0), colors(0), p(0), pal(0) {}
80 ~IFFImage() { delete [] p
; delete [] pal
; }
83 class WXDLLEXPORT wxIFFDecoder
86 IFFImage
*m_image
; // image data
87 wxInputStream
*m_f
; // input stream
88 unsigned char *databuf
;
89 unsigned char *picptr
;
90 unsigned char *decomp_mem
;
95 // get data of current frame
96 unsigned char* GetData() const;
97 unsigned char* GetPalette() const;
98 int GetNumColors() const;
99 unsigned int GetWidth() const;
100 unsigned int GetHeight() const;
101 int GetTransparentColour() const;
103 // constructor, destructor, etc.
104 wxIFFDecoder(wxInputStream
*s
);
105 ~wxIFFDecoder() { Destroy(); }
108 bool ConvertToImage(wxImage
*image
) const;
112 //---------------------------------------------------------------------------
113 // wxIFFDecoder constructor and destructor
114 //---------------------------------------------------------------------------
116 wxIFFDecoder::wxIFFDecoder(wxInputStream
*s
)
124 void wxIFFDecoder::Destroy()
130 delete [] decomp_mem
;
134 //---------------------------------------------------------------------------
135 // Convert this image to a wxImage object
136 //---------------------------------------------------------------------------
138 // This function was designed by Vaclav Slavik
140 bool wxIFFDecoder::ConvertToImage(wxImage
*image
) const
146 image
->Create(GetWidth(), GetHeight());
151 unsigned char *pal
= GetPalette();
152 unsigned char *src
= GetData();
153 unsigned char *dst
= image
->GetData();
154 int colors
= GetNumColors();
155 int transparent
= GetTransparentColour();
158 // set transparent colour mask
159 if (transparent
!= -1)
161 for (i
= 0; i
< colors
; i
++)
163 if ((pal
[3 * i
+ 0] == 255) &&
164 (pal
[3 * i
+ 1] == 0) &&
165 (pal
[3 * i
+ 2] == 255))
167 pal
[3 * i
+ 2] = 254;
171 pal
[3 * transparent
+ 0] = 255,
172 pal
[3 * transparent
+ 1] = 0,
173 pal
[3 * transparent
+ 2] = 255;
175 image
->SetMaskColour(255, 0, 255);
178 image
->SetMask(false);
181 if (pal
&& colors
> 0)
183 unsigned char* r
= new unsigned char[colors
];
184 unsigned char* g
= new unsigned char[colors
];
185 unsigned char* b
= new unsigned char[colors
];
187 for (i
= 0; i
< colors
; i
++)
194 image
->SetPalette(wxPalette(colors
, r
, g
, b
));
200 #endif // wxUSE_PALETTE
203 for (i
= 0; i
< (long)(GetWidth() * GetHeight()); i
++, src
+= 3, dst
+= 3)
214 //---------------------------------------------------------------------------
216 //---------------------------------------------------------------------------
218 // Get data for current frame
220 unsigned char* wxIFFDecoder::GetData() const { return (m_image
->p
); }
221 unsigned char* wxIFFDecoder::GetPalette() const { return (m_image
->pal
); }
222 int wxIFFDecoder::GetNumColors() const { return m_image
->colors
; }
223 unsigned int wxIFFDecoder::GetWidth() const { return (m_image
->w
); }
224 unsigned int wxIFFDecoder::GetHeight() const { return (m_image
->h
); }
225 int wxIFFDecoder::GetTransparentColour() const { return m_image
->transparent
; }
227 //---------------------------------------------------------------------------
228 // IFF reading and decoding
229 //---------------------------------------------------------------------------
233 // Returns true if the file looks like a valid IFF, false otherwise.
235 bool wxIFFDecoder::CanRead()
237 unsigned char buf
[12];
239 if ( !m_f
->Read(buf
, WXSIZEOF(buf
)) )
242 m_f
->SeekI(-(wxFileOffset
)WXSIZEOF(buf
), wxFromCurrent
);
244 return (memcmp(buf
, "FORM", 4) == 0) && (memcmp(buf
+8, "ILBM", 4) == 0);
249 // Based on xv source code by Thomas Meyer
250 // Permission for use in wxWidgets has been gratefully given.
252 typedef unsigned char byte
;
255 /*************************************************************************
256 void decomprle(source, destination, source length, buffer size)
258 Decompress run-length encoded data from source to destination. Terminates
259 when source is decoded completely or destination buffer is full.
261 The decruncher is as optimized as I could make it, without risking
262 safety in case of corrupt BODY chunks.
263 **************************************************************************/
265 static void decomprle(const byte
*sptr
, byte
*dptr
, long slen
, long dlen
)
267 byte codeByte
, dataByte
;
269 while ((slen
> 0) && (dlen
> 0)) {
273 if (codeByte
< 0x80) {
275 if ((slen
> (long) codeByte
) && (dlen
>= (long) codeByte
)) {
276 slen
-= codeByte
+ 1;
278 while (codeByte
> 0) {
286 else if (codeByte
> 0x80) {
287 codeByte
= 0x81 - (codeByte
& 0x7f);
288 if ((slen
> (long) 0) && (dlen
>= (long) codeByte
)) {
292 while (codeByte
> 0) {
302 /******************************************/
303 static unsigned int iff_getword(const byte
*ptr
)
312 /******************************************/
313 static unsigned long iff_getlong(const byte
*ptr
)
318 l
= (l
<< 8) + *ptr
++;
319 l
= (l
<< 8) + *ptr
++;
324 // Define internal ILBM types
325 #define ILBM_NORMAL 0
331 int wxIFFDecoder::ReadIFF()
335 m_image
= new IFFImage();
341 // compute file length
342 wxFileOffset currentPos
= m_f
->TellI();
343 m_f
->SeekI(0, wxFromEnd
);
344 long filesize
= m_f
->TellI();
345 m_f
->SeekI(currentPos
, wxFromStart
);
347 // allocate memory for complete file
348 if ((databuf
= new byte
[filesize
]) == 0) {
353 m_f
->Read(databuf
, filesize
);
354 const byte
*dataend
= databuf
+ filesize
;
356 // initialize work pointer. used to trace the buffer for IFF chunks
357 const byte
*dataptr
= databuf
;
359 // check for minmal size
360 if (dataptr
+ 12 > dataend
) {
362 return wxIFF_INVFORMAT
;
365 // check if we really got an IFF file
366 if (strncmp((char *)dataptr
, "FORM", 4) != 0) {
368 return wxIFF_INVFORMAT
;
371 dataptr
= dataptr
+ 8; // skip ID and length of FORM
373 // check if the IFF file is an ILBM (picture) file
374 if (strncmp((char *) dataptr
, "ILBM", 4) != 0) {
376 return wxIFF_INVFORMAT
;
379 wxLogTrace(_T("iff"), _T("IFF ILBM file recognized"));
381 dataptr
= dataptr
+ 4; // skip ID
384 // main decoding loop. searches IFF chunks and handles them.
385 // terminates when BODY chunk was found or dataptr ran over end of file
387 bool BMHDok
= false, CMAPok
= false, CAMGok
= false;
388 int bmhd_width
= 0, bmhd_height
= 0, bmhd_bitplanes
= 0, bmhd_transcol
= -1;
389 byte bmhd_masking
= 0, bmhd_compression
= 0;
390 long camg_viewmode
= 0;
392 while (dataptr
+ 8 <= dataend
) {
393 // get chunk length and make even
394 size_t chunkLen
= (iff_getlong(dataptr
+ 4) + 1) & 0xfffffffe;
396 // Silence compiler warning
398 chunkLen_
= chunkLen
;
399 if (chunkLen_
< 0) { // format error?
401 if (chunkLen
< 0) { // format error?
405 bool truncated
= (dataptr
+ 8 + chunkLen
> dataend
);
407 if (strncmp((char *)dataptr
, "BMHD", 4) == 0) { // BMHD chunk?
408 if (chunkLen
< 12 + 2 || truncated
) {
411 bmhd_width
= iff_getword(dataptr
+ 8); // width of picture
412 bmhd_height
= iff_getword(dataptr
+ 8 + 2); // height of picture
413 bmhd_bitplanes
= *(dataptr
+ 8 + 8); // # of bitplanes
414 bmhd_masking
= *(dataptr
+ 8 + 9);
415 bmhd_compression
= *(dataptr
+ 8 + 10); // get compression
416 bmhd_transcol
= iff_getword(dataptr
+ 8 + 12);
417 BMHDok
= true; // got BMHD
418 dataptr
+= 8 + chunkLen
; // to next chunk
420 else if (strncmp((char *)dataptr
, "CMAP", 4) == 0) { // CMAP ?
424 const byte
*cmapptr
= dataptr
+ 8;
425 colors
= chunkLen
/ 3; // calc no of colors
429 m_image
->colors
= colors
;
431 m_image
->pal
= new byte
[3*colors
];
437 // copy colors to color map
438 for (int i
=0; i
< colors
; i
++) {
439 m_image
->pal
[3*i
+ 0] = *cmapptr
++;
440 m_image
->pal
[3*i
+ 1] = *cmapptr
++;
441 m_image
->pal
[3*i
+ 2] = *cmapptr
++;
445 wxLogTrace(_T("iff"), _T("Read %d colors from IFF file."),
448 CMAPok
= true; // got CMAP
449 dataptr
+= 8 + chunkLen
; // to next chunk
450 } else if (strncmp((char *)dataptr
, "CAMG", 4) == 0) { // CAMG ?
451 if (chunkLen
< 4 || truncated
) {
454 camg_viewmode
= iff_getlong(dataptr
+ 8); // get viewmodes
455 CAMGok
= true; // got CAMG
456 dataptr
+= 8 + chunkLen
; // to next chunk
458 else if (strncmp((char *)dataptr
, "BODY", 4) == 0) { // BODY ?
459 if (!BMHDok
) { // BMHD found?
462 const byte
*bodyptr
= dataptr
+ 8; // -> BODY data
465 chunkLen
= dataend
- dataptr
;
469 // if BODY is compressed, allocate buffer for decrunched BODY
470 // and decompress it (run length encoding)
472 if (bmhd_compression
== 1) {
473 // calc size of decrunch buffer - (size of the actual pic.
474 // decompressed in interleaved Amiga bitplane format)
476 size_t decomp_bufsize
= (((bmhd_width
+ 15) >> 4) << 1)
477 * bmhd_height
* bmhd_bitplanes
;
479 if ((decomp_mem
= new byte
[decomp_bufsize
]) == 0) {
484 decomprle(bodyptr
, decomp_mem
, chunkLen
, decomp_bufsize
);
485 bodyptr
= decomp_mem
; // -> uncompressed BODY
486 chunkLen
= decomp_bufsize
;
491 // the following determines the type of the ILBM file.
492 // it's either NORMAL, EHB, HAM, HAM8 or 24BIT
494 int fmt
= ILBM_NORMAL
; // assume normal ILBM
495 if (bmhd_bitplanes
== 24) {
497 } else if (bmhd_bitplanes
== 8) {
498 if (CAMGok
&& (camg_viewmode
& 0x800)) {
501 } else if ((bmhd_bitplanes
> 5) && CAMGok
) {
502 if (camg_viewmode
& 0x80) {
504 } else if (camg_viewmode
& 0x800) {
509 wxLogTrace(_T("iff"),
510 _T("LoadIFF: %s %dx%d, planes=%d (%d cols), comp=%d"),
511 (fmt
==ILBM_NORMAL
) ? "Normal ILBM" :
512 (fmt
==ILBM_HAM
) ? "HAM ILBM" :
513 (fmt
==ILBM_HAM8
) ? "HAM8 ILBM" :
514 (fmt
==ILBM_EHB
) ? "EHB ILBM" :
515 (fmt
==ILBM_24BIT
) ? "24BIT ILBM" : "unknown ILBM",
516 bmhd_width
, bmhd_height
, bmhd_bitplanes
,
517 1<<bmhd_bitplanes
, bmhd_compression
);
519 if ((fmt
==ILBM_NORMAL
) || (fmt
==ILBM_EHB
) || (fmt
==ILBM_HAM
)) {
520 wxLogTrace(_T("iff"),
521 _T("Converting CMAP from normal ILBM CMAP"));
524 case ILBM_NORMAL
: colors
= 1 << bmhd_bitplanes
; break;
525 case ILBM_EHB
: colors
= 32*2; break;
526 case ILBM_HAM
: colors
= 16; break;
529 if (colors
> m_image
->colors
) {
530 byte
*pal
= new byte
[colors
*3];
536 for (i
= 0; i
< m_image
->colors
; i
++) {
537 pal
[3*i
+ 0] = m_image
->pal
[3*i
+ 0];
538 pal
[3*i
+ 1] = m_image
->pal
[3*i
+ 1];
539 pal
[3*i
+ 2] = m_image
->pal
[3*i
+ 2];
541 for (; i
< colors
; i
++) {
548 m_image
->colors
= colors
;
551 for (int i
=0; i
< colors
; i
++) {
552 m_image
->pal
[3*i
+ 0] = (m_image
->pal
[3*i
+ 0] >> 4) * 17;
553 m_image
->pal
[3*i
+ 1] = (m_image
->pal
[3*i
+ 1] >> 4) * 17;
554 m_image
->pal
[3*i
+ 2] = (m_image
->pal
[3*i
+ 2] >> 4) * 17;
558 m_image
->p
= new byte
[bmhd_width
* bmhd_height
* 3];
559 byte
*picptr
= m_image
->p
;
565 byte
*pal
= m_image
->pal
;
566 int lineskip
= ((bmhd_width
+ 15) >> 4) << 1;
567 int height
= chunkLen
/ (lineskip
* bmhd_bitplanes
);
569 if (bmhd_height
< height
) {
570 height
= bmhd_height
;
573 if (fmt
== ILBM_HAM
|| fmt
== ILBM_HAM8
|| fmt
== ILBM_24BIT
) {
575 const byte
*workptr
= bodyptr
;
577 for (int i
=0; i
< height
; i
++) {
579 const byte
*workptr2
= workptr
;
581 // at start of each line, init RGB values to background
586 for (int j
=0; j
< bmhd_width
; j
++) {
589 const byte
*workptr3
= workptr2
;
590 for (int k
=0; k
< bmhd_bitplanes
; k
++) {
591 if (*workptr3
& bitmsk
) {
594 workptr3
+= lineskip
;
599 int c
= (col
& 0x0f);
600 switch (col
& 0x30) {
601 case 0x00: if (c
>= 0 && c
< colors
) {
608 case 0x10: bval
= c
* 17;
611 case 0x20: rval
= c
* 17;
614 case 0x30: gval
= c
* 17;
617 } else if (fmt
== ILBM_HAM8
) {
618 int c
= (col
& 0x3f);
620 case 0x00: if (c
>= 0 && c
< colors
) {
627 case 0x40: bval
= (bval
& 3) | (c
<< 2);
630 case 0x80: rval
= (rval
& 3) | (c
<< 2);
633 case 0xc0: gval
= (rval
& 3) | (c
<< 2);
637 gval
= (col
>> 8) & 0xff;
638 bval
= (col
>> 16) & 0xff;
645 bitmsk
= bitmsk
>> 1;
651 workptr
+= lineskip
* bmhd_bitplanes
;
653 } else if ((fmt
== ILBM_NORMAL
) || (fmt
== ILBM_EHB
)) {
654 if (fmt
== ILBM_EHB
) {
655 wxLogTrace(_T("iff"), _T("Doubling CMAP for EHB mode"));
657 for (int i
=0; i
<32; i
++) {
658 pal
[3*(i
+ 32) + 0] = pal
[3*i
+ 0] >> 1;
659 pal
[3*(i
+ 32) + 1] = pal
[3*i
+ 1] >> 1;
660 pal
[3*(i
+ 32) + 2] = pal
[3*i
+ 2] >> 1;
664 byte
*pic
= picptr
; // ptr to buffer
665 const byte
*workptr
= bodyptr
; // ptr to pic, planar format
667 if (bmhd_height
< height
) {
668 height
= bmhd_height
;
671 for (int i
=0; i
< height
; i
++) {
672 byte bitmsk
= 0x80; // left most bit (mask)
673 const byte
*workptr2
= workptr
; // work ptr to source
674 for (int j
=0; j
< bmhd_width
; j
++) {
677 const byte
*workptr3
= workptr2
; // 1st byte in 1st pln
679 for (int k
=0; k
< bmhd_bitplanes
; k
++) {
680 if (*workptr3
& bitmsk
) { // if bit set in this pln
681 col
= col
+ colbit
; // add bit to chunky byte
683 workptr3
+= lineskip
; // go to next line
684 colbit
<<= 1; // shift color bit
687 if (col
>= 0 && col
< colors
) {
688 pic
[0] = pal
[3*col
+ 0];
689 pic
[1] = pal
[3*col
+ 1];
690 pic
[2] = pal
[3*col
+ 2];
692 pic
[0] = pic
[1] = pic
[2] = 0;
695 bitmsk
= bitmsk
>> 1; // shift mask to next bit
696 if (bitmsk
== 0) { // if mask is zero
697 bitmsk
= 0x80; // reset mask
698 workptr2
++; // mv ptr to next byte
702 workptr
+= lineskip
* bmhd_bitplanes
; // to next line
705 break; // unknown format
708 m_image
->w
= bmhd_width
;
710 m_image
->transparent
= bmhd_transcol
;
712 wxLogTrace(_T("iff"), _T("Loaded IFF picture %s"),
713 truncated
? "truncated" : "completely");
715 return (truncated
? wxIFF_TRUNCATED
: wxIFF_OK
);
717 wxLogTrace(_T("iff"), _T("Skipping unknown chunk '%c%c%c%c'"),
718 *dataptr
, *(dataptr
+1), *(dataptr
+2), *(dataptr
+3));
720 dataptr
= dataptr
+ 8 + chunkLen
; // skip unknown chunk
725 return wxIFF_INVFORMAT
;
730 //-----------------------------------------------------------------------------
732 //-----------------------------------------------------------------------------
734 IMPLEMENT_DYNAMIC_CLASS(wxIFFHandler
, wxImageHandler
)
738 bool wxIFFHandler::LoadFile(wxImage
*image
, wxInputStream
& stream
,
739 bool verbose
, int WXUNUSED(index
))
745 decod
= new wxIFFDecoder(&stream
);
746 error
= decod
->ReadIFF();
748 if ((error
!= wxIFF_OK
) && (error
!= wxIFF_TRUNCATED
))
754 case wxIFF_INVFORMAT
:
755 wxLogError(_("IFF: error in IFF image format."));
758 wxLogError(_("IFF: not enough memory."));
761 wxLogError(_("IFF: unknown error!!!"));
769 if ((error
== wxIFF_TRUNCATED
) && verbose
)
771 wxLogError(_("IFF: data stream seems to be truncated."));
772 /* go on; image data is OK */
775 ok
= decod
->ConvertToImage(image
);
781 bool wxIFFHandler::SaveFile(wxImage
* WXUNUSED(image
),
782 wxOutputStream
& WXUNUSED(stream
), bool verbose
)
785 wxLogDebug(wxT("IFF: the handler is read-only!!"));
790 bool wxIFFHandler::DoCanRead(wxInputStream
& stream
)
792 wxIFFDecoder
decod(&stream
);
794 return decod
.CanRead();
797 #endif // wxUSE_STREAMS