]> rtime.felk.cvut.cz Git - l4.git/blob - l4/pkg/libsdl-image/contrib/IMG_gif.c
update
[l4.git] / l4 / pkg / libsdl-image / contrib / IMG_gif.c
1 /*
2     SDL_image:  An example image loading library for use with SDL
3     Copyright (C) 1997-2009 Sam Lantinga
4
5     This library is free software; you can redistribute it and/or
6     modify it under the terms of the GNU Lesser General Public
7     License as published by the Free Software Foundation; either
8     version 2.1 of the License, or (at your option) any later version.
9
10     This library is distributed in the hope that it will be useful,
11     but WITHOUT ANY WARRANTY; without even the implied warranty of
12     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13     Lesser General Public License for more details.
14
15     You should have received a copy of the GNU Lesser General Public
16     License along with this library; if not, write to the Free Software
17     Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
18
19     Sam Lantinga
20     slouken@libsdl.org
21 */
22
23 #if !defined(__APPLE__) || defined(SDL_IMAGE_USE_COMMON_BACKEND)
24
25 /* This is a GIF image file loading framework */
26
27 #include <stdio.h>
28 #include <string.h>
29
30 #include "SDL_image.h"
31
32 #ifdef LOAD_GIF
33
34 /* See if an image is contained in a data source */
35 int IMG_isGIF(SDL_RWops *src)
36 {
37         int start;
38         int is_GIF;
39         char magic[6];
40
41         if ( !src )
42                 return 0;
43         start = SDL_RWtell(src);
44         is_GIF = 0;
45         if ( SDL_RWread(src, magic, sizeof(magic), 1) ) {
46                 if ( (strncmp(magic, "GIF", 3) == 0) &&
47                      ((memcmp(magic + 3, "87a", 3) == 0) ||
48                       (memcmp(magic + 3, "89a", 3) == 0)) ) {
49                         is_GIF = 1;
50                 }
51         }
52         SDL_RWseek(src, start, RW_SEEK_SET);
53         return(is_GIF);
54 }
55
56 /* Code from here to end of file has been adapted from XPaint:           */
57 /* +-------------------------------------------------------------------+ */
58 /* | Copyright 1990, 1991, 1993 David Koblas.                          | */
59 /* | Copyright 1996 Torsten Martinsen.                                 | */
60 /* |   Permission to use, copy, modify, and distribute this software   | */
61 /* |   and its documentation for any purpose and without fee is hereby | */
62 /* |   granted, provided that the above copyright notice appear in all | */
63 /* |   copies and that both that copyright notice and this permission  | */
64 /* |   notice appear in supporting documentation.  This software is    | */
65 /* |   provided "as is" without express or implied warranty.           | */
66 /* +-------------------------------------------------------------------+ */
67
68 /* Adapted for use in SDL by Sam Lantinga -- 7/20/98 */
69 #define USED_BY_SDL
70
71 #include <stdio.h>
72 #include <string.h>
73
74 #ifdef USED_BY_SDL
75 /* Changes to work with SDL:
76
77    Include SDL header file
78    Use SDL_Surface rather than xpaint Image structure
79    Define SDL versions of RWSetMsg(), ImageNewCmap() and ImageSetCmap()
80 */
81 #include "SDL.h"
82
83 #define Image                   SDL_Surface
84 #define RWSetMsg                IMG_SetError
85 #define ImageNewCmap(w, h, s)   SDL_AllocSurface(SDL_SWSURFACE,w,h,8,0,0,0,0)
86 #define ImageSetCmap(s, i, R, G, B) do { \
87                                 s->format->palette->colors[i].r = R; \
88                                 s->format->palette->colors[i].g = G; \
89                                 s->format->palette->colors[i].b = B; \
90                         } while (0)
91 /* * * * * */
92
93 #else
94
95 /* Original XPaint sources */
96
97 #include "image.h"
98 #include "rwTable.h"
99
100 #define SDL_RWops       FILE
101 #define SDL_RWclose     fclose
102
103 #endif /* USED_BY_SDL */
104
105
106 #define MAXCOLORMAPSIZE         256
107
108 #define TRUE    1
109 #define FALSE   0
110
111 #define CM_RED          0
112 #define CM_GREEN        1
113 #define CM_BLUE         2
114
115 #define MAX_LWZ_BITS            12
116
117 #define INTERLACE               0x40
118 #define LOCALCOLORMAP   0x80
119 #define BitSet(byte, bit)       (((byte) & (bit)) == (bit))
120
121 #define ReadOK(file,buffer,len) SDL_RWread(file, buffer, len, 1)
122
123 #define LM_to_uint(a,b)                 (((b)<<8)|(a))
124
125 static struct {
126     unsigned int Width;
127     unsigned int Height;
128     unsigned char ColorMap[3][MAXCOLORMAPSIZE];
129     unsigned int BitPixel;
130     unsigned int ColorResolution;
131     unsigned int Background;
132     unsigned int AspectRatio;
133     int GrayScale;
134 } GifScreen;
135
136 static struct {
137     int transparent;
138     int delayTime;
139     int inputFlag;
140     int disposal;
141 } Gif89;
142
143 static int ReadColorMap(SDL_RWops * src, int number,
144                         unsigned char buffer[3][MAXCOLORMAPSIZE], int *flag);
145 static int DoExtension(SDL_RWops * src, int label);
146 static int GetDataBlock(SDL_RWops * src, unsigned char *buf);
147 static int GetCode(SDL_RWops * src, int code_size, int flag);
148 static int LWZReadByte(SDL_RWops * src, int flag, int input_code_size);
149 static Image *ReadImage(SDL_RWops * src, int len, int height, int,
150                         unsigned char cmap[3][MAXCOLORMAPSIZE],
151                         int gray, int interlace, int ignore);
152
153 Image *
154 IMG_LoadGIF_RW(SDL_RWops *src)
155 {
156     int start;
157     unsigned char buf[16];
158     unsigned char c;
159     unsigned char localColorMap[3][MAXCOLORMAPSIZE];
160     int grayScale;
161     int useGlobalColormap;
162     int bitPixel;
163     int imageCount = 0;
164     char version[4];
165     int imageNumber = 1;
166     Image *image = NULL;
167
168     if ( src == NULL ) {
169         return NULL;
170     }
171     start = SDL_RWtell(src);
172
173     if (!ReadOK(src, buf, 6)) {
174         RWSetMsg("error reading magic number");
175         goto done;
176     }
177     if (strncmp((char *) buf, "GIF", 3) != 0) {
178         RWSetMsg("not a GIF file");
179         goto done;
180     }
181     strncpy(version, (char *) buf + 3, 3);
182     version[3] = '\0';
183
184     if ((strcmp(version, "87a") != 0) && (strcmp(version, "89a") != 0)) {
185         RWSetMsg("bad version number, not '87a' or '89a'");
186         goto done;
187     }
188     Gif89.transparent = -1;
189     Gif89.delayTime = -1;
190     Gif89.inputFlag = -1;
191     Gif89.disposal = 0;
192
193     if (!ReadOK(src, buf, 7)) {
194         RWSetMsg("failed to read screen descriptor");
195         goto done;
196     }
197     GifScreen.Width = LM_to_uint(buf[0], buf[1]);
198     GifScreen.Height = LM_to_uint(buf[2], buf[3]);
199     GifScreen.BitPixel = 2 << (buf[4] & 0x07);
200     GifScreen.ColorResolution = (((buf[4] & 0x70) >> 3) + 1);
201     GifScreen.Background = buf[5];
202     GifScreen.AspectRatio = buf[6];
203
204     if (BitSet(buf[4], LOCALCOLORMAP)) {        /* Global Colormap */
205         if (ReadColorMap(src, GifScreen.BitPixel, GifScreen.ColorMap,
206                          &GifScreen.GrayScale)) {
207             RWSetMsg("error reading global colormap");
208             goto done;
209         }
210     }
211     do {
212         if (!ReadOK(src, &c, 1)) {
213             RWSetMsg("EOF / read error on image data");
214             goto done;
215         }
216         if (c == ';') {         /* GIF terminator */
217             if (imageCount < imageNumber) {
218                 RWSetMsg("only %d image%s found in file",
219                          imageCount, imageCount > 1 ? "s" : "");
220                 goto done;
221             }
222         }
223         if (c == '!') {         /* Extension */
224             if (!ReadOK(src, &c, 1)) {
225                 RWSetMsg("EOF / read error on extention function code");
226                 goto done;
227             }
228             DoExtension(src, c);
229             continue;
230         }
231         if (c != ',') {         /* Not a valid start character */
232             continue;
233         }
234         ++imageCount;
235
236         if (!ReadOK(src, buf, 9)) {
237             RWSetMsg("couldn't read left/top/width/height");
238             goto done;
239         }
240         useGlobalColormap = !BitSet(buf[8], LOCALCOLORMAP);
241
242         bitPixel = 1 << ((buf[8] & 0x07) + 1);
243
244         if (!useGlobalColormap) {
245             if (ReadColorMap(src, bitPixel, localColorMap, &grayScale)) {
246                 RWSetMsg("error reading local colormap");
247                 goto done;
248             }
249             image = ReadImage(src, LM_to_uint(buf[4], buf[5]),
250                               LM_to_uint(buf[6], buf[7]),
251                               bitPixel, localColorMap, grayScale,
252                               BitSet(buf[8], INTERLACE),
253                               imageCount != imageNumber);
254         } else {
255             image = ReadImage(src, LM_to_uint(buf[4], buf[5]),
256                               LM_to_uint(buf[6], buf[7]),
257                               GifScreen.BitPixel, GifScreen.ColorMap,
258                               GifScreen.GrayScale, BitSet(buf[8], INTERLACE),
259                               imageCount != imageNumber);
260         }
261     } while (image == NULL);
262
263 #ifdef USED_BY_SDL
264     if ( Gif89.transparent >= 0 ) {
265         SDL_SetColorKey(image, SDL_SRCCOLORKEY, Gif89.transparent);
266     }
267 #endif
268
269 done:
270     if ( image == NULL ) {
271         SDL_RWseek(src, start, RW_SEEK_SET);
272     }
273     return image;
274 }
275
276 static int
277 ReadColorMap(SDL_RWops *src, int number,
278              unsigned char buffer[3][MAXCOLORMAPSIZE], int *gray)
279 {
280     int i;
281     unsigned char rgb[3];
282     int flag;
283
284     flag = TRUE;
285
286     for (i = 0; i < number; ++i) {
287         if (!ReadOK(src, rgb, sizeof(rgb))) {
288             RWSetMsg("bad colormap");
289             return 1;
290         }
291         buffer[CM_RED][i] = rgb[0];
292         buffer[CM_GREEN][i] = rgb[1];
293         buffer[CM_BLUE][i] = rgb[2];
294         flag &= (rgb[0] == rgb[1] && rgb[1] == rgb[2]);
295     }
296
297 #if 0
298     if (flag)
299         *gray = (number == 2) ? PBM_TYPE : PGM_TYPE;
300     else
301         *gray = PPM_TYPE;
302 #else
303     *gray = 0;
304 #endif
305
306     return FALSE;
307 }
308
309 static int
310 DoExtension(SDL_RWops *src, int label)
311 {
312     static unsigned char buf[256];
313     char *str;
314
315     switch (label) {
316     case 0x01:                  /* Plain Text Extension */
317         str = "Plain Text Extension";
318         break;
319     case 0xff:                  /* Application Extension */
320         str = "Application Extension";
321         break;
322     case 0xfe:                  /* Comment Extension */
323         str = "Comment Extension";
324         while (GetDataBlock(src, (unsigned char *) buf) != 0)
325             ;
326         return FALSE;
327     case 0xf9:                  /* Graphic Control Extension */
328         str = "Graphic Control Extension";
329         (void) GetDataBlock(src, (unsigned char *) buf);
330         Gif89.disposal = (buf[0] >> 2) & 0x7;
331         Gif89.inputFlag = (buf[0] >> 1) & 0x1;
332         Gif89.delayTime = LM_to_uint(buf[1], buf[2]);
333         if ((buf[0] & 0x1) != 0)
334             Gif89.transparent = buf[3];
335
336         while (GetDataBlock(src, (unsigned char *) buf) != 0)
337             ;
338         return FALSE;
339     default:
340         str = (char *)buf;
341         sprintf(str, "UNKNOWN (0x%02x)", label);
342         break;
343     }
344
345     while (GetDataBlock(src, (unsigned char *) buf) != 0)
346         ;
347
348     return FALSE;
349 }
350
351 static int ZeroDataBlock = FALSE;
352
353 static int
354 GetDataBlock(SDL_RWops *src, unsigned char *buf)
355 {
356     unsigned char count;
357
358     if (!ReadOK(src, &count, 1)) {
359         /* pm_message("error in getting DataBlock size" ); */
360         return -1;
361     }
362     ZeroDataBlock = count == 0;
363
364     if ((count != 0) && (!ReadOK(src, buf, count))) {
365         /* pm_message("error in reading DataBlock" ); */
366         return -1;
367     }
368     return count;
369 }
370
371 static int
372 GetCode(SDL_RWops *src, int code_size, int flag)
373 {
374     static unsigned char buf[280];
375     static int curbit, lastbit, done, last_byte;
376     int i, j, ret;
377     unsigned char count;
378
379     if (flag) {
380         curbit = 0;
381         lastbit = 0;
382         done = FALSE;
383         return 0;
384     }
385     if ((curbit + code_size) >= lastbit) {
386         if (done) {
387             if (curbit >= lastbit)
388                 RWSetMsg("ran off the end of my bits");
389             return -1;
390         }
391         buf[0] = buf[last_byte - 2];
392         buf[1] = buf[last_byte - 1];
393
394         if ((count = GetDataBlock(src, &buf[2])) == 0)
395             done = TRUE;
396
397         last_byte = 2 + count;
398         curbit = (curbit - lastbit) + 16;
399         lastbit = (2 + count) * 8;
400     }
401     ret = 0;
402     for (i = curbit, j = 0; j < code_size; ++i, ++j)
403         ret |= ((buf[i / 8] & (1 << (i % 8))) != 0) << j;
404
405     curbit += code_size;
406
407     return ret;
408 }
409
410 static int
411 LWZReadByte(SDL_RWops *src, int flag, int input_code_size)
412 {
413     static int fresh = FALSE;
414     int code, incode;
415     static int code_size, set_code_size;
416     static int max_code, max_code_size;
417     static int firstcode, oldcode;
418     static int clear_code, end_code;
419     static int table[2][(1 << MAX_LWZ_BITS)];
420     static int stack[(1 << (MAX_LWZ_BITS)) * 2], *sp;
421     register int i;
422
423     /* Fixed buffer overflow found by Michael Skladnikiewicz */
424     if (input_code_size > MAX_LWZ_BITS)
425         return -1;
426
427     if (flag) {
428         set_code_size = input_code_size;
429         code_size = set_code_size + 1;
430         clear_code = 1 << set_code_size;
431         end_code = clear_code + 1;
432         max_code_size = 2 * clear_code;
433         max_code = clear_code + 2;
434
435         GetCode(src, 0, TRUE);
436
437         fresh = TRUE;
438
439         for (i = 0; i < clear_code; ++i) {
440             table[0][i] = 0;
441             table[1][i] = i;
442         }
443         for (; i < (1 << MAX_LWZ_BITS); ++i)
444             table[0][i] = table[1][0] = 0;
445
446         sp = stack;
447
448         return 0;
449     } else if (fresh) {
450         fresh = FALSE;
451         do {
452             firstcode = oldcode = GetCode(src, code_size, FALSE);
453         } while (firstcode == clear_code);
454         return firstcode;
455     }
456     if (sp > stack)
457         return *--sp;
458
459     while ((code = GetCode(src, code_size, FALSE)) >= 0) {
460         if (code == clear_code) {
461             for (i = 0; i < clear_code; ++i) {
462                 table[0][i] = 0;
463                 table[1][i] = i;
464             }
465             for (; i < (1 << MAX_LWZ_BITS); ++i)
466                 table[0][i] = table[1][i] = 0;
467             code_size = set_code_size + 1;
468             max_code_size = 2 * clear_code;
469             max_code = clear_code + 2;
470             sp = stack;
471             firstcode = oldcode = GetCode(src, code_size, FALSE);
472             return firstcode;
473         } else if (code == end_code) {
474             int count;
475             unsigned char buf[260];
476
477             if (ZeroDataBlock)
478                 return -2;
479
480             while ((count = GetDataBlock(src, buf)) > 0)
481                 ;
482
483             if (count != 0) {
484                 /*
485                  * pm_message("missing EOD in data stream (common occurence)");
486                  */
487             }
488             return -2;
489         }
490         incode = code;
491
492         if (code >= max_code) {
493             *sp++ = firstcode;
494             code = oldcode;
495         }
496         while (code >= clear_code) {
497             *sp++ = table[1][code];
498             if (code == table[0][code])
499                 RWSetMsg("circular table entry BIG ERROR");
500             code = table[0][code];
501         }
502
503         *sp++ = firstcode = table[1][code];
504
505         if ((code = max_code) < (1 << MAX_LWZ_BITS)) {
506             table[0][code] = oldcode;
507             table[1][code] = firstcode;
508             ++max_code;
509             if ((max_code >= max_code_size) &&
510                 (max_code_size < (1 << MAX_LWZ_BITS))) {
511                 max_code_size *= 2;
512                 ++code_size;
513             }
514         }
515         oldcode = incode;
516
517         if (sp > stack)
518             return *--sp;
519     }
520     return code;
521 }
522
523 static Image *
524 ReadImage(SDL_RWops * src, int len, int height, int cmapSize,
525           unsigned char cmap[3][MAXCOLORMAPSIZE],
526           int gray, int interlace, int ignore)
527 {
528     Image *image;
529     unsigned char c;
530     int i, v;
531     int xpos = 0, ypos = 0, pass = 0;
532
533     /*
534     **  Initialize the compression routines
535      */
536     if (!ReadOK(src, &c, 1)) {
537         RWSetMsg("EOF / read error on image data");
538         return NULL;
539     }
540     if (LWZReadByte(src, TRUE, c) < 0) {
541         RWSetMsg("error reading image");
542         return NULL;
543     }
544     /*
545     **  If this is an "uninteresting picture" ignore it.
546      */
547     if (ignore) {
548         while (LWZReadByte(src, FALSE, c) >= 0)
549             ;
550         return NULL;
551     }
552     image = ImageNewCmap(len, height, cmapSize);
553
554     for (i = 0; i < cmapSize; i++)
555         ImageSetCmap(image, i, cmap[CM_RED][i],
556                      cmap[CM_GREEN][i], cmap[CM_BLUE][i]);
557
558     while ((v = LWZReadByte(src, FALSE, c)) >= 0) {
559 #ifdef USED_BY_SDL
560         ((Uint8 *)image->pixels)[xpos + ypos * image->pitch] = v;
561 #else
562         image->data[xpos + ypos * len] = v;
563 #endif
564         ++xpos;
565         if (xpos == len) {
566             xpos = 0;
567             if (interlace) {
568                 switch (pass) {
569                 case 0:
570                 case 1:
571                     ypos += 8;
572                     break;
573                 case 2:
574                     ypos += 4;
575                     break;
576                 case 3:
577                     ypos += 2;
578                     break;
579                 }
580
581                 if (ypos >= height) {
582                     ++pass;
583                     switch (pass) {
584                     case 1:
585                         ypos = 4;
586                         break;
587                     case 2:
588                         ypos = 2;
589                         break;
590                     case 3:
591                         ypos = 1;
592                         break;
593                     default:
594                         goto fini;
595                     }
596                 }
597             } else {
598                 ++ypos;
599             }
600         }
601         if (ypos >= height)
602             break;
603     }
604
605   fini:
606
607     return image;
608 }
609
610 #else
611
612 /* See if an image is contained in a data source */
613 int IMG_isGIF(SDL_RWops *src)
614 {
615         return(0);
616 }
617
618 /* Load a GIF type image from an SDL datasource */
619 SDL_Surface *IMG_LoadGIF_RW(SDL_RWops *src)
620 {
621         return(NULL);
622 }
623
624 #endif /* LOAD_GIF */
625
626 #endif /* !defined(__APPLE__) || defined(SDL_IMAGE_USE_COMMON_BACKEND) */