]> git.saurik.com Git - wxWidgets.git/blob - src/common/iffdecod.cpp
more DLL compilation fixes
[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 = new unsigned char[colors];
106 unsigned char* g = new unsigned char[colors];
107 unsigned char* b = new unsigned char[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 delete [] r;
119 delete [] g;
120 delete [] b;
121 }
122 #endif // wxUSE_PALETTE
123
124 // copy image data
125 for (i = 0; i < (long)(GetWidth() * GetHeight()); i++, src += 3, dst += 3)
126 {
127 dst[0] = src[0];
128 dst[1] = src[1];
129 dst[2] = src[2];
130 }
131
132 return TRUE;
133 }
134
135
136 //---------------------------------------------------------------------------
137 // Data accessors
138 //---------------------------------------------------------------------------
139
140 // Get data for current frame
141
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; }
148
149 //---------------------------------------------------------------------------
150 // IFF reading and decoding
151 //---------------------------------------------------------------------------
152
153 //
154 // CanRead:
155 // Returns TRUE if the file looks like a valid IFF, FALSE otherwise.
156 //
157 bool wxIFFDecoder::CanRead()
158 {
159 unsigned char buf[12] = "";
160
161 m_f->Read(buf, 12);
162 m_f->SeekI(-12, wxFromCurrent);
163
164 return (memcmp(buf, "FORM", 4) == 0 && memcmp(buf+8, "ILBM", 4) == 0);
165 }
166
167
168 // ReadIFF:
169 // Based on xv source code by Thomas Meyer
170 // Permission for use in wxWindows has been gratefully given.
171
172 typedef unsigned char byte;
173 #define IFFDEBUG 0
174
175 /*************************************************************************
176 void decomprle(source, destination, source length, buffer size)
177
178 Decompress run-length encoded data from source to destination. Terminates
179 when source is decoded completely or destination buffer is full.
180
181 The decruncher is as optimized as I could make it, without risking
182 safety in case of corrupt BODY chunks.
183 **************************************************************************/
184
185 static void decomprle(const byte *sptr, byte *dptr, long slen, long dlen)
186 {
187 byte codeByte, dataByte;
188
189 while ((slen > 0) && (dlen > 0)) {
190 // read control byte
191 codeByte = *sptr++;
192
193 if (codeByte < 0x80) {
194 codeByte++;
195 if ((slen > (long) codeByte) && (dlen >= (long) codeByte)) {
196 slen -= codeByte + 1;
197 dlen -= codeByte;
198 while (codeByte > 0) {
199 *dptr++ = *sptr++;
200 codeByte--;
201 }
202 }
203 else slen = 0;
204 }
205
206 else if (codeByte > 0x80) {
207 codeByte = 0x81 - (codeByte & 0x7f);
208 if ((slen > (long) 0) && (dlen >= (long) codeByte)) {
209 dataByte = *sptr++;
210 slen -= 2;
211 dlen -= codeByte;
212 while (codeByte > 0) {
213 *dptr++ = dataByte;
214 codeByte--;
215 }
216 }
217 else slen = 0;
218 }
219 }
220 }
221
222 /******************************************/
223 static unsigned int iff_getword(const byte *ptr)
224 {
225 unsigned int v;
226
227 v = *ptr++;
228 v = (v << 8) + *ptr;
229 return v;
230 }
231
232 /******************************************/
233 static unsigned long iff_getlong(const byte *ptr)
234 {
235 unsigned long l;
236
237 l = *ptr++;
238 l = (l << 8) + *ptr++;
239 l = (l << 8) + *ptr++;
240 l = (l << 8) + *ptr;
241 return l;
242 }
243
244 // Define internal ILBM types
245 #define ILBM_NORMAL 0
246 #define ILBM_EHB 1
247 #define ILBM_HAM 2
248 #define ILBM_HAM8 3
249 #define ILBM_24BIT 4
250
251 int wxIFFDecoder::ReadIFF()
252 {
253 Destroy();
254
255 m_image = new IFFImage();
256 if (m_image == 0) {
257 Destroy();
258 return wxIFF_MEMERR;
259 }
260
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);
266
267 // allocate memory for complete file
268 if ((databuf = new byte[filesize]) == 0) {
269 Destroy();
270 return wxIFF_MEMERR;
271 }
272
273 m_f->Read(databuf, filesize);
274 const byte *dataend = databuf + filesize;
275
276 // initialize work pointer. used to trace the buffer for IFF chunks
277 const byte *dataptr = databuf;
278
279 // check for minmal size
280 if (dataptr + 12 > dataend) {
281 Destroy();
282 return wxIFF_INVFORMAT;
283 }
284
285 // check if we really got an IFF file
286 if (strncmp((char *)dataptr, "FORM", 4) != 0) {
287 Destroy();
288 return wxIFF_INVFORMAT;
289 }
290
291 dataptr = dataptr + 8; // skip ID and length of FORM
292
293 // check if the IFF file is an ILBM (picture) file
294 if (strncmp((char *) dataptr, "ILBM", 4) != 0) {
295 Destroy();
296 return wxIFF_INVFORMAT;
297 }
298
299 wxLogTrace(_T("iff"), _T("IFF ILBM file recognized"));
300
301 dataptr = dataptr + 4; // skip ID
302
303 //
304 // main decoding loop. searches IFF chunks and handles them.
305 // terminates when BODY chunk was found or dataptr ran over end of file
306 //
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;
311 int colors = 0;
312 while (dataptr + 8 <= dataend) {
313 // get chunk length and make even
314 size_t chunkLen = (iff_getlong(dataptr + 4) + 1) & 0xfffffffe;
315 #ifdef __VMS
316 // Silence compiler warning
317 int chunkLen_;
318 chunkLen_ = chunkLen;
319 if (chunkLen_ < 0) { // format error?
320 #else
321 if (chunkLen < 0) { // format error?
322 #endif
323 break;
324 }
325 bool truncated = (dataptr + 8 + chunkLen > dataend);
326
327 if (strncmp((char *)dataptr, "BMHD", 4) == 0) { // BMHD chunk?
328 if (chunkLen < 12 + 2 || truncated) {
329 break;
330 }
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
339 }
340 else if (strncmp((char *)dataptr, "CMAP", 4) == 0) { // CMAP ?
341 if (truncated) {
342 break;
343 }
344 const byte *cmapptr = dataptr + 8;
345 colors = chunkLen / 3; // calc no of colors
346
347 delete m_image->pal;
348 m_image->pal = 0;
349 m_image->colors = colors;
350 if (colors > 0) {
351 m_image->pal = new byte[3*colors];
352 if (!m_image->pal) {
353 Destroy();
354 return wxIFF_MEMERR;
355 }
356
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++;
362 }
363 }
364
365 wxLogTrace(_T("iff"), _T("Read %d colors from IFF file."),
366 colors);
367
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) {
372 break;
373 }
374 camg_viewmode = iff_getlong(dataptr + 8); // get viewmodes
375 CAMGok = true; // got CAMG
376 dataptr += 8 + chunkLen; // to next chunk
377 }
378 else if (strncmp((char *)dataptr, "BODY", 4) == 0) { // BODY ?
379 if (!BMHDok) { // BMHD found?
380 break;
381 }
382 const byte *bodyptr = dataptr + 8; // -> BODY data
383
384 if (truncated) {
385 chunkLen = dataend - dataptr;
386 }
387
388 //
389 // if BODY is compressed, allocate buffer for decrunched BODY
390 // and decompress it (run length encoding)
391 //
392 if (bmhd_compression == 1) {
393 // calc size of decrunch buffer - (size of the actual pic.
394 // decompressed in interleaved Amiga bitplane format)
395
396 size_t decomp_bufsize = (((bmhd_width + 15) >> 4) << 1)
397 * bmhd_height * bmhd_bitplanes;
398
399 if ((decomp_mem = new byte[decomp_bufsize]) == 0) {
400 Destroy();
401 return wxIFF_MEMERR;
402 }
403
404 decomprle(bodyptr, decomp_mem, chunkLen, decomp_bufsize);
405 bodyptr = decomp_mem; // -> uncompressed BODY
406 chunkLen = decomp_bufsize;
407 delete [] databuf;
408 databuf = 0;
409 }
410
411 // the following determines the type of the ILBM file.
412 // it's either NORMAL, EHB, HAM, HAM8 or 24BIT
413
414 int fmt = ILBM_NORMAL; // assume normal ILBM
415 if (bmhd_bitplanes == 24) {
416 fmt = ILBM_24BIT;
417 } else if (bmhd_bitplanes == 8) {
418 if (CAMGok && (camg_viewmode & 0x800)) {
419 fmt = ILBM_HAM8;
420 }
421 } else if ((bmhd_bitplanes > 5) && CAMGok) {
422 if (camg_viewmode & 0x80) {
423 fmt = ILBM_EHB;
424 } else if (camg_viewmode & 0x800) {
425 fmt = ILBM_HAM;
426 }
427 }
428
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);
438
439 if ((fmt==ILBM_NORMAL) || (fmt==ILBM_EHB) || (fmt==ILBM_HAM)) {
440 wxLogTrace(_T("iff"),
441 _T("Converting CMAP from normal ILBM CMAP"));
442
443 switch(fmt) {
444 case ILBM_NORMAL: colors = 1 << bmhd_bitplanes; break;
445 case ILBM_EHB: colors = 32*2; break;
446 case ILBM_HAM: colors = 16; break;
447 }
448
449 if (colors > m_image->colors) {
450 byte *pal = new byte[colors*3];
451 if (!pal) {
452 Destroy();
453 return wxIFF_MEMERR;
454 }
455 int i;
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];
460 }
461 for (; i < colors; i++) {
462 pal[3*i + 0] = 0;
463 pal[3*i + 1] = 0;
464 pal[3*i + 2] = 0;
465 }
466 delete m_image->pal;
467 m_image->pal = pal;
468 m_image->colors = colors;
469 }
470
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;
475 }
476 }
477
478 m_image->p = new byte[bmhd_width * bmhd_height * 3];
479 byte *picptr = m_image->p;
480 if (!picptr) {
481 Destroy();
482 return wxIFF_MEMERR;
483 }
484
485 byte *pal = m_image->pal;
486 int lineskip = ((bmhd_width + 15) >> 4) << 1;
487 int height = chunkLen / (lineskip * bmhd_bitplanes);
488
489 if (bmhd_height < height) {
490 height = bmhd_height;
491 }
492
493 if (fmt == ILBM_HAM || fmt == ILBM_HAM8 || fmt == ILBM_24BIT) {
494 byte *pic = picptr;
495 const byte *workptr = bodyptr;
496
497 for (int i=0; i < height; i++) {
498 byte bitmsk = 0x80;
499 const byte *workptr2 = workptr;
500
501 // at start of each line, init RGB values to background
502 byte rval = pal[0];
503 byte gval = pal[1];
504 byte bval = pal[2];
505
506 for (int j=0; j < bmhd_width; j++) {
507 long col = 0;
508 long colbit = 1;
509 const byte *workptr3 = workptr2;
510 for (int k=0; k < bmhd_bitplanes; k++) {
511 if (*workptr3 & bitmsk) {
512 col += colbit;
513 }
514 workptr3 += lineskip;
515 colbit <<= 1;
516 }
517
518 if (fmt==ILBM_HAM) {
519 int c = (col & 0x0f);
520 switch (col & 0x30) {
521 case 0x00: if (c >= 0 && c < colors) {
522 rval = pal[3*c + 0];
523 gval = pal[3*c + 1];
524 bval = pal[3*c + 2];
525 }
526 break;
527
528 case 0x10: bval = c * 17;
529 break;
530
531 case 0x20: rval = c * 17;
532 break;
533
534 case 0x30: gval = c * 17;
535 break;
536 }
537 } else if (fmt == ILBM_HAM8) {
538 int c = (col & 0x3f);
539 switch(col & 0xc0) {
540 case 0x00: if (c >= 0 && c < colors) {
541 rval = pal[3*c + 0];
542 gval = pal[3*c + 1];
543 bval = pal[3*c + 2];
544 }
545 break;
546
547 case 0x40: bval = (bval & 3) | (c << 2);
548 break;
549
550 case 0x80: rval = (rval & 3) | (c << 2);
551 break;
552
553 case 0xc0: gval = (rval & 3) | (c << 2);
554 }
555 } else {
556 rval = col & 0xff;
557 gval = (col >> 8) & 0xff;
558 bval = (col >> 16) & 0xff;
559 }
560
561 *pic++ = rval;
562 *pic++ = gval;
563 *pic++ = bval;
564
565 bitmsk = bitmsk >> 1;
566 if (bitmsk == 0) {
567 bitmsk = 0x80;
568 workptr2++;
569 }
570 }
571 workptr += lineskip * bmhd_bitplanes;
572 }
573 } else if ((fmt == ILBM_NORMAL) || (fmt == ILBM_EHB)) {
574 if (fmt == ILBM_EHB) {
575 wxLogTrace(_T("iff"), _T("Doubling CMAP for EHB mode"));
576
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;
581 }
582 }
583
584 byte *pic = picptr; // ptr to buffer
585 const byte *workptr = bodyptr; // ptr to pic, planar format
586
587 if (bmhd_height < height) {
588 height = bmhd_height;
589 }
590
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++) {
595 long col = 0;
596 long colbit = 1;
597 const byte *workptr3 = workptr2; // 1st byte in 1st pln
598
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
602 }
603 workptr3 += lineskip; // go to next line
604 colbit <<= 1; // shift color bit
605 }
606
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];
611 } else {
612 pic[0] = pic[1] = pic[2] = 0;
613 }
614 pic += 3;
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
619 }
620 }
621
622 workptr += lineskip * bmhd_bitplanes; // to next line
623 }
624 } else {
625 break; // unknown format
626 }
627
628 m_image->w = bmhd_width;
629 m_image->h = height;
630 m_image->transparent = bmhd_transcol;
631
632 wxLogTrace(_T("iff"), _T("Loaded IFF picture %s"),
633 truncated? "truncated" : "completely");
634
635 return (truncated? wxIFF_TRUNCATED : wxIFF_OK);
636 } else {
637 wxLogTrace(_T("iff"), _T("Skipping unknown chunk '%c%c%c%c'"),
638 *dataptr, *(dataptr+1), *(dataptr+2), *(dataptr+3));
639
640 dataptr = dataptr + 8 + chunkLen; // skip unknown chunk
641 }
642 }
643
644 Destroy();
645 return wxIFF_INVFORMAT;
646 }
647
648 #endif // wxUSE_STREAMS && wxUSE_IFF
649