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