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