The SWI-Prolog GIT repositories

CLEANUP: Removed all trailing whitespace from all source-files. This avoids
[packages/xpce.git] / src / img / gifread.c
1 /*  $Id$
2
3     Part of XPCE --- The SWI-Prolog GUI toolkit
4
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
9
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.
14
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.
19
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
23 */
24
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.
31
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
35 contribution.
36
37 http://www.pagesz.net/~chrisdl/software/jpegfile.htm
38
39 This reads GIF 87a and 89a.
40
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 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
45
46 #include <h/kernel.h>
47 #include <fcntl.h>
48 #include "gif.h"
49
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))
55
56 typedef short int               code_int;       /* was int */
57 typedef long int                count_int;
58 typedef unsigned char pixval;
59
60 static int
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);
68
69 static int ReadImage(IOSTREAM *fd,
70                      PIXEL *bigMemBuf,
71                      int width, int height,
72                      int interlace);
73
74
75 struct
76 { unsigned int Width;
77   unsigned int Height;
78   unsigned int BitPixel;
79   unsigned int ColorResolution;
80   unsigned int BackGround;
81   unsigned int AspectRatio;
82 } GifScreen;
83
84 struct
85 { int transparent;
86   int delayTime;
87   int inputFlag;
88   int disposal;
89 } Gif89 = { -1, -1, -1, 0 };
90
91 char *GIFErrorText;
92
93 const char *
94 GIFError()
95 { return GIFErrorText ? GIFErrorText : "No Error";
96 }
97
98 static void
99 setGifError(const char *fmt)
100 { if ( GIFErrorText )
101     pceFree(GIFErrorText);
102
103   if ( (GIFErrorText = pceMalloc(strlen(fmt)+1)) )
104     strcpy(GIFErrorText, fmt);
105 }
106
107
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 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
114
115 int
116 GIFReadFD(IOSTREAM *fd,
117           PIXEL **data, int *width, int *height,
118           GIFAllocColorTable at, GIFAllocColor ac,
119           GIFDoExtension doext,
120           void *closure)
121 {
122   UCHAR buf[16];
123   UCHAR c;
124   int useGlobalColormap;
125   int bitPixel;
126   char version[4];
127   int w = 0;
128   int h = 0;
129   PIXEL *bigBuf;
130   int rval;
131
132   Gif89.transparent = -1;
133   Gif89.delayTime   = -1;
134   Gif89.inputFlag   = -1;
135   Gif89.disposal    = 0;
136
137   /* read GIF file header */
138   if (!ReadOK(fd, buf, 6))
139   { setGifError("Error reading GIF Magic");
140     return GIF_INVALID;
141   }
142   /* need the string "GIF" in the header */
143   if (strncmp((char *) buf, "GIF", 3) != 0)
144   { setGifError("not a valid .GIF file");
145     return GIF_INVALID;
146   }
147   strncpy(version, (char *) (buf + 3), 3);
148   version[3] = '\0';
149
150   /* only handle v 87a and 89a */
151   if ((strcmp(version, "87a") != 0) && (strcmp(version, "89a") != 0))
152   { setGifError("Error, Bad GIF Version number");
153     return GIF_INVALID;
154   }
155   /* screen description */
156   if (!ReadOK(fd, buf, 7))
157   { setGifError("failed to GIF read screen descriptor. Giving up");
158     return GIF_INVALID;
159   }
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... */
165
166   GifScreen.AspectRatio = (UCHAR) buf[6];
167
168
169   /* read colormaps */
170   if ( BitSet((UCHAR) buf[4], LOCALCOLORMAP) )
171   { if ( (rval=ReadColorMap(fd, GifScreen.BitPixel, at, ac, closure))
172                                                                 != GIF_OK )
173     { setGifError("Error reading GIF colormap");
174       return rval;
175     }
176   }
177   /* non-square pixels, so what?   */
178   if ((GifScreen.AspectRatio != 0) && (GifScreen.AspectRatio != 49))
179   {
180     setGifError("Non-square pixels in GIF image.  Ignoring that fact ...");
181   }
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 */
185
186   for (;;)
187   { long bufsize;
188
189     /* read a byte; */
190     if (!ReadOK(fd, &c, 1))
191     {
192       setGifError("Unexpected EOF in GIF.  Giving up");
193       return GIF_INVALID;
194     }
195     /* image terminator */
196     if (c == ';')
197     {
198     }
199     if (c == '!')
200     { if (!ReadOK(fd, &c, 1))
201       { setGifError("Error on extension read.  Giving up");
202         return GIF_INVALID;
203       }
204       DoExtension(fd, c, doext, closure);
205       continue;
206     }
207     if (c != ',')
208     {
209       /* Ignoring c */
210       continue;
211     }
212     /* read image header */
213     if (!ReadOK(fd, buf, 9))
214     {
215       setGifError("Error on dimension read.  Giving up");
216       return GIF_INVALID;
217     }
218     useGlobalColormap = !BitSet((UCHAR) buf[8], LOCALCOLORMAP);
219
220     bitPixel = 1 << (((UCHAR) buf[8] & 0x07) + 1);
221
222     /* let's see if we have enough mem to continue? */
223
224     if ((int) buf[5] > 4)
225     {
226       /*AfxMessageBox("This GIF file claims to be > 2000 bytes wide!",MB_OK | MB_ICONINFORMATION); */
227     }
228     if ((int) buf[7] > 4)
229     {
230       /*AfxMessageBox("This GIF file claims to be > 2000 bytes high!",MB_OK | MB_ICONINFORMATION); */
231     }
232     w = LM_to_uint((UCHAR) buf[4], (UCHAR) buf[5]);
233     h = LM_to_uint((UCHAR) buf[6], (UCHAR) buf[7]);
234
235     if ((w < 0) || (h < 0))
236     { setGifError("Negative image dimensions!  Giving up");
237       return GIF_INVALID;
238     }
239     bufsize = (long) w *(long) h;
240     bigBuf  = (PIXEL *)pceMalloc(bufsize * sizeof(PIXEL));
241
242     if (bigBuf == NULL)
243     {
244       setGifError("Out of Memory in GIFRead");
245       return GIF_NOMEM;
246     }
247     if (!useGlobalColormap)
248     { if ( (rval=ReadColorMap(fd, bitPixel, at, ac, closure)) != GIF_OK )
249       { setGifError("Error reading GIF colormap. Giving up");
250         pceFree(bigBuf);
251         return rval;
252       }
253       /*read image */
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");
257         pceFree(bigBuf);
258         return rval;
259       }
260     } else
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");
264         pceFree(bigBuf);
265         return rval;
266       }
267     }
268     break;
269   }
270
271   *width = w;
272   *height = h;
273   *data = bigBuf;
274
275   return GIF_OK;
276 }
277
278
279 static int
280 ReadColorMap(IOSTREAM *fd, int number,
281              GIFAllocColorTable at, GIFAllocColor ac, void *closure)
282 { int i;
283   UCHAR rgb[3];
284   int rval;
285
286   if ( (rval=(*at)(number, closure)) != GIF_OK )
287     return rval;
288
289   for (i = 0; i < number; ++i)
290   { if (!ReadOK(fd, rgb, sizeof(rgb)))
291       return GIF_INVALID;
292
293     rval = (*ac)(i, rgb[0], rgb[1], rgb[2], closure);
294
295     if ( rval != GIF_OK )
296       return rval;
297   }
298
299   return GIF_OK;
300 }
301
302
303 static int
304 DoExtension(IOSTREAM * fd, int label, GIFDoExtension doext, void *cl)
305 {
306   static char buf[256];
307   char *str;
308
309   switch (label)
310   {
311   case 0x01:
312     str = "Plain Text Ext";
313     break;
314   case 0xff:
315     str = "Appl ext";
316     break;
317   case 0xfe:
318     str = "Comment Ext";
319     while (GetDataBlock(fd, (UCHAR *) buf) != 0)
320     {
321       /*AfxMessageBox(buf, MB_OK | MB_ICONINFORMATION); */
322     }
323     return FALSE;
324     break;
325   case 0XF9:
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);
334     }
335
336     while (GetDataBlock(fd, (UCHAR *) buf) != 0) ;
337     return FALSE;
338     break;
339   default:
340     str = buf;
341     sprintf(buf, "UNKNOWN (0x%02x)", label);
342     break;
343   }
344
345   while (GetDataBlock(fd, (UCHAR *) buf) != 0) ;
346
347   return FALSE;
348 }
349
350 static int ZeroDataBlock = FALSE;
351
352 static int
353 GetDataBlock(IOSTREAM * fd, UCHAR * buf)
354 {
355   UCHAR count;
356
357   if (!ReadOK(fd, &count, 1))
358   {
359     /*GIFErrorText="Error in GIF DataBlock Size"; */
360     return -1;
361   }
362   ZeroDataBlock = count == 0;
363
364   if ((count != 0) && (!ReadOK(fd, buf, count)))
365   {
366     /*GIFErrorText="Error reading GIF datablock"; */
367     return -1;
368   }
369   return count;
370 }
371
372 static int
373 GetCode(IOSTREAM * fd, int code_size, int flag)
374 {
375   static UCHAR buf[280];
376   static int curbit, lastbit, done, last_byte;
377   int i, j, ret;
378   UCHAR count;
379
380   if (flag)
381   {
382     curbit = 0;
383     lastbit = 0;
384     done = FALSE;
385     return 0;
386   }
387   if ((curbit + code_size) >= lastbit)
388   {
389     if (done)
390     {
391       if (curbit >= lastbit)
392       {
393         /*GIFErrorText="Ran off the end of my bits"; */
394         return 0;
395       }
396       return -1;
397     }
398     buf[0] = buf[last_byte - 2];
399     buf[1] = buf[last_byte - 1];
400
401     if ((count = GetDataBlock(fd, &buf[2])) == 0)
402       done = TRUE;
403
404     last_byte = 2 + count;
405
406     curbit = (curbit - lastbit) + 16;
407
408     lastbit = (2 + count) * 8;
409   }
410   ret = 0;
411   for (i = curbit, j = 0; j < code_size; ++i, ++j)
412     ret |= ((buf[i / 8] & (1 << (i % 8))) != 0) << j;
413
414   curbit += code_size;
415
416   return ret;
417 }
418
419 static int
420 LZWReadByte(IOSTREAM * fd, int flag, int input_code_size)
421 {
422   static int fresh = FALSE;
423   int code, incode;
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;
428
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)];
432   static UCHAR *sp;
433
434   register int i;
435
436   if (flag)
437   {
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;
444
445     GetCode(fd, 0, TRUE);
446
447     fresh = TRUE;
448
449     for (i = 0; i < clear_code; ++i)
450     {
451       next[i] = 0;
452       vals[i] = i;
453     }
454
455     for (; i < (1 << MAX_LZW_BITS); ++i)
456       next[i] = vals[0] = 0;
457
458     sp = stack;
459
460     return 0;
461   } else if (fresh)
462   {
463     fresh = FALSE;
464     do
465     {
466       firstcode = oldcode = GetCode(fd, code_size, FALSE);
467     }
468     while (firstcode == clear_code);
469     return firstcode;
470   }
471   if (sp > stack)
472     return *--sp;
473
474   while ((code = GetCode(fd, code_size, FALSE)) >= 0)
475   {
476     if (code == clear_code)
477     {
478       for (i = 0; i < clear_code; ++i)
479       {
480         next[i] = 0;
481         vals[i] = i;
482       }
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;
488       sp = stack;
489       firstcode = oldcode = GetCode(fd, code_size, FALSE);
490       return firstcode;
491     } else if (code == end_code)
492     {
493       int count;
494       UCHAR buf[260];
495
496       if (ZeroDataBlock)
497         return -2;
498
499       while ((count = GetDataBlock(fd, buf)) > 0) ;
500
501       if (count != 0)
502         /*AfxMessageBox("Missing EOD in GIF data stream (common occurrence)",MB_OK); */
503         return -2;
504     }
505     incode = code;
506
507     if (code >= max_code)
508     {
509       *sp++ = firstcode;
510       code = oldcode;
511     }
512     while (code >= clear_code)
513     {
514       *sp++ = vals[code];
515       if (code == (int) next[code])
516       {
517         /*GIFErrorText="Circular table entry, big GIF Error!"; */
518         return -1;
519       }
520       code = next[code];
521     }
522
523     *sp++ = firstcode = vals[code];
524
525     if ((code = max_code) < (1 << MAX_LZW_BITS))
526     {
527       next[code] = oldcode;
528       vals[code] = firstcode;
529       ++max_code;
530       if ((max_code >= max_code_size) &&
531           (max_code_size < (1 << MAX_LZW_BITS)))
532       {
533         max_code_size *= 2;
534         ++code_size;
535       }
536     }
537     oldcode = incode;
538
539     if (sp > stack)
540       return *--sp;
541   }
542   return code;
543 }
544
545
546 static int
547 ReadImage(IOSTREAM *fd,
548           PIXEL *bigMemBuf,
549           int width, int height,
550           int interlace)
551 {
552   UCHAR c;
553   int color;
554   int xpos = 0, ypos = 0, pass = 0;
555   long curidx;
556
557   if (!ReadOK(fd, &c, 1))
558   { return GIF_INVALID;
559   }
560   if (LZWReadByte(fd, TRUE, c) < 0)
561   { return GIF_INVALID;
562   }
563   while ((color = LZWReadByte(fd, FALSE, c)) >= 0)
564   {
565     curidx = (long) xpos + (long) ypos *(long) width; /* optimize */
566
567     bigMemBuf[curidx] = color;
568
569     ++xpos;
570     if (xpos == width)
571     {
572       xpos = 0;
573       if ( interlace )
574       {
575         switch (pass)
576         {
577         case 0:
578         case 1:
579           ypos += 8;
580           break;
581         case 2:
582           ypos += 4;
583           break;
584         case 3:
585           ypos += 2;
586           break;
587         }
588
589         if (ypos >= height)
590         {
591           ++pass;
592           switch (pass)
593           {
594           case 1:
595             ypos = 4;
596             break;
597           case 2:
598             ypos = 2;
599             break;
600           case 3:
601             ypos = 1;
602             break;
603           default:
604             goto fini;
605           }
606         }
607       } else
608       {
609         ++ypos;
610       }
611     }
612     if (ypos >= height)
613       break;
614   }
615
616 fini:
617
618   if (LZWReadByte(fd, FALSE, c) >= 0)
619   {
620
621   }
622   return GIF_OK;
623 }
624

Further information about the SWI-Prolog GIT repositories