]> git.saurik.com Git - wxWidgets.git/blob - src/common/iffdecod.cpp
Added IFF handler.
[wxWidgets.git] / src / common / iffdecod.cpp
1 //
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.
5 //
6 // (c) Steffen Gutmann, 2002
7 //
8 // Creation date: 08.01.2002
9 // Last modified: 12.01.2002
10 //
11
12 #ifdef __GNUG__
13 #pragma implementation "iffdecod.h"
14 #endif
15
16 // For compilers that support precompilation, includes "wx.h".
17 #include "wx/wxprec.h"
18
19 #ifdef __BORLANDC__
20 # pragma hdrstop
21 #endif
22
23 #ifndef WX_PRECOMP
24 # include "wx/defs.h"
25 # include "wx/log.h"
26 #endif
27
28 #include <stdlib.h>
29 #include <string.h>
30 #include "wx/iffdecod.h"
31
32 #if wxUSE_STREAMS && wxUSE_IFF
33
34 //---------------------------------------------------------------------------
35 // wxIFFDecoder constructor and destructor
36 //---------------------------------------------------------------------------
37
38 wxIFFDecoder::wxIFFDecoder(wxInputStream *s)
39 {
40 m_f = s;
41 m_image = 0;
42 databuf = 0;
43 decomp_mem = 0;
44 }
45
46 void wxIFFDecoder::Destroy()
47 {
48 delete m_image;
49 m_image = 0;
50 delete [] databuf;
51 databuf = 0;
52 delete [] decomp_mem;
53 decomp_mem = 0;
54 }
55
56 //---------------------------------------------------------------------------
57 // Convert this image to a wxImage object
58 //---------------------------------------------------------------------------
59
60 // This function was designed by Vaclav Slavik
61
62 bool wxIFFDecoder::ConvertToImage(wxImage *image) const
63 {
64 // just in case...
65 image->Destroy();
66
67 // create the image
68 image->Create(GetWidth(), GetHeight());
69
70 if (!image->Ok())
71 return FALSE;
72
73 unsigned char *pal = GetPalette();
74 unsigned char *src = GetData();
75 unsigned char *dst = image->GetData();
76 int colors = GetNumColors();
77 int transparent = GetTransparentColour();
78 long i;
79
80 // set transparent colour mask
81 if (transparent != -1)
82 {
83 for (i = 0; i < colors; i++)
84 {
85 if ((pal[3 * i + 0] == 255) &&
86 (pal[3 * i + 1] == 0) &&
87 (pal[3 * i + 2] == 255))
88 {
89 pal[3 * i + 2] = 254;
90 }
91 }
92
93 pal[3 * transparent + 0] = 255,
94 pal[3 * transparent + 1] = 0,
95 pal[3 * transparent + 2] = 255;
96
97 image->SetMaskColour(255, 0, 255);
98 }
99 else
100 image->SetMask(FALSE);
101
102 #if wxUSE_PALETTE
103 if (pal && colors > 0)
104 {
105 unsigned char r[colors];
106 unsigned char g[colors];
107 unsigned char b[colors];
108
109 for (i = 0; i < colors; i++)
110 {
111 r[i] = pal[3*i + 0];
112 g[i] = pal[3*i + 1];
113 b[i] = pal[3*i + 2];
114 }
115
116 image->SetPalette(wxPalette(colors, r, g, b));
117 }
118 #endif // wxUSE_PALETTE
119
120 // copy image data
121 for (i = 0; i < (long)(GetWidth() * GetHeight()); i++, src += 3, dst += 3)
122 {
123 dst[0] = src[0];
124 dst[1] = src[1];
125 dst[2] = src[2];
126 }
127
128 return TRUE;
129 }
130
131
132 //---------------------------------------------------------------------------
133 // Data accessors
134 //---------------------------------------------------------------------------
135
136 // Get data for current frame
137
138 unsigned char* wxIFFDecoder::GetData() const { return (m_image->p); }
139 unsigned char* wxIFFDecoder::GetPalette() const { return (m_image->pal); }
140 int wxIFFDecoder::GetNumColors() const { return m_image->colors; }
141 unsigned int wxIFFDecoder::GetWidth() const { return (m_image->w); }
142 unsigned int wxIFFDecoder::GetHeight() const { return (m_image->h); }
143 int wxIFFDecoder::GetTransparentColour() const { return m_image->transparent; }
144
145 //---------------------------------------------------------------------------
146 // IFF reading and decoding
147 //---------------------------------------------------------------------------
148
149 //
150 // CanRead:
151 // Returns TRUE if the file looks like a valid IFF, FALSE otherwise.
152 //
153 bool wxIFFDecoder::CanRead()
154 {
155 unsigned char buf[12] = "";
156
157 m_f->Read(buf, 12);
158 m_f->SeekI(-12, wxFromCurrent);
159
160 return (memcmp(buf, "FORM", 4) == 0 && memcmp(buf+8, "ILBM", 4) == 0);
161 }
162
163
164 // ReadIFF:
165 // Based on xv source code by Thomas Meyer
166 // Permission for use in wxWindows has been gratefully given.
167
168 typedef unsigned char byte;
169 #define IFFDEBUG 0
170
171 /*************************************************************************
172 void decomprle(source, destination, source length, buffer size)
173
174 Decompress run-length encoded data from source to destination. Terminates
175 when source is decoded completely or destination buffer is full.
176
177 The decruncher is as optimized as I could make it, without risking
178 safety in case of corrupt BODY chunks.
179 **************************************************************************/
180
181 static void decomprle(const byte *sptr, byte *dptr, long slen, long dlen)
182 {
183 byte codeByte, dataByte;
184
185 while ((slen > 0) && (dlen > 0)) {
186 // read control byte
187 codeByte = *sptr++;
188
189 if (codeByte < 0x80) {
190 codeByte++;
191 if ((slen > (long) codeByte) && (dlen >= (long) codeByte)) {
192 slen -= codeByte + 1;
193 dlen -= codeByte;
194 while (codeByte > 0) {
195 *dptr++ = *sptr++;
196 codeByte--;
197 }
198 }
199 else slen = 0;
200 }
201
202 else if (codeByte > 0x80) {
203 codeByte = 0x81 - (codeByte & 0x7f);
204 if ((slen > (long) 0) && (dlen >= (long) codeByte)) {
205 dataByte = *sptr++;
206 slen -= 2;
207 dlen -= codeByte;
208 while (codeByte > 0) {
209 *dptr++ = dataByte;
210 codeByte--;
211 }
212 }
213 else slen = 0;
214 }
215 }
216 }
217
218 /******************************************/
219 static unsigned int iff_getword(const byte *ptr)
220 {
221 unsigned int v;
222
223 v = *ptr++;
224 v = (v << 8) + *ptr;
225 return v;
226 }
227
228 /******************************************/
229 static unsigned long iff_getlong(const byte *ptr)
230 {
231 unsigned long l;
232
233 l = *ptr++;
234 l = (l << 8) + *ptr++;
235 l = (l << 8) + *ptr++;
236 l = (l << 8) + *ptr;
237 return l;
238 }
239
240 // Define internal ILBM types
241 #define ILBM_NORMAL 0
242 #define ILBM_EHB 1
243 #define ILBM_HAM 2
244 #define ILBM_HAM8 3
245 #define ILBM_24BIT 4
246
247 int wxIFFDecoder::ReadIFF()
248 {
249 Destroy();
250
251 m_image = new IFFImage();
252 if (m_image == 0) {
253 Destroy();
254 return wxIFF_MEMERR;
255 }
256
257 // compute file length
258 off_t currentPos = m_f->TellI();
259 m_f->SeekI(0, wxFromEnd);
260 long filesize = m_f->TellI();
261 m_f->SeekI(currentPos, wxFromStart);
262
263 // allocate memory for complete file
264 if ((databuf = new byte[filesize]) == 0) {
265 Destroy();
266 return wxIFF_MEMERR;
267 }
268
269 m_f->Read(databuf, filesize);
270 const byte *dataend = databuf + filesize;
271
272 // initialize work pointer. used to trace the buffer for IFF chunks
273 const byte *dataptr = databuf;
274
275 // check for minmal size
276 if (dataptr + 12 > dataend) {
277 Destroy();
278 return wxIFF_INVFORMAT;
279 }
280
281 // check if we really got an IFF file
282 if (strncmp((char *)dataptr, "FORM", 4) != 0) {
283 Destroy();
284 return wxIFF_INVFORMAT;
285 }
286
287 dataptr = dataptr + 8; // skip ID and length of FORM
288
289 // check if the IFF file is an ILBM (picture) file
290 if (strncmp((char *) dataptr, "ILBM", 4) != 0) {
291 Destroy();
292 return wxIFF_INVFORMAT;
293 }
294
295 wxLogTrace(_T("iff"), _T("IFF ILBM file recognized"));
296
297 dataptr = dataptr + 4; // skip ID
298
299 //
300 // main decoding loop. searches IFF chunks and handles them.
301 // terminates when BODY chunk was found or dataptr ran over end of file
302 //
303 bool BMHDok = false, CMAPok = false, CAMGok = false;
304 int bmhd_width = 0, bmhd_height = 0, bmhd_bitplanes = 0, bmhd_transcol = -1;
305 byte bmhd_masking = 0, bmhd_compression = 0;
306 long camg_viewmode = 0;
307 int colors = 0;
308 while (dataptr + 8 <= dataend) {
309 // get chunk length and make even
310 size_t chunkLen = (iff_getlong(dataptr + 4) + 1) & 0xfffffffe;
311 if (chunkLen < 0) { // format error?
312 break;
313 }
314 bool truncated = (dataptr + 8 + chunkLen > dataend);
315
316 if (strncmp((char *)dataptr, "BMHD", 4) == 0) { // BMHD chunk?
317 if (chunkLen < 12 + 2 || truncated) {
318 break;
319 }
320 bmhd_width = iff_getword(dataptr + 8); // width of picture
321 bmhd_height= iff_getword(dataptr + 8 + 2); // height of picture
322 bmhd_bitplanes = *(dataptr + 8 + 8); // # of bitplanes
323 bmhd_masking = *(dataptr + 8 + 9);
324 bmhd_compression = *(dataptr + 8 + 10); // get compression
325 bmhd_transcol = iff_getword(dataptr + 8 + 12);
326 BMHDok = true; // got BMHD
327 dataptr += 8 + chunkLen; // to next chunk
328 }
329 else if (strncmp((char *)dataptr, "CMAP", 4) == 0) { // CMAP ?
330 if (truncated) {
331 break;
332 }
333 const byte *cmapptr = dataptr + 8;
334 colors = chunkLen / 3; // calc no of colors
335
336 delete m_image->pal;
337 m_image->pal = 0;
338 m_image->colors = colors;
339 if (colors > 0) {
340 m_image->pal = new byte[3*colors];
341 if (!m_image->pal) {
342 Destroy();
343 return wxIFF_MEMERR;
344 }
345
346 // copy colors to color map
347 for (int i=0; i < colors; i++) {
348 m_image->pal[3*i + 0] = *cmapptr++;
349 m_image->pal[3*i + 1] = *cmapptr++;
350 m_image->pal[3*i + 2] = *cmapptr++;
351 }
352 }
353
354 wxLogTrace(_T("iff"), _T("Read %d colors from IFF file."),
355 colors);
356
357 CMAPok = true; // got CMAP
358 dataptr += 8 + chunkLen; // to next chunk
359 } else if (strncmp((char *)dataptr, "CAMG", 4) == 0) { // CAMG ?
360 if (chunkLen < 4 || truncated) {
361 break;
362 }
363 camg_viewmode = iff_getlong(dataptr + 8); // get viewmodes
364 CAMGok = true; // got CAMG
365 dataptr += 8 + chunkLen; // to next chunk
366 }
367 else if (strncmp((char *)dataptr, "BODY", 4) == 0) { // BODY ?
368 if (!BMHDok) { // BMHD found?
369 break;
370 }
371 const byte *bodyptr = dataptr + 8; // -> BODY data
372
373 if (truncated) {
374 chunkLen = dataend - dataptr;
375 }
376
377 //
378 // if BODY is compressed, allocate buffer for decrunched BODY
379 // and decompress it (run length encoding)
380 //
381 if (bmhd_compression == 1) {
382 // calc size of decrunch buffer - (size of the actual pic.
383 // decompressed in interleaved Amiga bitplane format)
384
385 size_t decomp_bufsize = (((bmhd_width + 15) >> 4) << 1)
386 * bmhd_height * bmhd_bitplanes;
387
388 if ((decomp_mem = new byte[decomp_bufsize]) == 0) {
389 Destroy();
390 return wxIFF_MEMERR;
391 }
392
393 decomprle(bodyptr, decomp_mem, chunkLen, decomp_bufsize);
394 bodyptr = decomp_mem; // -> uncompressed BODY
395 chunkLen = decomp_bufsize;
396 delete [] databuf;
397 databuf = 0;
398 }
399
400 // the following determines the type of the ILBM file.
401 // it's either NORMAL, EHB, HAM, HAM8 or 24BIT
402
403 int fmt = ILBM_NORMAL; // assume normal ILBM
404 if (bmhd_bitplanes == 24) {
405 fmt = ILBM_24BIT;
406 } else if (bmhd_bitplanes == 8) {
407 if (CAMGok && (camg_viewmode & 0x800)) {
408 fmt = ILBM_HAM8;
409 }
410 } else if ((bmhd_bitplanes > 5) && CAMGok) {
411 if (camg_viewmode & 0x80) {
412 fmt = ILBM_EHB;
413 } else if (camg_viewmode & 0x800) {
414 fmt = ILBM_HAM;
415 }
416 }
417
418 wxLogTrace(_T("iff"),
419 _T("LoadIFF: %s %dx%d, planes=%d (%d cols), comp=%d"),
420 (fmt==ILBM_NORMAL) ? "Normal ILBM" :
421 (fmt==ILBM_HAM) ? "HAM ILBM" :
422 (fmt==ILBM_HAM8) ? "HAM8 ILBM" :
423 (fmt==ILBM_EHB) ? "EHB ILBM" :
424 (fmt==ILBM_24BIT) ? "24BIT ILBM" : "unknown ILBM",
425 bmhd_width, bmhd_height, bmhd_bitplanes,
426 1<<bmhd_bitplanes, bmhd_compression);
427
428 if ((fmt==ILBM_NORMAL) || (fmt==ILBM_EHB) || (fmt==ILBM_HAM)) {
429 wxLogTrace(_T("iff"),
430 _T("Converting CMAP from normal ILBM CMAP"));
431
432 switch(fmt) {
433 case ILBM_NORMAL: colors = 1 << bmhd_bitplanes; break;
434 case ILBM_EHB: colors = 32*2; break;
435 case ILBM_HAM: colors = 16; break;
436 }
437
438 if (colors > m_image->colors) {
439 byte *pal = new byte[colors*3];
440 if (!pal) {
441 Destroy();
442 return wxIFF_MEMERR;
443 }
444 int i;
445 for (i = 0; i < m_image->colors; i++) {
446 pal[3*i + 0] = m_image->pal[3*i + 0];
447 pal[3*i + 1] = m_image->pal[3*i + 1];
448 pal[3*i + 2] = m_image->pal[3*i + 2];
449 }
450 for (; i < colors; i++) {
451 pal[3*i + 0] = 0;
452 pal[3*i + 1] = 0;
453 pal[3*i + 2] = 0;
454 }
455 delete m_image->pal;
456 m_image->pal = pal;
457 m_image->colors = colors;
458 }
459
460 for (int i=0; i < colors; i++) {
461 m_image->pal[3*i + 0] = (m_image->pal[3*i + 0] >> 4) * 17;
462 m_image->pal[3*i + 1] = (m_image->pal[3*i + 1] >> 4) * 17;
463 m_image->pal[3*i + 2] = (m_image->pal[3*i + 2] >> 4) * 17;
464 }
465 }
466
467 m_image->p = new byte[bmhd_width * bmhd_height * 3];
468 byte *picptr = m_image->p;
469 if (!picptr) {
470 Destroy();
471 return wxIFF_MEMERR;
472 }
473
474 byte *pal = m_image->pal;
475 int lineskip = ((bmhd_width + 15) >> 4) << 1;
476 int height = chunkLen / (lineskip * bmhd_bitplanes);
477
478 if (bmhd_height < height) {
479 height = bmhd_height;
480 }
481
482 if (fmt == ILBM_HAM || fmt == ILBM_HAM8 || fmt == ILBM_24BIT) {
483 byte *pic = picptr;
484 const byte *workptr = bodyptr;
485
486 for (int i=0; i < height; i++) {
487 byte bitmsk = 0x80;
488 const byte *workptr2 = workptr;
489
490 // at start of each line, init RGB values to background
491 byte rval = pal[0];
492 byte gval = pal[1];
493 byte bval = pal[2];
494
495 for (int j=0; j < bmhd_width; j++) {
496 long col = 0;
497 long colbit = 1;
498 const byte *workptr3 = workptr2;
499 for (int k=0; k < bmhd_bitplanes; k++) {
500 if (*workptr3 & bitmsk) {
501 col += colbit;
502 }
503 workptr3 += lineskip;
504 colbit <<= 1;
505 }
506
507 if (fmt==ILBM_HAM) {
508 int c = (col & 0x0f);
509 switch (col & 0x30) {
510 case 0x00: if (c >= 0 && c < colors) {
511 rval = pal[3*c + 0];
512 gval = pal[3*c + 1];
513 bval = pal[3*c + 2];
514 }
515 break;
516
517 case 0x10: bval = c * 17;
518 break;
519
520 case 0x20: rval = c * 17;
521 break;
522
523 case 0x30: gval = c * 17;
524 break;
525 }
526 } else if (fmt == ILBM_HAM8) {
527 int c = (col & 0x3f);
528 switch(col & 0xc0) {
529 case 0x00: if (c >= 0 & c < colors) {
530 rval = pal[3*c + 0];
531 gval = pal[3*c + 1];
532 bval = pal[3*c + 2];
533 }
534 break;
535
536 case 0x40: bval = (bval & 3) | (c << 2);
537 break;
538
539 case 0x80: rval = (rval & 3) | (c << 2);
540 break;
541
542 case 0xc0: gval = (rval & 3) | (c << 2);
543 }
544 } else {
545 rval = col & 0xff;
546 gval = (col >> 8) & 0xff;
547 bval = (col >> 16) & 0xff;
548 }
549
550 *pic++ = rval;
551 *pic++ = gval;
552 *pic++ = bval;
553
554 bitmsk = bitmsk >> 1;
555 if (bitmsk == 0) {
556 bitmsk = 0x80;
557 workptr2++;
558 }
559 }
560 workptr += lineskip * bmhd_bitplanes;
561 }
562 } else if ((fmt == ILBM_NORMAL) || (fmt == ILBM_EHB)) {
563 if (fmt == ILBM_EHB) {
564 wxLogTrace(_T("iff"), _T("Doubling CMAP for EHB mode"));
565
566 for (int i=0; i<32; i++) {
567 pal[3*(i + 32) + 0] = pal[3*i + 0] >> 1;
568 pal[3*(i + 32) + 1] = pal[3*i + 1] >> 1;
569 pal[3*(i + 32) + 2] = pal[3*i + 2] >> 1;
570 }
571 }
572
573 byte *pic = picptr; // ptr to buffer
574 const byte *workptr = bodyptr; // ptr to pic, planar format
575
576 if (bmhd_height < height) {
577 height = bmhd_height;
578 }
579
580 for (int i=0; i < height; i++) {
581 byte bitmsk = 0x80; // left most bit (mask)
582 const byte *workptr2 = workptr; // work ptr to source
583 for (int j=0; j < bmhd_width; j++) {
584 long col = 0;
585 long colbit = 1;
586 const byte *workptr3 = workptr2; // 1st byte in 1st pln
587
588 for (int k=0; k < bmhd_bitplanes; k++) {
589 if (*workptr3 & bitmsk) { // if bit set in this pln
590 col = col + colbit; // add bit to chunky byte
591 }
592 workptr3 += lineskip; // go to next line
593 colbit <<= 1; // shift color bit
594 }
595
596 if (col >= 0 && col < colors) {
597 pic[0] = pal[3*col + 0];
598 pic[1] = pal[3*col + 1];
599 pic[2] = pal[3*col + 2];
600 } else {
601 pic[0] = pic[1] = pic[2] = 0;
602 }
603 pic += 3;
604 bitmsk = bitmsk >> 1; // shift mask to next bit
605 if (bitmsk == 0) { // if mask is zero
606 bitmsk = 0x80; // reset mask
607 workptr2++; // mv ptr to next byte
608 }
609 }
610
611 workptr += lineskip * bmhd_bitplanes; // to next line
612 }
613 } else {
614 break; // unknown format
615 }
616
617 m_image->w = bmhd_width;
618 m_image->h = height;
619 m_image->transparent = bmhd_transcol;
620
621 wxLogTrace(_T("iff"), _T("Loaded IFF picture %s"),
622 truncated? "truncated" : "completely");
623
624 return (truncated? wxIFF_TRUNCATED : wxIFF_OK);
625 } else {
626 wxLogTrace(_T("iff"), _T("Skipping unknown chunk '%c%c%c%c'"),
627 *dataptr, *(dataptr+1), *(dataptr+2), *(dataptr+3));
628
629 dataptr = dataptr + 8 + chunkLen; // skip unknown chunk
630 }
631 }
632
633 Destroy();
634 return wxIFF_INVFORMAT;
635 }
636
637 #endif // wxUSE_STREAMS && wxUSE_IFF
638