2 SDL_image: An example image loading library for use with SDL
3 Copyright (C) 1997-2009 Sam Lantinga
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.
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.
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
23 /* This is a GIF image file loading framework */
28 #include "SDL_image.h"
32 /* See if an image is contained in a data source */
33 int IMG_isGIF(SDL_RWops *src)
41 start = SDL_RWtell(src);
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)) ) {
50 SDL_RWseek(src, start, SEEK_SET);
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 /* +-------------------------------------------------------------------+ */
66 /* Adapted for use in SDL by Sam Lantinga -- 7/20/98 */
73 /* Changes to work with SDL:
75 Include SDL header file
76 Use SDL_Surface rather than xpaint Image structure
77 Define SDL versions of RWSetMsg(), ImageNewCmap() and ImageSetCmap()
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; \
93 /* Original XPaint sources */
98 #define SDL_RWops FILE
99 #define SDL_RWclose fclose
101 #endif /* USED_BY_SDL */
104 #define MAXCOLORMAPSIZE 256
113 #define MAX_LWZ_BITS 12
115 #define INTERLACE 0x40
116 #define LOCALCOLORMAP 0x80
117 #define BitSet(byte, bit) (((byte) & (bit)) == (bit))
119 #define ReadOK(file,buffer,len) SDL_RWread(file, buffer, len, 1)
121 #define LM_to_uint(a,b) (((b)<<8)|(a))
126 unsigned char ColorMap[3][MAXCOLORMAPSIZE];
127 unsigned int BitPixel;
128 unsigned int ColorResolution;
129 unsigned int Background;
130 unsigned int AspectRatio;
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);
152 IMG_LoadGIF_RW(SDL_RWops *src)
155 unsigned char buf[16];
157 unsigned char localColorMap[3][MAXCOLORMAPSIZE];
159 int useGlobalColormap;
169 start = SDL_RWtell(src);
171 if (!ReadOK(src, buf, 6)) {
172 RWSetMsg("error reading magic number");
175 if (strncmp((char *) buf, "GIF", 3) != 0) {
176 RWSetMsg("not a GIF file");
179 strncpy(version, (char *) buf + 3, 3);
182 if ((strcmp(version, "87a") != 0) && (strcmp(version, "89a") != 0)) {
183 RWSetMsg("bad version number, not '87a' or '89a'");
186 Gif89.transparent = -1;
187 Gif89.delayTime = -1;
188 Gif89.inputFlag = -1;
191 if (!ReadOK(src, buf, 7)) {
192 RWSetMsg("failed to read screen descriptor");
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];
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");
210 if (!ReadOK(src, &c, 1)) {
211 RWSetMsg("EOF / read error on image data");
214 if (c == ';') { /* GIF terminator */
215 if (imageCount < imageNumber) {
216 RWSetMsg("only %d image%s found in file",
217 imageCount, imageCount > 1 ? "s" : "");
221 if (c == '!') { /* Extension */
222 if (!ReadOK(src, &c, 1)) {
223 RWSetMsg("EOF / read error on extention function code");
229 if (c != ',') { /* Not a valid start character */
234 if (!ReadOK(src, buf, 9)) {
235 RWSetMsg("couldn't read left/top/width/height");
238 useGlobalColormap = !BitSet(buf[8], LOCALCOLORMAP);
240 bitPixel = 1 << ((buf[8] & 0x07) + 1);
242 if (!useGlobalColormap) {
243 if (ReadColorMap(src, bitPixel, localColorMap, &grayScale)) {
244 RWSetMsg("error reading local colormap");
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);
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);
259 } while (image == NULL);
262 if ( Gif89.transparent >= 0 ) {
263 SDL_SetColorKey(image, SDL_SRCCOLORKEY, Gif89.transparent);
268 if ( image == NULL ) {
269 SDL_RWseek(src, start, SEEK_SET);
275 ReadColorMap(SDL_RWops *src, int number,
276 unsigned char buffer[3][MAXCOLORMAPSIZE], int *gray)
279 unsigned char rgb[3];
284 for (i = 0; i < number; ++i) {
285 if (!ReadOK(src, rgb, sizeof(rgb))) {
286 RWSetMsg("bad colormap");
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]);
297 *gray = (number == 2) ? PBM_TYPE : PGM_TYPE;
308 DoExtension(SDL_RWops *src, int label)
310 static unsigned char buf[256];
314 case 0x01: /* Plain Text Extension */
315 str = "Plain Text Extension";
317 case 0xff: /* Application Extension */
318 str = "Application Extension";
320 case 0xfe: /* Comment Extension */
321 str = "Comment Extension";
322 while (GetDataBlock(src, (unsigned char *) buf) != 0)
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];
334 while (GetDataBlock(src, (unsigned char *) buf) != 0)
339 sprintf(str, "UNKNOWN (0x%02x)", label);
343 while (GetDataBlock(src, (unsigned char *) buf) != 0)
349 static int ZeroDataBlock = FALSE;
352 GetDataBlock(SDL_RWops *src, unsigned char *buf)
356 if (!ReadOK(src, &count, 1)) {
357 /* pm_message("error in getting DataBlock size" ); */
360 ZeroDataBlock = count == 0;
362 if ((count != 0) && (!ReadOK(src, buf, count))) {
363 /* pm_message("error in reading DataBlock" ); */
370 GetCode(SDL_RWops *src, int code_size, int flag)
372 static unsigned char buf[280];
373 static int curbit, lastbit, done, last_byte;
383 if ((curbit + code_size) >= lastbit) {
385 if (curbit >= lastbit)
386 RWSetMsg("ran off the end of my bits");
389 buf[0] = buf[last_byte - 2];
390 buf[1] = buf[last_byte - 1];
392 if ((count = GetDataBlock(src, &buf[2])) == 0)
395 last_byte = 2 + count;
396 curbit = (curbit - lastbit) + 16;
397 lastbit = (2 + count) * 8;
400 for (i = curbit, j = 0; j < code_size; ++i, ++j)
401 ret |= ((buf[i / 8] & (1 << (i % 8))) != 0) << j;
409 LWZReadByte(SDL_RWops *src, int flag, int input_code_size)
411 static int fresh = FALSE;
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;
421 /* Fixed buffer overflow found by Michael Skladnikiewicz */
422 if (input_code_size > MAX_LWZ_BITS)
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;
433 GetCode(src, 0, TRUE);
437 for (i = 0; i < clear_code; ++i) {
441 for (; i < (1 << MAX_LWZ_BITS); ++i)
442 table[0][i] = table[1][0] = 0;
450 firstcode = oldcode = GetCode(src, code_size, FALSE);
451 } while (firstcode == clear_code);
457 while ((code = GetCode(src, code_size, FALSE)) >= 0) {
458 if (code == clear_code) {
459 for (i = 0; i < clear_code; ++i) {
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;
469 firstcode = oldcode = GetCode(src, code_size, FALSE);
471 } else if (code == end_code) {
473 unsigned char buf[260];
478 while ((count = GetDataBlock(src, buf)) > 0)
483 * pm_message("missing EOD in data stream (common occurence)");
490 if (code >= max_code) {
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];
501 *sp++ = firstcode = table[1][code];
503 if ((code = max_code) < (1 << MAX_LWZ_BITS)) {
504 table[0][code] = oldcode;
505 table[1][code] = firstcode;
507 if ((max_code >= max_code_size) &&
508 (max_code_size < (1 << MAX_LWZ_BITS))) {
522 ReadImage(SDL_RWops * src, int len, int height, int cmapSize,
523 unsigned char cmap[3][MAXCOLORMAPSIZE],
524 int gray, int interlace, int ignore)
529 int xpos = 0, ypos = 0, pass = 0;
532 ** Initialize the compression routines
534 if (!ReadOK(src, &c, 1)) {
535 RWSetMsg("EOF / read error on image data");
538 if (LWZReadByte(src, TRUE, c) < 0) {
539 RWSetMsg("error reading image");
543 ** If this is an "uninteresting picture" ignore it.
546 while (LWZReadByte(src, FALSE, c) >= 0)
550 image = ImageNewCmap(len, height, cmapSize);
552 for (i = 0; i < cmapSize; i++)
553 ImageSetCmap(image, i, cmap[CM_RED][i],
554 cmap[CM_GREEN][i], cmap[CM_BLUE][i]);
556 while ((v = LWZReadByte(src, FALSE, c)) >= 0) {
558 ((Uint8 *)image->pixels)[xpos + ypos * image->pitch] = v;
560 image->data[xpos + ypos * len] = v;
579 if (ypos >= height) {
610 /* See if an image is contained in a data source */
611 int IMG_isGIF(SDL_RWops *src)
616 /* Load a GIF type image from an SDL datasource */
617 SDL_Surface *IMG_LoadGIF_RW(SDL_RWops *src)
622 #endif /* LOAD_GIF */