xref: /haiku/src/add-ons/translators/gif/GIFLoad.cpp (revision 13581b3d2a71545960b98fefebc5225b5bf29072)
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 
34 extern bool debug;
35 
36 
37 GIFLoad::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 
127 GIFLoad::~GIFLoad()
128 {
129 	delete fPalette;
130 }
131 
132 
133 bool
134 GIFLoad::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 
182 bool
183 GIFLoad::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 
201 bool
202 GIFLoad::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 
221 bool
222 GIFLoad::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 
248 bool
249 GIFLoad::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 
266 bool
267 GIFLoad::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 
342 bool
343 GIFLoad::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 
460 bad_end:
461 	if (debug)
462 		syslog(LOG_ERR, "GIFLoad::ReadGIFImageData() - Reached a bad end\n");
463 
464 	MemblockDeleteAll();
465 	return false;
466 }
467 
468 
469 short
470 GIFLoad::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 
498 void
499 GIFLoad::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 
519 bool
520 GIFLoad::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 
551 unsigned char*
552 GIFLoad::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 
596 void
597 GIFLoad::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 
610 bool
611 GIFLoad::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