1 //////////////////////////////////////////////////////////////////////////////// 2 // 3 // File: GIFLoad.cpp 4 // 5 // Date: December 1999 6 // 7 // Author: Daniel Switkin 8 // 9 // Copyright 2003 (c) by Daniel Switkin. This file is made publically available 10 // under the BSD license, with the stipulations that this complete header must 11 // remain at the top of the file indefinitely, and credit must be given to the 12 // original author in any about box using this software. 13 // 14 //////////////////////////////////////////////////////////////////////////////// 15 16 // Additional authors: John Scipione, <jscipione@gmail.com> 17 18 19 #include "GIFLoad.h" 20 21 #include <stdio.h> 22 #include <stdlib.h> 23 #include <syslog.h> 24 25 #include <new> 26 27 #include <ByteOrder.h> 28 #include <InterfaceDefs.h> 29 #include <TranslatorFormats.h> 30 31 #include "GIFPrivate.h" 32 33 34 extern bool debug; 35 36 37 GIFLoad::GIFLoad(BPositionIO* input, BPositionIO* output) 38 : 39 fatalerror(false), 40 fInput(input), 41 fOutput(output), 42 fPalette(NULL), 43 fHeadMemblock(NULL), 44 fScanLine(NULL) 45 { 46 fInput->Seek(0, SEEK_SET); 47 48 if (!ReadGIFHeader()) { 49 fatalerror = true; 50 return; 51 } 52 53 if (debug) { 54 syslog(LOG_INFO, "GIFLoad::GIFLoad() - Image dimensions are %d x %d\n", 55 fWidth, fHeight); 56 } 57 58 unsigned char c; 59 if (fInput->Read(&c, 1) < 1) { 60 fatalerror = true; 61 return; 62 } 63 64 while (c != TERMINATOR_INTRODUCER) { 65 if (c == DESCRIPTOR_INTRODUCER) { 66 if ((!ReadGIFImageHeader()) || (!ReadGIFImageData())) { 67 if (debug) { 68 syslog(LOG_ERR, "GIFLoad::GIFLoad() - " 69 "A fatal error occurred\n"); 70 } 71 72 fatalerror = true; 73 } else { 74 if (debug) { 75 syslog(LOG_INFO, "GIFLoad::GIFLoad() - " 76 "Found a single image and leaving\n"); 77 } 78 } 79 free(fScanLine); 80 fScanLine = NULL; 81 return; 82 } else if (c == EXTENSION_INTRODUCER) { 83 unsigned char d; 84 if (fInput->Read(&d, 1) < 1) { 85 fatalerror = true; 86 return; 87 } 88 if (d == LOOP_BLOCK_LABEL) { 89 if (!ReadGIFLoopBlock()) { 90 fatalerror = true; 91 return; 92 } 93 } else if (d == GRAPHIC_CONTROL_LABEL) { 94 if (!ReadGIFControlBlock()) { 95 fatalerror = true; 96 return; 97 } 98 } else if (d == COMMENT_EXTENSION_LABEL) { 99 if (!ReadGIFCommentBlock()) { 100 fatalerror = true; 101 return; 102 } 103 } else { 104 if (!ReadGIFUnknownBlock(d)) { 105 fatalerror = true; 106 return; 107 } 108 } 109 } else if (c != BLOCK_TERMINATOR) { 110 if (!ReadGIFUnknownBlock(c)) { 111 fatalerror = true; 112 return; 113 } 114 } 115 116 if (fInput->Read(&c, 1) < 1) { 117 fatalerror = true; 118 return; 119 } 120 } 121 122 if (debug) 123 syslog(LOG_INFO, "GIFLoad::GIFLoad() - Done\n"); 124 } 125 126 127 GIFLoad::~GIFLoad() 128 { 129 delete fPalette; 130 } 131 132 133 bool 134 GIFLoad::ReadGIFHeader() 135 { 136 // standard header 137 unsigned char header[13]; 138 if (fInput->Read(header, 13) < 13) 139 return false; 140 141 fWidth = header[6] + (header[7] << 8); 142 fHeight = header[8] + (header[9] << 8); 143 144 fPalette = new(std::nothrow) LoadPalette(); 145 if (fPalette == NULL) 146 return false; 147 148 // Global palette 149 if (header[10] & GIF_LOCALCOLORMAP) { 150 fPalette->size_in_bits = (header[10] & 0x07) + 1; 151 if (debug) { 152 syslog(LOG_INFO, "GIFLoad::ReadGIFHeader() - " 153 "Found %d bit global palette\n", 154 fPalette->size_in_bits); 155 } 156 int s = 1 << fPalette->size_in_bits; 157 fPalette->size = s; 158 159 unsigned char gp[256 * 3]; 160 if (fInput->Read(gp, s * 3) < s * 3) 161 return false; 162 163 for (int x = 0; x < s; x++) 164 fPalette->SetColor(x, gp[x * 3], gp[x * 3 + 1], gp[x * 3 + 2]); 165 166 fPalette->backgroundindex = header[11]; 167 } else { 168 // install BeOS system palette in case local palette isn't present 169 color_map* map = (color_map*)system_colors(); 170 for (int x = 0; x < 256; x++) { 171 fPalette->SetColor(x, map->color_list[x].red, 172 map->color_list[x].green, map->color_list[x].blue); 173 } 174 fPalette->size = 256; 175 fPalette->size_in_bits = 8; 176 } 177 178 return true; 179 } 180 181 182 bool 183 GIFLoad::ReadGIFLoopBlock() 184 { 185 unsigned char length; 186 if (fInput->Read(&length, 1) < 1) 187 return false; 188 189 fInput->Seek(length, SEEK_CUR); 190 do { 191 if (fInput->Read(&length, 1) < 1) 192 return false; 193 194 fInput->Seek(length, SEEK_CUR); 195 } while (length != 0); 196 197 return true; 198 } 199 200 201 bool 202 GIFLoad::ReadGIFControlBlock() 203 { 204 unsigned char data[6]; 205 if (fInput->Read(data, 6) < 6) 206 return false; 207 208 if (data[1] & 0x01) { 209 fPalette->usetransparent = true; 210 fPalette->transparentindex = data[4]; 211 if (debug) { 212 syslog(LOG_INFO, "GIFLoad::ReadGIFControlBlock() - " 213 "Transparency active, using palette index %d\n", data[4]); 214 } 215 } 216 217 return true; 218 } 219 220 221 bool 222 GIFLoad::ReadGIFCommentBlock() 223 { 224 if (debug) 225 syslog(LOG_INFO, "GIFLoad::ReadGIFCommentBlock() - Found:\n"); 226 227 unsigned char length; 228 char comment_data[256]; 229 do { 230 if (fInput->Read(&length, 1) < 1) 231 return false; 232 233 if (fInput->Read(comment_data, length) < length) 234 return false; 235 236 comment_data[length] = BLOCK_TERMINATOR; 237 if (debug) 238 syslog(LOG_INFO, "%s", comment_data); 239 } while (length != BLOCK_TERMINATOR); 240 241 if (debug) 242 syslog(LOG_INFO, "\n"); 243 244 return true; 245 } 246 247 248 bool 249 GIFLoad::ReadGIFUnknownBlock(unsigned char c) 250 { 251 if (debug) 252 syslog(LOG_INFO, "GIFLoad::ReadGIFUnknownBlock() - Found: %d\n", c); 253 254 unsigned char length; 255 do { 256 if (fInput->Read(&length, 1) < 1) 257 return false; 258 259 fInput->Seek(length, SEEK_CUR); 260 } while (length != BLOCK_TERMINATOR); 261 262 return true; 263 } 264 265 266 bool 267 GIFLoad::ReadGIFImageHeader() 268 { 269 unsigned char data[9]; 270 if (fInput->Read(data, 9) < 9) 271 return false; 272 273 int left = data[0] + (data[1] << 8); 274 int top = data[2] + (data[3] << 8); 275 int localWidth = data[4] + (data[5] << 8); 276 int localHeight = data[6] + (data[7] << 8); 277 if (fWidth != localWidth || fHeight != localHeight) { 278 if (debug) { 279 syslog(LOG_ERR, "GIFLoad::ReadGIFImageHeader() - " 280 "Local dimensions do not match global, setting to %d x %d\n", 281 localWidth, localHeight); 282 } 283 fWidth = localWidth; 284 fHeight = localHeight; 285 } 286 287 fScanLine = (uint32*)malloc(fWidth * 4); 288 if (fScanLine == NULL) { 289 if (debug) { 290 syslog(LOG_ERR, "GIFLoad::ReadGIFImageHeader() - " 291 "Could not allocate scanline\n"); 292 } 293 return false; 294 } 295 296 BRect rect(left, top, left + fWidth - 1, top + fHeight - 1); 297 TranslatorBitmap header; 298 header.magic = B_HOST_TO_BENDIAN_INT32(B_TRANSLATOR_BITMAP); 299 header.bounds.left = B_HOST_TO_BENDIAN_FLOAT(rect.left); 300 header.bounds.top = B_HOST_TO_BENDIAN_FLOAT(rect.top); 301 header.bounds.right = B_HOST_TO_BENDIAN_FLOAT(rect.right); 302 header.bounds.bottom = B_HOST_TO_BENDIAN_FLOAT(rect.bottom); 303 header.rowBytes = B_HOST_TO_BENDIAN_INT32(fWidth * 4); 304 header.colors = (color_space)B_HOST_TO_BENDIAN_INT32(B_RGBA32); 305 header.dataSize = B_HOST_TO_BENDIAN_INT32(fWidth * 4 * fHeight); 306 if (fOutput->Write(&header, 32) < 32) 307 return false; 308 309 if (data[8] & GIF_LOCALCOLORMAP) { 310 // has local palette 311 fPalette->size_in_bits = (data[8] & 0x07) + 1; 312 int s = 1 << fPalette->size_in_bits; 313 fPalette->size = s; 314 if (debug) { 315 syslog(LOG_INFO, "GIFLoad::ReadGIFImageHeader() - " 316 "Found %d bit local palette\n", fPalette->size_in_bits); 317 } 318 319 unsigned char lp[256 * 3]; 320 if (fInput->Read(lp, s * 3) < s * 3) 321 return false; 322 323 for (int x = 0; x < s; x++) 324 fPalette->SetColor(x, lp[x * 3], lp[x * 3 + 1], lp[x * 3 + 2]); 325 } 326 327 fInterlaced = data[8] & GIF_INTERLACED; 328 if (debug) { 329 if (fInterlaced) { 330 syslog(LOG_INFO, "GIFLoad::ReadGIFImageHeader() - " 331 "Image is interlaced\n"); 332 } else { 333 syslog(LOG_INFO, "GIFLoad::ReadGIFImageHeader() - " 334 "Image is not interlaced\n"); 335 } 336 } 337 338 return true; 339 } 340 341 342 bool 343 GIFLoad::ReadGIFImageData() 344 { 345 unsigned char newEntry[ENTRY_COUNT]; 346 unsigned char codeSize; 347 if (fInput->Read(&codeSize, 1) < 1) 348 return false; 349 350 if (codeSize > fPalette->size_in_bits) { 351 if (debug) { 352 syslog(LOG_ERR, "GIFLoad::ReadGIFImageData() - " 353 "Code_size should be %d, not %d, allowing it\n", 354 fCodeSize, codeSize); 355 } 356 if (!InitFrame(codeSize)) 357 return false; 358 } else if (codeSize < fPalette->size_in_bits) { 359 if (debug) { 360 syslog(LOG_ERR, "GIFLoad::ReadGIFImageData() - " 361 "Code_size should be %d, not %d\n", fCodeSize, codeSize); 362 } 363 return false; 364 } else if (!InitFrame(fPalette->size_in_bits)) 365 return false; 366 367 if (debug) 368 syslog(LOG_INFO, "GIFLoad::ReadGIFImageData() - Starting LZW\n"); 369 370 while ((fNewCode = NextCode()) != -1 && fNewCode != fEndCode) { 371 if (fNewCode == fClearCode) { 372 ResetTable(); 373 fNewCode = NextCode(); 374 fOldCode[0] = fNewCode; 375 fOldCodeLength = 1; 376 if (!OutputColor(fOldCode, 1)) 377 goto bad_end; 378 379 if (fNewCode == -1 || fNewCode == fEndCode) { 380 if (debug) { 381 syslog(LOG_ERR, "GIFLoad::ReadGIFImageData() - " 382 "Premature fEndCode or error reading fNewCode\n"); 383 } 384 goto bad_end; 385 } 386 continue; 387 } 388 389 // explicitly check for lack of clear code at start of file 390 if (fOldCodeLength == 0) { 391 fOldCode[0] = fNewCode; 392 fOldCodeLength = 1; 393 if (!OutputColor(fOldCode, 1)) 394 goto bad_end; 395 396 continue; 397 } 398 399 // error out if we're trying to access an out-of-bounds index 400 if (fNextCode >= ENTRY_COUNT) 401 goto bad_end; 402 403 if (fTable[fNewCode] != NULL) { 404 // exists in table 405 406 if (!OutputColor(fTable[fNewCode], fEntrySize[fNewCode])) 407 goto bad_end; 408 409 //memcpy(newEntry, fOldCode, fOldCodeLength); 410 for (unsigned int x = 0; x < fOldCodeLength; x++) 411 newEntry[x] = fOldCode[x]; 412 413 //memcpy(newEntry + fOldCodeLength, fTable[fNewCode], 1); 414 newEntry[fOldCodeLength] = fTable[fNewCode][0]; 415 } else { 416 // does not exist in table 417 418 //memcpy(newEntry, fOldCode, fOldCodeLength); 419 for (unsigned int x = 0; x < fOldCodeLength; x++) 420 newEntry[x] = fOldCode[x]; 421 422 //memcpy(newEntry + fOldCodeLength, fOldCode, 1); 423 newEntry[fOldCodeLength] = fOldCode[0]; 424 425 if (!OutputColor(newEntry, fOldCodeLength + 1)) 426 goto bad_end; 427 } 428 fTable[fNextCode] = MemblockAllocate(fOldCodeLength + 1); 429 if (fTable[fNextCode] == NULL) 430 goto bad_end; 431 432 //memcpy(fTable[fNextCode], newEntry, fOldCodeLength + 1); 433 for (unsigned int x = 0; x < fOldCodeLength + 1; x++) 434 fTable[fNextCode][x] = newEntry[x]; 435 436 fEntrySize[fNextCode] = fOldCodeLength + 1; 437 438 //memcpy(fOldCode, fTable[fNewCode], fEntrySize[fNewCode]); 439 for (int x = 0; x < fEntrySize[fNewCode]; x++) 440 fOldCode[x] = fTable[fNewCode][x]; 441 442 fOldCodeLength = fEntrySize[fNewCode]; 443 fNextCode++; 444 445 if (fNextCode > fMaxCode && fBits < LZ_MAX_BITS) { 446 fBits++; 447 fMaxCode = (1 << fBits) - 1; 448 } 449 } 450 451 MemblockDeleteAll(); 452 if (fNewCode == -1) 453 return false; 454 455 if (debug) 456 syslog(LOG_INFO, "GIFLoad::ReadGIFImageData() - Done\n"); 457 458 return true; 459 460 bad_end: 461 if (debug) 462 syslog(LOG_ERR, "GIFLoad::ReadGIFImageData() - Reached a bad end\n"); 463 464 MemblockDeleteAll(); 465 return false; 466 } 467 468 469 short 470 GIFLoad::NextCode() 471 { 472 while (fBitCount < fBits) { 473 if (fByteCount == 0) { 474 if (fInput->Read(&fByteCount, 1) < 1) 475 return -1; 476 477 if (fByteCount == 0) 478 return fEndCode; 479 480 if (fInput->Read(fByteBuffer + (255 - fByteCount), fByteCount) 481 < fByteCount) { 482 return -1; 483 } 484 } 485 fBitBuffer |= (unsigned int)fByteBuffer[255 - fByteCount] << fBitCount; 486 fByteCount--; 487 fBitCount += 8; 488 } 489 490 short s = fBitBuffer & ((1 << fBits) - 1); 491 fBitBuffer >>= fBits; 492 fBitCount -= fBits; 493 494 return s; 495 } 496 497 498 void 499 GIFLoad::ResetTable() 500 { 501 fBits = fCodeSize + 1; 502 fNextCode = fClearCode + 2; 503 fMaxCode = (1 << fBits) - 1; 504 505 MemblockDeleteAll(); 506 for (int x = 0; x < ENTRY_COUNT; x++) { 507 fTable[x] = NULL; 508 if (x < (1 << fCodeSize)) { 509 fTable[x] = MemblockAllocate(1); 510 if (fTable[x] != NULL) { 511 fTable[x][0] = x; 512 fEntrySize[x] = 1; 513 } 514 } 515 } 516 } 517 518 519 bool 520 GIFLoad::InitFrame(int codeSize) 521 { 522 fCodeSize = codeSize; 523 if (fCodeSize == 1) 524 fCodeSize++; 525 526 fBits = fCodeSize + 1; 527 fClearCode = 1 << fCodeSize; 528 fEndCode = fClearCode + 1; 529 fNextCode = fClearCode + 2; 530 fMaxCode = (1 << fBits) - 1; 531 532 fPass = 0; 533 534 if (fInterlaced) 535 fRow = gl_pass_starts_at[0]; 536 else 537 fRow = 0; 538 539 fBitCount = 0; 540 fBitBuffer = 0; 541 fByteCount = 0; 542 fOldCodeLength = 0; 543 fNewCode = 0; 544 fScanlinePosition = 0; 545 546 ResetTable(); 547 return true; 548 } 549 550 551 unsigned char* 552 GIFLoad::MemblockAllocate(int size) 553 { 554 // Do 4k mallocs, keep them in a linked list, do a first fit across 555 // them when a new request comes along. 556 557 if (fHeadMemblock == NULL) { 558 fHeadMemblock = (Memblock*)malloc(sizeof(Memblock)); 559 if (fHeadMemblock == NULL) 560 return NULL; 561 562 unsigned char* value = fHeadMemblock->data; 563 fHeadMemblock->offset = size; 564 fHeadMemblock->next = NULL; 565 566 return value; 567 } else { 568 Memblock* block = fHeadMemblock; 569 Memblock* last = NULL; 570 while (block != NULL) { 571 if (ENTRY_COUNT - block->offset > size) { 572 unsigned char* value = block->data + block->offset; 573 block->offset += size; 574 575 return value; 576 } 577 last = block; 578 block = block->next; 579 } 580 581 block = (Memblock*)malloc(sizeof(Memblock)); 582 if (block == NULL) 583 return NULL; 584 585 unsigned char* value = block->data; 586 block->offset = size; 587 block->next = NULL; 588 if (last != NULL) 589 last->next = block; 590 591 return value; 592 } 593 } 594 595 596 void 597 GIFLoad::MemblockDeleteAll() 598 { 599 Memblock* block = NULL; 600 601 while (fHeadMemblock != NULL) { 602 // delete the linked list 603 block = fHeadMemblock->next; 604 delete fHeadMemblock; 605 fHeadMemblock = block; 606 } 607 } 608 609 610 bool 611 GIFLoad::OutputColor(unsigned char* string, int size) 612 { 613 int bpr = fWidth << 2; 614 615 for (int x = 0; x < size; x++) { 616 fScanLine[fScanlinePosition] = fPalette->ColorForIndex(string[x]); 617 fScanlinePosition++; 618 619 if (fScanlinePosition >= fWidth) { 620 if (fOutput->WriteAt(32 + (fRow * bpr), fScanLine, bpr) < bpr) 621 return false; 622 623 fScanlinePosition = 0; 624 if (fInterlaced) { 625 fRow += gl_increment_pass_by[fPass]; 626 while (fRow >= fHeight) { 627 fPass++; 628 if (fPass > 3) 629 return true; 630 631 fRow = gl_pass_starts_at[fPass]; 632 } 633 } else 634 fRow++; 635 } 636 } 637 638 return true; 639 } 640