IMG_gif.c
author Ozkan Sezer
Wed, 09 Oct 2019 20:30:10 +0300
changeset 720 58e4a9ae8cd2
parent 719 a1fd4472ed6b
child 721 2ab0052024a3
permissions -rw-r--r--
IMG_gif.c: build fixes.
     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     Sint64 start;
   206     unsigned char buf[16];
   207     unsigned char c;
   208     unsigned char localColorMap[3][MAXCOLORMAPSIZE];
   209     int grayScale;
   210     int useGlobalColormap;
   211     int bitPixel;
   212     char version[4];
   213     Image *image = NULL;
   214     Anim_t *anim;
   215     Frame_t *frames, *frame;
   216     State_t state;
   217 
   218     state.ZeroDataBlock = FALSE;
   219     state.fresh = FALSE;
   220     state.last_byte = 0;
   221 
   222     if (src == NULL) {
   223         return NULL;
   224     }
   225     start = SDL_RWtell(src);
   226 
   227     anim = (Anim_t *)SDL_calloc(1, sizeof(*anim));
   228     if (!anim) {
   229         SDL_OutOfMemory();
   230         return NULL;
   231     }
   232 
   233     if (!ReadOK(src, buf, 6)) {
   234         RWSetMsg("error reading magic number");
   235         goto done;
   236     }
   237     if (SDL_strncmp((char *) buf, "GIF", 3) != 0) {
   238         RWSetMsg("not a GIF file");
   239         goto done;
   240     }
   241     SDL_memcpy(version, (char *) buf + 3, 3);
   242     version[3] = '\0';
   243 
   244     if ((SDL_strcmp(version, "87a") != 0) && (SDL_strcmp(version, "89a") != 0)) {
   245         RWSetMsg("bad version number, not '87a' or '89a'");
   246         goto done;
   247     }
   248     state.Gif89.transparent = -1;
   249     state.Gif89.delayTime = -1;
   250     state.Gif89.inputFlag = -1;
   251     state.Gif89.disposal = GIF_DISPOSE_NA;
   252 
   253     if (!ReadOK(src, buf, 7)) {
   254         RWSetMsg("failed to read screen descriptor");
   255         goto done;
   256     }
   257     state.GifScreen.Width = LM_to_uint(buf[0], buf[1]);
   258     state.GifScreen.Height = LM_to_uint(buf[2], buf[3]);
   259     state.GifScreen.BitPixel = 2 << (buf[4] & 0x07);
   260     state.GifScreen.ColorResolution = (((buf[4] & 0x70) >> 3) + 1);
   261     state.GifScreen.Background = buf[5];
   262     state.GifScreen.AspectRatio = buf[6];
   263 
   264     if (BitSet(buf[4], LOCALCOLORMAP)) {    /* Global Colormap */
   265         if (ReadColorMap(src, state.GifScreen.BitPixel,
   266                          state.GifScreen.ColorMap, &state.GifScreen.GrayScale)) {
   267             RWSetMsg("error reading global colormap");
   268             goto done;
   269         }
   270     }
   271     for ( ; ; ) {
   272         if (!ReadOK(src, &c, 1)) {
   273             RWSetMsg("EOF / read error on image data");
   274             goto done;
   275         }
   276         if (c == ';') {     /* GIF terminator */
   277             goto done;
   278         }
   279         if (c == '!') {     /* Extension */
   280             if (!ReadOK(src, &c, 1)) {
   281                 RWSetMsg("EOF / read error on extention function code");
   282                 goto done;
   283             }
   284             DoExtension(src, c, &state);
   285             continue;
   286         }
   287         if (c != ',') {     /* Not a valid start character */
   288             continue;
   289         }
   290 
   291         if (!ReadOK(src, buf, 9)) {
   292             RWSetMsg("couldn't read left/top/width/height");
   293             goto done;
   294         }
   295         useGlobalColormap = !BitSet(buf[8], LOCALCOLORMAP);
   296 
   297         bitPixel = 1 << ((buf[8] & 0x07) + 1);
   298 
   299         if (!useGlobalColormap) {
   300             if (ReadColorMap(src, bitPixel, localColorMap, &grayScale)) {
   301                 RWSetMsg("error reading local colormap");
   302                 goto done;
   303             }
   304             image = ReadImage(src, LM_to_uint(buf[4], buf[5]),
   305                       LM_to_uint(buf[6], buf[7]),
   306                       bitPixel, localColorMap, grayScale,
   307                       BitSet(buf[8], INTERLACE),
   308                       0, &state);
   309         } else {
   310             image = ReadImage(src, LM_to_uint(buf[4], buf[5]),
   311                       LM_to_uint(buf[6], buf[7]),
   312                       state.GifScreen.BitPixel, state.GifScreen.ColorMap,
   313                       state.GifScreen.GrayScale, BitSet(buf[8], INTERLACE),
   314                       0, &state);
   315         }
   316 
   317         if (image) {
   318             if (state.Gif89.transparent >= 0) {
   319                 SDL_SetColorKey(image, SDL_TRUE, state.Gif89.transparent);
   320             }
   321 
   322             frames = (Frame_t *)SDL_realloc(anim->frames, (anim->count + 1) * sizeof(*anim->frames));
   323             if (!frames) {
   324                 SDL_OutOfMemory();
   325                 goto done;
   326             }
   327             ++anim->count;
   328             anim->frames = frames;
   329             frame = &anim->frames[anim->count - 1];
   330 
   331             frame->image = image;
   332             frame->x = LM_to_uint(buf[0], buf[1]);
   333             frame->y = LM_to_uint(buf[2], buf[3]);
   334             frame->disposal = state.Gif89.disposal;
   335             frame->delay = state.Gif89.delayTime*10;
   336 
   337             if (!load_anim) {
   338                 /* We only need one frame, we're done */
   339                 goto done;
   340             }
   341         }
   342     }
   343 
   344 done:
   345     if (anim->count > 1) {
   346         /* Normalize the frames */
   347         if (!NormalizeFrames(anim->frames, anim->count)) {
   348             int i;
   349             for (i = 0; i < anim->count; ++i) {
   350                 SDL_FreeSurface(anim->frames[i].image);
   351             }
   352             anim->count = 0;
   353         }
   354     }
   355     if (anim->count == 0) {
   356         SDL_free(anim->frames);
   357         SDL_free(anim);
   358         return NULL;
   359     }
   360     return anim;
   361 }
   362 
   363 static int
   364 ReadColorMap(SDL_RWops *src, int number,
   365              unsigned char buffer[3][MAXCOLORMAPSIZE], int *gray)
   366 {
   367     int i;
   368     unsigned char rgb[3];
   369     int flag;
   370 
   371     flag = TRUE;
   372 
   373     for (i = 0; i < number; ++i) {
   374         if (!ReadOK(src, rgb, sizeof(rgb))) {
   375             RWSetMsg("bad colormap");
   376             return 1;
   377         }
   378         buffer[CM_RED][i] = rgb[0];
   379         buffer[CM_GREEN][i] = rgb[1];
   380         buffer[CM_BLUE][i] = rgb[2];
   381         flag &= (rgb[0] == rgb[1] && rgb[1] == rgb[2]);
   382     }
   383 
   384 #if 0
   385     if (flag)
   386         *gray = (number == 2) ? PBM_TYPE : PGM_TYPE;
   387     else
   388         *gray = PPM_TYPE;
   389 #else
   390     *gray = 0;
   391 #endif
   392 
   393     return FALSE;
   394 }
   395 
   396 static int
   397 DoExtension(SDL_RWops *src, int label, State_t * state)
   398 {
   399     unsigned char buf[256];
   400     char *str;
   401 
   402     switch (label) {
   403     case 0x01:          /* Plain Text Extension */
   404         str = "Plain Text Extension";
   405         break;
   406     case 0xff:          /* Application Extension */
   407         str = "Application Extension";
   408         break;
   409     case 0xfe:          /* Comment Extension */
   410         str = "Comment Extension";
   411         while (GetDataBlock(src, (unsigned char *) buf, state) > 0)
   412             ;
   413         return FALSE;
   414     case 0xf9:          /* Graphic Control Extension */
   415         str = "Graphic Control Extension";
   416         (void) GetDataBlock(src, (unsigned char *) buf, state);
   417         state->Gif89.disposal = (buf[0] >> 2) & 0x7;
   418         state->Gif89.inputFlag = (buf[0] >> 1) & 0x1;
   419         state->Gif89.delayTime = LM_to_uint(buf[1], buf[2]);
   420         if ((buf[0] & 0x1) != 0)
   421             state->Gif89.transparent = buf[3];
   422 
   423         while (GetDataBlock(src, (unsigned char *) buf, state) > 0)
   424             ;
   425         return FALSE;
   426     default:
   427         str = (char *)buf;
   428         SDL_snprintf(str, 256, "UNKNOWN (0x%02x)", label);
   429         break;
   430     }
   431 
   432     while (GetDataBlock(src, (unsigned char *) buf, state) > 0)
   433         ;
   434 
   435     return FALSE;
   436 }
   437 
   438 static int
   439 GetDataBlock(SDL_RWops *src, unsigned char *buf, State_t * state)
   440 {
   441     unsigned char count;
   442 
   443     if (!ReadOK(src, &count, 1)) {
   444         /* pm_message("error in getting DataBlock size" ); */
   445         return -1;
   446     }
   447     state->ZeroDataBlock = count == 0;
   448 
   449     if ((count != 0) && (!ReadOK(src, buf, count))) {
   450         /* pm_message("error in reading DataBlock" ); */
   451         return -1;
   452     }
   453     return count;
   454 }
   455 
   456 static int
   457 GetCode(SDL_RWops *src, int code_size, int flag, State_t * state)
   458 {
   459     int i, j, ret;
   460     unsigned char count;
   461 
   462     if (flag) {
   463         state->curbit = 0;
   464         state->lastbit = 0;
   465         state->done = FALSE;
   466         return 0;
   467     }
   468     if ((state->curbit + code_size) >= state->lastbit) {
   469         if (state->done) {
   470             if (state->curbit >= state->lastbit)
   471                 RWSetMsg("ran off the end of my bits");
   472             return -1;
   473         }
   474         state->buf[0] = state->buf[state->last_byte - 2];
   475         state->buf[1] = state->buf[state->last_byte - 1];
   476 
   477         if ((ret = GetDataBlock(src, &state->buf[2], state)) > 0)
   478             count = (unsigned char) ret;
   479         else {
   480             count = 0;
   481             state->done = TRUE;
   482         }
   483 
   484         state->last_byte = 2 + count;
   485         state->curbit = (state->curbit - state->lastbit) + 16;
   486         state->lastbit = (2 + count) * 8;
   487     }
   488     ret = 0;
   489     for (i = state->curbit, j = 0; j < code_size; ++i, ++j)
   490         ret |= ((state->buf[i / 8] & (1 << (i % 8))) != 0) << j;
   491 
   492     state->curbit += code_size;
   493 
   494     return ret;
   495 }
   496 
   497 static int
   498 LWZReadByte(SDL_RWops *src, int flag, int input_code_size, State_t * state)
   499 {
   500     int code, incode;
   501     register int i;
   502 
   503     /* Fixed buffer overflow found by Michael Skladnikiewicz */
   504     if (input_code_size > MAX_LWZ_BITS)
   505         return -1;
   506 
   507     if (flag) {
   508         state->set_code_size = input_code_size;
   509         state->code_size = state->set_code_size + 1;
   510         state->clear_code = 1 << state->set_code_size;
   511         state->end_code = state->clear_code + 1;
   512         state->max_code_size = 2 * state->clear_code;
   513         state->max_code = state->clear_code + 2;
   514 
   515         GetCode(src, 0, TRUE, state);
   516 
   517         state->fresh = TRUE;
   518 
   519         for (i = 0; i < state->clear_code; ++i) {
   520             state->table[0][i] = 0;
   521             state->table[1][i] = i;
   522         }
   523         state->table[1][0] = 0;
   524         for (; i < (1 << MAX_LWZ_BITS); ++i)
   525             state->table[0][i] = 0;
   526 
   527         state->sp = state->stack;
   528 
   529         return 0;
   530     } else if (state->fresh) {
   531         state->fresh = FALSE;
   532         do {
   533             state->firstcode = state->oldcode = GetCode(src, state->code_size, FALSE, state);
   534         } while (state->firstcode == state->clear_code);
   535         return state->firstcode;
   536     }
   537     if (state->sp > state->stack)
   538         return *--state->sp;
   539 
   540     while ((code = GetCode(src, state->code_size, FALSE, state)) >= 0) {
   541         if (code == state->clear_code) {
   542             for (i = 0; i < state->clear_code; ++i) {
   543                 state->table[0][i] = 0;
   544                 state->table[1][i] = i;
   545             }
   546             for (; i < (1 << MAX_LWZ_BITS); ++i)
   547                 state->table[0][i] = state->table[1][i] = 0;
   548             state->code_size = state->set_code_size + 1;
   549             state->max_code_size = 2 * state->clear_code;
   550             state->max_code = state->clear_code + 2;
   551             state->sp = state->stack;
   552             state->firstcode = state->oldcode = GetCode(src, state->code_size, FALSE, state);
   553             return state->firstcode;
   554         } else if (code == state->end_code) {
   555             int count;
   556             unsigned char buf[260];
   557 
   558             if (state->ZeroDataBlock)
   559                 return -2;
   560 
   561             while ((count = GetDataBlock(src, buf, state)) > 0)
   562                 ;
   563 
   564             if (count != 0) {
   565             /*
   566              * pm_message("missing EOD in data stream (common occurence)");
   567              */
   568             }
   569             return -2;
   570         }
   571         incode = code;
   572 
   573         if (code >= state->max_code) {
   574             *state->sp++ = state->firstcode;
   575             code = state->oldcode;
   576         }
   577         while (code >= state->clear_code) {
   578             /* Guard against buffer overruns */
   579             if (code < 0 || code >= (1 << MAX_LWZ_BITS)) {
   580                 RWSetMsg("invalid LWZ data");
   581                 return -3;
   582             }
   583             *state->sp++ = state->table[1][code];
   584             if (code == state->table[0][code]) {
   585                 RWSetMsg("circular table entry BIG ERROR");
   586                 return -3;
   587             }
   588             code = state->table[0][code];
   589         }
   590 
   591         /* Guard against buffer overruns */
   592         if (code < 0 || code >= (1 << MAX_LWZ_BITS)) {
   593             RWSetMsg("invalid LWZ data");
   594             return -4;
   595         }
   596         *state->sp++ = state->firstcode = state->table[1][code];
   597 
   598         if ((code = state->max_code) < (1 << MAX_LWZ_BITS)) {
   599             state->table[0][code] = state->oldcode;
   600             state->table[1][code] = state->firstcode;
   601             ++state->max_code;
   602             if ((state->max_code >= state->max_code_size) &&
   603                 (state->max_code_size < (1 << MAX_LWZ_BITS))) {
   604                 state->max_code_size *= 2;
   605                 ++state->code_size;
   606             }
   607         }
   608         state->oldcode = incode;
   609 
   610         if (state->sp > state->stack)
   611             return *--state->sp;
   612     }
   613     return code;
   614 }
   615 
   616 static Image *
   617 ReadImage(SDL_RWops * src, int len, int height, int cmapSize,
   618           unsigned char cmap[3][MAXCOLORMAPSIZE],
   619           int gray, int interlace, int ignore, State_t * state)
   620 {
   621     Image *image;
   622     unsigned char c;
   623     int i, v;
   624     int xpos = 0, ypos = 0, pass = 0;
   625 
   626     (void) gray; /* unused */
   627 
   628     /*
   629     **  Initialize the compression routines
   630      */
   631     if (!ReadOK(src, &c, 1)) {
   632         RWSetMsg("EOF / read error on image data");
   633         return NULL;
   634     }
   635     if (LWZReadByte(src, TRUE, c, state) < 0) {
   636         RWSetMsg("error reading image");
   637         return NULL;
   638     }
   639     /*
   640     **  If this is an "uninteresting picture" ignore it.
   641      */
   642     if (ignore) {
   643         while (LWZReadByte(src, FALSE, c, state) >= 0)
   644             ;
   645         return NULL;
   646     }
   647     image = ImageNewCmap(len, height, cmapSize);
   648 
   649     for (i = 0; i < cmapSize; i++)
   650         ImageSetCmap(image, i, cmap[CM_RED][i],
   651                      cmap[CM_GREEN][i], cmap[CM_BLUE][i]);
   652 
   653     while ((v = LWZReadByte(src, FALSE, c, state)) >= 0) {
   654         ((Uint8 *)image->pixels)[xpos + ypos * image->pitch] = (Uint8)v;
   655         ++xpos;
   656         if (xpos == len) {
   657             xpos = 0;
   658             if (interlace) {
   659                 switch (pass) {
   660                 case 0:
   661                 case 1:
   662                     ypos += 8;
   663                     break;
   664                 case 2:
   665                     ypos += 4;
   666                     break;
   667                 case 3:
   668                     ypos += 2;
   669                     break;
   670                 }
   671 
   672                 if (ypos >= height) {
   673                     ++pass;
   674                     switch (pass) {
   675                     case 1:
   676                         ypos = 4;
   677                         break;
   678                     case 2:
   679                         ypos = 2;
   680                         break;
   681                     case 3:
   682                         ypos = 1;
   683                         break;
   684                     default:
   685                         goto fini;
   686                     }
   687                 }
   688             } else {
   689                 ++ypos;
   690             }
   691         }
   692         if (ypos >= height)
   693             break;
   694     }
   695 
   696   fini:
   697 
   698     return image;
   699 }
   700 
   701 /* Load a GIF type animation from an SDL datasource */
   702 IMG_Animation *IMG_LoadGIFAnimation_RW(SDL_RWops *src)
   703 {
   704     Anim_t *internal = IMG_LoadGIF_RW_Internal(src, SDL_TRUE);
   705     if (internal) {
   706         IMG_Animation *anim = (IMG_Animation *)SDL_malloc(sizeof(*anim));
   707         if (anim) {
   708             anim->w = internal->frames[0].image->w;
   709             anim->h = internal->frames[0].image->h;
   710             anim->count = internal->count;
   711 
   712             anim->frames = (SDL_Surface **)SDL_calloc(anim->count, sizeof(*anim->frames));
   713             anim->delays = (int *)SDL_calloc(anim->count, sizeof(*anim->delays));
   714 
   715             if (anim->frames && anim->delays) {
   716                 int i;
   717 
   718                 for (i = 0; i < anim->count; ++i) {
   719                     anim->frames[i] = internal->frames[i].image;
   720                     anim->delays[i] = internal->frames[i].delay;
   721                 }
   722             } else {
   723                 IMG_FreeAnimation(anim);
   724                 anim = NULL;
   725             }
   726         }
   727         if (!anim) {
   728             SDL_OutOfMemory();
   729         }
   730         SDL_free(internal->frames);
   731         SDL_free(internal);
   732         return anim;
   733     }
   734     return NULL;
   735 }
   736 
   737 #else
   738 
   739 /* Load a GIF type animation from an SDL datasource */
   740 IMG_Animation *IMG_LoadGIFAnimation_RW(SDL_RWops *src)
   741 {
   742     return NULL;
   743 }
   744 
   745 #endif /* LOAD_GIF */
   746 
   747 #if !defined(__APPLE__) || defined(SDL_IMAGE_USE_COMMON_BACKEND)
   748 
   749 #ifdef LOAD_GIF
   750 
   751 /* See if an image is contained in a data source */
   752 int IMG_isGIF(SDL_RWops *src)
   753 {
   754     Sint64 start;
   755     int is_GIF;
   756     char magic[6];
   757 
   758     if ( !src )
   759         return 0;
   760     start = SDL_RWtell(src);
   761     is_GIF = 0;
   762     if ( SDL_RWread(src, magic, sizeof(magic), 1) ) {
   763         if ( (SDL_strncmp(magic, "GIF", 3) == 0) &&
   764              ((SDL_memcmp(magic + 3, "87a", 3) == 0) ||
   765               (SDL_memcmp(magic + 3, "89a", 3) == 0)) ) {
   766             is_GIF = 1;
   767         }
   768     }
   769     SDL_RWseek(src, start, RW_SEEK_SET);
   770     return(is_GIF);
   771 }
   772 
   773 /* Load a GIF type image from an SDL datasource */
   774 SDL_Surface *IMG_LoadGIF_RW(SDL_RWops *src)
   775 {
   776     SDL_Surface *image = NULL;
   777     Anim_t *internal = IMG_LoadGIF_RW_Internal(src, SDL_FALSE);
   778     if (internal) {
   779         image = internal->frames[0].image;
   780         SDL_free(internal->frames);
   781         SDL_free(internal);
   782     }
   783     return image;
   784 }
   785 
   786 #else
   787 #if _MSC_VER >= 1300
   788 #pragma warning(disable : 4100) /* warning C4100: 'op' : unreferenced formal parameter */
   789 #endif
   790 
   791 /* See if an image is contained in a data source */
   792 int IMG_isGIF(SDL_RWops *src)
   793 {
   794     return(0);
   795 }
   796 
   797 /* Load a GIF type image from an SDL datasource */
   798 SDL_Surface *IMG_LoadGIF_RW(SDL_RWops *src)
   799 {
   800     return(NULL);
   801 }
   802 
   803 #endif /* LOAD_GIF */
   804 
   805 #endif /* !defined(__APPLE__) || defined(SDL_IMAGE_USE_COMMON_BACKEND) */