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