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