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 #include "GIFLoad.h" 17 #include <ByteOrder.h> 18 #include <TranslatorFormats.h> 19 #include <InterfaceDefs.h> 20 #include <stdlib.h> 21 #include <stdio.h> 22 #include <syslog.h> 23 24 extern bool debug; 25 26 27 GIFLoad::GIFLoad(BPositionIO *input, BPositionIO *output) 28 : 29 fatalerror(false), 30 fInput(input), 31 fOutput(output), 32 fPalette(NULL), 33 fHeadMemblock(NULL), 34 fScanLine(NULL) 35 { 36 fInput->Seek(0, SEEK_SET); 37 38 if (!ReadGIFHeader()) { 39 fatalerror = true; 40 return; 41 } 42 43 if (debug) 44 syslog(LOG_ERR, "GIFLoad::GIFLoad() - Image dimensions are %d x %d\n", 45 fWidth, fHeight); 46 47 unsigned char c; 48 if (fInput->Read(&c, 1) < 1) { 49 fatalerror = true; 50 return; 51 } 52 while (c != 0x3b) { 53 if (c == 0x2c) { 54 if ((!ReadGIFImageHeader()) || (!ReadGIFImageData())) { 55 if (debug) 56 syslog(LOG_ERR, "GIFLoad::GIFLoad() - A fatal error occurred\n"); 57 fatalerror = true; 58 } else { 59 if (debug) 60 syslog(LOG_ERR, "GIFLoad::GIFLoad() - Found a single image and " 61 "leaving\n"); 62 } 63 free(fScanLine); 64 fScanLine = NULL; 65 return; 66 } else if (c == 0x21) { 67 unsigned char d; 68 if (fInput->Read(&d, 1) < 1) { 69 fatalerror = true; 70 return; 71 } 72 if (d == 0xff) { 73 if (!ReadGIFLoopBlock()) { 74 fatalerror = true; 75 return; 76 } 77 } else if (d == 0xf9) { 78 if (!ReadGIFControlBlock()) { 79 fatalerror = true; 80 return; 81 } 82 } else if (d == 0xfe) { 83 if (!ReadGIFCommentBlock()) { 84 fatalerror = true; 85 return; 86 } 87 } else { 88 if (!ReadGIFUnknownBlock(d)) { 89 fatalerror = true; 90 return; 91 } 92 } 93 } else if (c != 0x00) { 94 if (!ReadGIFUnknownBlock(c)) { 95 fatalerror = true; 96 return; 97 } 98 } 99 if (fInput->Read(&c, 1) < 1) { 100 fatalerror = true; 101 return; 102 } 103 } 104 if (debug) 105 syslog(LOG_ERR, "GIFLoad::GIFLoad() - Done\n"); 106 } 107 108 109 bool 110 GIFLoad::ReadGIFHeader() 111 { 112 // Standard header 113 unsigned char header[13]; 114 if (fInput->Read(header, 13) < 13) return false; 115 fWidth = header[6] + (header[7] << 8); 116 fHeight = header[8] + (header[9] << 8); 117 118 fPalette = new LoadPalette(); 119 // Global palette 120 if (header[10] & GIF_LOCALCOLORMAP) { 121 fPalette->size_in_bits = (header[10] & 0x07) + 1; 122 if (debug) 123 syslog(LOG_ERR, "GIFLoad::ReadGIFHeader() - Found %d bit global palette\n", 124 fPalette->size_in_bits); 125 int s = 1 << fPalette->size_in_bits; 126 fPalette->size = s; 127 128 unsigned char gp[256 * 3]; 129 if (fInput->Read(gp, s * 3) < s * 3) 130 return false; 131 for (int x = 0; x < s; x++) { 132 fPalette->SetColor(x, gp[x * 3], gp[x * 3 + 1], gp[x * 3 + 2]); 133 } 134 fPalette->backgroundindex = header[11]; 135 } else { // Install BeOS system palette in case local palette isn't present 136 color_map *map = (color_map *)system_colors(); 137 for (int x = 0; x < 256; x++) { 138 fPalette->SetColor(x, map->color_list[x].red, 139 map->color_list[x].green, map->color_list[x].blue); 140 } 141 fPalette->size = 256; 142 fPalette->size_in_bits = 8; 143 } 144 return true; 145 } 146 147 148 bool 149 GIFLoad::ReadGIFLoopBlock() 150 { 151 unsigned char length; 152 if (fInput->Read(&length, 1) < 1) return false; 153 fInput->Seek(length, SEEK_CUR); 154 155 do { 156 if (fInput->Read(&length, 1) < 1) { 157 return false; 158 } 159 fInput->Seek(length, SEEK_CUR); 160 } while (length != 0); 161 162 return true; 163 } 164 165 166 bool 167 GIFLoad::ReadGIFControlBlock() 168 { 169 unsigned char data[6]; 170 if (fInput->Read(data, 6) < 6) 171 return false; 172 if (data[1] & 0x01) { 173 fPalette->usetransparent = true; 174 fPalette->transparentindex = data[4]; 175 if (debug) 176 syslog(LOG_ERR, "GIFLoad::ReadGIFControlBlock() - Transparency active, " 177 "using palette index %d\n", data[4]); 178 } 179 return true; 180 } 181 182 183 bool 184 GIFLoad::ReadGIFCommentBlock() 185 { 186 if (debug) syslog(LOG_ERR, "GIFLoad::ReadGIFCommentBlock() - Found:\n"); 187 unsigned char length; 188 char comment_data[256]; 189 do { 190 if (fInput->Read(&length, 1) < 1) 191 return false; 192 if (fInput->Read(comment_data, length) < length) 193 return false; 194 comment_data[length] = 0x00; 195 if (debug) 196 syslog(LOG_ERR, "%s", comment_data); 197 } while (length != 0x00); 198 if (debug) 199 syslog(LOG_ERR, "\n"); 200 return true; 201 } 202 203 204 bool 205 GIFLoad::ReadGIFUnknownBlock(unsigned char c) 206 { 207 if (debug) 208 syslog(LOG_ERR, "GIFLoad::ReadGIFUnknownBlock() - Found: %d\n", c); 209 unsigned char length; 210 do { 211 if (fInput->Read(&length, 1) < 1) 212 return false; 213 fInput->Seek(length, SEEK_CUR); 214 } while (length != 0x00); 215 return true; 216 } 217 218 219 bool 220 GIFLoad::ReadGIFImageHeader() 221 { 222 unsigned char data[9]; 223 if (fInput->Read(data, 9) < 9) 224 return false; 225 226 int localWidth = data[4] + (data[5] << 8); 227 int localHeight = data[6] + (data[7] << 8); 228 if (fWidth != localWidth || fHeight != localHeight) { 229 if (debug) 230 syslog(LOG_ERR, "GIFLoad::ReadGIFImageHeader() - Local dimensions do not " 231 "match global, setting to %d x %d\n", localWidth, localHeight); 232 fWidth = localWidth; 233 fHeight = localHeight; 234 } 235 236 fScanLine = (uint32 *)malloc(fWidth * 4); 237 if (fScanLine == NULL) { 238 if (debug) 239 syslog(LOG_ERR, "GIFLoad::ReadGIFImageHeader() - Could not allocate " 240 "scanline\n"); 241 return false; 242 } 243 244 BRect rect(0, 0, fWidth - 1, fHeight - 1); 245 TranslatorBitmap header; 246 header.magic = B_HOST_TO_BENDIAN_INT32(B_TRANSLATOR_BITMAP); 247 header.bounds.left = B_HOST_TO_BENDIAN_FLOAT(rect.left); 248 header.bounds.top = B_HOST_TO_BENDIAN_FLOAT(rect.top); 249 header.bounds.right = B_HOST_TO_BENDIAN_FLOAT(rect.right); 250 header.bounds.bottom = B_HOST_TO_BENDIAN_FLOAT(rect.bottom); 251 header.rowBytes = B_HOST_TO_BENDIAN_INT32(fWidth * 4); 252 header.colors = (color_space)B_HOST_TO_BENDIAN_INT32(B_RGBA32); 253 header.dataSize = B_HOST_TO_BENDIAN_INT32(fWidth * 4 * fHeight); 254 if (fOutput->Write(&header, 32) < 32) 255 return false; 256 257 // Has local palette 258 if (data[8] & GIF_LOCALCOLORMAP) { 259 fPalette->size_in_bits = (data[8] & 0x07) + 1; 260 int s = 1 << fPalette->size_in_bits; 261 fPalette->size = s; 262 if (debug) 263 syslog(LOG_ERR, "GIFLoad::ReadGIFImageHeader() - Found %d bit local " 264 "palette\n", fPalette->size_in_bits); 265 266 unsigned char lp[256 * 3]; 267 if (fInput->Read(lp, s * 3) < s * 3) 268 return false; 269 for (int x = 0; x < s; x++) { 270 fPalette->SetColor(x, lp[x * 3], lp[x * 3 + 1], lp[x * 3 + 2]); 271 } 272 } 273 274 fInterlaced = data[8] & GIF_INTERLACED; 275 if (debug) { 276 if (fInterlaced) 277 syslog(LOG_ERR, "GIFLoad::ReadGIFImageHeader() - Image is interlaced\n"); 278 else 279 syslog(LOG_ERR, "GIFLoad::ReadGIFImageHeader() - Image is not " 280 "interlaced\n"); 281 } 282 return true; 283 } 284 285 286 bool 287 GIFLoad::ReadGIFImageData() 288 { 289 unsigned char newEntry[4096]; 290 291 unsigned char cs; 292 fInput->Read(&cs, 1); 293 if (cs == fPalette->size_in_bits) { 294 if (!InitFrame(fPalette->size_in_bits)) 295 return false; 296 } else if (cs > fPalette->size_in_bits) { 297 if (debug) 298 syslog(LOG_ERR, "GIFLoad::ReadGIFImageData() - Code_size should be %d, not " 299 "%d, allowing it\n", fCodeSize, cs); 300 if (!InitFrame(cs)) 301 return false; 302 } else if (cs < fPalette->size_in_bits) { 303 if (debug) 304 syslog(LOG_ERR, "GIFLoad::ReadGIFImageData() - Code_size should be %d, not " 305 "%d\n", fCodeSize, cs); 306 return false; 307 } 308 309 if (debug) 310 syslog(LOG_ERR, "GIFLoad::ReadGIFImageData() - Starting LZW\n"); 311 312 while ((fNewCode = NextCode()) != -1 && fNewCode != fEndCode) { 313 if (fNewCode == fClearCode) { 314 ResetTable(); 315 fNewCode = NextCode(); 316 fOldCode[0] = fNewCode; 317 fOldCodeLength = 1; 318 if (!OutputColor(fOldCode, 1)) goto bad_end; 319 if (fNewCode == -1 || fNewCode == fEndCode) { 320 if (debug) 321 syslog(LOG_ERR, "GIFLoad::ReadGIFImageData() - Premature fEndCode " 322 "or error\n"); 323 goto bad_end; 324 } 325 continue; 326 } 327 328 // Explicitly check for lack of clear code at start of file 329 if (fOldCodeLength == 0) { 330 fOldCode[0] = fNewCode; 331 fOldCodeLength = 1; 332 if (!OutputColor(fOldCode, 1)) 333 goto bad_end; 334 continue; 335 } 336 337 if (fTable[fNewCode] != NULL) { // Does exist in table 338 if (!OutputColor(fTable[fNewCode], fEntrySize[fNewCode])) 339 goto bad_end; 340 341 //memcpy(newEntry, fOldCode, fOldCodeLength); 342 for (unsigned int x = 0; x < fOldCodeLength; x++) { 343 newEntry[x] = fOldCode[x]; 344 } 345 346 //memcpy(newEntry + fOldCodeLength, fTable[fNewCode], 1); 347 newEntry[fOldCodeLength] = *fTable[fNewCode]; 348 } else { // Does not exist in table 349 //memcpy(newEntry, fOldCode, fOldCodeLength); 350 for (unsigned int x = 0; x < fOldCodeLength; x++) { 351 newEntry[x] = fOldCode[x]; 352 } 353 354 //memcpy(newEntry + fOldCodeLength, fOldCode, 1); 355 newEntry[fOldCodeLength] = *fOldCode; 356 357 if (!OutputColor(newEntry, fOldCodeLength + 1)) 358 goto bad_end; 359 } 360 fTable[fNextCode] = MemblockAllocate(fOldCodeLength + 1); 361 362 //memcpy(fTable[fNextCode], newEntry, fOldCodeLength + 1); 363 for (unsigned int x = 0; x < fOldCodeLength + 1; x++) { 364 fTable[fNextCode][x] = newEntry[x]; 365 } 366 367 fEntrySize[fNextCode] = fOldCodeLength + 1; 368 369 //memcpy(fOldCode, fTable[fNewCode], fEntrySize[fNewCode]); 370 for (int x = 0; x < fEntrySize[fNewCode]; x++) { 371 fOldCode[x] = fTable[fNewCode][x]; 372 } 373 374 fOldCodeLength = fEntrySize[fNewCode]; 375 fNextCode++; 376 377 if (fNextCode > fMaxCode && fBits != 12) { 378 fBits++; 379 fMaxCode = (1 << fBits) - 1; 380 } 381 } 382 383 MemblockDeleteAll(); 384 if (fNewCode == -1) 385 return false; 386 if (debug) 387 syslog(LOG_ERR, "GIFLoad::ReadGIFImageData() - Done\n"); 388 return true; 389 390 bad_end: 391 if (debug) 392 syslog(LOG_ERR, "GIFLoad::ReadGIFImageData() - Reached a bad end\n"); 393 MemblockDeleteAll(); 394 return false; 395 } 396 397 398 short 399 GIFLoad::NextCode() 400 { 401 while (fBitCount < fBits) { 402 if (fByteCount == 0) { 403 if (fInput->Read(&fByteCount, 1) < 1) return -1; 404 if (fByteCount == 0) return fEndCode; 405 if (fInput->Read(fByteBuffer + (255 - fByteCount), 406 fByteCount) < fByteCount) return -1; 407 } 408 fBitBuffer |= (unsigned int)fByteBuffer[255 - fByteCount] << fBitCount; 409 fByteCount--; 410 fBitCount += 8; 411 } 412 413 short s = fBitBuffer & ((1 << fBits) - 1); 414 fBitBuffer >>= fBits; 415 fBitCount -= fBits; 416 return s; 417 } 418 419 420 void 421 GIFLoad::ResetTable() 422 { 423 fBits = fCodeSize + 1; 424 fNextCode = fClearCode + 2; 425 fMaxCode = (1 << fBits) - 1; 426 427 MemblockDeleteAll(); 428 for (int x = 0; x < 4096; x++) { 429 fTable[x] = NULL; 430 if (x < (1 << fCodeSize)) { 431 fTable[x] = MemblockAllocate(1); 432 fTable[x][0] = x; 433 fEntrySize[x] = 1; 434 } 435 } 436 } 437 438 439 bool 440 GIFLoad::InitFrame(int size) 441 { 442 fCodeSize = size; 443 if (fCodeSize == 1) 444 fCodeSize++; 445 fBits = fCodeSize + 1; 446 fClearCode = 1 << fCodeSize; 447 fEndCode = fClearCode + 1; 448 fNextCode = fClearCode + 2; 449 fMaxCode = (1 << fBits) - 1; 450 fPass = 0; 451 if (fInterlaced) 452 fRow = gl_pass_starts_at[0]; 453 else 454 fRow = 0; 455 456 fBitCount = 0; 457 fBitBuffer = 0; 458 fByteCount = 0; 459 fOldCodeLength = 0; 460 fNewCode = 0; 461 fScanlinePosition = 0; 462 463 ResetTable(); 464 return true; 465 } 466 467 468 // Do 4k mallocs, keep them in a linked list, do a first fit across them 469 // when a new request comes along 470 uchar * 471 GIFLoad::MemblockAllocate(int size) 472 { 473 if (fHeadMemblock == NULL) { 474 fHeadMemblock = new Memblock(); 475 uchar *value = fHeadMemblock->data; 476 fHeadMemblock->offset = size; 477 fHeadMemblock->next = NULL; 478 return value; 479 } else { 480 Memblock *block = fHeadMemblock; 481 Memblock *last = NULL; 482 while (block != NULL) { 483 if (4096 - block->offset > size) { 484 uchar *value = block->data + block->offset; 485 block->offset += size; 486 return value; 487 } 488 last = block; 489 block = block->next; 490 } 491 492 block = new Memblock(); 493 uchar *value = block->data; 494 block->offset = size; 495 block->next = NULL; 496 last->next = block; 497 return value; 498 } 499 } 500 501 502 // Delete the linked list 503 void 504 GIFLoad::MemblockDeleteAll() 505 { 506 Memblock *block = NULL; 507 while (fHeadMemblock != NULL) { 508 block = fHeadMemblock->next; 509 delete fHeadMemblock; 510 fHeadMemblock = block; 511 } 512 } 513 514 515 GIFLoad::~GIFLoad() 516 { 517 delete fPalette; 518 } 519 520