1 /////////////////////////////////////////////////////////////////////////////
2 // Name:        src/common/imagiff.h
3 // Purpose:     wxImage handler for Amiga IFF images
4 // Author:      Steffen Gutmann, Thomas Meyer
5 // RCS-ID:      $Id: imagiff.cpp 38787 2006-04-18 07:24:35Z ABX $
6 // Copyright:   (c) Steffen Gutmann, 2002
7 // Licence:     wxWindows licence
8 /////////////////////////////////////////////////////////////////////////////
9 
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.
14 
15 // For compilers that support precompilation, includes "wx.h".
16 #include "wx/wxprec.h"
17 
18 #ifdef __BORLANDC__
19     #pragma hdrstop
20 #endif
21 
22 #if wxUSE_IMAGE && wxUSE_IFF
23 
24 #ifndef WX_PRECOMP
25     #include "wx/log.h"
26     #include "wx/intl.h"
27 #endif
28 
29 #include "wx/imagiff.h"
30 #include "wx/wfstream.h"
31 
32 #if wxUSE_PALETTE
33     #include "wx/palette.h"
34 #endif // wxUSE_PALETTE
35 
36 #include <stdlib.h>
37 #include <string.h>
38 
39 
40 // --------------------------------------------------------------------------
41 // Constants
42 // --------------------------------------------------------------------------
43 
44 // Error codes:
45 //  Note that the error code wxIFF_TRUNCATED means that the image itself
46 //  is most probably OK, but the decoder didn't reach the end of the data
47 //  stream; this means that if it was not reading directly from file,
48 //  the stream will not be correctly positioned.
49 //
50 
51 enum
52 {
53     wxIFF_OK = 0,                   /* everything was OK */
54     wxIFF_INVFORMAT,                /* error in iff header */
55     wxIFF_MEMERR,                   /* error allocating memory */
56     wxIFF_TRUNCATED                 /* file appears to be truncated */
57 };
58 
59 // --------------------------------------------------------------------------
60 // wxIFFDecoder class
61 // --------------------------------------------------------------------------
62 
63 // internal class for storing IFF image data
64 class IFFImage
65 {
66 public:
67     unsigned int w;                 /* width */
68     unsigned int h;                 /* height */
69     int transparent;                /* transparent color (-1 = none) */
70     int colors;                     /* number of colors */
71     unsigned char *p;               /* bitmap */
72     unsigned char *pal;             /* palette */
73 
IFFImage()74     IFFImage() : w(0), h(0), colors(0), p(0), pal(0) {}
~IFFImage()75     ~IFFImage() { delete [] p; delete [] pal; }
76 };
77 
78 class WXDLLEXPORT wxIFFDecoder
79 {
80 private:
81     IFFImage *m_image;        // image data
82     wxInputStream *m_f;       // input stream
83     unsigned char *databuf;
84     unsigned char *picptr;
85     unsigned char *decomp_mem;
86 
87     void Destroy();
88 
89 public:
90     // get data of current frame
91     unsigned char* GetData() const;
92     unsigned char* GetPalette() const;
93     int GetNumColors() const;
94     unsigned int GetWidth() const;
95     unsigned int GetHeight() const;
96     int GetTransparentColour() const;
97 
98     // constructor, destructor, etc.
99     wxIFFDecoder(wxInputStream *s);
~wxIFFDecoder()100     ~wxIFFDecoder() { Destroy(); }
101     bool CanRead();
102     int ReadIFF();
103     bool ConvertToImage(wxImage *image) const;
104 };
105 
106 
107 //---------------------------------------------------------------------------
108 // wxIFFDecoder constructor and destructor
109 //---------------------------------------------------------------------------
110 
wxIFFDecoder(wxInputStream * s)111 wxIFFDecoder::wxIFFDecoder(wxInputStream *s)
112 {
113     m_f = s;
114     m_image = 0;
115     databuf = 0;
116     decomp_mem = 0;
117 }
118 
Destroy()119 void wxIFFDecoder::Destroy()
120 {
121     delete m_image;
122     m_image = 0;
123     delete [] databuf;
124     databuf = 0;
125     delete [] decomp_mem;
126     decomp_mem = 0;
127 }
128 
129 //---------------------------------------------------------------------------
130 // Convert this image to a wxImage object
131 //---------------------------------------------------------------------------
132 
133 // This function was designed by Vaclav Slavik
134 
ConvertToImage(wxImage * image) const135 bool wxIFFDecoder::ConvertToImage(wxImage *image) const
136 {
137     // just in case...
138     image->Destroy();
139 
140     // create the image
141     image->Create(GetWidth(), GetHeight());
142 
143     if (!image->Ok())
144         return false;
145 
146     unsigned char *pal = GetPalette();
147     unsigned char *src = GetData();
148     unsigned char *dst = image->GetData();
149     int colors = GetNumColors();
150     int transparent = GetTransparentColour();
151     long i;
152 
153     // set transparent colour mask
154     if (transparent != -1)
155     {
156         for (i = 0; i < colors; i++)
157         {
158             if ((pal[3 * i + 0] == 255) &&
159                 (pal[3 * i + 1] == 0) &&
160                 (pal[3 * i + 2] == 255))
161             {
162                 pal[3 * i + 2] = 254;
163             }
164         }
165 
166         pal[3 * transparent + 0] = 255,
167         pal[3 * transparent + 1] = 0,
168         pal[3 * transparent + 2] = 255;
169 
170         image->SetMaskColour(255, 0, 255);
171     }
172     else
173         image->SetMask(false);
174 
175 #if wxUSE_PALETTE
176     if (pal && colors > 0)
177     {
178         unsigned char* r = new unsigned char[colors];
179         unsigned char* g = new unsigned char[colors];
180         unsigned char* b = new unsigned char[colors];
181 
182         for (i = 0; i < colors; i++)
183         {
184             r[i] = pal[3*i + 0];
185             g[i] = pal[3*i + 1];
186             b[i] = pal[3*i + 2];
187         }
188 
189         image->SetPalette(wxPalette(colors, r, g, b));
190 
191         delete [] r;
192         delete [] g;
193         delete [] b;
194     }
195 #endif // wxUSE_PALETTE
196 
197     // copy image data
198     for (i = 0; i < (long)(GetWidth() * GetHeight()); i++, src += 3, dst += 3)
199     {
200     dst[0] = src[0];
201     dst[1] = src[1];
202     dst[2] = src[2];
203     }
204 
205     return true;
206 }
207 
208 
209 //---------------------------------------------------------------------------
210 // Data accessors
211 //---------------------------------------------------------------------------
212 
213 // Get data for current frame
214 
GetData() const215 unsigned char* wxIFFDecoder::GetData() const    { return (m_image->p); }
GetPalette() const216 unsigned char* wxIFFDecoder::GetPalette() const { return (m_image->pal); }
GetNumColors() const217 int wxIFFDecoder::GetNumColors() const          { return m_image->colors; }
GetWidth() const218 unsigned int wxIFFDecoder::GetWidth() const     { return (m_image->w); }
GetHeight() const219 unsigned int wxIFFDecoder::GetHeight() const    { return (m_image->h); }
GetTransparentColour() const220 int wxIFFDecoder::GetTransparentColour() const { return m_image->transparent; }
221 
222 //---------------------------------------------------------------------------
223 // IFF reading and decoding
224 //---------------------------------------------------------------------------
225 
226 //
227 // CanRead:
228 //  Returns true if the file looks like a valid IFF, false otherwise.
229 //
CanRead()230 bool wxIFFDecoder::CanRead()
231 {
232     unsigned char buf[12];
233 
234     if ( !m_f->Read(buf, WXSIZEOF(buf)) )
235         return false;
236 
237     m_f->SeekI(-(wxFileOffset)WXSIZEOF(buf), wxFromCurrent);
238 
239     return (memcmp(buf, "FORM", 4) == 0) && (memcmp(buf+8, "ILBM", 4) == 0);
240 }
241 
242 
243 // ReadIFF:
244 // Based on xv source code by Thomas Meyer
245 // Permission for use in wxWidgets has been gratefully given.
246 
247 typedef unsigned char byte;
248 #define IFFDEBUG 0
249 
250 /*************************************************************************
251   void decomprle(source, destination, source length, buffer size)
252 
253   Decompress run-length encoded data from source to destination. Terminates
254   when source is decoded completely or destination buffer is full.
255 
256   The decruncher is as optimized as I could make it, without risking
257   safety in case of corrupt BODY chunks.
258 **************************************************************************/
259 
decomprle(const byte * sptr,byte * dptr,long slen,long dlen)260 static void decomprle(const byte *sptr, byte *dptr, long slen, long dlen)
261 {
262     byte codeByte, dataByte;
263 
264     while ((slen > 0) && (dlen > 0)) {
265     // read control byte
266     codeByte = *sptr++;
267 
268     if (codeByte < 0x80) {
269         codeByte++;
270         if ((slen > (long) codeByte) && (dlen >= (long) codeByte)) {
271         slen -= codeByte + 1;
272         dlen -= codeByte;
273         while (codeByte > 0) {
274             *dptr++ = *sptr++;
275             codeByte--;
276         }
277         }
278         else slen = 0;
279     }
280 
281     else if (codeByte > 0x80) {
282         codeByte = 0x81 - (codeByte & 0x7f);
283         if ((slen > (long) 0) && (dlen >= (long) codeByte)) {
284         dataByte = *sptr++;
285         slen -= 2;
286         dlen -= codeByte;
287         while (codeByte > 0) {
288             *dptr++ = dataByte;
289             codeByte--;
290         }
291         }
292         else slen = 0;
293     }
294     }
295 }
296 
297 /******************************************/
iff_getword(const byte * ptr)298 static unsigned int iff_getword(const byte *ptr)
299 {
300     unsigned int v;
301 
302     v = *ptr++;
303     v = (v << 8) + *ptr;
304     return v;
305 }
306 
307 /******************************************/
iff_getlong(const byte * ptr)308 static unsigned long iff_getlong(const byte *ptr)
309 {
310     unsigned long l;
311 
312     l = *ptr++;
313     l = (l << 8) + *ptr++;
314     l = (l << 8) + *ptr++;
315     l = (l << 8) + *ptr;
316     return l;
317 }
318 
319 // Define internal ILBM types
320 #define ILBM_NORMAL     0
321 #define ILBM_EHB        1
322 #define ILBM_HAM        2
323 #define ILBM_HAM8       3
324 #define ILBM_24BIT      4
325 
ReadIFF()326 int wxIFFDecoder::ReadIFF()
327 {
328     Destroy();
329 
330     m_image = new IFFImage();
331     if (m_image == 0) {
332     Destroy();
333     return wxIFF_MEMERR;
334     }
335 
336     // compute file length
337     wxFileOffset currentPos = m_f->TellI();
338     m_f->SeekI(0, wxFromEnd);
339     long filesize = m_f->TellI();
340     m_f->SeekI(currentPos, wxFromStart);
341 
342     // allocate memory for complete file
343     if ((databuf = new byte[filesize]) == 0) {
344     Destroy();
345     return wxIFF_MEMERR;
346     }
347 
348     m_f->Read(databuf, filesize);
349     const byte *dataend = databuf + filesize;
350 
351     // initialize work pointer. used to trace the buffer for IFF chunks
352     const byte *dataptr = databuf;
353 
354     // check for minmal size
355     if (dataptr + 12 > dataend) {
356     Destroy();
357     return wxIFF_INVFORMAT;
358     }
359 
360     // check if we really got an IFF file
361     if (strncmp((char *)dataptr, "FORM", 4) != 0) {
362     Destroy();
363     return wxIFF_INVFORMAT;
364     }
365 
366     dataptr = dataptr + 8;                  // skip ID and length of FORM
367 
368     // check if the IFF file is an ILBM (picture) file
369     if (strncmp((char *) dataptr, "ILBM", 4) != 0) {
370     Destroy();
371     return wxIFF_INVFORMAT;
372     }
373 
374     wxLogTrace(_T("iff"), _T("IFF ILBM file recognized"));
375 
376     dataptr = dataptr + 4;                                // skip ID
377 
378     //
379     // main decoding loop. searches IFF chunks and handles them.
380     // terminates when BODY chunk was found or dataptr ran over end of file
381     //
382     bool BMHDok = false, CMAPok = false, CAMGok = false;
383     int bmhd_width = 0, bmhd_height = 0, bmhd_bitplanes = 0, bmhd_transcol = -1;
384     byte bmhd_masking = 0, bmhd_compression = 0;
385     long camg_viewmode = 0;
386     int colors = 0;
387     while (dataptr + 8 <= dataend) {
388     // get chunk length and make even
389     size_t chunkLen = (iff_getlong(dataptr + 4) + 1) & 0xfffffffe;
390 #ifdef __VMS
391        // Silence compiler warning
392        int chunkLen_;
393        chunkLen_ = chunkLen;
394        if (chunkLen_ < 0) {     // format error?
395 #else
396        if (chunkLen < 0) {     // format error?
397 #endif
398          break;
399     }
400     bool truncated = (dataptr + 8 + chunkLen > dataend);
401 
402     if (strncmp((char *)dataptr, "BMHD", 4) == 0) { // BMHD chunk?
403         if (chunkLen < 12 + 2 || truncated) {
404         break;
405         }
406         bmhd_width = iff_getword(dataptr + 8);      // width of picture
407         bmhd_height= iff_getword(dataptr + 8 + 2);  // height of picture
408         bmhd_bitplanes = *(dataptr + 8 + 8);        // # of bitplanes
409         bmhd_masking  = *(dataptr + 8 + 9);
410         bmhd_compression = *(dataptr + 8 + 10);     // get compression
411         bmhd_transcol    = iff_getword(dataptr + 8 + 12);
412         BMHDok = true;                              // got BMHD
413         dataptr += 8 + chunkLen;                    // to next chunk
414     }
415     else if (strncmp((char *)dataptr, "CMAP", 4) == 0) { // CMAP ?
416         if (truncated) {
417         break;
418         }
419         const byte *cmapptr = dataptr + 8;
420         colors = chunkLen / 3;                  // calc no of colors
421 
422         delete m_image->pal;
423         m_image->pal = 0;
424         m_image->colors = colors;
425         if (colors > 0) {
426         m_image->pal = new byte[3*colors];
427         if (!m_image->pal) {
428             Destroy();
429             return wxIFF_MEMERR;
430         }
431 
432         // copy colors to color map
433         for (int i=0; i < colors; i++) {
434             m_image->pal[3*i + 0] = *cmapptr++;
435             m_image->pal[3*i + 1] = *cmapptr++;
436             m_image->pal[3*i + 2] = *cmapptr++;
437         }
438         }
439 
440         wxLogTrace(_T("iff"), _T("Read %d colors from IFF file."),
441             colors);
442 
443         CMAPok = true;                              // got CMAP
444         dataptr += 8 + chunkLen;                    // to next chunk
445     } else if (strncmp((char *)dataptr, "CAMG", 4) == 0) { // CAMG ?
446         if (chunkLen < 4 || truncated) {
447         break;
448         }
449         camg_viewmode = iff_getlong(dataptr + 8);   // get viewmodes
450         CAMGok = true;                              // got CAMG
451         dataptr += 8 + chunkLen;                    // to next chunk
452     }
453     else if (strncmp((char *)dataptr, "BODY", 4) == 0) { // BODY ?
454         if (!BMHDok) {                              // BMHD found?
455         break;
456         }
457         const byte *bodyptr = dataptr + 8;          // -> BODY data
458 
459         if (truncated) {
460         chunkLen = dataend - dataptr;
461         }
462 
463         //
464             // if BODY is compressed, allocate buffer for decrunched BODY
465         // and decompress it (run length encoding)
466         //
467         if (bmhd_compression == 1) {
468         // calc size of decrunch buffer - (size of the actual pic.
469         // decompressed in interleaved Amiga bitplane format)
470 
471         size_t decomp_bufsize = (((bmhd_width + 15) >> 4) << 1)
472             * bmhd_height * bmhd_bitplanes;
473 
474         if ((decomp_mem = new byte[decomp_bufsize]) == 0) {
475             Destroy();
476             return wxIFF_MEMERR;
477         }
478 
479         decomprle(bodyptr, decomp_mem, chunkLen, decomp_bufsize);
480         bodyptr = decomp_mem;                 // -> uncompressed BODY
481         chunkLen = decomp_bufsize;
482         delete [] databuf;
483         databuf = 0;
484         }
485 
486         // the following determines the type of the ILBM file.
487         // it's either NORMAL, EHB, HAM, HAM8 or 24BIT
488 
489         int fmt = ILBM_NORMAL;                 // assume normal ILBM
490         if (bmhd_bitplanes == 24) {
491         fmt = ILBM_24BIT;
492         } else if (bmhd_bitplanes == 8) {
493         if (CAMGok && (camg_viewmode & 0x800)) {
494             fmt = ILBM_HAM8;
495         }
496         } else if ((bmhd_bitplanes > 5) && CAMGok) {
497         if (camg_viewmode & 0x80) {
498             fmt = ILBM_EHB;
499         } else if (camg_viewmode & 0x800) {
500             fmt = ILBM_HAM;
501         }
502         }
503 
504         wxLogTrace(_T("iff"),
505             _T("LoadIFF: %s %dx%d, planes=%d (%d cols), comp=%d"),
506             (fmt==ILBM_NORMAL) ? "Normal ILBM" :
507             (fmt==ILBM_HAM)    ? "HAM ILBM" :
508             (fmt==ILBM_HAM8)   ? "HAM8 ILBM" :
509             (fmt==ILBM_EHB)    ? "EHB ILBM" :
510             (fmt==ILBM_24BIT)  ? "24BIT ILBM" : "unknown ILBM",
511             bmhd_width, bmhd_height, bmhd_bitplanes,
512             1<<bmhd_bitplanes, bmhd_compression);
513 
514         if ((fmt==ILBM_NORMAL) || (fmt==ILBM_EHB) || (fmt==ILBM_HAM)) {
515         wxLogTrace(_T("iff"),
516             _T("Converting CMAP from normal ILBM CMAP"));
517 
518         switch(fmt) {
519             case ILBM_NORMAL: colors = 1 << bmhd_bitplanes; break;
520             case ILBM_EHB:    colors = 32*2; break;
521             case ILBM_HAM:    colors = 16; break;
522         }
523 
524         if (colors > m_image->colors) {
525             byte *pal = new byte[colors*3];
526             if (!pal) {
527             Destroy();
528             return wxIFF_MEMERR;
529             }
530             int i;
531             for (i = 0; i < m_image->colors; i++) {
532             pal[3*i + 0] = m_image->pal[3*i + 0];
533             pal[3*i + 1] = m_image->pal[3*i + 1];
534             pal[3*i + 2] = m_image->pal[3*i + 2];
535             }
536             for (; i < colors; i++) {
537             pal[3*i + 0] = 0;
538             pal[3*i + 1] = 0;
539             pal[3*i + 2] = 0;
540             }
541             delete m_image->pal;
542             m_image->pal = pal;
543             m_image->colors = colors;
544         }
545 
546             for (int i=0; i < colors; i++) {
547             m_image->pal[3*i + 0] = (m_image->pal[3*i + 0] >> 4) * 17;
548             m_image->pal[3*i + 1] = (m_image->pal[3*i + 1] >> 4) * 17;
549             m_image->pal[3*i + 2] = (m_image->pal[3*i + 2] >> 4) * 17;
550         }
551         }
552 
553         m_image->p = new byte[bmhd_width * bmhd_height * 3];
554             byte *picptr = m_image->p;
555         if (!picptr) {
556         Destroy();
557         return wxIFF_MEMERR;
558         }
559 
560         byte *pal = m_image->pal;
561         int lineskip = ((bmhd_width + 15) >> 4) << 1;
562             int height = chunkLen / (lineskip * bmhd_bitplanes);
563 
564         if (bmhd_height < height) {
565             height = bmhd_height;
566         }
567 
568         if (fmt == ILBM_HAM || fmt == ILBM_HAM8 || fmt == ILBM_24BIT) {
569         byte *pic = picptr;
570         const byte *workptr = bodyptr;
571 
572         for (int i=0; i < height; i++) {
573             byte bitmsk = 0x80;
574             const byte *workptr2 = workptr;
575 
576             // at start of each line, init RGB values to background
577             byte rval = pal[0];
578             byte gval = pal[1];
579             byte bval = pal[2];
580 
581             for (int j=0; j < bmhd_width; j++) {
582             long col = 0;
583             long colbit = 1;
584             const byte *workptr3 = workptr2;
585             for (int k=0; k < bmhd_bitplanes; k++) {
586                 if (*workptr3 & bitmsk) {
587                 col += colbit;
588                 }
589                 workptr3 += lineskip;
590                 colbit <<= 1;
591             }
592 
593             if (fmt==ILBM_HAM) {
594                 int c = (col & 0x0f);
595                 switch (col & 0x30) {
596                 case 0x00: if (c >= 0 && c < colors) {
597                            rval = pal[3*c + 0];
598                            gval = pal[3*c + 1];
599                            bval = pal[3*c + 2];
600                        }
601                        break;
602 
603                 case 0x10: bval = c * 17;
604                        break;
605 
606                 case 0x20: rval = c * 17;
607                        break;
608 
609                 case 0x30: gval = c * 17;
610                        break;
611                 }
612             } else if (fmt == ILBM_HAM8) {
613                 int c = (col & 0x3f);
614                 switch(col & 0xc0) {
615                 case 0x00: if (c >= 0 && c < colors) {
616                            rval = pal[3*c + 0];
617                            gval = pal[3*c + 1];
618                            bval = pal[3*c + 2];
619                        }
620                        break;
621 
622                 case 0x40: bval = (bval & 3) | (c << 2);
623                        break;
624 
625                 case 0x80: rval = (rval & 3) | (c << 2);
626                        break;
627 
628                 case 0xc0: gval = (rval & 3) | (c << 2);
629                 }
630             } else {
631                 rval = col & 0xff;
632                 gval = (col >> 8) & 0xff;
633                 bval = (col >> 16) & 0xff;
634             }
635 
636             *pic++ = rval;
637             *pic++ = gval;
638             *pic++ = bval;
639 
640             bitmsk = bitmsk >> 1;
641             if (bitmsk == 0) {
642                 bitmsk = 0x80;
643                 workptr2++;
644             }
645             }
646             workptr += lineskip * bmhd_bitplanes;
647         }
648         }  else if ((fmt == ILBM_NORMAL) || (fmt == ILBM_EHB)) {
649         if (fmt == ILBM_EHB) {
650             wxLogTrace(_T("iff"), _T("Doubling CMAP for EHB mode"));
651 
652             for (int i=0; i<32; i++) {
653             pal[3*(i + 32) + 0] = pal[3*i + 0] >> 1;
654             pal[3*(i + 32) + 1] = pal[3*i + 1] >> 1;
655             pal[3*(i + 32) + 2] = pal[3*i + 2] >> 1;
656             }
657         }
658 
659         byte *pic = picptr;         // ptr to buffer
660         const byte *workptr = bodyptr;  // ptr to pic, planar format
661 
662         if (bmhd_height < height) {
663             height = bmhd_height;
664         }
665 
666         for (int i=0; i < height; i++) {
667             byte bitmsk = 0x80;                 // left most bit (mask)
668             const byte *workptr2 = workptr;     // work ptr to source
669             for (int j=0; j < bmhd_width; j++) {
670             long col = 0;
671             long colbit = 1;
672             const byte *workptr3 = workptr2;  // 1st byte in 1st pln
673 
674             for (int k=0; k < bmhd_bitplanes; k++) {
675                 if (*workptr3 & bitmsk) { // if bit set in this pln
676                 col = col + colbit; // add bit to chunky byte
677                 }
678                 workptr3 += lineskip;   // go to next line
679                 colbit <<= 1;           // shift color bit
680             }
681 
682             if (col >= 0 && col < colors) {
683                 pic[0] = pal[3*col + 0];
684                 pic[1] = pal[3*col + 1];
685                 pic[2] = pal[3*col + 2];
686             } else {
687                 pic[0] = pic[1] = pic[2] = 0;
688             }
689             pic += 3;
690             bitmsk = bitmsk >> 1;   // shift mask to next bit
691             if (bitmsk == 0) {      // if mask is zero
692                 bitmsk = 0x80;      // reset mask
693                 workptr2++;         // mv ptr to next byte
694             }
695             }
696 
697             workptr += lineskip * bmhd_bitplanes;  // to next line
698         }
699         } else {
700         break;      // unknown format
701         }
702 
703         m_image->w = bmhd_width;
704         m_image->h = height;
705         m_image->transparent = bmhd_transcol;
706 
707         wxLogTrace(_T("iff"), _T("Loaded IFF picture %s"),
708             truncated? "truncated" : "completely");
709 
710         return (truncated? wxIFF_TRUNCATED : wxIFF_OK);
711     } else {
712         wxLogTrace(_T("iff"), _T("Skipping unknown chunk '%c%c%c%c'"),
713                 *dataptr, *(dataptr+1), *(dataptr+2), *(dataptr+3));
714 
715         dataptr = dataptr + 8 + chunkLen;      // skip unknown chunk
716     }
717     }
718 
719     Destroy();
720     return wxIFF_INVFORMAT;
721 }
722 
723 
724 
725 //-----------------------------------------------------------------------------
726 // wxIFFHandler
727 //-----------------------------------------------------------------------------
728 
729 IMPLEMENT_DYNAMIC_CLASS(wxIFFHandler, wxImageHandler)
730 
731 #if wxUSE_STREAMS
732 
733 bool wxIFFHandler::LoadFile(wxImage *image, wxInputStream& stream,
734                             bool verbose, int WXUNUSED(index))
735 {
736     wxIFFDecoder *decod;
737     int error;
738     bool ok;
739 
740     decod = new wxIFFDecoder(&stream);
741     error = decod->ReadIFF();
742 
743     if ((error != wxIFF_OK) && (error != wxIFF_TRUNCATED))
744     {
745         if (verbose)
746         {
747             switch (error)
748             {
749                 case wxIFF_INVFORMAT:
750                     wxLogError(_("IFF: error in IFF image format."));
751                     break;
752                 case wxIFF_MEMERR:
753                     wxLogError(_("IFF: not enough memory."));
754                     break;
755                 default:
756                     wxLogError(_("IFF: unknown error!!!"));
757                     break;
758             }
759         }
760         delete decod;
761         return false;
762     }
763 
764     if ((error == wxIFF_TRUNCATED) && verbose)
765     {
766         wxLogError(_("IFF: data stream seems to be truncated."));
767         /* go on; image data is OK */
768     }
769 
770     ok = decod->ConvertToImage(image);
771     delete decod;
772 
773     return ok;
774 }
775 
776 bool wxIFFHandler::SaveFile(wxImage * WXUNUSED(image),
777                             wxOutputStream& WXUNUSED(stream), bool verbose)
778 {
779     if (verbose)
780         wxLogDebug(wxT("IFF: the handler is read-only!!"));
781 
782     return false;
783 }
784 
785 bool wxIFFHandler::DoCanRead(wxInputStream& stream)
786 {
787     wxIFFDecoder decod(&stream);
788 
789     return decod.CanRead();
790 }
791 
792 #endif // wxUSE_STREAMS
793 
794 #endif // wxUSE_IFF
795