1 //////////////////////////////////////////////////////////////////////////////// 2 // 3 // File: GIFSave.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: Stephan Aßmus, <superstippi@gmx.de> 17 // Philippe Saint-Pierre, <stpere@gmail.com> 18 // John Scipione, <jscipione@gmail.com> 19 20 21 #include "GIFSave.h" 22 23 #include <stdio.h> 24 #include <stdlib.h> 25 #include <syslog.h> 26 27 #include <new> 28 29 #include "GIFPrivate.h" 30 31 32 const int gs_pass_starts_at[] = {0, 4, 2, 1, 0}; 33 const int gs_increment_pass_by[] = {8, 8, 4, 2, 0}; 34 const int32 one_sixteenth = (int32)((1.0 / 16.0) * 32768); 35 const int32 three_sixteenth = (int32)((3.0 / 16.0) * 32768); 36 const int32 five_sixteenth = (int32)((5.0 / 16.0) * 32768); 37 const int32 seven_sixteenth = (int32)((7.0 / 16.0) * 32768); 38 39 extern bool debug; 40 41 42 class ColorCache : public HashItem { 43 public: 44 unsigned char index; 45 }; 46 47 48 GIFSave::GIFSave(BBitmap* bitmap, BPositionIO* output, 49 TranslatorSettings* settings) 50 { 51 fSettings = settings; 52 color_space colorSpace = bitmap->ColorSpace(); 53 if (colorSpace != B_RGB32 && colorSpace != B_RGBA32 54 && colorSpace != B_RGB32_BIG && colorSpace != B_RGBA32_BIG) { 55 if (debug) 56 syslog(LOG_ERR, "GIFSave::GIFSave() - Unknown color space\n"); 57 58 fatalerror = true; 59 return; 60 } 61 62 fatalerror = false; 63 if (fSettings->SetGetInt32(GIF_SETTING_PALETTE_MODE) == OPTIMAL_PALETTE) { 64 palette = new(std::nothrow) SavePalette(bitmap, 65 fSettings->SetGetInt32(GIF_SETTING_PALETTE_SIZE)); 66 } else { 67 palette = new(std::nothrow) SavePalette( 68 fSettings->SetGetInt32(GIF_SETTING_PALETTE_MODE)); 69 } 70 71 if (palette == NULL) { 72 fatalerror = true; 73 return; 74 } 75 76 if (!palette->IsValid()) { 77 delete palette; 78 fatalerror = true; 79 return; 80 } 81 82 width = bitmap->Bounds().IntegerWidth() + 1; 83 height = bitmap->Bounds().IntegerHeight() + 1; 84 if (debug) { 85 syslog(LOG_INFO, "GIFSave::GIFSave() - " 86 "Image dimensions are %d by %d\n", width, height); 87 } 88 89 if (fSettings->SetGetBool(GIF_SETTING_USE_DITHERING)) { 90 if (debug) 91 syslog(LOG_INFO, "GIFSave::GIFSave() - Using dithering\n"); 92 93 red_error = new(std::nothrow) int32[width + 2]; 94 if (red_error == NULL) { 95 delete palette; 96 fatalerror = true; 97 return; 98 } 99 red_error = &red_error[1]; 100 // Allow index of -1 too 101 102 green_error = new(std::nothrow) int32[width + 2]; 103 if (green_error == NULL) { 104 delete palette; 105 delete[] red_error; 106 fatalerror = true; 107 return; 108 } 109 green_error = &green_error[1]; 110 // Allow index of -1 too 111 112 blue_error = new(std::nothrow) int32[width + 2]; 113 if (blue_error == NULL) { 114 delete palette; 115 delete[] red_error; 116 delete[] green_error; 117 fatalerror = true; 118 return; 119 } 120 blue_error = &blue_error[1]; 121 // Allow index of -1 too 122 123 red_side_error = green_side_error = blue_side_error = 0; 124 for (int32 x = -1; x < width + 1; x++) { 125 red_error[x] = 0; 126 green_error[x] = 0; 127 blue_error[x] = 0; 128 } 129 } else if (debug) 130 syslog(LOG_INFO, "GIFSave::GIFSave() - Not using dithering\n"); 131 132 if (debug) { 133 if (fSettings->SetGetBool(GIF_SETTING_INTERLACED)) 134 syslog(LOG_INFO, "GIFSave::GIFSave() - Interlaced, "); 135 else 136 syslog(LOG_INFO, "GIFSave::GIFSave() - Not interlaced, "); 137 138 switch (fSettings->SetGetInt32(GIF_SETTING_PALETTE_MODE)) { 139 case WEB_SAFE_PALETTE: 140 syslog(LOG_INFO, "web safe palette\n"); 141 break; 142 case BEOS_SYSTEM_PALETTE: 143 syslog(LOG_INFO, "BeOS system palette\n"); 144 break; 145 case GREYSCALE_PALETTE: 146 syslog(LOG_INFO, "greyscale palette\n"); 147 break; 148 case OPTIMAL_PALETTE: 149 default: 150 syslog(LOG_INFO, "optimal palette\n"); 151 break; 152 } 153 } 154 155 if (fSettings->SetGetBool(GIF_SETTING_USE_TRANSPARENT)) { 156 if (fSettings->SetGetBool(GIF_SETTING_USE_TRANSPARENT_AUTO)) { 157 palette->PrepareForAutoTransparency(); 158 if (debug) { 159 syslog(LOG_INFO, "GIFSave::GIFSave() - " 160 "Using transparent index %d\n", 161 palette->TransparentIndex()); 162 } 163 } else { 164 palette->SetTransparentColor( 165 (uint8)fSettings->SetGetInt32(GIF_SETTING_TRANSPARENT_RED), 166 (uint8)fSettings->SetGetInt32(GIF_SETTING_TRANSPARENT_GREEN), 167 (uint8)fSettings->SetGetInt32(GIF_SETTING_TRANSPARENT_BLUE)); 168 if (debug) { 169 syslog(LOG_INFO, "GIFSave::GIFSave() - " 170 "Found transparent color %d,%d,%d at index %d\n", 171 fSettings->SetGetInt32(GIF_SETTING_TRANSPARENT_RED), 172 fSettings->SetGetInt32(GIF_SETTING_TRANSPARENT_GREEN), 173 fSettings->SetGetInt32(GIF_SETTING_TRANSPARENT_BLUE), 174 palette->TransparentIndex()); 175 } 176 } 177 } else { 178 if (debug) 179 syslog(LOG_INFO, "GIFSave::GIFSave() - Not using transparency\n"); 180 } 181 182 this->output = output; 183 this->bitmap = bitmap; 184 185 if (WriteGIFHeader() != B_OK) { 186 delete palette; 187 delete[] red_error; 188 delete[] green_error; 189 delete[] blue_error; 190 fatalerror = true; 191 return; 192 } 193 194 if (debug) 195 syslog(LOG_INFO, "GIFSave::GIFSave() - Wrote gif header\n"); 196 197 hash = new(std::nothrow) SFHash(1 << 16); 198 if (hash == NULL) { 199 delete palette; 200 delete[] red_error; 201 delete[] green_error; 202 delete[] blue_error; 203 fatalerror = true; 204 return; 205 } 206 207 WriteGIFControlBlock(); 208 if (debug) 209 syslog(LOG_INFO, "GIFSave::GIFSave() - Wrote gif control block\n"); 210 211 WriteGIFImageHeader(); 212 if (debug) 213 syslog(LOG_INFO, "GIFSave::GIFSave() - Wrote gif image header\n"); 214 215 WriteGIFImageData(); 216 if (debug) 217 syslog(LOG_INFO, "GIFSave::GIFSave() - Wrote gif image data\n"); 218 219 if (fSettings->SetGetBool(GIF_SETTING_USE_DITHERING)) { 220 delete[] &red_error[-1]; 221 delete[] &green_error[-1]; 222 delete[] &blue_error[-1]; 223 } 224 delete hash; 225 226 // Terminating character 227 char t = TERMINATOR_INTRODUCER; 228 output->Write(&t, 1); 229 } 230 231 232 GIFSave::~GIFSave() 233 { 234 delete palette; 235 fSettings->Release(); 236 } 237 238 239 status_t 240 GIFSave::WriteGIFHeader() 241 { 242 // Standard header 243 unsigned char header[] 244 = { 'G', 'I', 'F', '8', '9', 'a', 0, 0, 0, 0, 0, 0, 0 }; 245 header[6] = width & 0xff; 246 header[7] = (width & 0xff00) >> 8; 247 header[8] = height & 0xff; 248 header[9] = (height & 0xff00) >> 8; 249 header[10] = 0xf0 | (palette->SizeInBits() - 1); 250 header[11] = palette->BackgroundIndex(); 251 if (output->Write(header, 13) < 0) 252 return B_IO_ERROR; 253 254 // global palette 255 int size = (1 << palette->SizeInBits()) * 3; 256 // can't be bigger than this 257 uint8* buffer = new(std::nothrow) uint8[size]; 258 if (buffer == NULL) 259 return B_NO_MEMORY; 260 261 palette->GetColors(buffer, size); 262 if (output->Write(buffer, size) < 0) { 263 delete[] buffer; 264 return B_IO_ERROR; 265 } 266 delete[] buffer; 267 268 return B_OK; 269 } 270 271 272 status_t 273 GIFSave::WriteGIFControlBlock() 274 { 275 unsigned char b[8] = { 276 EXTENSION_INTRODUCER, GRAPHIC_CONTROL_LABEL, 0x04, 0x00, 0x00, 0x00, 277 0x00, BLOCK_TERMINATOR 278 }; 279 if (palette->UseTransparent()) { 280 b[3] = b[3] | 1; 281 b[6] = palette->TransparentIndex(); 282 } 283 return output->Write(b, 8) < 0 ? B_IO_ERROR : B_OK; 284 } 285 286 287 status_t 288 GIFSave::WriteGIFImageHeader() 289 { 290 unsigned char header[10]; 291 header[0] = DESCRIPTOR_INTRODUCER; 292 header[1] = header[2] = 0; 293 header[3] = header[4] = 0; 294 295 header[5] = width & 0xff; 296 header[6] = (width & 0xff00) >> 8; 297 header[7] = height & 0xff; 298 header[8] = (height & 0xff00) >> 8; 299 300 if (fSettings->SetGetBool(GIF_SETTING_INTERLACED)) 301 header[9] = 0x40; 302 else 303 header[9] = BLOCK_TERMINATOR; 304 305 return output->Write(header, 10) < 0 ? B_IO_ERROR : B_OK; 306 } 307 308 309 status_t 310 GIFSave::WriteGIFImageData() 311 { 312 InitFrame(); 313 314 status_t result = B_OK; 315 316 code_value = (short*)malloc(HASHSIZE * 2); 317 if (code_value == NULL) 318 return B_NO_MEMORY; 319 320 prefix_code = (short*)malloc(HASHSIZE * 2); 321 if (prefix_code == NULL) { 322 free(code_value); 323 324 return B_NO_MEMORY; 325 } 326 327 append_char = (unsigned char*)malloc(HASHSIZE); 328 if (append_char == NULL) { 329 free(code_value); 330 free(prefix_code); 331 332 return B_NO_MEMORY; 333 } 334 335 ResetHashtable(); 336 337 if (output->Write(&code_size, 1) < 0) { 338 free(code_value); 339 free(prefix_code); 340 free(append_char); 341 342 return B_IO_ERROR; 343 } 344 345 result = OutputCode(clear_code, BITS); 346 if (result != B_OK) { 347 free(code_value); 348 free(prefix_code); 349 free(append_char); 350 351 return B_IO_ERROR; 352 } 353 354 string_code = NextPixel(0); 355 int area = height * width; 356 357 for (int x = 1; x < area; x++) { 358 character = NextPixel(x); 359 int y = 0; 360 if ((y = CheckHashtable(string_code, character)) != -1) 361 string_code = y; 362 else { 363 AddToHashtable(string_code, character); 364 result = OutputCode(string_code, BITS); 365 if (result != B_OK) { 366 free(code_value); 367 free(prefix_code); 368 free(append_char); 369 370 return B_IO_ERROR; 371 } 372 373 if (next_code > max_code) { 374 BITS++; 375 if (BITS > LZ_MAX_BITS) { 376 result = OutputCode(clear_code, LZ_MAX_BITS); 377 if (result != B_OK) { 378 free(code_value); 379 free(prefix_code); 380 free(append_char); 381 382 return B_IO_ERROR; 383 } 384 385 BITS = code_size + 1; 386 ResetHashtable(); 387 next_code = clear_code + 1; 388 // this is different 389 } 390 max_code = (1 << BITS) - 1; 391 } 392 string_code = character; 393 next_code++; 394 } 395 } 396 397 result = OutputCode(string_code, BITS); 398 if (result != B_OK) { 399 free(code_value); 400 free(prefix_code); 401 free(append_char); 402 403 return B_IO_ERROR; 404 } 405 406 result = OutputCode(end_code, BITS); 407 if (result != B_OK) { 408 free(code_value); 409 free(prefix_code); 410 free(append_char); 411 412 return B_IO_ERROR; 413 } 414 415 result = OutputCode(0, BITS, true); 416 if (result != B_OK) { 417 free(code_value); 418 free(prefix_code); 419 free(append_char); 420 421 return B_IO_ERROR; 422 } 423 424 char t = BLOCK_TERMINATOR; 425 if (output->Write(&t, 1) < 0) { 426 free(code_value); 427 free(prefix_code); 428 free(append_char); 429 430 return B_IO_ERROR; 431 } 432 433 free(code_value); 434 free(prefix_code); 435 free(append_char); 436 437 return result; 438 } 439 440 441 status_t 442 GIFSave::OutputCode(short code, int BITS, bool flush) 443 { 444 if (!flush) { 445 bit_buffer |= (unsigned int)code << bit_count; 446 bit_count += BITS; 447 while (bit_count >= 8) { 448 byte_buffer[byte_count + 1] = (unsigned char)(bit_buffer & 0xff); 449 byte_count++; 450 bit_buffer >>= 8; 451 bit_count -= 8; 452 } 453 if (byte_count >= 255) { 454 byte_buffer[0] = 255; 455 if (output->Write(byte_buffer, 256) < 0) 456 return B_IO_ERROR; 457 458 if (byte_count == 256) { 459 byte_buffer[1] = byte_buffer[256]; 460 byte_count = 1; 461 } else 462 byte_count = 0; 463 } 464 } else { 465 bit_buffer |= (unsigned int) code << bit_count; 466 bit_count += BITS; 467 while (bit_count > 0) { 468 byte_buffer[byte_count + 1] = (unsigned char)(bit_buffer & 0xff); 469 byte_count++; 470 bit_buffer >>= 8; 471 bit_count -= 8; 472 } 473 if (byte_count > 0) { 474 byte_buffer[0] = (unsigned char)byte_count; 475 if (output->Write(byte_buffer, byte_count + 1) < 0) 476 return B_IO_ERROR; 477 } 478 } 479 480 return B_OK; 481 } 482 483 484 void 485 GIFSave::ResetHashtable() 486 { 487 for (int q = 0; q < HASHSIZE; q++) { 488 code_value[q] = -1; 489 prefix_code[q] = 0; 490 append_char[q] = 0; 491 } 492 } 493 494 495 int 496 GIFSave::CheckHashtable(int s, unsigned char c) 497 { 498 if (s == -1) 499 return c; 500 501 int hashindex = HASH(s, c); 502 int nextindex; 503 while ((nextindex = code_value[hashindex]) != -1) { 504 if (prefix_code[nextindex] == s && append_char[nextindex] == c) 505 return nextindex; 506 507 hashindex = (hashindex + HASHSTEP) % HASHSIZE; 508 } 509 510 return -1; 511 } 512 513 514 void 515 GIFSave::AddToHashtable(int s, unsigned char c) 516 { 517 int hashindex = HASH(s, c); 518 while (code_value[hashindex] != -1) 519 hashindex = (hashindex + HASHSTEP) % HASHSIZE; 520 521 code_value[hashindex] = next_code; 522 prefix_code[next_code] = s; 523 append_char[next_code] = c; 524 } 525 526 527 unsigned char 528 GIFSave::NextPixel(int pixel) 529 { 530 int bpr = bitmap->BytesPerRow(); 531 color_space colorSpace = bitmap->ColorSpace(); 532 bool useAlphaForTransparency = colorSpace == B_RGBA32_BIG 533 || (fSettings->SetGetBool(GIF_SETTING_USE_TRANSPARENT_AUTO) 534 && colorSpace == B_RGBA32); 535 unsigned char r; 536 unsigned char g; 537 unsigned char b; 538 unsigned char a; 539 540 if (colorSpace == B_RGB32 || colorSpace == B_RGBA32) { 541 b = gifbits[0]; 542 g = gifbits[1]; 543 r = gifbits[2]; 544 a = gifbits[3]; 545 } else { 546 a = gifbits[0]; 547 r = gifbits[1]; 548 g = gifbits[2]; 549 b = gifbits[3]; 550 } 551 gifbits += 4; 552 pos += 4; 553 554 if (!fSettings->SetGetBool(GIF_SETTING_USE_TRANSPARENT) 555 || fSettings->SetGetBool(GIF_SETTING_USE_TRANSPARENT_AUTO) 556 || r != fSettings->SetGetInt32(GIF_SETTING_TRANSPARENT_RED) 557 || g != fSettings->SetGetInt32(GIF_SETTING_TRANSPARENT_GREEN) 558 || b != fSettings->SetGetInt32(GIF_SETTING_TRANSPARENT_BLUE)) { 559 if (fSettings->SetGetBool(GIF_SETTING_USE_DITHERING)) { 560 if (pixel % width == 0) 561 red_side_error = green_side_error = blue_side_error = 0; 562 563 b = min_c(255, max_c(0, b - blue_side_error)); 564 g = min_c(255, max_c(0, g - green_side_error)); 565 r = min_c(255, max_c(0, r - red_side_error)); 566 } 567 } 568 569 if (fSettings->SetGetBool(GIF_SETTING_INTERLACED)) { 570 if (pos >= bpr) { 571 pos = 0; 572 row += gs_increment_pass_by[pass]; 573 while (row >= height) { 574 pass++; 575 row = gs_pass_starts_at[pass]; 576 } 577 gifbits = (unsigned char*)bitmap->Bits() + (bpr * row); 578 } 579 } 580 #if 0 581 unsigned int key = (r << 16) + (g << 8) + b; 582 ColorCache* cc = (ColorCache*)hash->GetItem(key); 583 if (cc == NULL) { 584 cc = new ColorCache(); 585 cc->key = key; 586 cc->index = palette->IndexForColor(r, g, b); 587 hash->AddItem((HashItem*)cc); 588 } 589 590 if (prefs->usedithering) { 591 int x = pixel % width; 592 // Don't carry error on to next line when interlaced because 593 // that line won't be adjacent, hence error is meaningless 594 if (prefs->interlaced && x == width - 1) { 595 for (int32 y = -1; y < width + 1; y++) { 596 red_error[y] = 0; 597 green_error[y] = 0; 598 blue_error[y] = 0; 599 } 600 } 601 602 int32 red_total_error = palette->pal[cc->index].red - r; 603 int32 green_total_error = palette->pal[cc->index].green - g; 604 int32 blue_total_error = palette->pal[cc->index].blue - b; 605 606 red_side_error = (red_error[x + 1] 607 + (red_total_error * seven_sixteenth)) >> 15; 608 blue_side_error = (blue_error[x + 1] 609 + (blue_total_error * seven_sixteenth)) >> 15; 610 green_side_error = (green_error[x + 1] 611 + (green_total_error * seven_sixteenth)) >> 15; 612 613 red_error[x - 1] += (red_total_error * three_sixteenth); 614 green_error[x - 1] += (green_total_error * three_sixteenth); 615 blue_error[x - 1] += (blue_total_error * three_sixteenth); 616 617 red_error[x] += (red_total_error * five_sixteenth); 618 green_error[x] += (green_total_error * five_sixteenth); 619 blue_error[x] += (blue_total_error * five_sixteenth); 620 621 red_error[x + 1] = (red_total_error * one_sixteenth); 622 green_error[x + 1] = (green_total_error * one_sixteenth); 623 blue_error[x + 1] = (blue_total_error * one_sixteenth); 624 } 625 626 return cc->index; 627 #endif 628 629 int index = palette->IndexForColor(r, g, b, useAlphaForTransparency 630 ? a : 255); 631 632 if (index != palette->TransparentIndex() 633 && fSettings->SetGetBool(GIF_SETTING_USE_DITHERING)) { 634 int x = pixel % width; 635 // don't carry error on to next line when interlaced because 636 // that line won't be adjacent, hence error is meaningless 637 if (fSettings->SetGetBool(GIF_SETTING_INTERLACED) && x == width - 1) { 638 for (int32 y = -1; y < width + 1; y++) { 639 red_error[y] = 0; 640 green_error[y] = 0; 641 blue_error[y] = 0; 642 } 643 } 644 645 int32 red_total_error = palette->pal[index].red - r; 646 int32 green_total_error = palette->pal[index].green - g; 647 int32 blue_total_error = palette->pal[index].blue - b; 648 649 red_side_error = (red_error[x + 1] 650 + (red_total_error * seven_sixteenth)) >> 15; 651 blue_side_error = (blue_error[x + 1] 652 + (blue_total_error * seven_sixteenth)) >> 15; 653 green_side_error = (green_error[x + 1] 654 + (green_total_error * seven_sixteenth)) >> 15; 655 656 red_error[x - 1] += (red_total_error * three_sixteenth); 657 green_error[x - 1] += (green_total_error * three_sixteenth); 658 blue_error[x - 1] += (blue_total_error * three_sixteenth); 659 660 red_error[x] += (red_total_error * five_sixteenth); 661 green_error[x] += (green_total_error * five_sixteenth); 662 blue_error[x] += (blue_total_error * five_sixteenth); 663 664 red_error[x + 1] = (red_total_error * one_sixteenth); 665 green_error[x + 1] = (green_total_error * one_sixteenth); 666 blue_error[x + 1] = (blue_total_error * one_sixteenth); 667 } 668 669 return index; 670 } 671 672 673 void 674 GIFSave::InitFrame() 675 { 676 code_size = palette->SizeInBits(); 677 if (code_size == 1) 678 code_size++; 679 680 BITS = code_size + 1; 681 clear_code = 1 << code_size; 682 end_code = clear_code + 1; 683 next_code = clear_code + 2; 684 max_code = (1 << BITS) - 1; 685 string_code = 0; 686 character = 0; 687 table_size = 1 << LZ_MAX_BITS; 688 689 bit_count = 0; 690 bit_buffer = 0; 691 byte_count = 0; 692 693 pass = pos = 0; 694 row = gs_pass_starts_at[0]; 695 696 gifbits = (unsigned char*)bitmap->Bits(); 697 } 698