1 ////////////////////////////////////////////////////////////////////////////////
7 // Author: Daniel Switkin
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>
27 #include <ByteOrder.h>
28 #include <InterfaceDefs.h>
29 #include <TranslatorFormats.h>
31 #include "GIFPrivate.h"
37 GIFLoad::GIFLoad(BPositionIO
* input
, BPositionIO
* output
)
46 fInput
->Seek(0, SEEK_SET
);
48 if (!ReadGIFHeader()) {
54 syslog(LOG_INFO
, "GIFLoad::GIFLoad() - Image dimensions are %d x %d\n",
59 if (fInput
->Read(&c
, 1) < 1) {
64 while (c
!= TERMINATOR_INTRODUCER
) {
65 if (c
== DESCRIPTOR_INTRODUCER
) {
66 if ((!ReadGIFImageHeader()) || (!ReadGIFImageData())) {
68 syslog(LOG_ERR
, "GIFLoad::GIFLoad() - "
69 "A fatal error occurred\n");
75 syslog(LOG_INFO
, "GIFLoad::GIFLoad() - "
76 "Found a single image and leaving\n");
82 } else if (c
== EXTENSION_INTRODUCER
) {
84 if (fInput
->Read(&d
, 1) < 1) {
88 if (d
== LOOP_BLOCK_LABEL
) {
89 if (!ReadGIFLoopBlock()) {
93 } else if (d
== GRAPHIC_CONTROL_LABEL
) {
94 if (!ReadGIFControlBlock()) {
98 } else if (d
== COMMENT_EXTENSION_LABEL
) {
99 if (!ReadGIFCommentBlock()) {
104 if (!ReadGIFUnknownBlock(d
)) {
109 } else if (c
!= BLOCK_TERMINATOR
) {
110 if (!ReadGIFUnknownBlock(c
)) {
116 if (fInput
->Read(&c
, 1) < 1) {
123 syslog(LOG_INFO
, "GIFLoad::GIFLoad() - Done\n");
134 GIFLoad::ReadGIFHeader()
137 unsigned char header
[13];
138 if (fInput
->Read(header
, 13) < 13)
141 fWidth
= header
[6] + (header
[7] << 8);
142 fHeight
= header
[8] + (header
[9] << 8);
144 fPalette
= new(std::nothrow
) LoadPalette();
145 if (fPalette
== NULL
)
149 if (header
[10] & GIF_LOCALCOLORMAP
) {
150 fPalette
->size_in_bits
= (header
[10] & 0x07) + 1;
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
;
159 unsigned char gp
[256 * 3];
160 if (fInput
->Read(gp
, s
* 3) < s
* 3)
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];
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;
183 GIFLoad::ReadGIFLoopBlock()
185 unsigned char length
;
186 if (fInput
->Read(&length
, 1) < 1)
189 fInput
->Seek(length
, SEEK_CUR
);
191 if (fInput
->Read(&length
, 1) < 1)
194 fInput
->Seek(length
, SEEK_CUR
);
195 } while (length
!= 0);
202 GIFLoad::ReadGIFControlBlock()
204 unsigned char data
[6];
205 if (fInput
->Read(data
, 6) < 6)
208 if ((data
[1] & 0x01) != 0) {
209 fPalette
->usetransparent
= true;
210 fPalette
->transparentindex
= data
[4];
212 syslog(LOG_INFO
, "GIFLoad::ReadGIFControlBlock() - "
213 "Transparency active, using palette index %d\n", data
[4]);
222 GIFLoad::ReadGIFCommentBlock()
225 syslog(LOG_INFO
, "GIFLoad::ReadGIFCommentBlock() - Found:\n");
227 unsigned char length
;
228 char comment_data
[256];
230 if (fInput
->Read(&length
, 1) < 1)
233 if (fInput
->Read(comment_data
, length
) < length
)
236 comment_data
[length
] = BLOCK_TERMINATOR
;
238 syslog(LOG_INFO
, "%s", comment_data
);
239 } while (length
!= BLOCK_TERMINATOR
);
242 syslog(LOG_INFO
, "\n");
249 GIFLoad::ReadGIFUnknownBlock(unsigned char c
)
252 syslog(LOG_INFO
, "GIFLoad::ReadGIFUnknownBlock() - Found: %d\n", c
);
254 unsigned char length
;
256 if (fInput
->Read(&length
, 1) < 1)
259 fInput
->Seek(length
, SEEK_CUR
);
260 } while (length
!= BLOCK_TERMINATOR
);
267 GIFLoad::ReadGIFImageHeader()
269 unsigned char data
[9];
270 if (fInput
->Read(data
, 9) < 9)
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
) {
279 syslog(LOG_ERR
, "GIFLoad::ReadGIFImageHeader() - "
280 "Local dimensions do not match global, setting to %d x %d\n",
281 localWidth
, localHeight
);
284 fHeight
= localHeight
;
287 fScanLine
= (uint32
*)malloc(fWidth
* 4);
288 if (fScanLine
== NULL
) {
290 syslog(LOG_ERR
, "GIFLoad::ReadGIFImageHeader() - "
291 "Could not allocate scanline\n");
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)
309 if (data
[8] & GIF_LOCALCOLORMAP
) {
311 fPalette
->size_in_bits
= (data
[8] & 0x07) + 1;
312 int s
= 1 << fPalette
->size_in_bits
;
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)
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
;
330 syslog(LOG_INFO
, "GIFLoad::ReadGIFImageHeader() - "
331 "Image is interlaced\n");
333 syslog(LOG_INFO
, "GIFLoad::ReadGIFImageHeader() - "
334 "Image is not interlaced\n");
343 GIFLoad::ReadGIFImageData()
345 unsigned char newEntry
[ENTRY_COUNT
];
346 unsigned char codeSize
;
347 if (fInput
->Read(&codeSize
, 1) < 1)
350 if (codeSize
> fPalette
->size_in_bits
) {
352 syslog(LOG_ERR
, "GIFLoad::ReadGIFImageData() - "
353 "Code_size should be %d, not %d, allowing it\n",
354 fCodeSize
, codeSize
);
356 if (!InitFrame(codeSize
))
358 } else if (codeSize
< fPalette
->size_in_bits
) {
360 syslog(LOG_ERR
, "GIFLoad::ReadGIFImageData() - "
361 "Code_size should be %d, not %d\n", fCodeSize
, codeSize
);
364 } else if (!InitFrame(fPalette
->size_in_bits
))
368 syslog(LOG_INFO
, "GIFLoad::ReadGIFImageData() - Starting LZW\n");
370 while ((fNewCode
= NextCode()) != -1 && fNewCode
!= fEndCode
) {
371 if (fNewCode
== fClearCode
) {
373 fNewCode
= NextCode();
374 fOldCode
[0] = fNewCode
;
376 if (!OutputColor(fOldCode
, 1))
379 if (fNewCode
== -1 || fNewCode
== fEndCode
) {
381 syslog(LOG_ERR
, "GIFLoad::ReadGIFImageData() - "
382 "Premature fEndCode or error reading fNewCode\n");
389 // explicitly check for lack of clear code at start of file
390 if (fOldCodeLength
== 0) {
391 fOldCode
[0] = fNewCode
;
393 if (!OutputColor(fOldCode
, 1))
399 // error out if we're trying to access an out-of-bounds index
400 if (fNextCode
>= ENTRY_COUNT
)
403 if (fTable
[fNewCode
] != NULL
) {
406 if (!OutputColor(fTable
[fNewCode
], fEntrySize
[fNewCode
]))
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];
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))
428 fTable
[fNextCode
] = MemblockAllocate(fOldCodeLength
+ 1);
429 if (fTable
[fNextCode
] == NULL
)
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
];
445 if (fNextCode
> fMaxCode
&& fBits
< LZ_MAX_BITS
) {
447 fMaxCode
= (1 << fBits
) - 1;
456 syslog(LOG_INFO
, "GIFLoad::ReadGIFImageData() - Done\n");
462 syslog(LOG_ERR
, "GIFLoad::ReadGIFImageData() - Reached a bad end\n");
472 while (fBitCount
< fBits
) {
473 if (fByteCount
== 0) {
474 if (fInput
->Read(&fByteCount
, 1) < 1)
480 if (fInput
->Read(fByteBuffer
+ (255 - fByteCount
), fByteCount
)
485 fBitBuffer
|= (unsigned int)fByteBuffer
[255 - fByteCount
] << fBitCount
;
490 short s
= fBitBuffer
& ((1 << fBits
) - 1);
491 fBitBuffer
>>= fBits
;
499 GIFLoad::ResetTable()
501 fBits
= fCodeSize
+ 1;
502 fNextCode
= fClearCode
+ 2;
503 fMaxCode
= (1 << fBits
) - 1;
506 for (int x
= 0; x
< ENTRY_COUNT
; x
++) {
508 if (x
< (1 << fCodeSize
)) {
509 fTable
[x
] = MemblockAllocate(1);
510 if (fTable
[x
] != NULL
) {
520 GIFLoad::InitFrame(int codeSize
)
522 fCodeSize
= codeSize
;
526 fBits
= fCodeSize
+ 1;
527 fClearCode
= 1 << fCodeSize
;
528 fEndCode
= fClearCode
+ 1;
529 fNextCode
= fClearCode
+ 2;
530 fMaxCode
= (1 << fBits
) - 1;
535 fRow
= gl_pass_starts_at
[0];
544 fScanlinePosition
= 0;
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
)
562 unsigned char* value
= fHeadMemblock
->data
;
563 fHeadMemblock
->offset
= size
;
564 fHeadMemblock
->next
= NULL
;
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
;
581 block
= (Memblock
*)malloc(sizeof(Memblock
));
585 unsigned char* value
= block
->data
;
586 block
->offset
= size
;
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
;
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
]);
619 if (fScanlinePosition
>= fWidth
) {
620 if (fOutput
->WriteAt(32 + (fRow
* bpr
), fScanLine
, bpr
) < bpr
)
623 fScanlinePosition
= 0;
625 fRow
+= gl_increment_pass_by
[fPass
];
626 while (fRow
>= fHeight
) {
631 fRow
= gl_pass_starts_at
[fPass
];