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 18 #include "GIFSave.h" 19 #include <stdio.h> 20 #include <stdlib.h> 21 22 const int gs_pass_starts_at[] = {0, 4, 2, 1, 0}; 23 const int gs_increment_pass_by[] = {8, 8, 4, 2, 0}; 24 const int32 one_sixteenth = (int32)((1.0 / 16.0) * 32768); 25 const int32 three_sixteenth = (int32)((3.0 / 16.0) * 32768); 26 const int32 five_sixteenth = (int32)((5.0 / 16.0) * 32768); 27 const int32 seven_sixteenth = (int32)((7.0 / 16.0) * 32768); 28 29 extern bool debug; 30 31 class ColorCache : public HashItem { 32 public: 33 unsigned char index; 34 }; 35 36 // constructor 37 GIFSave::GIFSave(BBitmap *bitmap, BPositionIO *output) 38 { 39 color_space cs = bitmap->ColorSpace(); 40 if (cs != B_RGB32 && cs != B_RGBA32 && cs != B_RGB32_BIG && cs != B_RGBA32_BIG) { 41 if (debug) printf("GIFSave::GIFSave() - Unknown color space\n"); 42 fatalerror = true; 43 return; 44 } 45 46 fatalerror = false; 47 prefs = new Prefs(); 48 if (prefs->palettemode == OPTIMAL_PALETTE) 49 palette = new SavePalette(bitmap, prefs->palette_size_in_bits); 50 else 51 palette = new SavePalette(prefs->palettemode); 52 if (!palette->IsValid()) { 53 fatalerror = true; 54 return; 55 } 56 57 width = bitmap->Bounds().IntegerWidth() + 1; 58 height = bitmap->Bounds().IntegerHeight() + 1; 59 if (debug) printf("GIFSave::GIFSave() - Image dimensions are %d by %d\n", width, height); 60 61 if (prefs->usedithering) { 62 if (debug) printf("GIFSave::GIFSave() - Using dithering\n"); 63 red_error = new int32[width + 2]; 64 red_error = &red_error[1]; // Allow index of -1 too 65 green_error = new int32[width + 2]; 66 green_error = &green_error[1]; // Allow index of -1 too 67 blue_error = new int32[width + 2]; 68 blue_error = &blue_error[1]; // Allow index of -1 too 69 70 red_side_error = green_side_error = blue_side_error = 0; 71 for (int32 x = -1; x < width + 1; x++) { 72 red_error[x] = 0; 73 green_error[x] = 0; 74 blue_error[x] = 0; 75 } 76 } else { 77 if (debug) printf("GIFSave::GIFSave() - Not using dithering\n"); 78 } 79 80 if (debug) { 81 if (prefs->interlaced) printf("GIFSave::GIFSave() - Interlaced, "); 82 else printf("GIFSave::GIFSave() - Not interlaced, "); 83 switch (prefs->palettemode) { 84 case WEB_SAFE_PALETTE: 85 printf("web safe palette\n"); 86 break; 87 case BEOS_SYSTEM_PALETTE: 88 printf("BeOS system palette\n"); 89 break; 90 case GREYSCALE_PALETTE: 91 printf("greyscale palette\n"); 92 break; 93 case OPTIMAL_PALETTE: 94 default: 95 printf("optimal palette\n"); 96 break; 97 } 98 } 99 100 if (prefs->usetransparent) { 101 if (prefs->usetransparentauto) { 102 palette->PrepareForAutoTransparency(); 103 if (debug) 104 printf("GIFSave::GIFSave() - Using transparent index %d\n", palette->TransparentIndex()); 105 } else { 106 palette->SetTransparentColor((uint8)prefs->transparentred, 107 (uint8)prefs->transparentgreen, 108 (uint8)prefs->transparentblue); 109 if (debug) { 110 printf("GIFSave::GIFSave() - Found transparent color %d,%d,%d at index %d\n", 111 prefs->transparentred, prefs->transparentgreen, prefs->transparentblue, 112 palette->TransparentIndex()); 113 } 114 } 115 } else { 116 if (debug) 117 printf("GIFSave::GIFSave() - Not using transparency\n"); 118 } 119 120 this->output = output; 121 this->bitmap = bitmap; 122 WriteGIFHeader(); 123 if (debug) printf("GIFSave::GIFSave() - Wrote gif header\n"); 124 125 hash = new SFHash(1 << 16); 126 WriteGIFControlBlock(); 127 if (debug) printf("GIFSave::GIFSave() - Wrote gif control block\n"); 128 WriteGIFImageHeader(); 129 if (debug) printf("GIFSave::GIFSave() - Wrote gif image header\n"); 130 WriteGIFImageData(); 131 if (debug) printf("GIFSave::GIFSave() - Wrote gif image data\n"); 132 133 if (prefs->usedithering) { 134 delete [] &red_error[-1]; 135 delete [] &green_error[-1]; 136 delete [] &blue_error[-1]; 137 } 138 delete hash; 139 140 // Terminating character 141 char t = 0x3b; 142 output->Write(&t, 1); 143 } 144 145 // destructor 146 GIFSave::~GIFSave() 147 { 148 delete palette; 149 delete prefs; 150 } 151 152 // WriteGIFHeader 153 void 154 GIFSave::WriteGIFHeader() 155 { 156 // Standard header 157 unsigned char header[] = {'G', 'I', 'F', '8', '9', 'a', 0, 0, 0, 0, 0, 0, 0}; 158 header[6] = width & 0xff; 159 header[7] = (width & 0xff00) >> 8; 160 header[8] = height & 0xff; 161 header[9] = (height & 0xff00) >> 8; 162 header[10] = 0xf0 | (palette->SizeInBits() - 1); 163 header[11] = palette->BackgroundIndex(); 164 output->Write(header, 13); 165 166 // Global palette 167 int size = (1 << palette->SizeInBits()) * 3; 168 uint8* buffer = new uint8[size]; // can't be bigger than this 169 palette->GetColors(buffer, size); 170 output->Write(buffer, size); 171 delete[] buffer; 172 } 173 174 // WriteGIFControlBlock 175 void 176 GIFSave::WriteGIFControlBlock() 177 { 178 unsigned char b[8] = {0x21, 0xf9, 0x04, 0, 0, 0, 0, 0x00}; 179 if (palette->UseTransparent()) { 180 b[3] = b[3] | 1; 181 b[6] = palette->TransparentIndex(); 182 } 183 output->Write(b, 8); 184 } 185 186 // WriteGIFImageHeader 187 void GIFSave::WriteGIFImageHeader() 188 { 189 unsigned char header[10]; 190 header[0] = 0x2c; 191 header[1] = header[2] = 0; 192 header[3] = header[4] = 0; 193 194 header[5] = width & 0xff; 195 header[6] = (width & 0xff00) >> 8; 196 header[7] = height & 0xff; 197 header[8] = (height & 0xff00) >> 8; 198 199 if (prefs->interlaced) header[9] = 0x40; 200 else header[9] = 0x00; 201 output->Write(header, 10); 202 } 203 204 // WriteGIFImageData 205 void GIFSave::WriteGIFImageData() 206 { 207 InitFrame(); 208 code_value = (short *)malloc(HASHSIZE * 2); 209 prefix_code = (short *)malloc(HASHSIZE * 2); 210 append_char = (unsigned char *)malloc(HASHSIZE); 211 ResetHashtable(); 212 213 output->Write(&code_size, 1); 214 OutputCode(clear_code, BITS); 215 string_code = NextPixel(0); 216 int area = height * width; 217 218 for (int x = 1; x < area; x++) { 219 character = NextPixel(x); 220 int y = 0; 221 if ((y = CheckHashtable(string_code, character)) != -1) { 222 string_code = y; 223 } else { 224 AddToHashtable(string_code, character); 225 OutputCode(string_code, BITS); 226 227 if (next_code > max_code) { 228 BITS++; 229 if (BITS > 12) { 230 OutputCode(clear_code, 12); 231 BITS = code_size + 1; 232 ResetHashtable(); 233 next_code = clear_code + 1; // this is different 234 } 235 max_code = (1 << BITS) - 1; 236 } 237 string_code = character; 238 next_code++; 239 } 240 } 241 OutputCode(string_code, BITS); 242 OutputCode(end_code, BITS); 243 OutputCode(0, BITS, true); 244 char t = 0x00; 245 output->Write(&t, 1); 246 free(code_value); 247 free(prefix_code); 248 free(append_char); 249 } 250 251 // OutputCode 252 void 253 GIFSave::OutputCode(short code, int BITS, bool flush) 254 { 255 if (!flush) { 256 bit_buffer |= (unsigned int) code << bit_count; 257 bit_count += BITS; 258 while (bit_count >= 8) { 259 byte_buffer[byte_count + 1] = (unsigned char)(bit_buffer & 0xff); 260 byte_count++; 261 bit_buffer >>= 8; 262 bit_count -= 8; 263 } 264 if (byte_count >= 255) { 265 byte_buffer[0] = 255; 266 output->Write(byte_buffer, 256); 267 if (byte_count == 256) { 268 byte_buffer[1] = byte_buffer[256]; 269 byte_count = 1; 270 } else byte_count = 0; 271 } 272 } else { 273 bit_buffer |= (unsigned int) code << bit_count; 274 bit_count += BITS; 275 while (bit_count > 0) { 276 byte_buffer[byte_count + 1] = (unsigned char)(bit_buffer & 0xff); 277 byte_count++; 278 bit_buffer >>= 8; 279 bit_count -= 8; 280 } 281 if (byte_count > 0) { 282 byte_buffer[0] = (unsigned char)byte_count; 283 output->Write(byte_buffer, byte_count + 1); 284 } 285 } 286 } 287 288 // ResetHashtable 289 void 290 GIFSave::ResetHashtable() 291 { 292 for (int q = 0; q < HASHSIZE; q++) { 293 code_value[q] = -1; 294 prefix_code[q] = 0; 295 append_char[q] = 0; 296 } 297 } 298 299 // CheckHashtable 300 int 301 GIFSave::CheckHashtable(int s, unsigned char c) 302 { 303 if (s == -1) return c; 304 int hashindex = HASH(s, c); 305 int nextindex; 306 while ((nextindex = code_value[hashindex]) != -1) { 307 if (prefix_code[nextindex] == s && append_char[nextindex] == c) 308 return nextindex; 309 hashindex = (hashindex + HASHSTEP) % HASHSIZE; 310 } 311 return -1; 312 } 313 314 // AddToHashtable 315 void 316 GIFSave::AddToHashtable(int s, unsigned char c) 317 { 318 int hashindex = HASH(s, c); 319 while (code_value[hashindex] != -1) hashindex = (hashindex + HASHSTEP) % HASHSIZE; 320 code_value[hashindex] = next_code; 321 prefix_code[next_code] = s; 322 append_char[next_code] = c; 323 } 324 325 // NextPixel 326 unsigned char 327 GIFSave::NextPixel(int pixel) 328 { 329 int bpr = bitmap->BytesPerRow(); 330 color_space cs = bitmap->ColorSpace(); 331 bool useAlphaForTransparency = prefs->usetransparentauto && cs == B_RGBA32 || cs == B_RGBA32_BIG; 332 unsigned char r, g, b, a; 333 334 if (cs == B_RGB32 || cs == B_RGBA32) { 335 b = gifbits[0]; 336 g = gifbits[1]; 337 r = gifbits[2]; 338 a = gifbits[3]; 339 } else { 340 a = gifbits[0]; 341 r = gifbits[1]; 342 g = gifbits[2]; 343 b = gifbits[3]; 344 } 345 gifbits += 4; 346 pos += 4; 347 348 if (!prefs->usetransparent || prefs->usetransparentauto || 349 r != prefs->transparentred || 350 g != prefs->transparentgreen || 351 b != prefs->transparentblue) { 352 353 if (prefs->usedithering) { 354 if (pixel % width == 0) { 355 red_side_error = green_side_error = blue_side_error = 0; 356 } 357 b = min_c(255, max_c(0, b - blue_side_error)); 358 g = min_c(255, max_c(0, g - green_side_error)); 359 r = min_c(255, max_c(0, r - red_side_error)); 360 } 361 } 362 363 if (prefs->interlaced) { 364 if (pos >= bpr) { 365 pos = 0; 366 row += gs_increment_pass_by[pass]; 367 while (row >= height) { 368 pass++; 369 row = gs_pass_starts_at[pass]; 370 } 371 gifbits = (unsigned char *)bitmap->Bits() + (bpr * row); 372 } 373 } 374 /* 375 unsigned int key = (r << 16) + (g << 8) + b; 376 ColorCache *cc = (ColorCache *)hash->GetItem(key); 377 if (cc == NULL) { 378 cc = new ColorCache(); 379 cc->key = key; 380 cc->index = palette->IndexForColor(r, g, b); 381 hash->AddItem((HashItem *)cc); 382 } 383 384 if (prefs->usedithering) { 385 int x = pixel % width; 386 // Don't carry error on to next line when interlaced because 387 // that line won't be adjacent, hence error is meaningless 388 if (prefs->interlaced && x == width - 1) { 389 for (int32 y = -1; y < width + 1; y++) { 390 red_error[y] = 0; 391 green_error[y] = 0; 392 blue_error[y] = 0; 393 } 394 } 395 396 int32 red_total_error = palette->pal[cc->index].red - r; 397 int32 green_total_error = palette->pal[cc->index].green - g; 398 int32 blue_total_error = palette->pal[cc->index].blue - b; 399 400 red_side_error = (red_error[x + 1] + (red_total_error * seven_sixteenth)) >> 15; 401 blue_side_error = (blue_error[x + 1] + (blue_total_error * seven_sixteenth)) >> 15; 402 green_side_error = (green_error[x + 1] + (green_total_error * seven_sixteenth)) >> 15; 403 404 red_error[x - 1] += (red_total_error * three_sixteenth); 405 green_error[x - 1] += (green_total_error * three_sixteenth); 406 blue_error[x - 1] += (blue_total_error * three_sixteenth); 407 408 red_error[x] += (red_total_error * five_sixteenth); 409 green_error[x] += (green_total_error * five_sixteenth); 410 blue_error[x] += (blue_total_error * five_sixteenth); 411 412 red_error[x + 1] = (red_total_error * one_sixteenth); 413 green_error[x + 1] = (green_total_error * one_sixteenth); 414 blue_error[x + 1] = (blue_total_error * one_sixteenth); 415 } 416 417 return cc->index;*/ 418 419 int index = palette->IndexForColor(r, g, b, useAlphaForTransparency ? a : 255); 420 421 if (index != palette->TransparentIndex() && prefs->usedithering) { 422 int x = pixel % width; 423 // Don't carry error on to next line when interlaced because 424 // that line won't be adjacent, hence error is meaningless 425 if (prefs->interlaced && x == width - 1) { 426 for (int32 y = -1; y < width + 1; y++) { 427 red_error[y] = 0; 428 green_error[y] = 0; 429 blue_error[y] = 0; 430 } 431 } 432 433 int32 red_total_error = palette->pal[index].red - r; 434 int32 green_total_error = palette->pal[index].green - g; 435 int32 blue_total_error = palette->pal[index].blue - b; 436 437 red_side_error = (red_error[x + 1] + (red_total_error * seven_sixteenth)) >> 15; 438 blue_side_error = (blue_error[x + 1] + (blue_total_error * seven_sixteenth)) >> 15; 439 green_side_error = (green_error[x + 1] + (green_total_error * seven_sixteenth)) >> 15; 440 441 red_error[x - 1] += (red_total_error * three_sixteenth); 442 green_error[x - 1] += (green_total_error * three_sixteenth); 443 blue_error[x - 1] += (blue_total_error * three_sixteenth); 444 445 red_error[x] += (red_total_error * five_sixteenth); 446 green_error[x] += (green_total_error * five_sixteenth); 447 blue_error[x] += (blue_total_error * five_sixteenth); 448 449 red_error[x + 1] = (red_total_error * one_sixteenth); 450 green_error[x + 1] = (green_total_error * one_sixteenth); 451 blue_error[x + 1] = (blue_total_error * one_sixteenth); 452 } 453 454 return index; 455 } 456 457 // InitFrame 458 void 459 GIFSave::InitFrame() 460 { 461 code_size = palette->SizeInBits(); 462 if (code_size == 1) 463 code_size++; 464 BITS = code_size + 1; 465 clear_code = 1 << code_size; 466 end_code = clear_code + 1; 467 next_code = clear_code + 2; 468 max_code = (1 << BITS) - 1; 469 string_code = 0; 470 character = 0; 471 table_size = 1 << 12; 472 473 bit_count = 0; 474 bit_buffer = 0; 475 byte_count = 0; 476 477 pass = pos = 0; 478 row = gs_pass_starts_at[0]; 479 480 gifbits = (unsigned char *)bitmap->Bits(); 481 } 482