xref: /haiku/src/add-ons/translators/gif/GIFLoad.cpp (revision f8da8f3477d3c18142e59d17d05a545982faa5a8)
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 
24 extern bool debug;
25 
26 
27 GIFLoad::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 
109 bool
110 GIFLoad::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 
148 bool
149 GIFLoad::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 
166 bool
167 GIFLoad::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 
183 bool
184 GIFLoad::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 
204 bool
205 GIFLoad::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 
219 bool
220 GIFLoad::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 
286 bool
287 GIFLoad::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 (unsigned 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 (unsigned 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 (unsigned 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 
390 bad_end:
391 	if (debug)
392 		syslog(LOG_ERR, "GIFLoad::ReadGIFImageData() - Reached a bad end\n");
393 	MemblockDeleteAll();
394 	return false;
395 }
396 
397 
398 short
399 GIFLoad::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 
420 void
421 GIFLoad::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 
439 bool
440 GIFLoad::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
470 uchar *
471 GIFLoad::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
503 void
504 GIFLoad::MemblockDeleteAll()
505 {
506 	Memblock *block = NULL;
507 	while (fHeadMemblock != NULL) {
508 		block = fHeadMemblock->next;
509 		delete fHeadMemblock;
510 		fHeadMemblock = block;
511 	}
512 }
513 
514 
515 GIFLoad::~GIFLoad()
516 {
517 	delete fPalette;
518 }
519 
520