3 Part of XPCE --- The SWI-Prolog GUI toolkit
5 Author: Jan Wielemaker and Anjo Anjewierden
6 E-mail: jan@swi.psy.uva.nl
7 WWW: http://www.swi.psy.uva.nl/projects/xpce/
8 Copyright (C): 1985-2002, University of Amsterdam
10 This library is free software; you can redistribute it and/or
11 modify it under the terms of the GNU Lesser General Public
12 License as published by the Free Software Foundation; either
13 version 2.1 of the License, or (at your option) any later version.
15 This library is distributed in the hope that it will be useful,
16 but WITHOUT ANY WARRANTY; without even the implied warranty of
17 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
18 Lesser General Public License for more details.
20 You should have received a copy of the GNU Lesser General Public
21 License along with this library; if not, write to the Free Software
22 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
25 /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
26 This file reads a .GIF image file. It is used by giftoxpm.c to create an
27 XpmImage structure from a .GIF file. After that, we can use
28 XpmCreateImagefromImage() to do the tricky things such as colour
29 allocation. This route is probably a little slower than the direct one,
30 but acceptable and a lot less code.
32 The code in this file is based on gifread.cpp by chrisdl@pagesz.net,
33 from whom I cannot find his real name. He based his code on
34 ``Programming for Graphics Files'' by John Levine. I'm grateful for his
37 http://www.pagesz.net/~chrisdl/software/jpegfile.htm
39 This reads GIF 87a and 89a.
41 I converted the code to plain ANSI-C and changed various Windows idiom
42 to standard C for easier integration in XPCE and modified the interface
43 to reuse the Xpm package to do the tricky work.
44 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
50 #define INTERLACE 0x40
51 #define LOCALCOLORMAP 0x80
52 #define BitSet(byte,bit) (((byte) & (bit))==(bit))
53 #define ReadOK(file,buffer,len) (Sfread(buffer,1,(len),file)==(len))
54 #define LM_to_uint(a,b) (((b)<<8)|(a))
56 typedef short int code_int; /* was int */
57 typedef long int count_int;
58 typedef unsigned char pixval;
61 ReadColorMap(IOSTREAM *fd, int number,
62 GIFAllocColorTable at, GIFAllocColor ac, void *closure);
63 static int DoExtension(IOSTREAM *fd, int label,
64 GIFDoExtension func, void *closure);
65 static int GetDataBlock(IOSTREAM *fd, UCHAR *buf);
66 static int GetCode (IOSTREAM *fd, int code_size, int flag);
67 static int LZWReadByte (IOSTREAM *fd,int flag, int input_code_size);
69 static int ReadImage(IOSTREAM *fd,
71 int width, int height,
78 unsigned int BitPixel;
79 unsigned int ColorResolution;
80 unsigned int BackGround;
81 unsigned int AspectRatio;
89 } Gif89 = { -1, -1, -1, 0 };
95 { return GIFErrorText ? GIFErrorText : "No Error";
99 setGifError(const char *fmt)
100 { if ( GIFErrorText )
101 pceFree(GIFErrorText);
103 if ( (GIFErrorText = pceMalloc(strlen(fmt)+1)) )
104 strcpy(GIFErrorText, fmt);
108 /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
109 GIFReadFD(IOSTREAM *fd,
110 PIXEL **data, int *width, int *height,
111 GIFAllocColorTable at, GIFAllocColor ac, void *closure)
112 Read GIF image from the given IO/stream
113 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
116 GIFReadFD(IOSTREAM *fd,
117 PIXEL **data, int *width, int *height,
118 GIFAllocColorTable at, GIFAllocColor ac,
119 GIFDoExtension doext,
124 int useGlobalColormap;
132 Gif89.transparent = -1;
133 Gif89.delayTime = -1;
134 Gif89.inputFlag = -1;
137 /* read GIF file header */
138 if (!ReadOK(fd, buf, 6))
139 { setGifError("Error reading GIF Magic");
142 /* need the string "GIF" in the header */
143 if (strncmp((char *) buf, "GIF", 3) != 0)
144 { setGifError("not a valid .GIF file");
147 strncpy(version, (char *) (buf + 3), 3);
150 /* only handle v 87a and 89a */
151 if ((strcmp(version, "87a") != 0) && (strcmp(version, "89a") != 0))
152 { setGifError("Error, Bad GIF Version number");
155 /* screen description */
156 if (!ReadOK(fd, buf, 7))
157 { setGifError("failed to GIF read screen descriptor. Giving up");
160 GifScreen.Width = LM_to_uint((UCHAR) buf[0], (UCHAR) buf[1]);
161 GifScreen.Height = LM_to_uint((UCHAR) buf[2], (UCHAR) buf[3]);
162 GifScreen.BitPixel = 2 << ((UCHAR) buf[4] & 0x07);
163 GifScreen.ColorResolution = ((((UCHAR) buf[4] & 0x70) >> 3) + 1);
164 GifScreen.BackGround = (UCHAR) buf[5]; /* background color... */
166 GifScreen.AspectRatio = (UCHAR) buf[6];
170 if ( BitSet((UCHAR) buf[4], LOCALCOLORMAP) )
171 { if ( (rval=ReadColorMap(fd, GifScreen.BitPixel, at, ac, closure))
173 { setGifError("Error reading GIF colormap");
177 /* non-square pixels, so what? */
178 if ((GifScreen.AspectRatio != 0) && (GifScreen.AspectRatio != 49))
180 setGifError("Non-square pixels in GIF image. Ignoring that fact ...");
182 /* there can be multiple images in a GIF file... uh? */
183 /* what the hell do we do with multiple images? */
184 /* so, we'll be interested in just the first image, cause we're lazy */
190 if (!ReadOK(fd, &c, 1))
192 setGifError("Unexpected EOF in GIF. Giving up");
195 /* image terminator */
200 { if (!ReadOK(fd, &c, 1))
201 { setGifError("Error on extension read. Giving up");
204 DoExtension(fd, c, doext, closure);
212 /* read image header */
213 if (!ReadOK(fd, buf, 9))
215 setGifError("Error on dimension read. Giving up");
218 useGlobalColormap = !BitSet((UCHAR) buf[8], LOCALCOLORMAP);
220 bitPixel = 1 << (((UCHAR) buf[8] & 0x07) + 1);
222 /* let's see if we have enough mem to continue? */
224 if ((int) buf[5] > 4)
226 /*AfxMessageBox("This GIF file claims to be > 2000 bytes wide!",MB_OK | MB_ICONINFORMATION); */
228 if ((int) buf[7] > 4)
230 /*AfxMessageBox("This GIF file claims to be > 2000 bytes high!",MB_OK | MB_ICONINFORMATION); */
232 w = LM_to_uint((UCHAR) buf[4], (UCHAR) buf[5]);
233 h = LM_to_uint((UCHAR) buf[6], (UCHAR) buf[7]);
235 if ((w < 0) || (h < 0))
236 { setGifError("Negative image dimensions! Giving up");
239 bufsize = (long) w *(long) h;
240 bigBuf = (PIXEL *)pceMalloc(bufsize * sizeof(PIXEL));
244 setGifError("Out of Memory in GIFRead");
247 if (!useGlobalColormap)
248 { if ( (rval=ReadColorMap(fd, bitPixel, at, ac, closure)) != GIF_OK )
249 { setGifError("Error reading GIF colormap. Giving up");
254 if ( (rval=ReadImage(fd, bigBuf, w, h,
255 BitSet((UCHAR) buf[8], INTERLACE))) != GIF_OK )
256 { setGifError("Error reading GIF file. LocalColorMap. Giving up");
261 { if ( (rval=ReadImage(fd, bigBuf, w, h,
262 BitSet((UCHAR) buf[8], INTERLACE))) != GIF_OK )
263 { setGifError("Error reading GIF file. GIFScreen Colormap. Giving up");
280 ReadColorMap(IOSTREAM *fd, int number,
281 GIFAllocColorTable at, GIFAllocColor ac, void *closure)
286 if ( (rval=(*at)(number, closure)) != GIF_OK )
289 for (i = 0; i < number; ++i)
290 { if (!ReadOK(fd, rgb, sizeof(rgb)))
293 rval = (*ac)(i, rgb[0], rgb[1], rgb[2], closure);
295 if ( rval != GIF_OK )
304 DoExtension(IOSTREAM * fd, int label, GIFDoExtension doext, void *cl)
306 static char buf[256];
312 str = "Plain Text Ext";
319 while (GetDataBlock(fd, (UCHAR *) buf) != 0)
321 /*AfxMessageBox(buf, MB_OK | MB_ICONINFORMATION); */
326 str = "Graphic Ctrl Ext";
327 (void) GetDataBlock(fd, (UCHAR *) buf);
328 Gif89.disposal = (buf[0] >> 2) & 0x7;
329 Gif89.inputFlag = (buf[0] >> 1) & 0x1;
330 Gif89.delayTime = LM_to_uint(buf[1], buf[2]);
331 if ((buf[0] & 0x1) != 0)
332 { Gif89.transparent = buf[3]&0xff;
333 (*doext)(GIFEXT_TRANSPARENT, (void *)(long)Gif89.transparent, cl);
336 while (GetDataBlock(fd, (UCHAR *) buf) != 0) ;
341 sprintf(buf, "UNKNOWN (0x%02x)", label);
345 while (GetDataBlock(fd, (UCHAR *) buf) != 0) ;
350 static int ZeroDataBlock = FALSE;
353 GetDataBlock(IOSTREAM * fd, UCHAR * buf)
357 if (!ReadOK(fd, &count, 1))
359 /*GIFErrorText="Error in GIF DataBlock Size"; */
362 ZeroDataBlock = count == 0;
364 if ((count != 0) && (!ReadOK(fd, buf, count)))
366 /*GIFErrorText="Error reading GIF datablock"; */
373 GetCode(IOSTREAM * fd, int code_size, int flag)
375 static UCHAR buf[280];
376 static int curbit, lastbit, done, last_byte;
387 if ((curbit + code_size) >= lastbit)
391 if (curbit >= lastbit)
393 /*GIFErrorText="Ran off the end of my bits"; */
398 buf[0] = buf[last_byte - 2];
399 buf[1] = buf[last_byte - 1];
401 if ((count = GetDataBlock(fd, &buf[2])) == 0)
404 last_byte = 2 + count;
406 curbit = (curbit - lastbit) + 16;
408 lastbit = (2 + count) * 8;
411 for (i = curbit, j = 0; j < code_size; ++i, ++j)
412 ret |= ((buf[i / 8] & (1 << (i % 8))) != 0) << j;
420 LZWReadByte(IOSTREAM * fd, int flag, int input_code_size)
422 static int fresh = FALSE;
424 static int code_size, set_code_size;
425 static int max_code, max_code_size;
426 static int firstcode, oldcode;
427 static int clear_code, end_code;
429 static unsigned short next[1 << MAX_LZW_BITS];
430 static UCHAR vals[1 << MAX_LZW_BITS];
431 static UCHAR stack[1 << (MAX_LZW_BITS + 1)];
438 set_code_size = input_code_size;
439 code_size = set_code_size + 1;
440 clear_code = 1 << set_code_size;
441 end_code = clear_code + 1;
442 max_code = clear_code + 2;
443 max_code_size = 2 * clear_code;
445 GetCode(fd, 0, TRUE);
449 for (i = 0; i < clear_code; ++i)
455 for (; i < (1 << MAX_LZW_BITS); ++i)
456 next[i] = vals[0] = 0;
466 firstcode = oldcode = GetCode(fd, code_size, FALSE);
468 while (firstcode == clear_code);
469 return (firstcode&255);
474 while ((code = GetCode(fd, code_size, FALSE)) >= 0)
476 if (code == clear_code)
478 for (i = 0; i < clear_code; ++i)
483 for (; i < (1 << MAX_LZW_BITS); ++i)
484 next[i] = vals[i] = 0;
485 code_size = set_code_size + 1;
486 max_code_size = 2 * clear_code;
487 max_code = clear_code + 2;
489 firstcode = oldcode = GetCode(fd, code_size, FALSE);
490 return (firstcode&255);
491 } else if (code == end_code || code > max_code)
494 UCHAR buf[260]; /* Block buffer */
499 while ((count = GetDataBlock(fd, buf)) > 0) ;
502 /*AfxMessageBox("Missing EOD in GIF data stream (common occurrence)",MB_OK); */
507 if (code == max_code)
508 { if ( sp < stack+sizeof(stack) ) /* stack is UCHAR */
512 while (code >= clear_code && sp < stack+sizeof(stack) )
515 if (code == (int) next[code])
517 /*GIFErrorText="Circular table entry, big GIF Error!"; */
523 if ( sp < stack+sizeof(stack) )
524 *sp++ = firstcode = vals[code];
526 if ((code = max_code) < (1 << MAX_LZW_BITS))
528 next[code] = oldcode;
529 vals[code] = firstcode;
531 if ((max_code >= max_code_size) &&
532 (max_code_size < (1 << MAX_LZW_BITS)))
541 return ((*--sp) & 255);
548 ReadImage(IOSTREAM *fd,
550 int width, int height,
555 int xpos = 0, ypos = 0, pass = 0;
558 if (!ReadOK(fd, &c, 1))
559 { return GIF_INVALID;
561 if (LZWReadByte(fd, TRUE, c) < 0)
562 { return GIF_INVALID;
564 while ((color = LZWReadByte(fd, FALSE, c)) >= 0)
566 curidx = (long) xpos + (long) ypos *(long) width; /* optimize */
568 bigMemBuf[curidx] = color;
619 if (LZWReadByte(fd, FALSE, c) >= 0)