xref: /haiku/src/add-ons/translators/gif/GIFSave.cpp (revision 1e36cfc2721ef13a187c6f7354dc9cbc485e89d3)
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