xref: /haiku/src/add-ons/translators/gif/GIFLoad.cpp (revision 4f00613311d0bd6b70fa82ce19931c41f071ea4e)
1 ////////////////////////////////////////////////////////////////////////////////
2 //
3 //	File: GIFLoad.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 #include "GIFLoad.h"
17 #include <ByteOrder.h>
18 #include <TranslatorFormats.h>
19 #include <InterfaceDefs.h>
20 #include <stdlib.h>
21 #include <stdio.h>
22 
23 extern bool debug;
24 
25 GIFLoad::GIFLoad(BPositionIO *input, BPositionIO *output) {
26 	this->input = input;
27 	this->output = output;
28 	Init();
29 
30 	if (!ReadGIFHeader()) {
31 		fatalerror = true;
32 		return;
33 	}
34 
35 	if (debug) printf("GIFLoad::GIFLoad() - Image dimensions are %d x %d\n", width, height);
36 
37 	unsigned char c;
38 	if (input->Read(&c, 1) < 1) {
39 		fatalerror = true;
40 		return;
41 	}
42 	while (c != 0x3b) {
43 		if (c == 0x2c) {
44 			if ((!ReadGIFImageHeader()) || (!ReadGIFImageData())) {
45 				if (debug) printf("GIFLoad::GIFLoad() - A fatal error occurred\n");
46 				fatalerror = true;
47 			} else {
48 				if (debug) printf("GIFLoad::GIFLoad() - Found a single image and leaving\n");
49 			}
50 			if (scanline != NULL) free(scanline);
51 			return;
52 		} else if (c == 0x21) {
53 			unsigned char d;
54 			if (input->Read(&d, 1) < 1) {
55 				fatalerror = true;
56 				return;
57 			}
58 			if (d == 0xff) {
59 				if (!ReadGIFLoopBlock()) {
60 					fatalerror = true;
61 					return;
62 				}
63 			} else if (d == 0xf9) {
64 				if (!ReadGIFControlBlock()) {
65 					fatalerror = true;
66 					return;
67 				}
68 			} else if (d == 0xfe) {
69 				if (!ReadGIFCommentBlock()) {
70 					fatalerror = true;
71 					return;
72 				}
73 			} else {
74 				if (!ReadGIFUnknownBlock(d)) {
75 					fatalerror = true;
76 					return;
77 				}
78 			}
79 		} else if (c != 0x00) {
80 			if (!ReadGIFUnknownBlock(c)) {
81 				fatalerror = true;
82 				return;
83 			}
84 		}
85 		if (input->Read(&c, 1) < 1) {
86 			fatalerror = true;
87 			return;
88 		}
89 	}
90 	if (debug) printf("GIFLoad::GIFLoad() - Done\n");
91 }
92 
93 void GIFLoad::Init() {
94 	fatalerror = false;
95 	scanline = NULL;
96 	palette = NULL;
97 	input->Seek(0, SEEK_SET);
98 	head_memblock = NULL;
99 }
100 
101 bool GIFLoad::ReadGIFHeader() {
102 	// Standard header
103 	unsigned char header[13];
104 	if (input->Read(header, 13) < 13) return false;
105 	width = header[6] + (header[7] << 8);
106 	height = header[8] + (header[9] << 8);
107 
108 	palette = new LoadPalette();
109 	// Global palette
110 	if (header[10] & 0x80) {
111 		palette->size_in_bits = (header[10] & 0x07) + 1;
112 		if (debug) printf("GIFLoad::ReadGIFHeader() - Found %d bit global palette\n", palette->size_in_bits);
113 		int s = 1 << palette->size_in_bits;
114 		palette->size = s;
115 
116 		unsigned char gp[256 * 3];
117 		if (input->Read(gp, s * 3) < s * 3) return false;
118 		for (int x = 0; x < s; x++) {
119 			palette->SetColor(x, gp[x * 3], gp[x * 3 + 1], gp[x * 3 + 2]);
120 		}
121 		palette->backgroundindex = header[11];
122 	} else { // Install BeOS system palette in case local palette isn't present
123 		color_map *map = (color_map *)system_colors();
124 		for (int x = 0; x < 256; x++) {
125 			palette->SetColor(x, map->color_list[x].red, map->color_list[x].green,
126 				map->color_list[x].blue);
127 		}
128 		palette->size = 256;
129 		palette->size_in_bits = 8;
130 	}
131 	return true;
132 }
133 
134 bool GIFLoad::ReadGIFLoopBlock() {
135 	unsigned char length;
136 	if (input->Read(&length, 1) < 1) return false;
137 	input->Seek(length, SEEK_CUR);
138 
139 	do {
140 		if (input->Read(&length, 1) < 1) {
141 			return false;
142 		}
143 		input->Seek(length, SEEK_CUR);
144 	} while (length != 0);
145 
146 	return true;
147 }
148 
149 bool GIFLoad::ReadGIFControlBlock() {
150 	unsigned char data[6];
151 	if (input->Read(data, 6) < 6) return false;
152 	if (data[1] & 0x01) {
153 		palette->usetransparent = true;
154 		palette->transparentindex = data[4];
155 		if (debug) printf("GIFLoad::ReadGIFControlBlock() - Transparency active, using palette index %d\n", data[4]);
156 	}
157 	return true;
158 }
159 
160 bool GIFLoad::ReadGIFCommentBlock() {
161 	if (debug) printf("GIFLoad::ReadGIFCommentBlock() - Found:\n");
162 	unsigned char length;
163 	char comment_data[256];
164 	do {
165 		if (input->Read(&length, 1) < 1) return false;
166 		if (input->Read(comment_data, length) < length) return false;
167 		comment_data[length] = 0x00;
168 		if (debug) printf("%s", comment_data);
169 	} while (length != 0x00);
170 	if (debug) printf("\n");
171 	return true;
172 }
173 
174 bool GIFLoad::ReadGIFUnknownBlock(unsigned char c) {
175 	if (debug) printf("GIFLoad::ReadGIFUnknownBlock() - Found: %d\n", c);
176 	unsigned char length;
177 	do {
178 		if (input->Read(&length, 1) < 1) return false;
179 		input->Seek(length, SEEK_CUR);
180 	} while (length != 0x00);
181 	return true;
182 }
183 
184 bool GIFLoad::ReadGIFImageHeader() {
185 	unsigned char data[9];
186 	if (input->Read(data, 9) < 9) return false;
187 
188 	int local_width = data[4] + (data[5] << 8);
189 	int local_height = data[6] + (data[7] << 8);
190 	if (width != local_width || height != local_height) {
191 		if (debug) printf("GIFLoad::ReadGIFImageHeader() - Local dimensions do not match global, setting to %d x %d\n",
192 			local_width, local_height);
193 		width = local_width;
194 		height = local_height;
195 	}
196 
197 	scanline = (uint32 *)malloc(width * 4);
198 	if (scanline == NULL) {
199 		if (debug) printf("GIFLoad::ReadGIFImageHeader() - Could not allocate scanline\n");
200 		return false;
201 	}
202 
203 	BRect rect(0, 0, width - 1, height - 1);
204 	TranslatorBitmap header;
205 	header.magic = B_HOST_TO_BENDIAN_INT32(B_TRANSLATOR_BITMAP);
206 	header.bounds.left = B_HOST_TO_BENDIAN_FLOAT(rect.left);
207 	header.bounds.top = B_HOST_TO_BENDIAN_FLOAT(rect.top);
208 	header.bounds.right = B_HOST_TO_BENDIAN_FLOAT(rect.right);
209 	header.bounds.bottom = B_HOST_TO_BENDIAN_FLOAT(rect.bottom);
210 	header.rowBytes = B_HOST_TO_BENDIAN_INT32(width * 4);
211 	header.colors = (color_space)B_HOST_TO_BENDIAN_INT32(B_RGBA32);
212 	header.dataSize = B_HOST_TO_BENDIAN_INT32(width * 4 * height);
213 	if (output->Write(&header, 32) < 32) return false;
214 
215 	// Has local palette
216 	if (data[8] & 0x80) {
217 		palette->size_in_bits = (data[8] & 0x07) + 1;
218 		int s = 1 << palette->size_in_bits;
219 		palette->size = s;
220 		if (debug) printf("GIFLoad::ReadGIFImageHeader() - Found %d bit local palette\n",
221 			palette->size_in_bits);
222 
223 		unsigned char lp[256 * 3];
224 		if (input->Read(lp, s * 3) < s * 3) return false;
225 		for (int x = 0; x < s; x++) {
226 			palette->SetColor(x, lp[x * 3], lp[x * 3 + 1], lp[x * 3 + 2]);
227 		}
228 	}
229 
230 	if (data[8] & 0x40) {
231 		interlaced = true;
232 		if (debug) printf("GIFLoad::ReadGIFImageHeader() - Image is interlaced\n");
233 	} else {
234 		interlaced = false;
235 		if (debug) printf("GIFLoad::ReadGIFImageHeader() - Image is not interlaced\n");
236 	}
237 	return true;
238 }
239 
240 bool GIFLoad::ReadGIFImageData() {
241 	unsigned char new_entry[4096];
242 
243 	unsigned char cs;
244 	input->Read(&cs, 1);
245 	if (cs == palette->size_in_bits) {
246 		if (!InitFrame(palette->size_in_bits)) return false;
247 	} else if (cs > palette->size_in_bits) {
248 		if (debug) printf("GIFLoad::ReadGIFImageData() - Code_size should be %d, not %d, allowing it\n", code_size, cs);
249 		if (!InitFrame(cs)) return false;
250 	} else if (cs < palette->size_in_bits) {
251 		if (debug) printf("GIFLoad::ReadGIFImageData() - Code_size should be %d, not %d\n", code_size, cs);
252 		return false;
253 	}
254 
255 	if (debug) printf("GIFLoad::ReadGIFImageData() - Starting LZW\n");
256 
257 	while ((new_code = NextCode()) != -1 && new_code != end_code) {
258 		if (new_code == clear_code) {
259 			ResetTable();
260 			new_code = NextCode();
261 			old_code[0] = new_code;
262 			old_code_length = 1;
263 			if (!OutputColor(old_code, 1)) goto bad_end;
264 			if (new_code == -1 || new_code == end_code) {
265 				if (debug) printf("GIFLoad::ReadGIFImageData() - Premature end_code or error\n");
266 				goto bad_end;
267 			}
268 			continue;
269 		}
270 
271 		// Explicitly check for lack of clear code at start of file
272 		if (old_code_length == 0) {
273 			old_code[0] = new_code;
274 			old_code_length = 1;
275 			if (!OutputColor(old_code, 1)) goto bad_end;
276 			continue;
277 		}
278 
279 		if (table[new_code] != NULL) { // Does exist in table
280 			if (!OutputColor(table[new_code], entry_size[new_code])) goto bad_end;
281 
282 			//memcpy(new_entry, old_code, old_code_length);
283 			for (int x = 0; x < old_code_length; x++) {
284 				new_entry[x] = old_code[x];
285 			}
286 
287 			//memcpy(new_entry + old_code_length, table[new_code], 1);
288 			new_entry[old_code_length] = *table[new_code];
289 		} else { // Does not exist in table
290 			//memcpy(new_entry, old_code, old_code_length);
291 			for (int x = 0; x < old_code_length; x++) {
292 				new_entry[x] = old_code[x];
293 			}
294 
295 			//memcpy(new_entry + old_code_length, old_code, 1);
296 			new_entry[old_code_length] = *old_code;
297 
298 			if (!OutputColor(new_entry, old_code_length + 1)) goto bad_end;
299 		}
300 		table[next_code] = MemblockAllocate(old_code_length + 1);
301 
302 		//memcpy(table[next_code], new_entry, old_code_length + 1);
303 		for (int x = 0; x < old_code_length + 1; x++) {
304 			table[next_code][x] = new_entry[x];
305 		}
306 
307 		entry_size[next_code] = old_code_length + 1;
308 
309 		//memcpy(old_code, table[new_code], entry_size[new_code]);
310 		for (int x = 0; x < entry_size[new_code]; x++) {
311 			old_code[x] = table[new_code][x];
312 		}
313 
314 		old_code_length = entry_size[new_code];
315 		next_code++;
316 
317 		if (next_code > max_code && BITS != 12) {
318 			BITS++;
319 			max_code = (1 << BITS) - 1;
320 		}
321 	}
322 
323 	MemblockDeleteAll();
324 	if (new_code == -1) return false;
325 	if (debug) printf("GIFLoad::ReadGIFImageData() - Done\n");
326 	return true;
327 
328 bad_end:
329 	if (debug) printf("GIFLoad::ReadGIFImageData() - Reached a bad end\n");
330 	MemblockDeleteAll();
331 	return false;
332 }
333 
334 short GIFLoad::NextCode() {
335 	while (bit_count < BITS) {
336 		if (byte_count == 0) {
337 			if (input->Read(&byte_count, 1) < 1) return -1;
338 			if (byte_count == 0) return end_code;
339 			if (input->Read(byte_buffer + (255 - byte_count), byte_count) < byte_count) return -1;
340 		}
341 		bit_buffer |= (unsigned int)byte_buffer[255 - byte_count] << bit_count;
342 		byte_count--;
343 		bit_count += 8;
344 	}
345 
346 	short s = bit_buffer & ((1 << BITS) - 1);
347 	bit_buffer >>= BITS;
348 	bit_count -= BITS;
349 	return s;
350 }
351 
352 void GIFLoad::ResetTable() {
353 	BITS = code_size + 1;
354 	next_code = clear_code + 2;
355 	max_code = (1 << BITS) - 1;
356 
357 	MemblockDeleteAll();
358 	for (int x = 0; x < 4096; x++) {
359 		table[x] = NULL;
360 		if (x < (1 << code_size)) {
361 			table[x] = MemblockAllocate(1);
362 			table[x][0] = x;
363 			entry_size[x] = 1;
364 		}
365 	}
366 }
367 
368 bool GIFLoad::InitFrame(int size) {
369 	code_size = size;
370 	if (code_size == 1) code_size++;
371 	BITS = code_size + 1;
372 	clear_code = 1 << code_size;
373 	end_code = clear_code + 1;
374 	next_code = clear_code + 2;
375 	max_code = (1 << BITS) - 1;
376 	pass = 0;
377 	if (interlaced) row = gl_pass_starts_at[0];
378 	else row = 0;
379 
380 	bit_count = 0;
381 	bit_buffer = 0;
382 	byte_count = 0;
383 	old_code_length = 0;
384 	new_code = 0;
385 	scanline_position = 0;
386 
387 	ResetTable();
388 	return true;
389 }
390 
391 // Do 4k mallocs, keep them in a linked list, do a first fit across them
392 // when a new request comes along
393 uchar *GIFLoad::MemblockAllocate(int size) {
394 	if (head_memblock == NULL) {
395 		head_memblock = new Memblock();
396 		uchar *value = head_memblock->data;
397 		head_memblock->offset = size;
398 		head_memblock->next = NULL;
399 		return value;
400 	} else {
401 		Memblock *block = head_memblock;
402 		Memblock *last = NULL;
403 		while (block != NULL) {
404 			if (4096 - block->offset > size) {
405 				uchar *value = block->data + block->offset;
406 				block->offset += size;
407 				return value;
408 			}
409 			last = block;
410 			block = block->next;
411 		}
412 
413 		block = new Memblock();
414 		uchar *value = block->data;
415 		block->offset = size;
416 		block->next = NULL;
417 		last->next = block;
418 		return value;
419 	}
420 }
421 
422 // Delete the linked list
423 void GIFLoad::MemblockDeleteAll() {
424 	Memblock *block = NULL;
425 	while (head_memblock != NULL) {
426 		block = head_memblock->next;
427 		delete head_memblock;
428 		head_memblock = block;
429 	}
430 }
431 
432 GIFLoad::~GIFLoad() {
433 	delete palette;
434 }
435 
436