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