IMG_gif.c
author Sam Lantinga <slouken@libsdl.org>
Mon, 08 Dec 2008 00:27:32 +0000
changeset 186 5fbbeabf647f
parent 181 9ed028587fd1
child 236 cce1251de477
permissions -rw-r--r--
Updated copyright date
     1 /*
     2     SDL_image:  An example image loading library for use with SDL
     3     Copyright (C) 1997-2009 Sam Lantinga
     4 
     5     This library is free software; you can redistribute it and/or
     6     modify it under the terms of the GNU Lesser General Public
     7     License as published by the Free Software Foundation; either
     8     version 2.1 of the License, or (at your option) any later version.
     9 
    10     This library is distributed in the hope that it will be useful,
    11     but WITHOUT ANY WARRANTY; without even the implied warranty of
    12     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
    13     Lesser General Public License for more details.
    14 
    15     You should have received a copy of the GNU Lesser General Public
    16     License along with this library; if not, write to the Free Software
    17     Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
    18 
    19     Sam Lantinga
    20     slouken@libsdl.org
    21 */
    22 
    23 /* This is a GIF image file loading framework */
    24 
    25 #include <stdio.h>
    26 #include <string.h>
    27 
    28 #include "SDL_image.h"
    29 
    30 #ifdef LOAD_GIF
    31 
    32 /* See if an image is contained in a data source */
    33 int IMG_isGIF(SDL_RWops *src)
    34 {
    35 	int start;
    36 	int is_GIF;
    37 	char magic[6];
    38 
    39 	if ( !src )
    40 		return 0;
    41 	start = SDL_RWtell(src);
    42 	is_GIF = 0;
    43 	if ( SDL_RWread(src, magic, sizeof(magic), 1) ) {
    44 		if ( (strncmp(magic, "GIF", 3) == 0) &&
    45 		     ((memcmp(magic + 3, "87a", 3) == 0) ||
    46 		      (memcmp(magic + 3, "89a", 3) == 0)) ) {
    47 			is_GIF = 1;
    48 		}
    49 	}
    50 	SDL_RWseek(src, start, SEEK_SET);
    51 	return(is_GIF);
    52 }
    53 
    54 /* Code from here to end of file has been adapted from XPaint:           */
    55 /* +-------------------------------------------------------------------+ */
    56 /* | Copyright 1990, 1991, 1993 David Koblas.			       | */
    57 /* | Copyright 1996 Torsten Martinsen.				       | */
    58 /* |   Permission to use, copy, modify, and distribute this software   | */
    59 /* |   and its documentation for any purpose and without fee is hereby | */
    60 /* |   granted, provided that the above copyright notice appear in all | */
    61 /* |   copies and that both that copyright notice and this permission  | */
    62 /* |   notice appear in supporting documentation.  This software is    | */
    63 /* |   provided "as is" without express or implied warranty.	       | */
    64 /* +-------------------------------------------------------------------+ */
    65 
    66 /* Adapted for use in SDL by Sam Lantinga -- 7/20/98 */
    67 #define USED_BY_SDL
    68 
    69 #include <stdio.h>
    70 #include <string.h>
    71 
    72 #ifdef USED_BY_SDL
    73 /* Changes to work with SDL:
    74 
    75    Include SDL header file
    76    Use SDL_Surface rather than xpaint Image structure
    77    Define SDL versions of RWSetMsg(), ImageNewCmap() and ImageSetCmap()
    78 */
    79 #include "SDL.h"
    80 
    81 #define Image			SDL_Surface
    82 #define RWSetMsg		IMG_SetError
    83 #define ImageNewCmap(w, h, s)	SDL_AllocSurface(SDL_SWSURFACE,w,h,8,0,0,0,0)
    84 #define ImageSetCmap(s, i, R, G, B) do { \
    85 				s->format->palette->colors[i].r = R; \
    86 				s->format->palette->colors[i].g = G; \
    87 				s->format->palette->colors[i].b = B; \
    88 			} while (0)
    89 /* * * * * */
    90 
    91 #else
    92 
    93 /* Original XPaint sources */
    94 
    95 #include "image.h"
    96 #include "rwTable.h"
    97 
    98 #define SDL_RWops	FILE
    99 #define SDL_RWclose	fclose
   100 
   101 #endif /* USED_BY_SDL */
   102 
   103 
   104 #define	MAXCOLORMAPSIZE		256
   105 
   106 #define	TRUE	1
   107 #define	FALSE	0
   108 
   109 #define CM_RED		0
   110 #define CM_GREEN	1
   111 #define CM_BLUE		2
   112 
   113 #define	MAX_LWZ_BITS		12
   114 
   115 #define INTERLACE		0x40
   116 #define LOCALCOLORMAP	0x80
   117 #define BitSet(byte, bit)	(((byte) & (bit)) == (bit))
   118 
   119 #define	ReadOK(file,buffer,len)	SDL_RWread(file, buffer, len, 1)
   120 
   121 #define LM_to_uint(a,b)			(((b)<<8)|(a))
   122 
   123 static struct {
   124     unsigned int Width;
   125     unsigned int Height;
   126     unsigned char ColorMap[3][MAXCOLORMAPSIZE];
   127     unsigned int BitPixel;
   128     unsigned int ColorResolution;
   129     unsigned int Background;
   130     unsigned int AspectRatio;
   131     int GrayScale;
   132 } GifScreen;
   133 
   134 static struct {
   135     int transparent;
   136     int delayTime;
   137     int inputFlag;
   138     int disposal;
   139 } Gif89;
   140 
   141 static int ReadColorMap(SDL_RWops * src, int number,
   142 			unsigned char buffer[3][MAXCOLORMAPSIZE], int *flag);
   143 static int DoExtension(SDL_RWops * src, int label);
   144 static int GetDataBlock(SDL_RWops * src, unsigned char *buf);
   145 static int GetCode(SDL_RWops * src, int code_size, int flag);
   146 static int LWZReadByte(SDL_RWops * src, int flag, int input_code_size);
   147 static Image *ReadImage(SDL_RWops * src, int len, int height, int,
   148 			unsigned char cmap[3][MAXCOLORMAPSIZE],
   149 			int gray, int interlace, int ignore);
   150 
   151 Image *
   152 IMG_LoadGIF_RW(SDL_RWops *src)
   153 {
   154     int start;
   155     unsigned char buf[16];
   156     unsigned char c;
   157     unsigned char localColorMap[3][MAXCOLORMAPSIZE];
   158     int grayScale;
   159     int useGlobalColormap;
   160     int bitPixel;
   161     int imageCount = 0;
   162     char version[4];
   163     int imageNumber = 1;
   164     Image *image = NULL;
   165 
   166     if ( src == NULL ) {
   167 	return NULL;
   168     }
   169     start = SDL_RWtell(src);
   170 
   171     if (!ReadOK(src, buf, 6)) {
   172 	RWSetMsg("error reading magic number");
   173         goto done;
   174     }
   175     if (strncmp((char *) buf, "GIF", 3) != 0) {
   176 	RWSetMsg("not a GIF file");
   177         goto done;
   178     }
   179     strncpy(version, (char *) buf + 3, 3);
   180     version[3] = '\0';
   181 
   182     if ((strcmp(version, "87a") != 0) && (strcmp(version, "89a") != 0)) {
   183 	RWSetMsg("bad version number, not '87a' or '89a'");
   184         goto done;
   185     }
   186     Gif89.transparent = -1;
   187     Gif89.delayTime = -1;
   188     Gif89.inputFlag = -1;
   189     Gif89.disposal = 0;
   190 
   191     if (!ReadOK(src, buf, 7)) {
   192 	RWSetMsg("failed to read screen descriptor");
   193         goto done;
   194     }
   195     GifScreen.Width = LM_to_uint(buf[0], buf[1]);
   196     GifScreen.Height = LM_to_uint(buf[2], buf[3]);
   197     GifScreen.BitPixel = 2 << (buf[4] & 0x07);
   198     GifScreen.ColorResolution = (((buf[4] & 0x70) >> 3) + 1);
   199     GifScreen.Background = buf[5];
   200     GifScreen.AspectRatio = buf[6];
   201 
   202     if (BitSet(buf[4], LOCALCOLORMAP)) {	/* Global Colormap */
   203 	if (ReadColorMap(src, GifScreen.BitPixel, GifScreen.ColorMap,
   204 			 &GifScreen.GrayScale)) {
   205 	    RWSetMsg("error reading global colormap");
   206             goto done;
   207 	}
   208     }
   209     do {
   210 	if (!ReadOK(src, &c, 1)) {
   211 	    RWSetMsg("EOF / read error on image data");
   212             goto done;
   213 	}
   214 	if (c == ';') {		/* GIF terminator */
   215 	    if (imageCount < imageNumber) {
   216 		RWSetMsg("only %d image%s found in file",
   217 			 imageCount, imageCount > 1 ? "s" : "");
   218                 goto done;
   219 	    }
   220 	}
   221 	if (c == '!') {		/* Extension */
   222 	    if (!ReadOK(src, &c, 1)) {
   223 		RWSetMsg("EOF / read error on extention function code");
   224                 goto done;
   225 	    }
   226 	    DoExtension(src, c);
   227 	    continue;
   228 	}
   229 	if (c != ',') {		/* Not a valid start character */
   230 	    continue;
   231 	}
   232 	++imageCount;
   233 
   234 	if (!ReadOK(src, buf, 9)) {
   235 	    RWSetMsg("couldn't read left/top/width/height");
   236             goto done;
   237 	}
   238 	useGlobalColormap = !BitSet(buf[8], LOCALCOLORMAP);
   239 
   240 	bitPixel = 1 << ((buf[8] & 0x07) + 1);
   241 
   242 	if (!useGlobalColormap) {
   243 	    if (ReadColorMap(src, bitPixel, localColorMap, &grayScale)) {
   244 		RWSetMsg("error reading local colormap");
   245                 goto done;
   246 	    }
   247 	    image = ReadImage(src, LM_to_uint(buf[4], buf[5]),
   248 			      LM_to_uint(buf[6], buf[7]),
   249 			      bitPixel, localColorMap, grayScale,
   250 			      BitSet(buf[8], INTERLACE),
   251 			      imageCount != imageNumber);
   252 	} else {
   253 	    image = ReadImage(src, LM_to_uint(buf[4], buf[5]),
   254 			      LM_to_uint(buf[6], buf[7]),
   255 			      GifScreen.BitPixel, GifScreen.ColorMap,
   256 			      GifScreen.GrayScale, BitSet(buf[8], INTERLACE),
   257 			      imageCount != imageNumber);
   258 	}
   259     } while (image == NULL);
   260 
   261 #ifdef USED_BY_SDL
   262     if ( Gif89.transparent >= 0 ) {
   263         SDL_SetColorKey(image, SDL_SRCCOLORKEY, Gif89.transparent);
   264     }
   265 #endif
   266 
   267 done:
   268     if ( image == NULL ) {
   269         SDL_RWseek(src, start, SEEK_SET);
   270     }
   271     return image;
   272 }
   273 
   274 static int
   275 ReadColorMap(SDL_RWops *src, int number,
   276              unsigned char buffer[3][MAXCOLORMAPSIZE], int *gray)
   277 {
   278     int i;
   279     unsigned char rgb[3];
   280     int flag;
   281 
   282     flag = TRUE;
   283 
   284     for (i = 0; i < number; ++i) {
   285 	if (!ReadOK(src, rgb, sizeof(rgb))) {
   286 	    RWSetMsg("bad colormap");
   287 	    return 1;
   288 	}
   289 	buffer[CM_RED][i] = rgb[0];
   290 	buffer[CM_GREEN][i] = rgb[1];
   291 	buffer[CM_BLUE][i] = rgb[2];
   292 	flag &= (rgb[0] == rgb[1] && rgb[1] == rgb[2]);
   293     }
   294 
   295 #if 0
   296     if (flag)
   297 	*gray = (number == 2) ? PBM_TYPE : PGM_TYPE;
   298     else
   299 	*gray = PPM_TYPE;
   300 #else
   301     *gray = 0;
   302 #endif
   303 
   304     return FALSE;
   305 }
   306 
   307 static int
   308 DoExtension(SDL_RWops *src, int label)
   309 {
   310     static unsigned char buf[256];
   311     char *str;
   312 
   313     switch (label) {
   314     case 0x01:			/* Plain Text Extension */
   315 	str = "Plain Text Extension";
   316 	break;
   317     case 0xff:			/* Application Extension */
   318 	str = "Application Extension";
   319 	break;
   320     case 0xfe:			/* Comment Extension */
   321 	str = "Comment Extension";
   322 	while (GetDataBlock(src, (unsigned char *) buf) != 0)
   323 	    ;
   324 	return FALSE;
   325     case 0xf9:			/* Graphic Control Extension */
   326 	str = "Graphic Control Extension";
   327 	(void) GetDataBlock(src, (unsigned char *) buf);
   328 	Gif89.disposal = (buf[0] >> 2) & 0x7;
   329 	Gif89.inputFlag = (buf[0] >> 1) & 0x1;
   330 	Gif89.delayTime = LM_to_uint(buf[1], buf[2]);
   331 	if ((buf[0] & 0x1) != 0)
   332 	    Gif89.transparent = buf[3];
   333 
   334 	while (GetDataBlock(src, (unsigned char *) buf) != 0)
   335 	    ;
   336 	return FALSE;
   337     default:
   338 	str = (char *)buf;
   339 	sprintf(str, "UNKNOWN (0x%02x)", label);
   340 	break;
   341     }
   342 
   343     while (GetDataBlock(src, (unsigned char *) buf) != 0)
   344 	;
   345 
   346     return FALSE;
   347 }
   348 
   349 static int ZeroDataBlock = FALSE;
   350 
   351 static int
   352 GetDataBlock(SDL_RWops *src, unsigned char *buf)
   353 {
   354     unsigned char count;
   355 
   356     if (!ReadOK(src, &count, 1)) {
   357 	/* pm_message("error in getting DataBlock size" ); */
   358 	return -1;
   359     }
   360     ZeroDataBlock = count == 0;
   361 
   362     if ((count != 0) && (!ReadOK(src, buf, count))) {
   363 	/* pm_message("error in reading DataBlock" ); */
   364 	return -1;
   365     }
   366     return count;
   367 }
   368 
   369 static int
   370 GetCode(SDL_RWops *src, int code_size, int flag)
   371 {
   372     static unsigned char buf[280];
   373     static int curbit, lastbit, done, last_byte;
   374     int i, j, ret;
   375     unsigned char count;
   376 
   377     if (flag) {
   378 	curbit = 0;
   379 	lastbit = 0;
   380 	done = FALSE;
   381 	return 0;
   382     }
   383     if ((curbit + code_size) >= lastbit) {
   384 	if (done) {
   385 	    if (curbit >= lastbit)
   386 		RWSetMsg("ran off the end of my bits");
   387 	    return -1;
   388 	}
   389 	buf[0] = buf[last_byte - 2];
   390 	buf[1] = buf[last_byte - 1];
   391 
   392 	if ((count = GetDataBlock(src, &buf[2])) == 0)
   393 	    done = TRUE;
   394 
   395 	last_byte = 2 + count;
   396 	curbit = (curbit - lastbit) + 16;
   397 	lastbit = (2 + count) * 8;
   398     }
   399     ret = 0;
   400     for (i = curbit, j = 0; j < code_size; ++i, ++j)
   401 	ret |= ((buf[i / 8] & (1 << (i % 8))) != 0) << j;
   402 
   403     curbit += code_size;
   404 
   405     return ret;
   406 }
   407 
   408 static int
   409 LWZReadByte(SDL_RWops *src, int flag, int input_code_size)
   410 {
   411     static int fresh = FALSE;
   412     int code, incode;
   413     static int code_size, set_code_size;
   414     static int max_code, max_code_size;
   415     static int firstcode, oldcode;
   416     static int clear_code, end_code;
   417     static int table[2][(1 << MAX_LWZ_BITS)];
   418     static int stack[(1 << (MAX_LWZ_BITS)) * 2], *sp;
   419     register int i;
   420 
   421     /* Fixed buffer overflow found by Michael Skladnikiewicz */
   422     if (input_code_size > MAX_LWZ_BITS)
   423         return -1;
   424 
   425     if (flag) {
   426 	set_code_size = input_code_size;
   427 	code_size = set_code_size + 1;
   428 	clear_code = 1 << set_code_size;
   429 	end_code = clear_code + 1;
   430 	max_code_size = 2 * clear_code;
   431 	max_code = clear_code + 2;
   432 
   433 	GetCode(src, 0, TRUE);
   434 
   435 	fresh = TRUE;
   436 
   437 	for (i = 0; i < clear_code; ++i) {
   438 	    table[0][i] = 0;
   439 	    table[1][i] = i;
   440 	}
   441 	for (; i < (1 << MAX_LWZ_BITS); ++i)
   442 	    table[0][i] = table[1][0] = 0;
   443 
   444 	sp = stack;
   445 
   446 	return 0;
   447     } else if (fresh) {
   448 	fresh = FALSE;
   449 	do {
   450 	    firstcode = oldcode = GetCode(src, code_size, FALSE);
   451 	} while (firstcode == clear_code);
   452 	return firstcode;
   453     }
   454     if (sp > stack)
   455 	return *--sp;
   456 
   457     while ((code = GetCode(src, code_size, FALSE)) >= 0) {
   458 	if (code == clear_code) {
   459 	    for (i = 0; i < clear_code; ++i) {
   460 		table[0][i] = 0;
   461 		table[1][i] = i;
   462 	    }
   463 	    for (; i < (1 << MAX_LWZ_BITS); ++i)
   464 		table[0][i] = table[1][i] = 0;
   465 	    code_size = set_code_size + 1;
   466 	    max_code_size = 2 * clear_code;
   467 	    max_code = clear_code + 2;
   468 	    sp = stack;
   469 	    firstcode = oldcode = GetCode(src, code_size, FALSE);
   470 	    return firstcode;
   471 	} else if (code == end_code) {
   472 	    int count;
   473 	    unsigned char buf[260];
   474 
   475 	    if (ZeroDataBlock)
   476 		return -2;
   477 
   478 	    while ((count = GetDataBlock(src, buf)) > 0)
   479 		;
   480 
   481 	    if (count != 0) {
   482 		/*
   483 		 * pm_message("missing EOD in data stream (common occurence)");
   484 		 */
   485 	    }
   486 	    return -2;
   487 	}
   488 	incode = code;
   489 
   490 	if (code >= max_code) {
   491 	    *sp++ = firstcode;
   492 	    code = oldcode;
   493 	}
   494 	while (code >= clear_code) {
   495 	    *sp++ = table[1][code];
   496 	    if (code == table[0][code])
   497 		RWSetMsg("circular table entry BIG ERROR");
   498 	    code = table[0][code];
   499 	}
   500 
   501 	*sp++ = firstcode = table[1][code];
   502 
   503 	if ((code = max_code) < (1 << MAX_LWZ_BITS)) {
   504 	    table[0][code] = oldcode;
   505 	    table[1][code] = firstcode;
   506 	    ++max_code;
   507 	    if ((max_code >= max_code_size) &&
   508 		(max_code_size < (1 << MAX_LWZ_BITS))) {
   509 		max_code_size *= 2;
   510 		++code_size;
   511 	    }
   512 	}
   513 	oldcode = incode;
   514 
   515 	if (sp > stack)
   516 	    return *--sp;
   517     }
   518     return code;
   519 }
   520 
   521 static Image *
   522 ReadImage(SDL_RWops * src, int len, int height, int cmapSize,
   523 	  unsigned char cmap[3][MAXCOLORMAPSIZE],
   524 	  int gray, int interlace, int ignore)
   525 {
   526     Image *image;
   527     unsigned char c;
   528     int i, v;
   529     int xpos = 0, ypos = 0, pass = 0;
   530 
   531     /*
   532     **	Initialize the compression routines
   533      */
   534     if (!ReadOK(src, &c, 1)) {
   535 	RWSetMsg("EOF / read error on image data");
   536 	return NULL;
   537     }
   538     if (LWZReadByte(src, TRUE, c) < 0) {
   539 	RWSetMsg("error reading image");
   540 	return NULL;
   541     }
   542     /*
   543     **	If this is an "uninteresting picture" ignore it.
   544      */
   545     if (ignore) {
   546 	while (LWZReadByte(src, FALSE, c) >= 0)
   547 	    ;
   548 	return NULL;
   549     }
   550     image = ImageNewCmap(len, height, cmapSize);
   551 
   552     for (i = 0; i < cmapSize; i++)
   553 	ImageSetCmap(image, i, cmap[CM_RED][i],
   554 		     cmap[CM_GREEN][i], cmap[CM_BLUE][i]);
   555 
   556     while ((v = LWZReadByte(src, FALSE, c)) >= 0) {
   557 #ifdef USED_BY_SDL
   558 	((Uint8 *)image->pixels)[xpos + ypos * image->pitch] = v;
   559 #else
   560 	image->data[xpos + ypos * len] = v;
   561 #endif
   562 	++xpos;
   563 	if (xpos == len) {
   564 	    xpos = 0;
   565 	    if (interlace) {
   566 		switch (pass) {
   567 		case 0:
   568 		case 1:
   569 		    ypos += 8;
   570 		    break;
   571 		case 2:
   572 		    ypos += 4;
   573 		    break;
   574 		case 3:
   575 		    ypos += 2;
   576 		    break;
   577 		}
   578 
   579 		if (ypos >= height) {
   580 		    ++pass;
   581 		    switch (pass) {
   582 		    case 1:
   583 			ypos = 4;
   584 			break;
   585 		    case 2:
   586 			ypos = 2;
   587 			break;
   588 		    case 3:
   589 			ypos = 1;
   590 			break;
   591 		    default:
   592 			goto fini;
   593 		    }
   594 		}
   595 	    } else {
   596 		++ypos;
   597 	    }
   598 	}
   599 	if (ypos >= height)
   600 	    break;
   601     }
   602 
   603   fini:
   604 
   605     return image;
   606 }
   607 
   608 #else
   609 
   610 /* See if an image is contained in a data source */
   611 int IMG_isGIF(SDL_RWops *src)
   612 {
   613 	return(0);
   614 }
   615 
   616 /* Load a GIF type image from an SDL datasource */
   617 SDL_Surface *IMG_LoadGIF_RW(SDL_RWops *src)
   618 {
   619 	return(NULL);
   620 }
   621 
   622 #endif /* LOAD_GIF */