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// Additional authors:	John Scipione, <jscipione@gmail.com>
17
18
19#include "GIFLoad.h"
20
21#include <stdio.h>
22#include <stdlib.h>
23#include <syslog.h>
24
25#include <new>
26
27#include <ByteOrder.h>
28#include <InterfaceDefs.h>
29#include <TranslatorFormats.h>
30
31#include "GIFPrivate.h"
32
33
34extern bool debug;
35
36
37GIFLoad::GIFLoad(BPositionIO* input, BPositionIO* output)
38	:
39	fatalerror(false),
40	fInput(input),
41	fOutput(output),
42	fPalette(NULL),
43 	fHeadMemblock(NULL),
44	fScanLine(NULL)
45{
46	fInput->Seek(0, SEEK_SET);
47
48	if (!ReadGIFHeader()) {
49		fatalerror = true;
50		return;
51	}
52
53	if (debug) {
54		syslog(LOG_INFO, "GIFLoad::GIFLoad() - Image dimensions are %d x %d\n",
55			fWidth, fHeight);
56	}
57
58	unsigned char c;
59	if (fInput->Read(&c, 1) < 1) {
60		fatalerror = true;
61		return;
62	}
63
64	while (c != TERMINATOR_INTRODUCER) {
65		if (c == DESCRIPTOR_INTRODUCER) {
66			if ((!ReadGIFImageHeader()) || (!ReadGIFImageData())) {
67				if (debug) {
68					syslog(LOG_ERR, "GIFLoad::GIFLoad() - "
69						"A fatal error occurred\n");
70				}
71
72				fatalerror = true;
73			} else {
74				if (debug) {
75					syslog(LOG_INFO, "GIFLoad::GIFLoad() - "
76						"Found a single image and leaving\n");
77				}
78			}
79			free(fScanLine);
80			fScanLine = NULL;
81			return;
82		} else if (c == EXTENSION_INTRODUCER) {
83			unsigned char d;
84			if (fInput->Read(&d, 1) < 1) {
85				fatalerror = true;
86				return;
87			}
88			if (d == LOOP_BLOCK_LABEL) {
89				if (!ReadGIFLoopBlock()) {
90					fatalerror = true;
91					return;
92				}
93			} else if (d == GRAPHIC_CONTROL_LABEL) {
94				if (!ReadGIFControlBlock()) {
95					fatalerror = true;
96					return;
97				}
98			} else if (d == COMMENT_EXTENSION_LABEL) {
99				if (!ReadGIFCommentBlock()) {
100					fatalerror = true;
101					return;
102				}
103			} else {
104				if (!ReadGIFUnknownBlock(d)) {
105					fatalerror = true;
106					return;
107				}
108			}
109		} else if (c != BLOCK_TERMINATOR) {
110			if (!ReadGIFUnknownBlock(c)) {
111				fatalerror = true;
112				return;
113			}
114		}
115
116		if (fInput->Read(&c, 1) < 1) {
117			fatalerror = true;
118			return;
119		}
120	}
121
122	if (debug)
123		syslog(LOG_INFO, "GIFLoad::GIFLoad() - Done\n");
124}
125
126
127GIFLoad::~GIFLoad()
128{
129	delete fPalette;
130}
131
132
133bool
134GIFLoad::ReadGIFHeader()
135{
136	// standard header
137	unsigned char header[13];
138	if (fInput->Read(header, 13) < 13)
139		return false;
140
141	fWidth = header[6] + (header[7] << 8);
142	fHeight = header[8] + (header[9] << 8);
143
144	fPalette = new(std::nothrow) LoadPalette();
145	if (fPalette == NULL)
146		return false;
147
148	// Global palette
149	if (header[10] & GIF_LOCALCOLORMAP) {
150		fPalette->size_in_bits = (header[10] & 0x07) + 1;
151		if (debug) {
152			syslog(LOG_INFO, "GIFLoad::ReadGIFHeader() - "
153				"Found %d bit global palette\n",
154				fPalette->size_in_bits);
155		}
156		int s = 1 << fPalette->size_in_bits;
157		fPalette->size = s;
158
159		unsigned char gp[256 * 3];
160		if (fInput->Read(gp, s * 3) < s * 3)
161			return false;
162
163		for (int x = 0; x < s; x++)
164			fPalette->SetColor(x, gp[x * 3], gp[x * 3 + 1], gp[x * 3 + 2]);
165
166		fPalette->backgroundindex = header[11];
167	} else {
168		// install BeOS system palette in case local palette isn't present
169		color_map* map = (color_map*)system_colors();
170		for (int x = 0; x < 256; x++) {
171			fPalette->SetColor(x, map->color_list[x].red,
172				map->color_list[x].green, map->color_list[x].blue);
173		}
174		fPalette->size = 256;
175		fPalette->size_in_bits = 8;
176	}
177
178	return true;
179}
180
181
182bool
183GIFLoad::ReadGIFLoopBlock()
184{
185	unsigned char length;
186	if (fInput->Read(&length, 1) < 1)
187		return false;
188
189	fInput->Seek(length, SEEK_CUR);
190	do {
191		if (fInput->Read(&length, 1) < 1)
192			return false;
193
194		fInput->Seek(length, SEEK_CUR);
195	} while (length != 0);
196
197	return true;
198}
199
200
201bool
202GIFLoad::ReadGIFControlBlock()
203{
204	unsigned char data[6];
205	if (fInput->Read(data, 6) < 6)
206		return false;
207
208	if ((data[1] & 0x01) != 0) {
209		fPalette->usetransparent = true;
210		fPalette->transparentindex = data[4];
211		if (debug) {
212			syslog(LOG_INFO, "GIFLoad::ReadGIFControlBlock() - "
213				"Transparency active, using palette index %d\n", data[4]);
214		}
215	}
216
217	return true;
218}
219
220
221bool
222GIFLoad::ReadGIFCommentBlock()
223{
224	if (debug)
225		syslog(LOG_INFO, "GIFLoad::ReadGIFCommentBlock() - Found:\n");
226
227	unsigned char length;
228	char comment_data[256];
229	do {
230		if (fInput->Read(&length, 1) < 1)
231			return false;
232
233		if (fInput->Read(comment_data, length) < length)
234			return false;
235
236		comment_data[length] = BLOCK_TERMINATOR;
237		if (debug)
238			syslog(LOG_INFO, "%s", comment_data);
239	} while (length != BLOCK_TERMINATOR);
240
241	if (debug)
242		syslog(LOG_INFO, "\n");
243
244	return true;
245}
246
247
248bool
249GIFLoad::ReadGIFUnknownBlock(unsigned char c)
250{
251	if (debug)
252		syslog(LOG_INFO, "GIFLoad::ReadGIFUnknownBlock() - Found: %d\n", c);
253
254	unsigned char length;
255	do {
256		if (fInput->Read(&length, 1) < 1)
257			return false;
258
259		fInput->Seek(length, SEEK_CUR);
260	} while (length != BLOCK_TERMINATOR);
261
262	return true;
263}
264
265
266bool
267GIFLoad::ReadGIFImageHeader()
268{
269	unsigned char data[9];
270	if (fInput->Read(data, 9) < 9)
271		return false;
272
273	int left = data[0] + (data[1] << 8);
274	int top = data[2] + (data[3] << 8);
275	int localWidth = data[4] + (data[5] << 8);
276	int localHeight = data[6] + (data[7] << 8);
277	if (fWidth != localWidth || fHeight != localHeight) {
278		if (debug) {
279			syslog(LOG_ERR, "GIFLoad::ReadGIFImageHeader() - "
280				"Local dimensions do not match global, setting to %d x %d\n",
281				localWidth, localHeight);
282		}
283		fWidth = localWidth;
284		fHeight = localHeight;
285	}
286
287	fScanLine = (uint32*)malloc(fWidth * 4);
288	if (fScanLine == NULL) {
289		if (debug) {
290			syslog(LOG_ERR, "GIFLoad::ReadGIFImageHeader() - "
291				"Could not allocate scanline\n");
292		}
293		return false;
294	}
295
296	BRect rect(left, top, left + fWidth - 1, top + fHeight - 1);
297	TranslatorBitmap header;
298	header.magic = B_HOST_TO_BENDIAN_INT32(B_TRANSLATOR_BITMAP);
299	header.bounds.left = B_HOST_TO_BENDIAN_FLOAT(rect.left);
300	header.bounds.top = B_HOST_TO_BENDIAN_FLOAT(rect.top);
301	header.bounds.right = B_HOST_TO_BENDIAN_FLOAT(rect.right);
302	header.bounds.bottom = B_HOST_TO_BENDIAN_FLOAT(rect.bottom);
303	header.rowBytes = B_HOST_TO_BENDIAN_INT32(fWidth * 4);
304	header.colors = (color_space)B_HOST_TO_BENDIAN_INT32(B_RGBA32);
305	header.dataSize = B_HOST_TO_BENDIAN_INT32(fWidth * 4 * fHeight);
306	if (fOutput->Write(&header, 32) < 32)
307		return false;
308
309	if (data[8] & GIF_LOCALCOLORMAP) {
310		// has local palette
311		fPalette->size_in_bits = (data[8] & 0x07) + 1;
312		int s = 1 << fPalette->size_in_bits;
313		fPalette->size = s;
314		if (debug) {
315			syslog(LOG_INFO, "GIFLoad::ReadGIFImageHeader() - "
316				"Found %d bit local palette\n", fPalette->size_in_bits);
317		}
318
319		unsigned char lp[256 * 3];
320		if (fInput->Read(lp, s * 3) < s * 3)
321			return false;
322
323		for (int x = 0; x < s; x++)
324			fPalette->SetColor(x, lp[x * 3], lp[x * 3 + 1], lp[x * 3 + 2]);
325	}
326
327	fInterlaced = data[8] & GIF_INTERLACED;
328	if (debug) {
329		if (fInterlaced) {
330			syslog(LOG_INFO, "GIFLoad::ReadGIFImageHeader() - "
331				"Image is interlaced\n");
332		} else {
333			syslog(LOG_INFO, "GIFLoad::ReadGIFImageHeader() - "
334				"Image is not interlaced\n");
335		}
336	}
337
338	return true;
339}
340
341
342bool
343GIFLoad::ReadGIFImageData()
344{
345	unsigned char newEntry[ENTRY_COUNT];
346	unsigned char codeSize;
347	if (fInput->Read(&codeSize, 1) < 1)
348		return false;
349
350	if (codeSize > fPalette->size_in_bits) {
351		if (debug) {
352			syslog(LOG_ERR, "GIFLoad::ReadGIFImageData() - "
353				"Code_size should be %d, not %d, allowing it\n",
354				fCodeSize, codeSize);
355		}
356		if (!InitFrame(codeSize))
357			return false;
358	} else if (codeSize < fPalette->size_in_bits) {
359		if (debug) {
360			syslog(LOG_ERR, "GIFLoad::ReadGIFImageData() - "
361				"Code_size should be %d, not %d\n", fCodeSize, codeSize);
362		}
363		return false;
364	} else if (!InitFrame(fPalette->size_in_bits))
365		return false;
366
367	if (debug)
368		syslog(LOG_INFO, "GIFLoad::ReadGIFImageData() - Starting LZW\n");
369
370	while ((fNewCode = NextCode()) != -1 && fNewCode != fEndCode) {
371		if (fNewCode == fClearCode) {
372			ResetTable();
373			fNewCode = NextCode();
374			fOldCode[0] = fNewCode;
375			fOldCodeLength = 1;
376			if (!OutputColor(fOldCode, 1))
377				goto bad_end;
378
379			if (fNewCode == -1 || fNewCode == fEndCode) {
380				if (debug) {
381					syslog(LOG_ERR, "GIFLoad::ReadGIFImageData() - "
382						"Premature fEndCode or error reading fNewCode\n");
383				}
384				goto bad_end;
385			}
386			continue;
387		}
388
389		// explicitly check for lack of clear code at start of file
390		if (fOldCodeLength == 0) {
391			fOldCode[0] = fNewCode;
392			fOldCodeLength = 1;
393			if (!OutputColor(fOldCode, 1))
394				goto bad_end;
395
396			continue;
397		}
398
399		// error out if we're trying to access an out-of-bounds index
400		if (fNextCode >= ENTRY_COUNT)
401			goto bad_end;
402
403		if (fTable[fNewCode] != NULL) {
404			// exists in table
405
406			if (!OutputColor(fTable[fNewCode], fEntrySize[fNewCode]))
407				goto bad_end;
408
409			//memcpy(newEntry, fOldCode, fOldCodeLength);
410			for (unsigned int x = 0; x < fOldCodeLength; x++)
411				newEntry[x] = fOldCode[x];
412
413			//memcpy(newEntry + fOldCodeLength, fTable[fNewCode], 1);
414			newEntry[fOldCodeLength] = fTable[fNewCode][0];
415		} else {
416			// does not exist in table
417
418			//memcpy(newEntry, fOldCode, fOldCodeLength);
419			for (unsigned int x = 0; x < fOldCodeLength; x++)
420				newEntry[x] = fOldCode[x];
421
422			//memcpy(newEntry + fOldCodeLength, fOldCode, 1);
423			newEntry[fOldCodeLength] = fOldCode[0];
424
425			if (!OutputColor(newEntry, fOldCodeLength + 1))
426				goto bad_end;
427		}
428		fTable[fNextCode] = MemblockAllocate(fOldCodeLength + 1);
429		if (fTable[fNextCode] == NULL)
430			goto bad_end;
431
432		//memcpy(fTable[fNextCode], newEntry, fOldCodeLength + 1);
433		for (unsigned int x = 0; x < fOldCodeLength + 1; x++)
434			fTable[fNextCode][x] = newEntry[x];
435
436		fEntrySize[fNextCode] = fOldCodeLength + 1;
437
438		//memcpy(fOldCode, fTable[fNewCode], fEntrySize[fNewCode]);
439		for (int x = 0; x < fEntrySize[fNewCode]; x++)
440			fOldCode[x] = fTable[fNewCode][x];
441
442		fOldCodeLength = fEntrySize[fNewCode];
443		fNextCode++;
444
445		if (fNextCode > fMaxCode && fBits < LZ_MAX_BITS) {
446			fBits++;
447			fMaxCode = (1 << fBits) - 1;
448		}
449	}
450
451	MemblockDeleteAll();
452	if (fNewCode == -1)
453		return false;
454
455	if (debug)
456		syslog(LOG_INFO, "GIFLoad::ReadGIFImageData() - Done\n");
457
458	return true;
459
460bad_end:
461	if (debug)
462		syslog(LOG_ERR, "GIFLoad::ReadGIFImageData() - Reached a bad end\n");
463
464	MemblockDeleteAll();
465	return false;
466}
467
468
469short
470GIFLoad::NextCode()
471{
472	while (fBitCount < fBits) {
473		if (fByteCount == 0) {
474			if (fInput->Read(&fByteCount, 1) < 1)
475				return -1;
476
477			if (fByteCount == 0)
478				return fEndCode;
479
480			if (fInput->Read(fByteBuffer + (255 - fByteCount), fByteCount)
481					< fByteCount) {
482				return -1;
483			}
484		}
485		fBitBuffer |= (unsigned int)fByteBuffer[255 - fByteCount] << fBitCount;
486		fByteCount--;
487		fBitCount += 8;
488	}
489
490	short s = fBitBuffer & ((1 << fBits) - 1);
491	fBitBuffer >>= fBits;
492	fBitCount -= fBits;
493
494	return s;
495}
496
497
498void
499GIFLoad::ResetTable()
500{
501	fBits = fCodeSize + 1;
502	fNextCode = fClearCode + 2;
503	fMaxCode = (1 << fBits) - 1;
504
505	MemblockDeleteAll();
506	for (int x = 0; x < ENTRY_COUNT; x++) {
507		fTable[x] = NULL;
508		if (x < (1 << fCodeSize)) {
509			fTable[x] = MemblockAllocate(1);
510			if (fTable[x] != NULL) {
511				fTable[x][0] = x;
512				fEntrySize[x] = 1;
513			}
514		}
515	}
516}
517
518
519bool
520GIFLoad::InitFrame(int codeSize)
521{
522	fCodeSize = codeSize;
523	if (fCodeSize == 1)
524		fCodeSize++;
525
526	fBits = fCodeSize + 1;
527	fClearCode = 1 << fCodeSize;
528	fEndCode = fClearCode + 1;
529	fNextCode = fClearCode + 2;
530	fMaxCode = (1 << fBits) - 1;
531
532	fPass = 0;
533
534	if (fInterlaced)
535		fRow = gl_pass_starts_at[0];
536	else
537		fRow = 0;
538
539	fBitCount = 0;
540	fBitBuffer = 0;
541	fByteCount = 0;
542	fOldCodeLength = 0;
543	fNewCode = 0;
544	fScanlinePosition = 0;
545
546	ResetTable();
547	return true;
548}
549
550
551unsigned char*
552GIFLoad::MemblockAllocate(int size)
553{
554	// Do 4k mallocs, keep them in a linked list, do a first fit across
555	// them when a new request comes along.
556
557	if (fHeadMemblock == NULL) {
558		fHeadMemblock = (Memblock*)malloc(sizeof(Memblock));
559		if (fHeadMemblock == NULL)
560			return NULL;
561
562		unsigned char* value = fHeadMemblock->data;
563		fHeadMemblock->offset = size;
564		fHeadMemblock->next = NULL;
565
566		return value;
567	} else {
568		Memblock* block = fHeadMemblock;
569		Memblock* last = NULL;
570		while (block != NULL) {
571			if (ENTRY_COUNT - block->offset > size) {
572				unsigned char* value = block->data + block->offset;
573				block->offset += size;
574
575				return value;
576			}
577			last = block;
578			block = block->next;
579		}
580
581		block = (Memblock*)malloc(sizeof(Memblock));
582		if (block == NULL)
583			return NULL;
584
585		unsigned char* value = block->data;
586		block->offset = size;
587		block->next = NULL;
588		if (last != NULL)
589			last->next = block;
590
591		return value;
592	}
593}
594
595
596void
597GIFLoad::MemblockDeleteAll()
598{
599	Memblock* block = NULL;
600
601	while (fHeadMemblock != NULL) {
602		// delete the linked list
603		block = fHeadMemblock->next;
604		free(fHeadMemblock);
605		fHeadMemblock = block;
606	}
607}
608
609
610bool
611GIFLoad::OutputColor(unsigned char* string, int size)
612{
613	int bpr = fWidth << 2;
614
615	for (int x = 0; x < size; x++) {
616		fScanLine[fScanlinePosition] = fPalette->ColorForIndex(string[x]);
617		fScanlinePosition++;
618
619		if (fScanlinePosition >= fWidth) {
620			if (fOutput->WriteAt(32 + (fRow * bpr), fScanLine, bpr) < bpr)
621				return false;
622
623			fScanlinePosition = 0;
624			if (fInterlaced) {
625				fRow += gl_increment_pass_by[fPass];
626				while (fRow >= fHeight) {
627					fPass++;
628					if (fPass > 3)
629						return true;
630
631					fRow = gl_pass_starts_at[fPass];
632				}
633			} else
634				fRow++;
635		}
636	}
637
638	return true;
639}
640