IMG_gif.c
author Ozkan Sezer <sezeroz@gmail.com>
Tue, 16 Oct 2018 20:02:02 +0300
branchSDL-1.2
changeset 607 1a1189c2978f
parent 288 cbf4a9d168ff
permissions -rw-r--r--
backports of multiple bug fixes from 2.0:

3999783e (r340): bug 1413 - Fix image corruption when using ImageIO framework
5bf0f0d6 (r343), 326a6025 (r361): fixes from VS code analysis and code review
2742fe58 (r355), 1e7a55d7 (r356). dd40be56 (r358): support for webp on big endian systems
ce8091ca (r365): bug 1801 - typo in the xcf decoder, condition is always false
35beff02 (r369): bug 1831 - Memory leak issue in SDL_image-1.2.12/IMG_xpm.c file
1700d607 (r415): bug 1991 - XCF and LBM image loading [only the memory leak parts.]
e108e122 (r419): bug 2010 - Memory leaks in do_layer_surface function in IMG_xcf.c
7a360f7d (r436): bug 2295 - Memory leak in IMG_LoadWEBP_RW
ee17b8eb (r443): bug 2454 - Crash when loading some XPM files
bca82f1c (r476): proper fix for Bugzilla #2965.
fd721465 (r490): crash if some initialization succeeded and some didn't
915de300 (r492): bug 3474 - IMG_tif leaks memory on errors
b6f8fbe5 (r493): bug 3475 - Remove unnecessary loop from IMG_tif.c
d3e819a0 (r499): bug 2318 - h->cm_map resource getting leak in read_xcf_header function
1e32e1f4 (r503): bug 3008 - Compiler warnings: "warning: initialization discards 'const'
318484db (r513): security vulnerability in XCF image loader
181ef57f (r530): failing to reset the file pointer when detecting file types with ImageIO
16772bbb (r555): lbm: use correct variable to check color planes.
97f7f01e (r556): lbm: Fail to load images with unsupported/bogus color depth.
bfa08dc0 (r557): lbm: Don't overflow static colormap buffer.
a1e9b624 (r558): ico: reject obviously incorrect image sizes.
37445f61 (r559): bmp: don't overflow palette buffer with bogus biClrUsed values.
7df1580f (r560): xcf: deal with bogus data in rle tile decoding.
45e750f9 (r563): gif: report error on bogus LWZ data, instead of overflowing a buffer.
2938fc80 (r567): pcx: don't overflow buffer if bytes-per-line is less than image width.
c5f9cbb5 (r568): xcf: Prevent infinite loop and/or buffer overflow on bogus data.
fb643e37 (r569): xcf: check for some potential integer overflows.
170d7d32 (r585): potential buffer overflow on corrupt or maliciously-crafted XCF file.
19beb4a1 (r586): Don't get into infinite loops on truncated GIF files.
32a18ca0 (r587): Don't get into infinite loops on truncated PNM files.
8b4ee1d7 (r590): memory leak in IMG_xcf.c
90a531f2 (r591): PNM: Improve checks when loading a file
31263a04 (r592): XCF: check if there's sufficient data in the stream before allocating
cec9b759 (r593): More error checking, and null terminate strings...
     1 /*
     2   SDL_image:  An example image loading library for use with SDL
     3   Copyright (C) 1997-2012 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 	int 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 ( (strncmp(magic, "GIF", 3) == 0) &&
    46 		     ((memcmp(magic + 3, "87a", 3) == 0) ||
    47 		      (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_AllocSurface(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     int 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 (strncmp((char *) buf, "GIF", 3) != 0) {
   177 	RWSetMsg("not a GIF file");
   178         goto done;
   179     }
   180     memcpy(version, (char *) buf + 3, 3);
   181     version[3] = '\0';
   182 
   183     if ((strcmp(version, "87a") != 0) && (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_SRCCOLORKEY, 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 	sprintf(str, "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 		return -3;
   506 	    }
   507 	    code = table[0][code];
   508 	}
   509 
   510 	/* Guard against buffer overruns */
   511 	if (code < 0 || code >= (1 << MAX_LWZ_BITS)) {
   512 	    RWSetMsg("invalid LWZ data");
   513 	    return -4;
   514 	}
   515 	*sp++ = firstcode = table[1][code];
   516 
   517 	if ((code = max_code) < (1 << MAX_LWZ_BITS)) {
   518 	    table[0][code] = oldcode;
   519 	    table[1][code] = firstcode;
   520 	    ++max_code;
   521 	    if ((max_code >= max_code_size) &&
   522 		(max_code_size < (1 << MAX_LWZ_BITS))) {
   523 		max_code_size *= 2;
   524 		++code_size;
   525 	    }
   526 	}
   527 	oldcode = incode;
   528 
   529 	if (sp > stack)
   530 	    return *--sp;
   531     }
   532     return code;
   533 }
   534 
   535 static Image *
   536 ReadImage(SDL_RWops * src, int len, int height, int cmapSize,
   537 	  unsigned char cmap[3][MAXCOLORMAPSIZE],
   538 	  int gray, int interlace, int ignore)
   539 {
   540     Image *image;
   541     unsigned char c;
   542     int i, v;
   543     int xpos = 0, ypos = 0, pass = 0;
   544 
   545     /*
   546     **	Initialize the compression routines
   547      */
   548     if (!ReadOK(src, &c, 1)) {
   549 	RWSetMsg("EOF / read error on image data");
   550 	return NULL;
   551     }
   552     if (LWZReadByte(src, TRUE, c) < 0) {
   553 	RWSetMsg("error reading image");
   554 	return NULL;
   555     }
   556     /*
   557     **	If this is an "uninteresting picture" ignore it.
   558      */
   559     if (ignore) {
   560 	while (LWZReadByte(src, FALSE, c) >= 0)
   561 	    ;
   562 	return NULL;
   563     }
   564     image = ImageNewCmap(len, height, cmapSize);
   565 
   566     for (i = 0; i < cmapSize; i++)
   567 	ImageSetCmap(image, i, cmap[CM_RED][i],
   568 		     cmap[CM_GREEN][i], cmap[CM_BLUE][i]);
   569 
   570     while ((v = LWZReadByte(src, FALSE, c)) >= 0) {
   571 #ifdef USED_BY_SDL
   572 	((Uint8 *)image->pixels)[xpos + ypos * image->pitch] = v;
   573 #else
   574 	image->data[xpos + ypos * len] = v;
   575 #endif
   576 	++xpos;
   577 	if (xpos == len) {
   578 	    xpos = 0;
   579 	    if (interlace) {
   580 		switch (pass) {
   581 		case 0:
   582 		case 1:
   583 		    ypos += 8;
   584 		    break;
   585 		case 2:
   586 		    ypos += 4;
   587 		    break;
   588 		case 3:
   589 		    ypos += 2;
   590 		    break;
   591 		}
   592 
   593 		if (ypos >= height) {
   594 		    ++pass;
   595 		    switch (pass) {
   596 		    case 1:
   597 			ypos = 4;
   598 			break;
   599 		    case 2:
   600 			ypos = 2;
   601 			break;
   602 		    case 3:
   603 			ypos = 1;
   604 			break;
   605 		    default:
   606 			goto fini;
   607 		    }
   608 		}
   609 	    } else {
   610 		++ypos;
   611 	    }
   612 	}
   613 	if (ypos >= height)
   614 	    break;
   615     }
   616 
   617   fini:
   618 
   619     return image;
   620 }
   621 
   622 #else
   623 
   624 /* See if an image is contained in a data source */
   625 int IMG_isGIF(SDL_RWops *src)
   626 {
   627 	return(0);
   628 }
   629 
   630 /* Load a GIF type image from an SDL datasource */
   631 SDL_Surface *IMG_LoadGIF_RW(SDL_RWops *src)
   632 {
   633 	return(NULL);
   634 }
   635 
   636 #endif /* LOAD_GIF */
   637 
   638 #endif /* !defined(__APPLE__) || defined(SDL_IMAGE_USE_COMMON_BACKEND) */