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