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