]>
git.saurik.com Git - wxWidgets.git/blob - src/common/iffdecod.cpp
2 // iffdecod.cc - image handler for IFF/ILBM images
3 // parts of the source are based on xviff by Thomas Meyer
4 // Permission for use in wxWindows has been gratefully given.
6 // (c) Steffen Gutmann, 2002
8 // Creation date: 08.01.2002
9 // Last modified: 12.01.2002
13 #pragma implementation "iffdecod.h"
16 // For compilers that support precompilation, includes "wx.h".
17 #include "wx/wxprec.h"
30 #include "wx/iffdecod.h"
32 #if wxUSE_STREAMS && wxUSE_IFF
34 //---------------------------------------------------------------------------
35 // wxIFFDecoder constructor and destructor
36 //---------------------------------------------------------------------------
38 wxIFFDecoder::wxIFFDecoder(wxInputStream
*s
)
46 void wxIFFDecoder::Destroy()
56 //---------------------------------------------------------------------------
57 // Convert this image to a wxImage object
58 //---------------------------------------------------------------------------
60 // This function was designed by Vaclav Slavik
62 bool wxIFFDecoder::ConvertToImage(wxImage
*image
) const
68 image
->Create(GetWidth(), GetHeight());
73 unsigned char *pal
= GetPalette();
74 unsigned char *src
= GetData();
75 unsigned char *dst
= image
->GetData();
76 int colors
= GetNumColors();
77 int transparent
= GetTransparentColour();
80 // set transparent colour mask
81 if (transparent
!= -1)
83 for (i
= 0; i
< colors
; i
++)
85 if ((pal
[3 * i
+ 0] == 255) &&
86 (pal
[3 * i
+ 1] == 0) &&
87 (pal
[3 * i
+ 2] == 255))
93 pal
[3 * transparent
+ 0] = 255,
94 pal
[3 * transparent
+ 1] = 0,
95 pal
[3 * transparent
+ 2] = 255;
97 image
->SetMaskColour(255, 0, 255);
100 image
->SetMask(FALSE
);
103 if (pal
&& colors
> 0)
105 unsigned char* r
= new unsigned char[colors
];
106 unsigned char* g
= new unsigned char[colors
];
107 unsigned char* b
= new unsigned char[colors
];
109 for (i
= 0; i
< colors
; i
++)
116 image
->SetPalette(wxPalette(colors
, r
, g
, b
));
122 #endif // wxUSE_PALETTE
125 for (i
= 0; i
< (long)(GetWidth() * GetHeight()); i
++, src
+= 3, dst
+= 3)
136 //---------------------------------------------------------------------------
138 //---------------------------------------------------------------------------
140 // Get data for current frame
142 unsigned char* wxIFFDecoder::GetData() const { return (m_image
->p
); }
143 unsigned char* wxIFFDecoder::GetPalette() const { return (m_image
->pal
); }
144 int wxIFFDecoder::GetNumColors() const { return m_image
->colors
; }
145 unsigned int wxIFFDecoder::GetWidth() const { return (m_image
->w
); }
146 unsigned int wxIFFDecoder::GetHeight() const { return (m_image
->h
); }
147 int wxIFFDecoder::GetTransparentColour() const { return m_image
->transparent
; }
149 //---------------------------------------------------------------------------
150 // IFF reading and decoding
151 //---------------------------------------------------------------------------
155 // Returns TRUE if the file looks like a valid IFF, FALSE otherwise.
157 bool wxIFFDecoder::CanRead()
159 unsigned char buf
[12] = "";
162 m_f
->SeekI(-12, wxFromCurrent
);
164 return (memcmp(buf
, "FORM", 4) == 0 && memcmp(buf
+8, "ILBM", 4) == 0);
169 // Based on xv source code by Thomas Meyer
170 // Permission for use in wxWindows has been gratefully given.
172 typedef unsigned char byte
;
175 /*************************************************************************
176 void decomprle(source, destination, source length, buffer size)
178 Decompress run-length encoded data from source to destination. Terminates
179 when source is decoded completely or destination buffer is full.
181 The decruncher is as optimized as I could make it, without risking
182 safety in case of corrupt BODY chunks.
183 **************************************************************************/
185 static void decomprle(const byte
*sptr
, byte
*dptr
, long slen
, long dlen
)
187 byte codeByte
, dataByte
;
189 while ((slen
> 0) && (dlen
> 0)) {
193 if (codeByte
< 0x80) {
195 if ((slen
> (long) codeByte
) && (dlen
>= (long) codeByte
)) {
196 slen
-= codeByte
+ 1;
198 while (codeByte
> 0) {
206 else if (codeByte
> 0x80) {
207 codeByte
= 0x81 - (codeByte
& 0x7f);
208 if ((slen
> (long) 0) && (dlen
>= (long) codeByte
)) {
212 while (codeByte
> 0) {
222 /******************************************/
223 static unsigned int iff_getword(const byte
*ptr
)
232 /******************************************/
233 static unsigned long iff_getlong(const byte
*ptr
)
238 l
= (l
<< 8) + *ptr
++;
239 l
= (l
<< 8) + *ptr
++;
244 // Define internal ILBM types
245 #define ILBM_NORMAL 0
251 int wxIFFDecoder::ReadIFF()
255 m_image
= new IFFImage();
261 // compute file length
262 off_t currentPos
= m_f
->TellI();
263 m_f
->SeekI(0, wxFromEnd
);
264 long filesize
= m_f
->TellI();
265 m_f
->SeekI(currentPos
, wxFromStart
);
267 // allocate memory for complete file
268 if ((databuf
= new byte
[filesize
]) == 0) {
273 m_f
->Read(databuf
, filesize
);
274 const byte
*dataend
= databuf
+ filesize
;
276 // initialize work pointer. used to trace the buffer for IFF chunks
277 const byte
*dataptr
= databuf
;
279 // check for minmal size
280 if (dataptr
+ 12 > dataend
) {
282 return wxIFF_INVFORMAT
;
285 // check if we really got an IFF file
286 if (strncmp((char *)dataptr
, "FORM", 4) != 0) {
288 return wxIFF_INVFORMAT
;
291 dataptr
= dataptr
+ 8; // skip ID and length of FORM
293 // check if the IFF file is an ILBM (picture) file
294 if (strncmp((char *) dataptr
, "ILBM", 4) != 0) {
296 return wxIFF_INVFORMAT
;
299 wxLogTrace(_T("iff"), _T("IFF ILBM file recognized"));
301 dataptr
= dataptr
+ 4; // skip ID
304 // main decoding loop. searches IFF chunks and handles them.
305 // terminates when BODY chunk was found or dataptr ran over end of file
307 bool BMHDok
= false, CMAPok
= false, CAMGok
= false;
308 int bmhd_width
= 0, bmhd_height
= 0, bmhd_bitplanes
= 0, bmhd_transcol
= -1;
309 byte bmhd_masking
= 0, bmhd_compression
= 0;
310 long camg_viewmode
= 0;
312 while (dataptr
+ 8 <= dataend
) {
313 // get chunk length and make even
314 size_t chunkLen
= (iff_getlong(dataptr
+ 4) + 1) & 0xfffffffe;
316 // Silence compiler warning
318 chunkLen_
= chunkLen
;
319 if (chunkLen_
< 0) { // format error?
321 if (chunkLen
< 0) { // format error?
325 bool truncated
= (dataptr
+ 8 + chunkLen
> dataend
);
327 if (strncmp((char *)dataptr
, "BMHD", 4) == 0) { // BMHD chunk?
328 if (chunkLen
< 12 + 2 || truncated
) {
331 bmhd_width
= iff_getword(dataptr
+ 8); // width of picture
332 bmhd_height
= iff_getword(dataptr
+ 8 + 2); // height of picture
333 bmhd_bitplanes
= *(dataptr
+ 8 + 8); // # of bitplanes
334 bmhd_masking
= *(dataptr
+ 8 + 9);
335 bmhd_compression
= *(dataptr
+ 8 + 10); // get compression
336 bmhd_transcol
= iff_getword(dataptr
+ 8 + 12);
337 BMHDok
= true; // got BMHD
338 dataptr
+= 8 + chunkLen
; // to next chunk
340 else if (strncmp((char *)dataptr
, "CMAP", 4) == 0) { // CMAP ?
344 const byte
*cmapptr
= dataptr
+ 8;
345 colors
= chunkLen
/ 3; // calc no of colors
349 m_image
->colors
= colors
;
351 m_image
->pal
= new byte
[3*colors
];
357 // copy colors to color map
358 for (int i
=0; i
< colors
; i
++) {
359 m_image
->pal
[3*i
+ 0] = *cmapptr
++;
360 m_image
->pal
[3*i
+ 1] = *cmapptr
++;
361 m_image
->pal
[3*i
+ 2] = *cmapptr
++;
365 wxLogTrace(_T("iff"), _T("Read %d colors from IFF file."),
368 CMAPok
= true; // got CMAP
369 dataptr
+= 8 + chunkLen
; // to next chunk
370 } else if (strncmp((char *)dataptr
, "CAMG", 4) == 0) { // CAMG ?
371 if (chunkLen
< 4 || truncated
) {
374 camg_viewmode
= iff_getlong(dataptr
+ 8); // get viewmodes
375 CAMGok
= true; // got CAMG
376 dataptr
+= 8 + chunkLen
; // to next chunk
378 else if (strncmp((char *)dataptr
, "BODY", 4) == 0) { // BODY ?
379 if (!BMHDok
) { // BMHD found?
382 const byte
*bodyptr
= dataptr
+ 8; // -> BODY data
385 chunkLen
= dataend
- dataptr
;
389 // if BODY is compressed, allocate buffer for decrunched BODY
390 // and decompress it (run length encoding)
392 if (bmhd_compression
== 1) {
393 // calc size of decrunch buffer - (size of the actual pic.
394 // decompressed in interleaved Amiga bitplane format)
396 size_t decomp_bufsize
= (((bmhd_width
+ 15) >> 4) << 1)
397 * bmhd_height
* bmhd_bitplanes
;
399 if ((decomp_mem
= new byte
[decomp_bufsize
]) == 0) {
404 decomprle(bodyptr
, decomp_mem
, chunkLen
, decomp_bufsize
);
405 bodyptr
= decomp_mem
; // -> uncompressed BODY
406 chunkLen
= decomp_bufsize
;
411 // the following determines the type of the ILBM file.
412 // it's either NORMAL, EHB, HAM, HAM8 or 24BIT
414 int fmt
= ILBM_NORMAL
; // assume normal ILBM
415 if (bmhd_bitplanes
== 24) {
417 } else if (bmhd_bitplanes
== 8) {
418 if (CAMGok
&& (camg_viewmode
& 0x800)) {
421 } else if ((bmhd_bitplanes
> 5) && CAMGok
) {
422 if (camg_viewmode
& 0x80) {
424 } else if (camg_viewmode
& 0x800) {
429 wxLogTrace(_T("iff"),
430 _T("LoadIFF: %s %dx%d, planes=%d (%d cols), comp=%d"),
431 (fmt
==ILBM_NORMAL
) ? "Normal ILBM" :
432 (fmt
==ILBM_HAM
) ? "HAM ILBM" :
433 (fmt
==ILBM_HAM8
) ? "HAM8 ILBM" :
434 (fmt
==ILBM_EHB
) ? "EHB ILBM" :
435 (fmt
==ILBM_24BIT
) ? "24BIT ILBM" : "unknown ILBM",
436 bmhd_width
, bmhd_height
, bmhd_bitplanes
,
437 1<<bmhd_bitplanes
, bmhd_compression
);
439 if ((fmt
==ILBM_NORMAL
) || (fmt
==ILBM_EHB
) || (fmt
==ILBM_HAM
)) {
440 wxLogTrace(_T("iff"),
441 _T("Converting CMAP from normal ILBM CMAP"));
444 case ILBM_NORMAL
: colors
= 1 << bmhd_bitplanes
; break;
445 case ILBM_EHB
: colors
= 32*2; break;
446 case ILBM_HAM
: colors
= 16; break;
449 if (colors
> m_image
->colors
) {
450 byte
*pal
= new byte
[colors
*3];
456 for (i
= 0; i
< m_image
->colors
; i
++) {
457 pal
[3*i
+ 0] = m_image
->pal
[3*i
+ 0];
458 pal
[3*i
+ 1] = m_image
->pal
[3*i
+ 1];
459 pal
[3*i
+ 2] = m_image
->pal
[3*i
+ 2];
461 for (; i
< colors
; i
++) {
468 m_image
->colors
= colors
;
471 for (int i
=0; i
< colors
; i
++) {
472 m_image
->pal
[3*i
+ 0] = (m_image
->pal
[3*i
+ 0] >> 4) * 17;
473 m_image
->pal
[3*i
+ 1] = (m_image
->pal
[3*i
+ 1] >> 4) * 17;
474 m_image
->pal
[3*i
+ 2] = (m_image
->pal
[3*i
+ 2] >> 4) * 17;
478 m_image
->p
= new byte
[bmhd_width
* bmhd_height
* 3];
479 byte
*picptr
= m_image
->p
;
485 byte
*pal
= m_image
->pal
;
486 int lineskip
= ((bmhd_width
+ 15) >> 4) << 1;
487 int height
= chunkLen
/ (lineskip
* bmhd_bitplanes
);
489 if (bmhd_height
< height
) {
490 height
= bmhd_height
;
493 if (fmt
== ILBM_HAM
|| fmt
== ILBM_HAM8
|| fmt
== ILBM_24BIT
) {
495 const byte
*workptr
= bodyptr
;
497 for (int i
=0; i
< height
; i
++) {
499 const byte
*workptr2
= workptr
;
501 // at start of each line, init RGB values to background
506 for (int j
=0; j
< bmhd_width
; j
++) {
509 const byte
*workptr3
= workptr2
;
510 for (int k
=0; k
< bmhd_bitplanes
; k
++) {
511 if (*workptr3
& bitmsk
) {
514 workptr3
+= lineskip
;
519 int c
= (col
& 0x0f);
520 switch (col
& 0x30) {
521 case 0x00: if (c
>= 0 && c
< colors
) {
528 case 0x10: bval
= c
* 17;
531 case 0x20: rval
= c
* 17;
534 case 0x30: gval
= c
* 17;
537 } else if (fmt
== ILBM_HAM8
) {
538 int c
= (col
& 0x3f);
540 case 0x00: if (c
>= 0 && c
< colors
) {
547 case 0x40: bval
= (bval
& 3) | (c
<< 2);
550 case 0x80: rval
= (rval
& 3) | (c
<< 2);
553 case 0xc0: gval
= (rval
& 3) | (c
<< 2);
557 gval
= (col
>> 8) & 0xff;
558 bval
= (col
>> 16) & 0xff;
565 bitmsk
= bitmsk
>> 1;
571 workptr
+= lineskip
* bmhd_bitplanes
;
573 } else if ((fmt
== ILBM_NORMAL
) || (fmt
== ILBM_EHB
)) {
574 if (fmt
== ILBM_EHB
) {
575 wxLogTrace(_T("iff"), _T("Doubling CMAP for EHB mode"));
577 for (int i
=0; i
<32; i
++) {
578 pal
[3*(i
+ 32) + 0] = pal
[3*i
+ 0] >> 1;
579 pal
[3*(i
+ 32) + 1] = pal
[3*i
+ 1] >> 1;
580 pal
[3*(i
+ 32) + 2] = pal
[3*i
+ 2] >> 1;
584 byte
*pic
= picptr
; // ptr to buffer
585 const byte
*workptr
= bodyptr
; // ptr to pic, planar format
587 if (bmhd_height
< height
) {
588 height
= bmhd_height
;
591 for (int i
=0; i
< height
; i
++) {
592 byte bitmsk
= 0x80; // left most bit (mask)
593 const byte
*workptr2
= workptr
; // work ptr to source
594 for (int j
=0; j
< bmhd_width
; j
++) {
597 const byte
*workptr3
= workptr2
; // 1st byte in 1st pln
599 for (int k
=0; k
< bmhd_bitplanes
; k
++) {
600 if (*workptr3
& bitmsk
) { // if bit set in this pln
601 col
= col
+ colbit
; // add bit to chunky byte
603 workptr3
+= lineskip
; // go to next line
604 colbit
<<= 1; // shift color bit
607 if (col
>= 0 && col
< colors
) {
608 pic
[0] = pal
[3*col
+ 0];
609 pic
[1] = pal
[3*col
+ 1];
610 pic
[2] = pal
[3*col
+ 2];
612 pic
[0] = pic
[1] = pic
[2] = 0;
615 bitmsk
= bitmsk
>> 1; // shift mask to next bit
616 if (bitmsk
== 0) { // if mask is zero
617 bitmsk
= 0x80; // reset mask
618 workptr2
++; // mv ptr to next byte
622 workptr
+= lineskip
* bmhd_bitplanes
; // to next line
625 break; // unknown format
628 m_image
->w
= bmhd_width
;
630 m_image
->transparent
= bmhd_transcol
;
632 wxLogTrace(_T("iff"), _T("Loaded IFF picture %s"),
633 truncated
? "truncated" : "completely");
635 return (truncated
? wxIFF_TRUNCATED
: wxIFF_OK
);
637 wxLogTrace(_T("iff"), _T("Skipping unknown chunk '%c%c%c%c'"),
638 *dataptr
, *(dataptr
+1), *(dataptr
+2), *(dataptr
+3));
640 dataptr
= dataptr
+ 8 + chunkLen
; // skip unknown chunk
645 return wxIFF_INVFORMAT
;
648 #endif // wxUSE_STREAMS && wxUSE_IFF