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