IMG_gif.c
author Sam Lantinga <slouken@libsdl.org>
Sat, 31 Dec 2011 09:41:39 -0500
changeset 280 ec4ae96c100c
parent 237 d1a4fae44bd0
child 288 cbf4a9d168ff
permissions -rw-r--r--
Happy New Year!
     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     strncpy(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 	for (; i < (1 << MAX_LWZ_BITS); ++i)
   443 	    table[0][i] = table[1][0] = 0;
   444 
   445 	sp = stack;
   446 
   447 	return 0;
   448     } else if (fresh) {
   449 	fresh = FALSE;
   450 	do {
   451 	    firstcode = oldcode = GetCode(src, code_size, FALSE);
   452 	} while (firstcode == clear_code);
   453 	return firstcode;
   454     }
   455     if (sp > stack)
   456 	return *--sp;
   457 
   458     while ((code = GetCode(src, code_size, FALSE)) >= 0) {
   459 	if (code == clear_code) {
   460 	    for (i = 0; i < clear_code; ++i) {
   461 		table[0][i] = 0;
   462 		table[1][i] = i;
   463 	    }
   464 	    for (; i < (1 << MAX_LWZ_BITS); ++i)
   465 		table[0][i] = table[1][i] = 0;
   466 	    code_size = set_code_size + 1;
   467 	    max_code_size = 2 * clear_code;
   468 	    max_code = clear_code + 2;
   469 	    sp = stack;
   470 	    firstcode = oldcode = GetCode(src, code_size, FALSE);
   471 	    return firstcode;
   472 	} else if (code == end_code) {
   473 	    int count;
   474 	    unsigned char buf[260];
   475 
   476 	    if (ZeroDataBlock)
   477 		return -2;
   478 
   479 	    while ((count = GetDataBlock(src, buf)) > 0)
   480 		;
   481 
   482 	    if (count != 0) {
   483 		/*
   484 		 * pm_message("missing EOD in data stream (common occurence)");
   485 		 */
   486 	    }
   487 	    return -2;
   488 	}
   489 	incode = code;
   490 
   491 	if (code >= max_code) {
   492 	    *sp++ = firstcode;
   493 	    code = oldcode;
   494 	}
   495 	while (code >= clear_code) {
   496 	    *sp++ = table[1][code];
   497 	    if (code == table[0][code])
   498 		RWSetMsg("circular table entry BIG ERROR");
   499 	    code = table[0][code];
   500 	}
   501 
   502 	*sp++ = firstcode = table[1][code];
   503 
   504 	if ((code = max_code) < (1 << MAX_LWZ_BITS)) {
   505 	    table[0][code] = oldcode;
   506 	    table[1][code] = firstcode;
   507 	    ++max_code;
   508 	    if ((max_code >= max_code_size) &&
   509 		(max_code_size < (1 << MAX_LWZ_BITS))) {
   510 		max_code_size *= 2;
   511 		++code_size;
   512 	    }
   513 	}
   514 	oldcode = incode;
   515 
   516 	if (sp > stack)
   517 	    return *--sp;
   518     }
   519     return code;
   520 }
   521 
   522 static Image *
   523 ReadImage(SDL_RWops * src, int len, int height, int cmapSize,
   524 	  unsigned char cmap[3][MAXCOLORMAPSIZE],
   525 	  int gray, int interlace, int ignore)
   526 {
   527     Image *image;
   528     unsigned char c;
   529     int i, v;
   530     int xpos = 0, ypos = 0, pass = 0;
   531 
   532     /*
   533     **	Initialize the compression routines
   534      */
   535     if (!ReadOK(src, &c, 1)) {
   536 	RWSetMsg("EOF / read error on image data");
   537 	return NULL;
   538     }
   539     if (LWZReadByte(src, TRUE, c) < 0) {
   540 	RWSetMsg("error reading image");
   541 	return NULL;
   542     }
   543     /*
   544     **	If this is an "uninteresting picture" ignore it.
   545      */
   546     if (ignore) {
   547 	while (LWZReadByte(src, FALSE, c) >= 0)
   548 	    ;
   549 	return NULL;
   550     }
   551     image = ImageNewCmap(len, height, cmapSize);
   552 
   553     for (i = 0; i < cmapSize; i++)
   554 	ImageSetCmap(image, i, cmap[CM_RED][i],
   555 		     cmap[CM_GREEN][i], cmap[CM_BLUE][i]);
   556 
   557     while ((v = LWZReadByte(src, FALSE, c)) >= 0) {
   558 #ifdef USED_BY_SDL
   559 	((Uint8 *)image->pixels)[xpos + ypos * image->pitch] = v;
   560 #else
   561 	image->data[xpos + ypos * len] = v;
   562 #endif
   563 	++xpos;
   564 	if (xpos == len) {
   565 	    xpos = 0;
   566 	    if (interlace) {
   567 		switch (pass) {
   568 		case 0:
   569 		case 1:
   570 		    ypos += 8;
   571 		    break;
   572 		case 2:
   573 		    ypos += 4;
   574 		    break;
   575 		case 3:
   576 		    ypos += 2;
   577 		    break;
   578 		}
   579 
   580 		if (ypos >= height) {
   581 		    ++pass;
   582 		    switch (pass) {
   583 		    case 1:
   584 			ypos = 4;
   585 			break;
   586 		    case 2:
   587 			ypos = 2;
   588 			break;
   589 		    case 3:
   590 			ypos = 1;
   591 			break;
   592 		    default:
   593 			goto fini;
   594 		    }
   595 		}
   596 	    } else {
   597 		++ypos;
   598 	    }
   599 	}
   600 	if (ypos >= height)
   601 	    break;
   602     }
   603 
   604   fini:
   605 
   606     return image;
   607 }
   608 
   609 #else
   610 
   611 /* See if an image is contained in a data source */
   612 int IMG_isGIF(SDL_RWops *src)
   613 {
   614 	return(0);
   615 }
   616 
   617 /* Load a GIF type image from an SDL datasource */
   618 SDL_Surface *IMG_LoadGIF_RW(SDL_RWops *src)
   619 {
   620 	return(NULL);
   621 }
   622 
   623 #endif /* LOAD_GIF */
   624 
   625 #endif /* !defined(__APPLE__) || defined(SDL_IMAGE_USE_COMMON_BACKEND) */