IMG_gif.c
author Sam Lantinga <slouken@libsdl.org>
Sat, 04 Feb 2006 20:37:17 +0000
changeset 117 e613cf987897
parent 97 e1161bd417c4
child 118 c5e736a47ad2
permissions -rw-r--r--
Fixed image type functions so they seek back to where they started
     1 /*
     2     SDL_image:  An example image loading library for use with SDL
     3     Copyright (C) 1999-2004 Sam Lantinga
     4 
     5     This library is free software; you can redistribute it and/or
     6     modify it under the terms of the GNU Library General Public
     7     License as published by the Free Software Foundation; either
     8     version 2 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     Library General Public License for more details.
    14 
    15     You should have received a copy of the GNU Library General Public
    16     License along with this library; if not, write to the Free
    17     Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
    18 
    19     Sam Lantinga
    20     slouken@libsdl.org
    21 */
    22 
    23 /* $Id$ */
    24 
    25 /* This is a GIF image file loading framework */
    26 
    27 #include <stdio.h>
    28 #include <string.h>
    29 
    30 #include "SDL_image.h"
    31 
    32 #ifdef LOAD_GIF
    33 
    34 /* See if an image is contained in a data source */
    35 int IMG_isGIF(SDL_RWops *src)
    36 {
    37 	int start;
    38 	int is_GIF;
    39 	char magic[6];
    40 
    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     unsigned char buf[16];
   155     unsigned char c;
   156     unsigned char localColorMap[3][MAXCOLORMAPSIZE];
   157     int grayScale;
   158     int useGlobalColormap;
   159     int bitPixel;
   160     int imageCount = 0;
   161     char version[4];
   162     int imageNumber = 1;
   163     Image *image = NULL;
   164 
   165     if ( src == NULL ) {
   166         goto done;
   167     }
   168     if (!ReadOK(src, buf, 6)) {
   169 	RWSetMsg("error reading magic number");
   170         goto done;
   171     }
   172     if (strncmp((char *) buf, "GIF", 3) != 0) {
   173 	RWSetMsg("not a GIF file");
   174         goto done;
   175     }
   176     strncpy(version, (char *) buf + 3, 3);
   177     version[3] = '\0';
   178 
   179     if ((strcmp(version, "87a") != 0) && (strcmp(version, "89a") != 0)) {
   180 	RWSetMsg("bad version number, not '87a' or '89a'");
   181         goto done;
   182     }
   183     Gif89.transparent = -1;
   184     Gif89.delayTime = -1;
   185     Gif89.inputFlag = -1;
   186     Gif89.disposal = 0;
   187 
   188     if (!ReadOK(src, buf, 7)) {
   189 	RWSetMsg("failed to read screen descriptor");
   190         goto done;
   191     }
   192     GifScreen.Width = LM_to_uint(buf[0], buf[1]);
   193     GifScreen.Height = LM_to_uint(buf[2], buf[3]);
   194     GifScreen.BitPixel = 2 << (buf[4] & 0x07);
   195     GifScreen.ColorResolution = (((buf[4] & 0x70) >> 3) + 1);
   196     GifScreen.Background = buf[5];
   197     GifScreen.AspectRatio = buf[6];
   198 
   199     if (BitSet(buf[4], LOCALCOLORMAP)) {	/* Global Colormap */
   200 	if (ReadColorMap(src, GifScreen.BitPixel, GifScreen.ColorMap,
   201 			 &GifScreen.GrayScale)) {
   202 	    RWSetMsg("error reading global colormap");
   203             goto done;
   204 	}
   205     }
   206     do {
   207 	if (!ReadOK(src, &c, 1)) {
   208 	    RWSetMsg("EOF / read error on image data");
   209             goto done;
   210 	}
   211 	if (c == ';') {		/* GIF terminator */
   212 	    if (imageCount < imageNumber) {
   213 		RWSetMsg("only %d image%s found in file",
   214 			 imageCount, imageCount > 1 ? "s" : "");
   215                 goto done;
   216 	    }
   217 	}
   218 	if (c == '!') {		/* Extension */
   219 	    if (!ReadOK(src, &c, 1)) {
   220 		RWSetMsg("EOF / read error on extention function code");
   221                 goto done;
   222 	    }
   223 	    DoExtension(src, c);
   224 	    continue;
   225 	}
   226 	if (c != ',') {		/* Not a valid start character */
   227 	    continue;
   228 	}
   229 	++imageCount;
   230 
   231 	if (!ReadOK(src, buf, 9)) {
   232 	    RWSetMsg("couldn't read left/top/width/height");
   233             goto done;
   234 	}
   235 	useGlobalColormap = !BitSet(buf[8], LOCALCOLORMAP);
   236 
   237 	bitPixel = 1 << ((buf[8] & 0x07) + 1);
   238 
   239 	if (!useGlobalColormap) {
   240 	    if (ReadColorMap(src, bitPixel, localColorMap, &grayScale)) {
   241 		RWSetMsg("error reading local colormap");
   242                 goto done;
   243 	    }
   244 	    image = ReadImage(src, LM_to_uint(buf[4], buf[5]),
   245 			      LM_to_uint(buf[6], buf[7]),
   246 			      bitPixel, localColorMap, grayScale,
   247 			      BitSet(buf[8], INTERLACE),
   248 			      imageCount != imageNumber);
   249 	} else {
   250 	    image = ReadImage(src, LM_to_uint(buf[4], buf[5]),
   251 			      LM_to_uint(buf[6], buf[7]),
   252 			      GifScreen.BitPixel, GifScreen.ColorMap,
   253 			      GifScreen.GrayScale, BitSet(buf[8], INTERLACE),
   254 			      imageCount != imageNumber);
   255 	}
   256     } while (image == NULL);
   257 
   258 #ifdef USED_BY_SDL
   259     if ( Gif89.transparent >= 0 ) {
   260         SDL_SetColorKey(image, SDL_SRCCOLORKEY, Gif89.transparent);
   261     }
   262 #endif
   263 
   264 done:
   265     return image;
   266 }
   267 
   268 static int
   269 ReadColorMap(SDL_RWops *src, int number,
   270              unsigned char buffer[3][MAXCOLORMAPSIZE], int *gray)
   271 {
   272     int i;
   273     unsigned char rgb[3];
   274     int flag;
   275 
   276     flag = TRUE;
   277 
   278     for (i = 0; i < number; ++i) {
   279 	if (!ReadOK(src, rgb, sizeof(rgb))) {
   280 	    RWSetMsg("bad colormap");
   281 	    return 1;
   282 	}
   283 	buffer[CM_RED][i] = rgb[0];
   284 	buffer[CM_GREEN][i] = rgb[1];
   285 	buffer[CM_BLUE][i] = rgb[2];
   286 	flag &= (rgb[0] == rgb[1] && rgb[1] == rgb[2]);
   287     }
   288 
   289 #if 0
   290     if (flag)
   291 	*gray = (number == 2) ? PBM_TYPE : PGM_TYPE;
   292     else
   293 	*gray = PPM_TYPE;
   294 #else
   295     *gray = 0;
   296 #endif
   297 
   298     return FALSE;
   299 }
   300 
   301 static int
   302 DoExtension(SDL_RWops *src, int label)
   303 {
   304     static unsigned char buf[256];
   305     char *str;
   306 
   307     switch (label) {
   308     case 0x01:			/* Plain Text Extension */
   309 	str = "Plain Text Extension";
   310 	break;
   311     case 0xff:			/* Application Extension */
   312 	str = "Application Extension";
   313 	break;
   314     case 0xfe:			/* Comment Extension */
   315 	str = "Comment Extension";
   316 	while (GetDataBlock(src, (unsigned char *) buf) != 0);
   317 	return FALSE;
   318     case 0xf9:			/* Graphic Control Extension */
   319 	str = "Graphic Control Extension";
   320 	(void) GetDataBlock(src, (unsigned char *) buf);
   321 	Gif89.disposal = (buf[0] >> 2) & 0x7;
   322 	Gif89.inputFlag = (buf[0] >> 1) & 0x1;
   323 	Gif89.delayTime = LM_to_uint(buf[1], buf[2]);
   324 	if ((buf[0] & 0x1) != 0)
   325 	    Gif89.transparent = buf[3];
   326 
   327 	while (GetDataBlock(src, (unsigned char *) buf) != 0);
   328 	return FALSE;
   329     default:
   330 	str = (char *)buf;
   331 	sprintf(str, "UNKNOWN (0x%02x)", label);
   332 	break;
   333     }
   334 
   335     while (GetDataBlock(src, (unsigned char *) buf) != 0);
   336 
   337     return FALSE;
   338 }
   339 
   340 static int ZeroDataBlock = FALSE;
   341 
   342 static int
   343 GetDataBlock(SDL_RWops *src, unsigned char *buf)
   344 {
   345     unsigned char count;
   346 
   347     if (!ReadOK(src, &count, 1)) {
   348 	/* pm_message("error in getting DataBlock size" ); */
   349 	return -1;
   350     }
   351     ZeroDataBlock = count == 0;
   352 
   353     if ((count != 0) && (!ReadOK(src, buf, count))) {
   354 	/* pm_message("error in reading DataBlock" ); */
   355 	return -1;
   356     }
   357     return count;
   358 }
   359 
   360 static int
   361 GetCode(SDL_RWops *src, int code_size, int flag)
   362 {
   363     static unsigned char buf[280];
   364     static int curbit, lastbit, done, last_byte;
   365     int i, j, ret;
   366     unsigned char count;
   367 
   368     if (flag) {
   369 	curbit = 0;
   370 	lastbit = 0;
   371 	done = FALSE;
   372 	return 0;
   373     }
   374     if ((curbit + code_size) >= lastbit) {
   375 	if (done) {
   376 	    if (curbit >= lastbit)
   377 		RWSetMsg("ran off the end of my bits");
   378 	    return -1;
   379 	}
   380 	buf[0] = buf[last_byte - 2];
   381 	buf[1] = buf[last_byte - 1];
   382 
   383 	if ((count = GetDataBlock(src, &buf[2])) == 0)
   384 	    done = TRUE;
   385 
   386 	last_byte = 2 + count;
   387 	curbit = (curbit - lastbit) + 16;
   388 	lastbit = (2 + count) * 8;
   389     }
   390     ret = 0;
   391     for (i = curbit, j = 0; j < code_size; ++i, ++j)
   392 	ret |= ((buf[i / 8] & (1 << (i % 8))) != 0) << j;
   393 
   394     curbit += code_size;
   395 
   396     return ret;
   397 }
   398 
   399 static int
   400 LWZReadByte(SDL_RWops *src, int flag, int input_code_size)
   401 {
   402     static int fresh = FALSE;
   403     int code, incode;
   404     static int code_size, set_code_size;
   405     static int max_code, max_code_size;
   406     static int firstcode, oldcode;
   407     static int clear_code, end_code;
   408     static int table[2][(1 << MAX_LWZ_BITS)];
   409     static int stack[(1 << (MAX_LWZ_BITS)) * 2], *sp;
   410     register int i;
   411 
   412     if (flag) {
   413 	set_code_size = input_code_size;
   414 	code_size = set_code_size + 1;
   415 	clear_code = 1 << set_code_size;
   416 	end_code = clear_code + 1;
   417 	max_code_size = 2 * clear_code;
   418 	max_code = clear_code + 2;
   419 
   420 	GetCode(src, 0, TRUE);
   421 
   422 	fresh = TRUE;
   423 
   424 	for (i = 0; i < clear_code; ++i) {
   425 	    table[0][i] = 0;
   426 	    table[1][i] = i;
   427 	}
   428 	for (; i < (1 << MAX_LWZ_BITS); ++i)
   429 	    table[0][i] = table[1][0] = 0;
   430 
   431 	sp = stack;
   432 
   433 	return 0;
   434     } else if (fresh) {
   435 	fresh = FALSE;
   436 	do {
   437 	    firstcode = oldcode = GetCode(src, code_size, FALSE);
   438 	} while (firstcode == clear_code);
   439 	return firstcode;
   440     }
   441     if (sp > stack)
   442 	return *--sp;
   443 
   444     while ((code = GetCode(src, code_size, FALSE)) >= 0) {
   445 	if (code == clear_code) {
   446 	    for (i = 0; i < clear_code; ++i) {
   447 		table[0][i] = 0;
   448 		table[1][i] = i;
   449 	    }
   450 	    for (; i < (1 << MAX_LWZ_BITS); ++i)
   451 		table[0][i] = table[1][i] = 0;
   452 	    code_size = set_code_size + 1;
   453 	    max_code_size = 2 * clear_code;
   454 	    max_code = clear_code + 2;
   455 	    sp = stack;
   456 	    firstcode = oldcode = GetCode(src, code_size, FALSE);
   457 	    return firstcode;
   458 	} else if (code == end_code) {
   459 	    int count;
   460 	    unsigned char buf[260];
   461 
   462 	    if (ZeroDataBlock)
   463 		return -2;
   464 
   465 	    while ((count = GetDataBlock(src, buf)) > 0);
   466 
   467 	    if (count != 0) {
   468 		/*
   469 		 * pm_message("missing EOD in data stream (common occurence)");
   470 		 */
   471 	    }
   472 	    return -2;
   473 	}
   474 	incode = code;
   475 
   476 	if (code >= max_code) {
   477 	    *sp++ = firstcode;
   478 	    code = oldcode;
   479 	}
   480 	while (code >= clear_code) {
   481 	    *sp++ = table[1][code];
   482 	    if (code == table[0][code])
   483 		RWSetMsg("circular table entry BIG ERROR");
   484 	    code = table[0][code];
   485 	}
   486 
   487 	*sp++ = firstcode = table[1][code];
   488 
   489 	if ((code = max_code) < (1 << MAX_LWZ_BITS)) {
   490 	    table[0][code] = oldcode;
   491 	    table[1][code] = firstcode;
   492 	    ++max_code;
   493 	    if ((max_code >= max_code_size) &&
   494 		(max_code_size < (1 << MAX_LWZ_BITS))) {
   495 		max_code_size *= 2;
   496 		++code_size;
   497 	    }
   498 	}
   499 	oldcode = incode;
   500 
   501 	if (sp > stack)
   502 	    return *--sp;
   503     }
   504     return code;
   505 }
   506 
   507 static Image *
   508 ReadImage(SDL_RWops * src, int len, int height, int cmapSize,
   509 	  unsigned char cmap[3][MAXCOLORMAPSIZE],
   510 	  int gray, int interlace, int ignore)
   511 {
   512     Image *image;
   513     unsigned char c;
   514     int i, v;
   515     int xpos = 0, ypos = 0, pass = 0;
   516 
   517     /*
   518     **	Initialize the compression routines
   519      */
   520     if (!ReadOK(src, &c, 1)) {
   521 	RWSetMsg("EOF / read error on image data");
   522 	return NULL;
   523     }
   524     if (LWZReadByte(src, TRUE, c) < 0) {
   525 	RWSetMsg("error reading image");
   526 	return NULL;
   527     }
   528     /*
   529     **	If this is an "uninteresting picture" ignore it.
   530      */
   531     if (ignore) {
   532 	while (LWZReadByte(src, FALSE, c) >= 0);
   533 	return NULL;
   534     }
   535     image = ImageNewCmap(len, height, cmapSize);
   536 
   537     for (i = 0; i < cmapSize; i++)
   538 	ImageSetCmap(image, i, cmap[CM_RED][i],
   539 		     cmap[CM_GREEN][i], cmap[CM_BLUE][i]);
   540 
   541     while ((v = LWZReadByte(src, FALSE, c)) >= 0) {
   542 #ifdef USED_BY_SDL
   543 	((Uint8 *)image->pixels)[xpos + ypos * image->pitch] = v;
   544 #else
   545 	image->data[xpos + ypos * len] = v;
   546 #endif
   547 	++xpos;
   548 	if (xpos == len) {
   549 	    xpos = 0;
   550 	    if (interlace) {
   551 		switch (pass) {
   552 		case 0:
   553 		case 1:
   554 		    ypos += 8;
   555 		    break;
   556 		case 2:
   557 		    ypos += 4;
   558 		    break;
   559 		case 3:
   560 		    ypos += 2;
   561 		    break;
   562 		}
   563 
   564 		if (ypos >= height) {
   565 		    ++pass;
   566 		    switch (pass) {
   567 		    case 1:
   568 			ypos = 4;
   569 			break;
   570 		    case 2:
   571 			ypos = 2;
   572 			break;
   573 		    case 3:
   574 			ypos = 1;
   575 			break;
   576 		    default:
   577 			goto fini;
   578 		    }
   579 		}
   580 	    } else {
   581 		++ypos;
   582 	    }
   583 	}
   584 	if (ypos >= height)
   585 	    break;
   586     }
   587 
   588   fini:
   589 
   590     return image;
   591 }
   592 
   593 #else
   594 
   595 /* See if an image is contained in a data source */
   596 int IMG_isGIF(SDL_RWops *src)
   597 {
   598 	return(0);
   599 }
   600 
   601 /* Load a GIF type image from an SDL datasource */
   602 SDL_Surface *IMG_LoadGIF_RW(SDL_RWops *src)
   603 {
   604 	return(NULL);
   605 }
   606 
   607 #endif /* LOAD_GIF */