IMG_gif.c
author Sam Lantinga <slouken@libsdl.org>
Wed, 09 Oct 2019 19:11:28 -0700
changeset 723 1239eed726ae
parent 722 63ec5ac0b0b8
permissions -rw-r--r--
Fixed compiler warnings
     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 /* This is a GIF image file loading framework */
    23 
    24 #include "SDL_image.h"
    25 
    26 #ifdef LOAD_GIF
    27 
    28 /* Code from here to end of file has been adapted from XPaint:           */
    29 /* +-------------------------------------------------------------------+ */
    30 /* | Copyright 1990, 1991, 1993 David Koblas.                  | */
    31 /* | Copyright 1996 Torsten Martinsen.                     | */
    32 /* |   Permission to use, copy, modify, and distribute this software   | */
    33 /* |   and its documentation for any purpose and without fee is hereby | */
    34 /* |   granted, provided that the above copyright notice appear in all | */
    35 /* |   copies and that both that copyright notice and this permission  | */
    36 /* |   notice appear in supporting documentation.  This software is    | */
    37 /* |   provided "as is" without express or implied warranty.           | */
    38 /* +-------------------------------------------------------------------+ */
    39 
    40 /* Adapted for use in SDL by Sam Lantinga -- 7/20/98 */
    41 /* Changes to work with SDL:
    42 
    43    Include SDL header file
    44    Use SDL_Surface rather than xpaint Image structure
    45    Define SDL versions of RWSetMsg(), ImageNewCmap() and ImageSetCmap()
    46 */
    47 #include "SDL.h"
    48 
    49 #define Image           SDL_Surface
    50 #define RWSetMsg        IMG_SetError
    51 #define ImageNewCmap(w, h, s)   SDL_CreateRGBSurface(SDL_SWSURFACE,w,h,8,0,0,0,0)
    52 #define ImageSetCmap(s, i, R, G, B) do { \
    53                 s->format->palette->colors[i].r = R; \
    54                 s->format->palette->colors[i].g = G; \
    55                 s->format->palette->colors[i].b = B; \
    56             } while (0)
    57 /* * * * * */
    58 
    59 #define GIF_DISPOSE_NA                  0   /* No disposal specified */
    60 #define GIF_DISPOSE_NONE                1   /* Do not dispose */
    61 #define GIF_DISPOSE_RESTORE_BACKGROUND  2   /* Restore to background */
    62 #define GIF_DISPOSE_RESTORE_PREVIOUS    3   /* Restore to previous */
    63 
    64 #define MAXCOLORMAPSIZE     256
    65 
    66 #define TRUE    1
    67 #define FALSE   0
    68 
    69 #define CM_RED      0
    70 #define CM_GREEN    1
    71 #define CM_BLUE     2
    72 
    73 #define MAX_LWZ_BITS        12
    74 
    75 #define INTERLACE       0x40
    76 #define LOCALCOLORMAP   0x80
    77 #define BitSet(byte, bit)   (((byte) & (bit)) == (bit))
    78 
    79 #define ReadOK(file,buffer,len) SDL_RWread(file, buffer, len, 1)
    80 
    81 #define LM_to_uint(a,b)         (((b)<<8)|(a))
    82 
    83 typedef struct {
    84     struct {
    85         unsigned int Width;
    86         unsigned int Height;
    87         unsigned char ColorMap[3][MAXCOLORMAPSIZE];
    88         unsigned int BitPixel;
    89         unsigned int ColorResolution;
    90         unsigned int Background;
    91         unsigned int AspectRatio;
    92         int GrayScale;
    93     } GifScreen;
    94 
    95     struct {
    96         int transparent;
    97         int delayTime;
    98         int inputFlag;
    99         int disposal;
   100     } Gif89;
   101 
   102     unsigned char buf[280];
   103     int curbit, lastbit, done, last_byte;
   104 
   105     int fresh;
   106     int code_size, set_code_size;
   107     int max_code, max_code_size;
   108     int firstcode, oldcode;
   109     int clear_code, end_code;
   110     int table[2][(1 << MAX_LWZ_BITS)];
   111     int stack[(1 << (MAX_LWZ_BITS)) * 2], *sp;
   112 
   113     int ZeroDataBlock;
   114 } State_t;
   115 
   116 typedef struct
   117 {
   118     Image *image;
   119     int x, y;
   120     int disposal;
   121     int delay;
   122 } Frame_t;
   123 
   124 typedef struct
   125 {
   126     int count;
   127     Frame_t *frames;
   128 } Anim_t;
   129 
   130 static int ReadColorMap(SDL_RWops * src, int number,
   131             unsigned char buffer[3][MAXCOLORMAPSIZE], int *flag);
   132 static int DoExtension(SDL_RWops * src, int label, State_t * state);
   133 static int GetDataBlock(SDL_RWops * src, unsigned char *buf, State_t * state);
   134 static int GetCode(SDL_RWops * src, int code_size, int flag, State_t * state);
   135 static int LWZReadByte(SDL_RWops * src, int flag, int input_code_size, State_t * state);
   136 static Image *ReadImage(SDL_RWops * src, int len, int height, int,
   137             unsigned char cmap[3][MAXCOLORMAPSIZE],
   138             int gray, int interlace, int ignore, State_t * state);
   139 
   140 static SDL_bool NormalizeFrames(Frame_t *frames, int count)
   141 {
   142     SDL_Surface *image;
   143     int i;
   144     int lastDispose = GIF_DISPOSE_RESTORE_BACKGROUND;
   145     int iRestore = 0;
   146     Uint32 fill;
   147     SDL_Rect rect;
   148 
   149 
   150     if (SDL_HasColorKey(frames[0].image)) {
   151         image = SDL_ConvertSurfaceFormat(frames[0].image, SDL_PIXELFORMAT_ARGB8888, 0);
   152     } else {
   153         image = SDL_ConvertSurfaceFormat(frames[0].image, SDL_PIXELFORMAT_RGB888, 0);
   154     }
   155     if (!image) {
   156         return SDL_FALSE;
   157     }
   158 
   159     fill = SDL_MapRGBA(image->format, 0, 0, 0, SDL_ALPHA_TRANSPARENT);
   160 
   161     rect.x = 0;
   162     rect.y = 0;
   163     rect.w = image->w;
   164     rect.h = image->h;
   165 
   166     for (i = 0; i < count; ++i) {
   167         switch (lastDispose) {
   168         case GIF_DISPOSE_RESTORE_BACKGROUND:
   169             SDL_FillRect(image, &rect, fill);
   170             break;
   171         case GIF_DISPOSE_RESTORE_PREVIOUS:
   172             SDL_BlitSurface(frames[iRestore].image, &rect, image, &rect);
   173             break;
   174         default:
   175             break;
   176         }
   177 
   178         if (frames[i].disposal != GIF_DISPOSE_RESTORE_PREVIOUS) {
   179             iRestore = i;
   180         }
   181 
   182         rect.x = (Sint16)frames[i].x;
   183         rect.y = (Sint16)frames[i].y;
   184         rect.w = frames[i].image->w;
   185         rect.h = frames[i].image->h;
   186         SDL_BlitSurface(frames[i].image, NULL, image, &rect);
   187 
   188         SDL_FreeSurface(frames[i].image);
   189         frames[i].image = SDL_DuplicateSurface(image);
   190         if (!frames[i].image) {
   191             return SDL_FALSE;
   192         }
   193 
   194         lastDispose = frames[i].disposal;
   195     }
   196 
   197     SDL_FreeSurface( image );
   198 
   199     return SDL_TRUE;
   200 }
   201 
   202 Anim_t *
   203 IMG_LoadGIF_RW_Internal(SDL_RWops *src, SDL_bool load_anim)
   204 {
   205     unsigned char buf[16];
   206     unsigned char c;
   207     unsigned char localColorMap[3][MAXCOLORMAPSIZE];
   208     int grayScale;
   209     int useGlobalColormap;
   210     int bitPixel;
   211     char version[4];
   212     Image *image = NULL;
   213     Anim_t *anim;
   214     Frame_t *frames, *frame;
   215     State_t state;
   216 
   217     state.ZeroDataBlock = FALSE;
   218     state.fresh = FALSE;
   219     state.last_byte = 0;
   220 
   221     if (src == NULL) {
   222         return NULL;
   223     }
   224 
   225     anim = (Anim_t *)SDL_calloc(1, sizeof(*anim));
   226     if (!anim) {
   227         SDL_OutOfMemory();
   228         return NULL;
   229     }
   230 
   231     if (!ReadOK(src, buf, 6)) {
   232         RWSetMsg("error reading magic number");
   233         goto done;
   234     }
   235     if (SDL_strncmp((char *) buf, "GIF", 3) != 0) {
   236         RWSetMsg("not a GIF file");
   237         goto done;
   238     }
   239     SDL_memcpy(version, (char *) buf + 3, 3);
   240     version[3] = '\0';
   241 
   242     if ((SDL_strcmp(version, "87a") != 0) && (SDL_strcmp(version, "89a") != 0)) {
   243         RWSetMsg("bad version number, not '87a' or '89a'");
   244         goto done;
   245     }
   246     state.Gif89.transparent = -1;
   247     state.Gif89.delayTime = -1;
   248     state.Gif89.inputFlag = -1;
   249     state.Gif89.disposal = GIF_DISPOSE_NA;
   250 
   251     if (!ReadOK(src, buf, 7)) {
   252         RWSetMsg("failed to read screen descriptor");
   253         goto done;
   254     }
   255     state.GifScreen.Width = LM_to_uint(buf[0], buf[1]);
   256     state.GifScreen.Height = LM_to_uint(buf[2], buf[3]);
   257     state.GifScreen.BitPixel = 2 << (buf[4] & 0x07);
   258     state.GifScreen.ColorResolution = (((buf[4] & 0x70) >> 3) + 1);
   259     state.GifScreen.Background = buf[5];
   260     state.GifScreen.AspectRatio = buf[6];
   261 
   262     if (BitSet(buf[4], LOCALCOLORMAP)) {    /* Global Colormap */
   263         if (ReadColorMap(src, state.GifScreen.BitPixel,
   264                          state.GifScreen.ColorMap, &state.GifScreen.GrayScale)) {
   265             RWSetMsg("error reading global colormap");
   266             goto done;
   267         }
   268     }
   269     for ( ; ; ) {
   270         if (!ReadOK(src, &c, 1)) {
   271             RWSetMsg("EOF / read error on image data");
   272             goto done;
   273         }
   274         if (c == ';') {     /* GIF terminator */
   275             goto done;
   276         }
   277         if (c == '!') {     /* Extension */
   278             if (!ReadOK(src, &c, 1)) {
   279                 RWSetMsg("EOF / read error on extention function code");
   280                 goto done;
   281             }
   282             DoExtension(src, c, &state);
   283             continue;
   284         }
   285         if (c != ',') {     /* Not a valid start character */
   286             continue;
   287         }
   288 
   289         if (!ReadOK(src, buf, 9)) {
   290             RWSetMsg("couldn't read left/top/width/height");
   291             goto done;
   292         }
   293         useGlobalColormap = !BitSet(buf[8], LOCALCOLORMAP);
   294 
   295         bitPixel = 1 << ((buf[8] & 0x07) + 1);
   296 
   297         if (!useGlobalColormap) {
   298             if (ReadColorMap(src, bitPixel, localColorMap, &grayScale)) {
   299                 RWSetMsg("error reading local colormap");
   300                 goto done;
   301             }
   302             image = ReadImage(src, LM_to_uint(buf[4], buf[5]),
   303                       LM_to_uint(buf[6], buf[7]),
   304                       bitPixel, localColorMap, grayScale,
   305                       BitSet(buf[8], INTERLACE),
   306                       0, &state);
   307         } else {
   308             image = ReadImage(src, LM_to_uint(buf[4], buf[5]),
   309                       LM_to_uint(buf[6], buf[7]),
   310                       state.GifScreen.BitPixel, state.GifScreen.ColorMap,
   311                       state.GifScreen.GrayScale, BitSet(buf[8], INTERLACE),
   312                       0, &state);
   313         }
   314 
   315         if (image) {
   316             if (state.Gif89.transparent >= 0) {
   317                 SDL_SetColorKey(image, SDL_TRUE, state.Gif89.transparent);
   318             }
   319 
   320             frames = (Frame_t *)SDL_realloc(anim->frames, (anim->count + 1) * sizeof(*anim->frames));
   321             if (!frames) {
   322                 SDL_OutOfMemory();
   323                 goto done;
   324             }
   325             ++anim->count;
   326             anim->frames = frames;
   327             frame = &anim->frames[anim->count - 1];
   328 
   329             frame->image = image;
   330             frame->x = LM_to_uint(buf[0], buf[1]);
   331             frame->y = LM_to_uint(buf[2], buf[3]);
   332             frame->disposal = state.Gif89.disposal;
   333             if (state.Gif89.delayTime < 2) {
   334                 frame->delay = 100; /* Default animation delay, matching browsers and Qt */
   335             } else {
   336                 frame->delay = state.Gif89.delayTime * 10;
   337             }
   338 
   339             if (!load_anim) {
   340                 /* We only need one frame, we're done */
   341                 goto done;
   342             }
   343         }
   344     }
   345 
   346 done:
   347     if (anim->count > 1) {
   348         /* Normalize the frames */
   349         if (!NormalizeFrames(anim->frames, anim->count)) {
   350             int i;
   351             for (i = 0; i < anim->count; ++i) {
   352                 SDL_FreeSurface(anim->frames[i].image);
   353             }
   354             anim->count = 0;
   355         }
   356     }
   357     if (anim->count == 0) {
   358         SDL_free(anim->frames);
   359         SDL_free(anim);
   360         return NULL;
   361     }
   362     return anim;
   363 }
   364 
   365 static int
   366 ReadColorMap(SDL_RWops *src, int number,
   367              unsigned char buffer[3][MAXCOLORMAPSIZE], int *gray)
   368 {
   369     int i;
   370     unsigned char rgb[3];
   371     int flag;
   372 
   373     flag = TRUE;
   374 
   375     for (i = 0; i < number; ++i) {
   376         if (!ReadOK(src, rgb, sizeof(rgb))) {
   377             RWSetMsg("bad colormap");
   378             return 1;
   379         }
   380         buffer[CM_RED][i] = rgb[0];
   381         buffer[CM_GREEN][i] = rgb[1];
   382         buffer[CM_BLUE][i] = rgb[2];
   383         flag &= (rgb[0] == rgb[1] && rgb[1] == rgb[2]);
   384     }
   385 
   386 #if 0
   387     if (flag)
   388         *gray = (number == 2) ? PBM_TYPE : PGM_TYPE;
   389     else
   390         *gray = PPM_TYPE;
   391 #else
   392     *gray = 0;
   393 #endif
   394 
   395     return FALSE;
   396 }
   397 
   398 static int
   399 DoExtension(SDL_RWops *src, int label, State_t * state)
   400 {
   401     unsigned char buf[256];
   402 
   403     switch (label) {
   404     case 0x01:          /* Plain Text Extension */
   405         break;
   406     case 0xff:          /* Application Extension */
   407         break;
   408     case 0xfe:          /* Comment Extension */
   409         while (GetDataBlock(src, buf, state) > 0)
   410             ;
   411         return FALSE;
   412     case 0xf9:          /* Graphic Control Extension */
   413         (void) GetDataBlock(src, buf, state);
   414         state->Gif89.disposal = (buf[0] >> 2) & 0x7;
   415         state->Gif89.inputFlag = (buf[0] >> 1) & 0x1;
   416         state->Gif89.delayTime = LM_to_uint(buf[1], buf[2]);
   417         if ((buf[0] & 0x1) != 0)
   418             state->Gif89.transparent = buf[3];
   419 
   420         while (GetDataBlock(src, buf, state) > 0)
   421             ;
   422         return FALSE;
   423     default:
   424         break;
   425     }
   426 
   427     while (GetDataBlock(src, buf, state) > 0)
   428         ;
   429 
   430     return FALSE;
   431 }
   432 
   433 static int
   434 GetDataBlock(SDL_RWops *src, unsigned char *buf, State_t * state)
   435 {
   436     unsigned char count;
   437 
   438     if (!ReadOK(src, &count, 1)) {
   439         /* pm_message("error in getting DataBlock size" ); */
   440         return -1;
   441     }
   442     state->ZeroDataBlock = count == 0;
   443 
   444     if ((count != 0) && (!ReadOK(src, buf, count))) {
   445         /* pm_message("error in reading DataBlock" ); */
   446         return -1;
   447     }
   448     return count;
   449 }
   450 
   451 static int
   452 GetCode(SDL_RWops *src, int code_size, int flag, State_t * state)
   453 {
   454     int i, j, ret;
   455     unsigned char count;
   456 
   457     if (flag) {
   458         state->curbit = 0;
   459         state->lastbit = 0;
   460         state->done = FALSE;
   461         return 0;
   462     }
   463     if ((state->curbit + code_size) >= state->lastbit) {
   464         if (state->done) {
   465             if (state->curbit >= state->lastbit)
   466                 RWSetMsg("ran off the end of my bits");
   467             return -1;
   468         }
   469         state->buf[0] = state->buf[state->last_byte - 2];
   470         state->buf[1] = state->buf[state->last_byte - 1];
   471 
   472         if ((ret = GetDataBlock(src, &state->buf[2], state)) > 0)
   473             count = (unsigned char) ret;
   474         else {
   475             count = 0;
   476             state->done = TRUE;
   477         }
   478 
   479         state->last_byte = 2 + count;
   480         state->curbit = (state->curbit - state->lastbit) + 16;
   481         state->lastbit = (2 + count) * 8;
   482     }
   483     ret = 0;
   484     for (i = state->curbit, j = 0; j < code_size; ++i, ++j)
   485         ret |= ((state->buf[i / 8] & (1 << (i % 8))) != 0) << j;
   486 
   487     state->curbit += code_size;
   488 
   489     return ret;
   490 }
   491 
   492 static int
   493 LWZReadByte(SDL_RWops *src, int flag, int input_code_size, State_t * state)
   494 {
   495     int code, incode;
   496     register int i;
   497 
   498     /* Fixed buffer overflow found by Michael Skladnikiewicz */
   499     if (input_code_size > MAX_LWZ_BITS)
   500         return -1;
   501 
   502     if (flag) {
   503         state->set_code_size = input_code_size;
   504         state->code_size = state->set_code_size + 1;
   505         state->clear_code = 1 << state->set_code_size;
   506         state->end_code = state->clear_code + 1;
   507         state->max_code_size = 2 * state->clear_code;
   508         state->max_code = state->clear_code + 2;
   509 
   510         GetCode(src, 0, TRUE, state);
   511 
   512         state->fresh = TRUE;
   513 
   514         for (i = 0; i < state->clear_code; ++i) {
   515             state->table[0][i] = 0;
   516             state->table[1][i] = i;
   517         }
   518         state->table[1][0] = 0;
   519         for (; i < (1 << MAX_LWZ_BITS); ++i)
   520             state->table[0][i] = 0;
   521 
   522         state->sp = state->stack;
   523 
   524         return 0;
   525     } else if (state->fresh) {
   526         state->fresh = FALSE;
   527         do {
   528             state->firstcode = state->oldcode = GetCode(src, state->code_size, FALSE, state);
   529         } while (state->firstcode == state->clear_code);
   530         return state->firstcode;
   531     }
   532     if (state->sp > state->stack)
   533         return *--state->sp;
   534 
   535     while ((code = GetCode(src, state->code_size, FALSE, state)) >= 0) {
   536         if (code == state->clear_code) {
   537             for (i = 0; i < state->clear_code; ++i) {
   538                 state->table[0][i] = 0;
   539                 state->table[1][i] = i;
   540             }
   541             for (; i < (1 << MAX_LWZ_BITS); ++i)
   542                 state->table[0][i] = state->table[1][i] = 0;
   543             state->code_size = state->set_code_size + 1;
   544             state->max_code_size = 2 * state->clear_code;
   545             state->max_code = state->clear_code + 2;
   546             state->sp = state->stack;
   547             state->firstcode = state->oldcode = GetCode(src, state->code_size, FALSE, state);
   548             return state->firstcode;
   549         } else if (code == state->end_code) {
   550             int count;
   551             unsigned char buf[260];
   552 
   553             if (state->ZeroDataBlock)
   554                 return -2;
   555 
   556             while ((count = GetDataBlock(src, buf, state)) > 0)
   557                 ;
   558 
   559             if (count != 0) {
   560             /*
   561              * pm_message("missing EOD in data stream (common occurence)");
   562              */
   563             }
   564             return -2;
   565         }
   566         incode = code;
   567 
   568         if (code >= state->max_code) {
   569             *state->sp++ = state->firstcode;
   570             code = state->oldcode;
   571         }
   572         while (code >= state->clear_code) {
   573             /* Guard against buffer overruns */
   574             if (code < 0 || code >= (1 << MAX_LWZ_BITS)) {
   575                 RWSetMsg("invalid LWZ data");
   576                 return -3;
   577             }
   578             *state->sp++ = state->table[1][code];
   579             if (code == state->table[0][code]) {
   580                 RWSetMsg("circular table entry BIG ERROR");
   581                 return -3;
   582             }
   583             code = state->table[0][code];
   584         }
   585 
   586         /* Guard against buffer overruns */
   587         if (code < 0 || code >= (1 << MAX_LWZ_BITS)) {
   588             RWSetMsg("invalid LWZ data");
   589             return -4;
   590         }
   591         *state->sp++ = state->firstcode = state->table[1][code];
   592 
   593         if ((code = state->max_code) < (1 << MAX_LWZ_BITS)) {
   594             state->table[0][code] = state->oldcode;
   595             state->table[1][code] = state->firstcode;
   596             ++state->max_code;
   597             if ((state->max_code >= state->max_code_size) &&
   598                 (state->max_code_size < (1 << MAX_LWZ_BITS))) {
   599                 state->max_code_size *= 2;
   600                 ++state->code_size;
   601             }
   602         }
   603         state->oldcode = incode;
   604 
   605         if (state->sp > state->stack)
   606             return *--state->sp;
   607     }
   608     return code;
   609 }
   610 
   611 static Image *
   612 ReadImage(SDL_RWops * src, int len, int height, int cmapSize,
   613           unsigned char cmap[3][MAXCOLORMAPSIZE],
   614           int gray, int interlace, int ignore, State_t * state)
   615 {
   616     Image *image;
   617     unsigned char c;
   618     int i, v;
   619     int xpos = 0, ypos = 0, pass = 0;
   620 
   621     (void) gray; /* unused */
   622 
   623     /*
   624     **  Initialize the compression routines
   625      */
   626     if (!ReadOK(src, &c, 1)) {
   627         RWSetMsg("EOF / read error on image data");
   628         return NULL;
   629     }
   630     if (LWZReadByte(src, TRUE, c, state) < 0) {
   631         RWSetMsg("error reading image");
   632         return NULL;
   633     }
   634     /*
   635     **  If this is an "uninteresting picture" ignore it.
   636      */
   637     if (ignore) {
   638         while (LWZReadByte(src, FALSE, c, state) >= 0)
   639             ;
   640         return NULL;
   641     }
   642     image = ImageNewCmap(len, height, cmapSize);
   643 
   644     for (i = 0; i < cmapSize; i++)
   645         ImageSetCmap(image, i, cmap[CM_RED][i],
   646                      cmap[CM_GREEN][i], cmap[CM_BLUE][i]);
   647 
   648     while ((v = LWZReadByte(src, FALSE, c, state)) >= 0) {
   649         ((Uint8 *)image->pixels)[xpos + ypos * image->pitch] = (Uint8)v;
   650         ++xpos;
   651         if (xpos == len) {
   652             xpos = 0;
   653             if (interlace) {
   654                 switch (pass) {
   655                 case 0:
   656                 case 1:
   657                     ypos += 8;
   658                     break;
   659                 case 2:
   660                     ypos += 4;
   661                     break;
   662                 case 3:
   663                     ypos += 2;
   664                     break;
   665                 }
   666 
   667                 if (ypos >= height) {
   668                     ++pass;
   669                     switch (pass) {
   670                     case 1:
   671                         ypos = 4;
   672                         break;
   673                     case 2:
   674                         ypos = 2;
   675                         break;
   676                     case 3:
   677                         ypos = 1;
   678                         break;
   679                     default:
   680                         goto fini;
   681                     }
   682                 }
   683             } else {
   684                 ++ypos;
   685             }
   686         }
   687         if (ypos >= height)
   688             break;
   689     }
   690 
   691   fini:
   692 
   693     return image;
   694 }
   695 
   696 /* Load a GIF type animation from an SDL datasource */
   697 IMG_Animation *IMG_LoadGIFAnimation_RW(SDL_RWops *src)
   698 {
   699     Anim_t *internal = IMG_LoadGIF_RW_Internal(src, SDL_TRUE);
   700     if (internal) {
   701         IMG_Animation *anim = (IMG_Animation *)SDL_malloc(sizeof(*anim));
   702         if (anim) {
   703             anim->w = internal->frames[0].image->w;
   704             anim->h = internal->frames[0].image->h;
   705             anim->count = internal->count;
   706 
   707             anim->frames = (SDL_Surface **)SDL_calloc(anim->count, sizeof(*anim->frames));
   708             anim->delays = (int *)SDL_calloc(anim->count, sizeof(*anim->delays));
   709 
   710             if (anim->frames && anim->delays) {
   711                 int i;
   712 
   713                 for (i = 0; i < anim->count; ++i) {
   714                     anim->frames[i] = internal->frames[i].image;
   715                     anim->delays[i] = internal->frames[i].delay;
   716                 }
   717             } else {
   718                 IMG_FreeAnimation(anim);
   719                 anim = NULL;
   720             }
   721         }
   722         if (!anim) {
   723             SDL_OutOfMemory();
   724         }
   725         SDL_free(internal->frames);
   726         SDL_free(internal);
   727         return anim;
   728     }
   729     return NULL;
   730 }
   731 
   732 #else
   733 
   734 /* Load a GIF type animation from an SDL datasource */
   735 IMG_Animation *IMG_LoadGIFAnimation_RW(SDL_RWops *src)
   736 {
   737     return NULL;
   738 }
   739 
   740 #endif /* LOAD_GIF */
   741 
   742 #if !defined(__APPLE__) || defined(SDL_IMAGE_USE_COMMON_BACKEND)
   743 
   744 #ifdef LOAD_GIF
   745 
   746 /* See if an image is contained in a data source */
   747 int IMG_isGIF(SDL_RWops *src)
   748 {
   749     Sint64 start;
   750     int is_GIF;
   751     char magic[6];
   752 
   753     if ( !src )
   754         return 0;
   755     start = SDL_RWtell(src);
   756     is_GIF = 0;
   757     if ( SDL_RWread(src, magic, sizeof(magic), 1) ) {
   758         if ( (SDL_strncmp(magic, "GIF", 3) == 0) &&
   759              ((SDL_memcmp(magic + 3, "87a", 3) == 0) ||
   760               (SDL_memcmp(magic + 3, "89a", 3) == 0)) ) {
   761             is_GIF = 1;
   762         }
   763     }
   764     SDL_RWseek(src, start, RW_SEEK_SET);
   765     return(is_GIF);
   766 }
   767 
   768 /* Load a GIF type image from an SDL datasource */
   769 SDL_Surface *IMG_LoadGIF_RW(SDL_RWops *src)
   770 {
   771     SDL_Surface *image = NULL;
   772     Anim_t *internal = IMG_LoadGIF_RW_Internal(src, SDL_FALSE);
   773     if (internal) {
   774         image = internal->frames[0].image;
   775         SDL_free(internal->frames);
   776         SDL_free(internal);
   777     }
   778     return image;
   779 }
   780 
   781 #else
   782 #if _MSC_VER >= 1300
   783 #pragma warning(disable : 4100) /* warning C4100: 'op' : unreferenced formal parameter */
   784 #endif
   785 
   786 /* See if an image is contained in a data source */
   787 int IMG_isGIF(SDL_RWops *src)
   788 {
   789     return(0);
   790 }
   791 
   792 /* Load a GIF type image from an SDL datasource */
   793 SDL_Surface *IMG_LoadGIF_RW(SDL_RWops *src)
   794 {
   795     return(NULL);
   796 }
   797 
   798 #endif /* LOAD_GIF */
   799 
   800 #endif /* !defined(__APPLE__) || defined(SDL_IMAGE_USE_COMMON_BACKEND) */