IMG_gif.c
author Sam Lantinga <slouken@libsdl.org>
Sun, 01 Jan 2017 18:50:51 -0800
changeset 496 6332f9425dcc
parent 486 7bb8af91e887
child 528 7f9e88f4b45e
permissions -rw-r--r--
Updated copyright for 2017
     1 /*
     2   SDL_image:  An example image loading library for use with SDL
     3   Copyright (C) 1997-2017 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) */