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