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 #if !defined(__APPLE__) || defined(SDL_IMAGE_USE_COMMON_BACKEND)
25 /* This is a GIF image file loading framework */
30 #include "SDL_image.h"
34 /* See if an image is contained in a data source */
35 int IMG_isGIF(SDL_RWops *src)
43 start = SDL_RWtell(src);
45 if ( SDL_RWread(src, magic, sizeof(magic), 1) ) {
46 if ( (strncmp(magic, "GIF", 3) == 0) &&
47 ((memcmp(magic + 3, "87a", 3) == 0) ||
48 (memcmp(magic + 3, "89a", 3) == 0)) ) {
52 SDL_RWseek(src, start, RW_SEEK_SET);
56 /* Code from here to end of file has been adapted from XPaint: */
57 /* +-------------------------------------------------------------------+ */
58 /* | Copyright 1990, 1991, 1993 David Koblas. | */
59 /* | Copyright 1996 Torsten Martinsen. | */
60 /* | Permission to use, copy, modify, and distribute this software | */
61 /* | and its documentation for any purpose and without fee is hereby | */
62 /* | granted, provided that the above copyright notice appear in all | */
63 /* | copies and that both that copyright notice and this permission | */
64 /* | notice appear in supporting documentation. This software is | */
65 /* | provided "as is" without express or implied warranty. | */
66 /* +-------------------------------------------------------------------+ */
68 /* Adapted for use in SDL by Sam Lantinga -- 7/20/98 */
75 /* Changes to work with SDL:
77 Include SDL header file
78 Use SDL_Surface rather than xpaint Image structure
79 Define SDL versions of RWSetMsg(), ImageNewCmap() and ImageSetCmap()
83 #define Image SDL_Surface
84 #define RWSetMsg IMG_SetError
85 #define ImageNewCmap(w, h, s) SDL_AllocSurface(SDL_SWSURFACE,w,h,8,0,0,0,0)
86 #define ImageSetCmap(s, i, R, G, B) do { \
87 s->format->palette->colors[i].r = R; \
88 s->format->palette->colors[i].g = G; \
89 s->format->palette->colors[i].b = B; \
95 /* Original XPaint sources */
100 #define SDL_RWops FILE
101 #define SDL_RWclose fclose
103 #endif /* USED_BY_SDL */
106 #define MAXCOLORMAPSIZE 256
115 #define MAX_LWZ_BITS 12
117 #define INTERLACE 0x40
118 #define LOCALCOLORMAP 0x80
119 #define BitSet(byte, bit) (((byte) & (bit)) == (bit))
121 #define ReadOK(file,buffer,len) SDL_RWread(file, buffer, len, 1)
123 #define LM_to_uint(a,b) (((b)<<8)|(a))
128 unsigned char ColorMap[3][MAXCOLORMAPSIZE];
129 unsigned int BitPixel;
130 unsigned int ColorResolution;
131 unsigned int Background;
132 unsigned int AspectRatio;
143 static int ReadColorMap(SDL_RWops * src, int number,
144 unsigned char buffer[3][MAXCOLORMAPSIZE], int *flag);
145 static int DoExtension(SDL_RWops * src, int label);
146 static int GetDataBlock(SDL_RWops * src, unsigned char *buf);
147 static int GetCode(SDL_RWops * src, int code_size, int flag);
148 static int LWZReadByte(SDL_RWops * src, int flag, int input_code_size);
149 static Image *ReadImage(SDL_RWops * src, int len, int height, int,
150 unsigned char cmap[3][MAXCOLORMAPSIZE],
151 int gray, int interlace, int ignore);
154 IMG_LoadGIF_RW(SDL_RWops *src)
157 unsigned char buf[16];
159 unsigned char localColorMap[3][MAXCOLORMAPSIZE];
161 int useGlobalColormap;
171 start = SDL_RWtell(src);
173 if (!ReadOK(src, buf, 6)) {
174 RWSetMsg("error reading magic number");
177 if (strncmp((char *) buf, "GIF", 3) != 0) {
178 RWSetMsg("not a GIF file");
181 strncpy(version, (char *) buf + 3, 3);
184 if ((strcmp(version, "87a") != 0) && (strcmp(version, "89a") != 0)) {
185 RWSetMsg("bad version number, not '87a' or '89a'");
188 Gif89.transparent = -1;
189 Gif89.delayTime = -1;
190 Gif89.inputFlag = -1;
193 if (!ReadOK(src, buf, 7)) {
194 RWSetMsg("failed to read screen descriptor");
197 GifScreen.Width = LM_to_uint(buf[0], buf[1]);
198 GifScreen.Height = LM_to_uint(buf[2], buf[3]);
199 GifScreen.BitPixel = 2 << (buf[4] & 0x07);
200 GifScreen.ColorResolution = (((buf[4] & 0x70) >> 3) + 1);
201 GifScreen.Background = buf[5];
202 GifScreen.AspectRatio = buf[6];
204 if (BitSet(buf[4], LOCALCOLORMAP)) { /* Global Colormap */
205 if (ReadColorMap(src, GifScreen.BitPixel, GifScreen.ColorMap,
206 &GifScreen.GrayScale)) {
207 RWSetMsg("error reading global colormap");
212 if (!ReadOK(src, &c, 1)) {
213 RWSetMsg("EOF / read error on image data");
216 if (c == ';') { /* GIF terminator */
217 if (imageCount < imageNumber) {
218 RWSetMsg("only %d image%s found in file",
219 imageCount, imageCount > 1 ? "s" : "");
223 if (c == '!') { /* Extension */
224 if (!ReadOK(src, &c, 1)) {
225 RWSetMsg("EOF / read error on extention function code");
231 if (c != ',') { /* Not a valid start character */
236 if (!ReadOK(src, buf, 9)) {
237 RWSetMsg("couldn't read left/top/width/height");
240 useGlobalColormap = !BitSet(buf[8], LOCALCOLORMAP);
242 bitPixel = 1 << ((buf[8] & 0x07) + 1);
244 if (!useGlobalColormap) {
245 if (ReadColorMap(src, bitPixel, localColorMap, &grayScale)) {
246 RWSetMsg("error reading local colormap");
249 image = ReadImage(src, LM_to_uint(buf[4], buf[5]),
250 LM_to_uint(buf[6], buf[7]),
251 bitPixel, localColorMap, grayScale,
252 BitSet(buf[8], INTERLACE),
253 imageCount != imageNumber);
255 image = ReadImage(src, LM_to_uint(buf[4], buf[5]),
256 LM_to_uint(buf[6], buf[7]),
257 GifScreen.BitPixel, GifScreen.ColorMap,
258 GifScreen.GrayScale, BitSet(buf[8], INTERLACE),
259 imageCount != imageNumber);
261 } while (image == NULL);
264 if ( Gif89.transparent >= 0 ) {
265 SDL_SetColorKey(image, SDL_SRCCOLORKEY, Gif89.transparent);
270 if ( image == NULL ) {
271 SDL_RWseek(src, start, RW_SEEK_SET);
277 ReadColorMap(SDL_RWops *src, int number,
278 unsigned char buffer[3][MAXCOLORMAPSIZE], int *gray)
281 unsigned char rgb[3];
286 for (i = 0; i < number; ++i) {
287 if (!ReadOK(src, rgb, sizeof(rgb))) {
288 RWSetMsg("bad colormap");
291 buffer[CM_RED][i] = rgb[0];
292 buffer[CM_GREEN][i] = rgb[1];
293 buffer[CM_BLUE][i] = rgb[2];
294 flag &= (rgb[0] == rgb[1] && rgb[1] == rgb[2]);
299 *gray = (number == 2) ? PBM_TYPE : PGM_TYPE;
310 DoExtension(SDL_RWops *src, int label)
312 static unsigned char buf[256];
316 case 0x01: /* Plain Text Extension */
317 str = "Plain Text Extension";
319 case 0xff: /* Application Extension */
320 str = "Application Extension";
322 case 0xfe: /* Comment Extension */
323 str = "Comment Extension";
324 while (GetDataBlock(src, (unsigned char *) buf) != 0)
327 case 0xf9: /* Graphic Control Extension */
328 str = "Graphic Control Extension";
329 (void) GetDataBlock(src, (unsigned char *) buf);
330 Gif89.disposal = (buf[0] >> 2) & 0x7;
331 Gif89.inputFlag = (buf[0] >> 1) & 0x1;
332 Gif89.delayTime = LM_to_uint(buf[1], buf[2]);
333 if ((buf[0] & 0x1) != 0)
334 Gif89.transparent = buf[3];
336 while (GetDataBlock(src, (unsigned char *) buf) != 0)
341 sprintf(str, "UNKNOWN (0x%02x)", label);
345 while (GetDataBlock(src, (unsigned char *) buf) != 0)
351 static int ZeroDataBlock = FALSE;
354 GetDataBlock(SDL_RWops *src, unsigned char *buf)
358 if (!ReadOK(src, &count, 1)) {
359 /* pm_message("error in getting DataBlock size" ); */
362 ZeroDataBlock = count == 0;
364 if ((count != 0) && (!ReadOK(src, buf, count))) {
365 /* pm_message("error in reading DataBlock" ); */
372 GetCode(SDL_RWops *src, int code_size, int flag)
374 static unsigned char buf[280];
375 static int curbit, lastbit, done, last_byte;
385 if ((curbit + code_size) >= lastbit) {
387 if (curbit >= lastbit)
388 RWSetMsg("ran off the end of my bits");
391 buf[0] = buf[last_byte - 2];
392 buf[1] = buf[last_byte - 1];
394 if ((count = GetDataBlock(src, &buf[2])) == 0)
397 last_byte = 2 + count;
398 curbit = (curbit - lastbit) + 16;
399 lastbit = (2 + count) * 8;
402 for (i = curbit, j = 0; j < code_size; ++i, ++j)
403 ret |= ((buf[i / 8] & (1 << (i % 8))) != 0) << j;
411 LWZReadByte(SDL_RWops *src, int flag, int input_code_size)
413 static int fresh = FALSE;
415 static int code_size, set_code_size;
416 static int max_code, max_code_size;
417 static int firstcode, oldcode;
418 static int clear_code, end_code;
419 static int table[2][(1 << MAX_LWZ_BITS)];
420 static int stack[(1 << (MAX_LWZ_BITS)) * 2], *sp;
423 /* Fixed buffer overflow found by Michael Skladnikiewicz */
424 if (input_code_size > MAX_LWZ_BITS)
428 set_code_size = input_code_size;
429 code_size = set_code_size + 1;
430 clear_code = 1 << set_code_size;
431 end_code = clear_code + 1;
432 max_code_size = 2 * clear_code;
433 max_code = clear_code + 2;
435 GetCode(src, 0, TRUE);
439 for (i = 0; i < clear_code; ++i) {
443 for (; i < (1 << MAX_LWZ_BITS); ++i)
444 table[0][i] = table[1][0] = 0;
452 firstcode = oldcode = GetCode(src, code_size, FALSE);
453 } while (firstcode == clear_code);
459 while ((code = GetCode(src, code_size, FALSE)) >= 0) {
460 if (code == clear_code) {
461 for (i = 0; i < clear_code; ++i) {
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;
471 firstcode = oldcode = GetCode(src, code_size, FALSE);
473 } else if (code == end_code) {
475 unsigned char buf[260];
480 while ((count = GetDataBlock(src, buf)) > 0)
485 * pm_message("missing EOD in data stream (common occurence)");
492 if (code >= max_code) {
496 while (code >= clear_code) {
497 *sp++ = table[1][code];
498 if (code == table[0][code])
499 RWSetMsg("circular table entry BIG ERROR");
500 code = table[0][code];
503 *sp++ = firstcode = table[1][code];
505 if ((code = max_code) < (1 << MAX_LWZ_BITS)) {
506 table[0][code] = oldcode;
507 table[1][code] = firstcode;
509 if ((max_code >= max_code_size) &&
510 (max_code_size < (1 << MAX_LWZ_BITS))) {
524 ReadImage(SDL_RWops * src, int len, int height, int cmapSize,
525 unsigned char cmap[3][MAXCOLORMAPSIZE],
526 int gray, int interlace, int ignore)
531 int xpos = 0, ypos = 0, pass = 0;
534 ** Initialize the compression routines
536 if (!ReadOK(src, &c, 1)) {
537 RWSetMsg("EOF / read error on image data");
540 if (LWZReadByte(src, TRUE, c) < 0) {
541 RWSetMsg("error reading image");
545 ** If this is an "uninteresting picture" ignore it.
548 while (LWZReadByte(src, FALSE, c) >= 0)
552 image = ImageNewCmap(len, height, cmapSize);
554 for (i = 0; i < cmapSize; i++)
555 ImageSetCmap(image, i, cmap[CM_RED][i],
556 cmap[CM_GREEN][i], cmap[CM_BLUE][i]);
558 while ((v = LWZReadByte(src, FALSE, c)) >= 0) {
560 ((Uint8 *)image->pixels)[xpos + ypos * image->pitch] = v;
562 image->data[xpos + ypos * len] = v;
581 if (ypos >= height) {
612 /* See if an image is contained in a data source */
613 int IMG_isGIF(SDL_RWops *src)
618 /* Load a GIF type image from an SDL datasource */
619 SDL_Surface *IMG_LoadGIF_RW(SDL_RWops *src)
624 #endif /* LOAD_GIF */
626 #endif /* !defined(__APPLE__) || defined(SDL_IMAGE_USE_COMMON_BACKEND) */