IMG_gif.c
author Ryan C. Gordon <icculus@icculus.org>
Wed, 26 Sep 2018 15:19:17 -0400
changeset 586 19beb4a1bb54
parent 575 36e9e2255178
child 638 e3e9d7430674
permissions -rw-r--r--
gif: Don't get into infinite loops on truncated files.
     1 /*
     2   SDL_image:  An example image loading library for use with SDL
     3   Copyright (C) 1997-2018 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 */
    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 static struct {
   122     unsigned int Width;
   123     unsigned int Height;
   124     unsigned char ColorMap[3][MAXCOLORMAPSIZE];
   125     unsigned int BitPixel;
   126     unsigned int ColorResolution;
   127     unsigned int Background;
   128     unsigned int AspectRatio;
   129     int GrayScale;
   130 } GifScreen;
   131 
   132 static struct {
   133     int transparent;
   134     int delayTime;
   135     int inputFlag;
   136     int disposal;
   137 } Gif89;
   138 
   139 static int ReadColorMap(SDL_RWops * src, int number,
   140             unsigned char buffer[3][MAXCOLORMAPSIZE], int *flag);
   141 static int DoExtension(SDL_RWops * src, int label);
   142 static int GetDataBlock(SDL_RWops * src, unsigned char *buf);
   143 static int GetCode(SDL_RWops * src, int code_size, int flag);
   144 static int LWZReadByte(SDL_RWops * src, int flag, int input_code_size);
   145 static Image *ReadImage(SDL_RWops * src, int len, int height, int,
   146             unsigned char cmap[3][MAXCOLORMAPSIZE],
   147             int gray, int interlace, int ignore);
   148 
   149 Image *
   150 IMG_LoadGIF_RW(SDL_RWops *src)
   151 {
   152     Sint64 start;
   153     unsigned char buf[16];
   154     unsigned char c;
   155     unsigned char localColorMap[3][MAXCOLORMAPSIZE];
   156     int grayScale;
   157     int useGlobalColormap;
   158     int bitPixel;
   159     int imageCount = 0;
   160     char version[4];
   161     int imageNumber = 1;
   162     Image *image = NULL;
   163 
   164     if ( src == NULL ) {
   165     return NULL;
   166     }
   167     start = SDL_RWtell(src);
   168 
   169     if (!ReadOK(src, buf, 6)) {
   170     RWSetMsg("error reading magic number");
   171         goto done;
   172     }
   173     if (SDL_strncmp((char *) buf, "GIF", 3) != 0) {
   174     RWSetMsg("not a GIF file");
   175         goto done;
   176     }
   177     SDL_memcpy(version, (char *) buf + 3, 3);
   178     version[3] = '\0';
   179 
   180     if ((SDL_strcmp(version, "87a") != 0) && (SDL_strcmp(version, "89a") != 0)) {
   181     RWSetMsg("bad version number, not '87a' or '89a'");
   182         goto done;
   183     }
   184     Gif89.transparent = -1;
   185     Gif89.delayTime = -1;
   186     Gif89.inputFlag = -1;
   187     Gif89.disposal = 0;
   188 
   189     if (!ReadOK(src, buf, 7)) {
   190     RWSetMsg("failed to read screen descriptor");
   191         goto done;
   192     }
   193     GifScreen.Width = LM_to_uint(buf[0], buf[1]);
   194     GifScreen.Height = LM_to_uint(buf[2], buf[3]);
   195     GifScreen.BitPixel = 2 << (buf[4] & 0x07);
   196     GifScreen.ColorResolution = (((buf[4] & 0x70) >> 3) + 1);
   197     GifScreen.Background = buf[5];
   198     GifScreen.AspectRatio = buf[6];
   199 
   200     if (BitSet(buf[4], LOCALCOLORMAP)) {    /* Global Colormap */
   201     if (ReadColorMap(src, GifScreen.BitPixel, GifScreen.ColorMap,
   202              &GifScreen.GrayScale)) {
   203         RWSetMsg("error reading global colormap");
   204             goto done;
   205     }
   206     }
   207     do {
   208     if (!ReadOK(src, &c, 1)) {
   209         RWSetMsg("EOF / read error on image data");
   210             goto done;
   211     }
   212     if (c == ';') {     /* GIF terminator */
   213         if (imageCount < imageNumber) {
   214         RWSetMsg("only %d image%s found in file",
   215              imageCount, imageCount > 1 ? "s" : "");
   216                 goto done;
   217         }
   218     }
   219     if (c == '!') {     /* Extension */
   220         if (!ReadOK(src, &c, 1)) {
   221         RWSetMsg("EOF / read error on extention function code");
   222                 goto done;
   223         }
   224         DoExtension(src, c);
   225         continue;
   226     }
   227     if (c != ',') {     /* Not a valid start character */
   228         continue;
   229     }
   230     ++imageCount;
   231 
   232     if (!ReadOK(src, buf, 9)) {
   233         RWSetMsg("couldn't read left/top/width/height");
   234             goto done;
   235     }
   236     useGlobalColormap = !BitSet(buf[8], LOCALCOLORMAP);
   237 
   238     bitPixel = 1 << ((buf[8] & 0x07) + 1);
   239 
   240     if (!useGlobalColormap) {
   241         if (ReadColorMap(src, bitPixel, localColorMap, &grayScale)) {
   242         RWSetMsg("error reading local colormap");
   243                 goto done;
   244         }
   245         image = ReadImage(src, LM_to_uint(buf[4], buf[5]),
   246                   LM_to_uint(buf[6], buf[7]),
   247                   bitPixel, localColorMap, grayScale,
   248                   BitSet(buf[8], INTERLACE),
   249                   imageCount != imageNumber);
   250     } else {
   251         image = ReadImage(src, LM_to_uint(buf[4], buf[5]),
   252                   LM_to_uint(buf[6], buf[7]),
   253                   GifScreen.BitPixel, GifScreen.ColorMap,
   254                   GifScreen.GrayScale, BitSet(buf[8], INTERLACE),
   255                   imageCount != imageNumber);
   256     }
   257     } while (image == NULL);
   258 
   259 #ifdef USED_BY_SDL
   260     if ( Gif89.transparent >= 0 ) {
   261         SDL_SetColorKey(image, SDL_TRUE, Gif89.transparent);
   262     }
   263 #endif
   264 
   265 done:
   266     if ( image == NULL ) {
   267         SDL_RWseek(src, start, RW_SEEK_SET);
   268     }
   269     return image;
   270 }
   271 
   272 static int
   273 ReadColorMap(SDL_RWops *src, int number,
   274              unsigned char buffer[3][MAXCOLORMAPSIZE], int *gray)
   275 {
   276     int i;
   277     unsigned char rgb[3];
   278     int flag;
   279 
   280     flag = TRUE;
   281 
   282     for (i = 0; i < number; ++i) {
   283     if (!ReadOK(src, rgb, sizeof(rgb))) {
   284         RWSetMsg("bad colormap");
   285         return 1;
   286     }
   287     buffer[CM_RED][i] = rgb[0];
   288     buffer[CM_GREEN][i] = rgb[1];
   289     buffer[CM_BLUE][i] = rgb[2];
   290     flag &= (rgb[0] == rgb[1] && rgb[1] == rgb[2]);
   291     }
   292 
   293 #if 0
   294     if (flag)
   295     *gray = (number == 2) ? PBM_TYPE : PGM_TYPE;
   296     else
   297     *gray = PPM_TYPE;
   298 #else
   299     *gray = 0;
   300 #endif
   301 
   302     return FALSE;
   303 }
   304 
   305 static int
   306 DoExtension(SDL_RWops *src, int label)
   307 {
   308     static unsigned char buf[256];
   309     char *str;
   310 
   311     switch (label) {
   312     case 0x01:          /* Plain Text Extension */
   313     str = "Plain Text Extension";
   314     break;
   315     case 0xff:          /* Application Extension */
   316     str = "Application Extension";
   317     break;
   318     case 0xfe:          /* Comment Extension */
   319     str = "Comment Extension";
   320     while (GetDataBlock(src, (unsigned char *) buf) > 0)
   321         ;
   322     return FALSE;
   323     case 0xf9:          /* Graphic Control Extension */
   324     str = "Graphic Control Extension";
   325     (void) GetDataBlock(src, (unsigned char *) buf);
   326     Gif89.disposal = (buf[0] >> 2) & 0x7;
   327     Gif89.inputFlag = (buf[0] >> 1) & 0x1;
   328     Gif89.delayTime = LM_to_uint(buf[1], buf[2]);
   329     if ((buf[0] & 0x1) != 0)
   330         Gif89.transparent = buf[3];
   331 
   332     while (GetDataBlock(src, (unsigned char *) buf) > 0)
   333         ;
   334     return FALSE;
   335     default:
   336     str = (char *)buf;
   337     SDL_snprintf(str, 256, "UNKNOWN (0x%02x)", label);
   338     break;
   339     }
   340 
   341     while (GetDataBlock(src, (unsigned char *) buf) > 0)
   342     ;
   343 
   344     return FALSE;
   345 }
   346 
   347 static int ZeroDataBlock = FALSE;
   348 
   349 static int
   350 GetDataBlock(SDL_RWops *src, unsigned char *buf)
   351 {
   352     unsigned char count;
   353 
   354     if (!ReadOK(src, &count, 1)) {
   355     /* pm_message("error in getting DataBlock size" ); */
   356     return -1;
   357     }
   358     ZeroDataBlock = count == 0;
   359 
   360     if ((count != 0) && (!ReadOK(src, buf, count))) {
   361     /* pm_message("error in reading DataBlock" ); */
   362     return -1;
   363     }
   364     return count;
   365 }
   366 
   367 static int
   368 GetCode(SDL_RWops *src, int code_size, int flag)
   369 {
   370     static unsigned char buf[280];
   371     static int curbit, lastbit, done, last_byte;
   372     int i, j, ret;
   373     unsigned char count;
   374 
   375     if (flag) {
   376     curbit = 0;
   377     lastbit = 0;
   378     done = FALSE;
   379     return 0;
   380     }
   381     if ((curbit + code_size) >= lastbit) {
   382     if (done) {
   383         if (curbit >= lastbit)
   384         RWSetMsg("ran off the end of my bits");
   385         return -1;
   386     }
   387     buf[0] = buf[last_byte - 2];
   388     buf[1] = buf[last_byte - 1];
   389 
   390     if ((count = GetDataBlock(src, &buf[2])) <= 0)
   391         done = TRUE;
   392 
   393     last_byte = 2 + count;
   394     curbit = (curbit - lastbit) + 16;
   395     lastbit = (2 + count) * 8;
   396     }
   397     ret = 0;
   398     for (i = curbit, j = 0; j < code_size; ++i, ++j)
   399     ret |= ((buf[i / 8] & (1 << (i % 8))) != 0) << j;
   400 
   401     curbit += code_size;
   402 
   403     return ret;
   404 }
   405 
   406 static int
   407 LWZReadByte(SDL_RWops *src, int flag, int input_code_size)
   408 {
   409     static int fresh = FALSE;
   410     int code, incode;
   411     static int code_size, set_code_size;
   412     static int max_code, max_code_size;
   413     static int firstcode, oldcode;
   414     static int clear_code, end_code;
   415     static int table[2][(1 << MAX_LWZ_BITS)];
   416     static int stack[(1 << (MAX_LWZ_BITS)) * 2], *sp;
   417     register int i;
   418 
   419     /* Fixed buffer overflow found by Michael Skladnikiewicz */
   420     if (input_code_size > MAX_LWZ_BITS)
   421         return -1;
   422 
   423     if (flag) {
   424     set_code_size = input_code_size;
   425     code_size = set_code_size + 1;
   426     clear_code = 1 << set_code_size;
   427     end_code = clear_code + 1;
   428     max_code_size = 2 * clear_code;
   429     max_code = clear_code + 2;
   430 
   431     GetCode(src, 0, TRUE);
   432 
   433     fresh = TRUE;
   434 
   435     for (i = 0; i < clear_code; ++i) {
   436         table[0][i] = 0;
   437         table[1][i] = i;
   438     }
   439     table[1][0] = 0;
   440     for (; i < (1 << MAX_LWZ_BITS); ++i)
   441         table[0][i] = 0;
   442 
   443     sp = stack;
   444 
   445     return 0;
   446     } else if (fresh) {
   447     fresh = FALSE;
   448     do {
   449         firstcode = oldcode = GetCode(src, code_size, FALSE);
   450     } while (firstcode == clear_code);
   451     return firstcode;
   452     }
   453     if (sp > stack)
   454     return *--sp;
   455 
   456     while ((code = GetCode(src, code_size, FALSE)) >= 0) {
   457     if (code == clear_code) {
   458         for (i = 0; i < clear_code; ++i) {
   459         table[0][i] = 0;
   460         table[1][i] = i;
   461         }
   462         for (; i < (1 << MAX_LWZ_BITS); ++i)
   463         table[0][i] = table[1][i] = 0;
   464         code_size = set_code_size + 1;
   465         max_code_size = 2 * clear_code;
   466         max_code = clear_code + 2;
   467         sp = stack;
   468         firstcode = oldcode = GetCode(src, code_size, FALSE);
   469         return firstcode;
   470     } else if (code == end_code) {
   471         int count;
   472         unsigned char buf[260];
   473 
   474         if (ZeroDataBlock)
   475         return -2;
   476 
   477         while ((count = GetDataBlock(src, buf)) > 0)
   478         ;
   479 
   480         if (count != 0) {
   481         /*
   482          * pm_message("missing EOD in data stream (common occurence)");
   483          */
   484         }
   485         return -2;
   486     }
   487     incode = code;
   488 
   489     if (code >= max_code) {
   490         *sp++ = firstcode;
   491         code = oldcode;
   492     }
   493     while (code >= clear_code) {
   494         /* Guard against buffer overruns */
   495         if (code < 0 || code >= (1 << MAX_LWZ_BITS)) {
   496             RWSetMsg("invalid LWZ data");
   497             return -3;
   498         }
   499         *sp++ = table[1][code];
   500         if (code == table[0][code]) {
   501             RWSetMsg("circular table entry BIG ERROR");
   502             return -3;
   503         }
   504         code = table[0][code];
   505     }
   506 
   507     /* Guard against buffer overruns */
   508     if (code < 0 || code >= (1 << MAX_LWZ_BITS)) {
   509         RWSetMsg("invalid LWZ data");
   510         return -4;
   511     }
   512     *sp++ = firstcode = table[1][code];
   513 
   514     if ((code = max_code) < (1 << MAX_LWZ_BITS)) {
   515         table[0][code] = oldcode;
   516         table[1][code] = firstcode;
   517         ++max_code;
   518         if ((max_code >= max_code_size) &&
   519         (max_code_size < (1 << MAX_LWZ_BITS))) {
   520         max_code_size *= 2;
   521         ++code_size;
   522         }
   523     }
   524     oldcode = incode;
   525 
   526     if (sp > stack)
   527         return *--sp;
   528     }
   529     return code;
   530 }
   531 
   532 static Image *
   533 ReadImage(SDL_RWops * src, int len, int height, int cmapSize,
   534       unsigned char cmap[3][MAXCOLORMAPSIZE],
   535       int gray, int interlace, int ignore)
   536 {
   537     Image *image;
   538     unsigned char c;
   539     int i, v;
   540     int xpos = 0, ypos = 0, pass = 0;
   541 
   542     /*
   543     **  Initialize the compression routines
   544      */
   545     if (!ReadOK(src, &c, 1)) {
   546     RWSetMsg("EOF / read error on image data");
   547     return NULL;
   548     }
   549     if (LWZReadByte(src, TRUE, c) < 0) {
   550     RWSetMsg("error reading image");
   551     return NULL;
   552     }
   553     /*
   554     **  If this is an "uninteresting picture" ignore it.
   555      */
   556     if (ignore) {
   557     while (LWZReadByte(src, FALSE, c) >= 0)
   558         ;
   559     return NULL;
   560     }
   561     image = ImageNewCmap(len, height, cmapSize);
   562 
   563     for (i = 0; i < cmapSize; i++)
   564     ImageSetCmap(image, i, cmap[CM_RED][i],
   565              cmap[CM_GREEN][i], cmap[CM_BLUE][i]);
   566 
   567     while ((v = LWZReadByte(src, FALSE, c)) >= 0) {
   568 #ifdef USED_BY_SDL
   569     ((Uint8 *)image->pixels)[xpos + ypos * image->pitch] = v;
   570 #else
   571     image->data[xpos + ypos * len] = v;
   572 #endif
   573     ++xpos;
   574     if (xpos == len) {
   575         xpos = 0;
   576         if (interlace) {
   577         switch (pass) {
   578         case 0:
   579         case 1:
   580             ypos += 8;
   581             break;
   582         case 2:
   583             ypos += 4;
   584             break;
   585         case 3:
   586             ypos += 2;
   587             break;
   588         }
   589 
   590         if (ypos >= height) {
   591             ++pass;
   592             switch (pass) {
   593             case 1:
   594             ypos = 4;
   595             break;
   596             case 2:
   597             ypos = 2;
   598             break;
   599             case 3:
   600             ypos = 1;
   601             break;
   602             default:
   603             goto fini;
   604             }
   605         }
   606         } else {
   607         ++ypos;
   608         }
   609     }
   610     if (ypos >= height)
   611         break;
   612     }
   613 
   614   fini:
   615 
   616     return image;
   617 }
   618 
   619 #else
   620 
   621 /* See if an image is contained in a data source */
   622 int IMG_isGIF(SDL_RWops *src)
   623 {
   624     return(0);
   625 }
   626 
   627 /* Load a GIF type image from an SDL datasource */
   628 SDL_Surface *IMG_LoadGIF_RW(SDL_RWops *src)
   629 {
   630     return(NULL);
   631 }
   632 
   633 #endif /* LOAD_GIF */
   634 
   635 #endif /* !defined(__APPLE__) || defined(SDL_IMAGE_USE_COMMON_BACKEND) */