tcp: Add APICall trace entry and move TRACEs into locked parts.
[haiku.git] / src / add-ons / translators / gif / GIFLoad.cpp
blob3196e5fbc903a32af4ca34f515598463a85daaee
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.
14 ////////////////////////////////////////////////////////////////////////////////
16 // Additional authors: John Scipione, <jscipione@gmail.com>
19 #include "GIFLoad.h"
21 #include <stdio.h>
22 #include <stdlib.h>
23 #include <syslog.h>
25 #include <new>
27 #include <ByteOrder.h>
28 #include <InterfaceDefs.h>
29 #include <TranslatorFormats.h>
31 #include "GIFPrivate.h"
34 extern bool debug;
37 GIFLoad::GIFLoad(BPositionIO* input, BPositionIO* output)
39 fatalerror(false),
40 fInput(input),
41 fOutput(output),
42 fPalette(NULL),
43 fHeadMemblock(NULL),
44 fScanLine(NULL)
46 fInput->Seek(0, SEEK_SET);
48 if (!ReadGIFHeader()) {
49 fatalerror = true;
50 return;
53 if (debug) {
54 syslog(LOG_INFO, "GIFLoad::GIFLoad() - Image dimensions are %d x %d\n",
55 fWidth, fHeight);
58 unsigned char c;
59 if (fInput->Read(&c, 1) < 1) {
60 fatalerror = true;
61 return;
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");
72 fatalerror = true;
73 } else {
74 if (debug) {
75 syslog(LOG_INFO, "GIFLoad::GIFLoad() - "
76 "Found a single image and leaving\n");
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;
88 if (d == LOOP_BLOCK_LABEL) {
89 if (!ReadGIFLoopBlock()) {
90 fatalerror = true;
91 return;
93 } else if (d == GRAPHIC_CONTROL_LABEL) {
94 if (!ReadGIFControlBlock()) {
95 fatalerror = true;
96 return;
98 } else if (d == COMMENT_EXTENSION_LABEL) {
99 if (!ReadGIFCommentBlock()) {
100 fatalerror = true;
101 return;
103 } else {
104 if (!ReadGIFUnknownBlock(d)) {
105 fatalerror = true;
106 return;
109 } else if (c != BLOCK_TERMINATOR) {
110 if (!ReadGIFUnknownBlock(c)) {
111 fatalerror = true;
112 return;
116 if (fInput->Read(&c, 1) < 1) {
117 fatalerror = true;
118 return;
122 if (debug)
123 syslog(LOG_INFO, "GIFLoad::GIFLoad() - Done\n");
127 GIFLoad::~GIFLoad()
129 delete fPalette;
133 bool
134 GIFLoad::ReadGIFHeader()
136 // standard header
137 unsigned char header[13];
138 if (fInput->Read(header, 13) < 13)
139 return false;
141 fWidth = header[6] + (header[7] << 8);
142 fHeight = header[8] + (header[9] << 8);
144 fPalette = new(std::nothrow) LoadPalette();
145 if (fPalette == NULL)
146 return false;
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);
156 int s = 1 << fPalette->size_in_bits;
157 fPalette->size = s;
159 unsigned char gp[256 * 3];
160 if (fInput->Read(gp, s * 3) < s * 3)
161 return false;
163 for (int x = 0; x < s; x++)
164 fPalette->SetColor(x, gp[x * 3], gp[x * 3 + 1], gp[x * 3 + 2]);
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);
174 fPalette->size = 256;
175 fPalette->size_in_bits = 8;
178 return true;
182 bool
183 GIFLoad::ReadGIFLoopBlock()
185 unsigned char length;
186 if (fInput->Read(&length, 1) < 1)
187 return false;
189 fInput->Seek(length, SEEK_CUR);
190 do {
191 if (fInput->Read(&length, 1) < 1)
192 return false;
194 fInput->Seek(length, SEEK_CUR);
195 } while (length != 0);
197 return true;
201 bool
202 GIFLoad::ReadGIFControlBlock()
204 unsigned char data[6];
205 if (fInput->Read(data, 6) < 6)
206 return false;
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]);
217 return true;
221 bool
222 GIFLoad::ReadGIFCommentBlock()
224 if (debug)
225 syslog(LOG_INFO, "GIFLoad::ReadGIFCommentBlock() - Found:\n");
227 unsigned char length;
228 char comment_data[256];
229 do {
230 if (fInput->Read(&length, 1) < 1)
231 return false;
233 if (fInput->Read(comment_data, length) < length)
234 return false;
236 comment_data[length] = BLOCK_TERMINATOR;
237 if (debug)
238 syslog(LOG_INFO, "%s", comment_data);
239 } while (length != BLOCK_TERMINATOR);
241 if (debug)
242 syslog(LOG_INFO, "\n");
244 return true;
248 bool
249 GIFLoad::ReadGIFUnknownBlock(unsigned char c)
251 if (debug)
252 syslog(LOG_INFO, "GIFLoad::ReadGIFUnknownBlock() - Found: %d\n", c);
254 unsigned char length;
255 do {
256 if (fInput->Read(&length, 1) < 1)
257 return false;
259 fInput->Seek(length, SEEK_CUR);
260 } while (length != BLOCK_TERMINATOR);
262 return true;
266 bool
267 GIFLoad::ReadGIFImageHeader()
269 unsigned char data[9];
270 if (fInput->Read(data, 9) < 9)
271 return false;
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);
283 fWidth = localWidth;
284 fHeight = localHeight;
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");
293 return false;
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;
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);
319 unsigned char lp[256 * 3];
320 if (fInput->Read(lp, s * 3) < s * 3)
321 return false;
323 for (int x = 0; x < s; x++)
324 fPalette->SetColor(x, lp[x * 3], lp[x * 3 + 1], lp[x * 3 + 2]);
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");
338 return true;
342 bool
343 GIFLoad::ReadGIFImageData()
345 unsigned char newEntry[ENTRY_COUNT];
346 unsigned char codeSize;
347 if (fInput->Read(&codeSize, 1) < 1)
348 return false;
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);
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);
363 return false;
364 } else if (!InitFrame(fPalette->size_in_bits))
365 return false;
367 if (debug)
368 syslog(LOG_INFO, "GIFLoad::ReadGIFImageData() - Starting LZW\n");
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;
379 if (fNewCode == -1 || fNewCode == fEndCode) {
380 if (debug) {
381 syslog(LOG_ERR, "GIFLoad::ReadGIFImageData() - "
382 "Premature fEndCode or error reading fNewCode\n");
384 goto bad_end;
386 continue;
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;
396 continue;
399 // error out if we're trying to access an out-of-bounds index
400 if (fNextCode >= ENTRY_COUNT)
401 goto bad_end;
403 if (fTable[fNewCode] != NULL) {
404 // exists in table
406 if (!OutputColor(fTable[fNewCode], fEntrySize[fNewCode]))
407 goto bad_end;
409 //memcpy(newEntry, fOldCode, fOldCodeLength);
410 for (unsigned int x = 0; x < fOldCodeLength; x++)
411 newEntry[x] = fOldCode[x];
413 //memcpy(newEntry + fOldCodeLength, fTable[fNewCode], 1);
414 newEntry[fOldCodeLength] = fTable[fNewCode][0];
415 } else {
416 // does not exist in table
418 //memcpy(newEntry, fOldCode, fOldCodeLength);
419 for (unsigned int x = 0; x < fOldCodeLength; x++)
420 newEntry[x] = fOldCode[x];
422 //memcpy(newEntry + fOldCodeLength, fOldCode, 1);
423 newEntry[fOldCodeLength] = fOldCode[0];
425 if (!OutputColor(newEntry, fOldCodeLength + 1))
426 goto bad_end;
428 fTable[fNextCode] = MemblockAllocate(fOldCodeLength + 1);
429 if (fTable[fNextCode] == NULL)
430 goto bad_end;
432 //memcpy(fTable[fNextCode], newEntry, fOldCodeLength + 1);
433 for (unsigned int x = 0; x < fOldCodeLength + 1; x++)
434 fTable[fNextCode][x] = newEntry[x];
436 fEntrySize[fNextCode] = fOldCodeLength + 1;
438 //memcpy(fOldCode, fTable[fNewCode], fEntrySize[fNewCode]);
439 for (int x = 0; x < fEntrySize[fNewCode]; x++)
440 fOldCode[x] = fTable[fNewCode][x];
442 fOldCodeLength = fEntrySize[fNewCode];
443 fNextCode++;
445 if (fNextCode > fMaxCode && fBits < LZ_MAX_BITS) {
446 fBits++;
447 fMaxCode = (1 << fBits) - 1;
451 MemblockDeleteAll();
452 if (fNewCode == -1)
453 return false;
455 if (debug)
456 syslog(LOG_INFO, "GIFLoad::ReadGIFImageData() - Done\n");
458 return true;
460 bad_end:
461 if (debug)
462 syslog(LOG_ERR, "GIFLoad::ReadGIFImageData() - Reached a bad end\n");
464 MemblockDeleteAll();
465 return false;
469 short
470 GIFLoad::NextCode()
472 while (fBitCount < fBits) {
473 if (fByteCount == 0) {
474 if (fInput->Read(&fByteCount, 1) < 1)
475 return -1;
477 if (fByteCount == 0)
478 return fEndCode;
480 if (fInput->Read(fByteBuffer + (255 - fByteCount), fByteCount)
481 < fByteCount) {
482 return -1;
485 fBitBuffer |= (unsigned int)fByteBuffer[255 - fByteCount] << fBitCount;
486 fByteCount--;
487 fBitCount += 8;
490 short s = fBitBuffer & ((1 << fBits) - 1);
491 fBitBuffer >>= fBits;
492 fBitCount -= fBits;
494 return s;
498 void
499 GIFLoad::ResetTable()
501 fBits = fCodeSize + 1;
502 fNextCode = fClearCode + 2;
503 fMaxCode = (1 << fBits) - 1;
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;
519 bool
520 GIFLoad::InitFrame(int codeSize)
522 fCodeSize = codeSize;
523 if (fCodeSize == 1)
524 fCodeSize++;
526 fBits = fCodeSize + 1;
527 fClearCode = 1 << fCodeSize;
528 fEndCode = fClearCode + 1;
529 fNextCode = fClearCode + 2;
530 fMaxCode = (1 << fBits) - 1;
532 fPass = 0;
534 if (fInterlaced)
535 fRow = gl_pass_starts_at[0];
536 else
537 fRow = 0;
539 fBitCount = 0;
540 fBitBuffer = 0;
541 fByteCount = 0;
542 fOldCodeLength = 0;
543 fNewCode = 0;
544 fScanlinePosition = 0;
546 ResetTable();
547 return true;
551 unsigned char*
552 GIFLoad::MemblockAllocate(int size)
554 // Do 4k mallocs, keep them in a linked list, do a first fit across
555 // them when a new request comes along.
557 if (fHeadMemblock == NULL) {
558 fHeadMemblock = (Memblock*)malloc(sizeof(Memblock));
559 if (fHeadMemblock == NULL)
560 return NULL;
562 unsigned char* value = fHeadMemblock->data;
563 fHeadMemblock->offset = size;
564 fHeadMemblock->next = NULL;
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;
575 return value;
577 last = block;
578 block = block->next;
581 block = (Memblock*)malloc(sizeof(Memblock));
582 if (block == NULL)
583 return NULL;
585 unsigned char* value = block->data;
586 block->offset = size;
587 block->next = NULL;
588 if (last != NULL)
589 last->next = block;
591 return value;
596 void
597 GIFLoad::MemblockDeleteAll()
599 Memblock* block = NULL;
601 while (fHeadMemblock != NULL) {
602 // delete the linked list
603 block = fHeadMemblock->next;
604 delete fHeadMemblock;
605 fHeadMemblock = block;
610 bool
611 GIFLoad::OutputColor(unsigned char* string, int size)
613 int bpr = fWidth << 2;
615 for (int x = 0; x < size; x++) {
616 fScanLine[fScanlinePosition] = fPalette->ColorForIndex(string[x]);
617 fScanlinePosition++;
619 if (fScanlinePosition >= fWidth) {
620 if (fOutput->WriteAt(32 + (fRow * bpr), fScanLine, bpr) < bpr)
621 return false;
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;
631 fRow = gl_pass_starts_at[fPass];
633 } else
634 fRow++;
638 return true;