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