2 SDL_image: An example image loading library for use with SDL
3 Copyright (C) 1997-2013 Sam Lantinga <slouken@libsdl.org>
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.
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:
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.
22 #if !defined(__APPLE__) || defined(SDL_IMAGE_USE_COMMON_BACKEND)
24 /* This is a GIF image file loading framework */
29 #include "SDL_image.h"
33 /* See if an image is contained in a data source */
34 int IMG_isGIF(SDL_RWops *src)
42 start = SDL_RWtell(src);
44 if ( SDL_RWread(src, magic, sizeof(magic), 1) ) {
45 if ( (SDL_strncmp(magic, "GIF", 3) == 0) &&
46 ((SDL_memcmp(magic + 3, "87a", 3) == 0) ||
47 (SDL_memcmp(magic + 3, "89a", 3) == 0)) ) {
51 SDL_RWseek(src, start, RW_SEEK_SET);
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 /* +-------------------------------------------------------------------+ */
67 /* Adapted for use in SDL by Sam Lantinga -- 7/20/98 */
74 /* Changes to work with SDL:
76 Include SDL header file
77 Use SDL_Surface rather than xpaint Image structure
78 Define SDL versions of RWSetMsg(), ImageNewCmap() and ImageSetCmap()
82 #define Image SDL_Surface
83 #define RWSetMsg IMG_SetError
84 #define ImageNewCmap(w, h, s) SDL_CreateRGBSurface(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; \
94 /* Original XPaint sources */
99 #define SDL_RWops FILE
100 #define SDL_RWclose fclose
102 #endif /* USED_BY_SDL */
105 #define MAXCOLORMAPSIZE 256
114 #define MAX_LWZ_BITS 12
116 #define INTERLACE 0x40
117 #define LOCALCOLORMAP 0x80
118 #define BitSet(byte, bit) (((byte) & (bit)) == (bit))
120 #define ReadOK(file,buffer,len) SDL_RWread(file, buffer, len, 1)
122 #define LM_to_uint(a,b) (((b)<<8)|(a))
127 unsigned char ColorMap[3][MAXCOLORMAPSIZE];
128 unsigned int BitPixel;
129 unsigned int ColorResolution;
130 unsigned int Background;
131 unsigned int AspectRatio;
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);
153 IMG_LoadGIF_RW(SDL_RWops *src)
156 unsigned char buf[16];
158 unsigned char localColorMap[3][MAXCOLORMAPSIZE];
160 int useGlobalColormap;
170 start = SDL_RWtell(src);
172 if (!ReadOK(src, buf, 6)) {
173 RWSetMsg("error reading magic number");
176 if (SDL_strncmp((char *) buf, "GIF", 3) != 0) {
177 RWSetMsg("not a GIF file");
180 SDL_memcpy(version, (char *) buf + 3, 3);
183 if ((SDL_strcmp(version, "87a") != 0) && (SDL_strcmp(version, "89a") != 0)) {
184 RWSetMsg("bad version number, not '87a' or '89a'");
187 Gif89.transparent = -1;
188 Gif89.delayTime = -1;
189 Gif89.inputFlag = -1;
192 if (!ReadOK(src, buf, 7)) {
193 RWSetMsg("failed to read screen descriptor");
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];
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");
211 if (!ReadOK(src, &c, 1)) {
212 RWSetMsg("EOF / read error on image data");
215 if (c == ';') { /* GIF terminator */
216 if (imageCount < imageNumber) {
217 RWSetMsg("only %d image%s found in file",
218 imageCount, imageCount > 1 ? "s" : "");
222 if (c == '!') { /* Extension */
223 if (!ReadOK(src, &c, 1)) {
224 RWSetMsg("EOF / read error on extention function code");
230 if (c != ',') { /* Not a valid start character */
235 if (!ReadOK(src, buf, 9)) {
236 RWSetMsg("couldn't read left/top/width/height");
239 useGlobalColormap = !BitSet(buf[8], LOCALCOLORMAP);
241 bitPixel = 1 << ((buf[8] & 0x07) + 1);
243 if (!useGlobalColormap) {
244 if (ReadColorMap(src, bitPixel, localColorMap, &grayScale)) {
245 RWSetMsg("error reading local colormap");
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);
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);
260 } while (image == NULL);
263 if ( Gif89.transparent >= 0 ) {
264 SDL_SetColorKey(image, SDL_TRUE, Gif89.transparent);
269 if ( image == NULL ) {
270 SDL_RWseek(src, start, RW_SEEK_SET);
276 ReadColorMap(SDL_RWops *src, int number,
277 unsigned char buffer[3][MAXCOLORMAPSIZE], int *gray)
280 unsigned char rgb[3];
285 for (i = 0; i < number; ++i) {
286 if (!ReadOK(src, rgb, sizeof(rgb))) {
287 RWSetMsg("bad colormap");
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]);
298 *gray = (number == 2) ? PBM_TYPE : PGM_TYPE;
309 DoExtension(SDL_RWops *src, int label)
311 static unsigned char buf[256];
315 case 0x01: /* Plain Text Extension */
316 str = "Plain Text Extension";
318 case 0xff: /* Application Extension */
319 str = "Application Extension";
321 case 0xfe: /* Comment Extension */
322 str = "Comment Extension";
323 while (GetDataBlock(src, (unsigned char *) buf) != 0)
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];
335 while (GetDataBlock(src, (unsigned char *) buf) != 0)
340 SDL_snprintf(str, 256, "UNKNOWN (0x%02x)", label);
344 while (GetDataBlock(src, (unsigned char *) buf) != 0)
350 static int ZeroDataBlock = FALSE;
353 GetDataBlock(SDL_RWops *src, unsigned char *buf)
357 if (!ReadOK(src, &count, 1)) {
358 /* pm_message("error in getting DataBlock size" ); */
361 ZeroDataBlock = count == 0;
363 if ((count != 0) && (!ReadOK(src, buf, count))) {
364 /* pm_message("error in reading DataBlock" ); */
371 GetCode(SDL_RWops *src, int code_size, int flag)
373 static unsigned char buf[280];
374 static int curbit, lastbit, done, last_byte;
384 if ((curbit + code_size) >= lastbit) {
386 if (curbit >= lastbit)
387 RWSetMsg("ran off the end of my bits");
390 buf[0] = buf[last_byte - 2];
391 buf[1] = buf[last_byte - 1];
393 if ((count = GetDataBlock(src, &buf[2])) == 0)
396 last_byte = 2 + count;
397 curbit = (curbit - lastbit) + 16;
398 lastbit = (2 + count) * 8;
401 for (i = curbit, j = 0; j < code_size; ++i, ++j)
402 ret |= ((buf[i / 8] & (1 << (i % 8))) != 0) << j;
410 LWZReadByte(SDL_RWops *src, int flag, int input_code_size)
412 static int fresh = FALSE;
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;
422 /* Fixed buffer overflow found by Michael Skladnikiewicz */
423 if (input_code_size > MAX_LWZ_BITS)
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;
434 GetCode(src, 0, TRUE);
438 for (i = 0; i < clear_code; ++i) {
443 for (; i < (1 << MAX_LWZ_BITS); ++i)
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 /* Guard against buffer overruns */
498 if (code < 0 || code >= (1 << MAX_LWZ_BITS)) {
499 RWSetMsg("invalid LWZ data");
502 *sp++ = table[1][code];
503 if (code == table[0][code])
504 RWSetMsg("circular table entry BIG ERROR");
505 code = table[0][code];
508 /* Guard against buffer overruns */
509 if (code < 0 || code >= (1 << MAX_LWZ_BITS)) {
510 RWSetMsg("invalid LWZ data");
513 *sp++ = firstcode = table[1][code];
515 if ((code = max_code) < (1 << MAX_LWZ_BITS)) {
516 table[0][code] = oldcode;
517 table[1][code] = firstcode;
519 if ((max_code >= max_code_size) &&
520 (max_code_size < (1 << MAX_LWZ_BITS))) {
534 ReadImage(SDL_RWops * src, int len, int height, int cmapSize,
535 unsigned char cmap[3][MAXCOLORMAPSIZE],
536 int gray, int interlace, int ignore)
541 int xpos = 0, ypos = 0, pass = 0;
544 ** Initialize the compression routines
546 if (!ReadOK(src, &c, 1)) {
547 RWSetMsg("EOF / read error on image data");
550 if (LWZReadByte(src, TRUE, c) < 0) {
551 RWSetMsg("error reading image");
555 ** If this is an "uninteresting picture" ignore it.
558 while (LWZReadByte(src, FALSE, c) >= 0)
562 image = ImageNewCmap(len, height, cmapSize);
564 for (i = 0; i < cmapSize; i++)
565 ImageSetCmap(image, i, cmap[CM_RED][i],
566 cmap[CM_GREEN][i], cmap[CM_BLUE][i]);
568 while ((v = LWZReadByte(src, FALSE, c)) >= 0) {
570 ((Uint8 *)image->pixels)[xpos + ypos * image->pitch] = v;
572 image->data[xpos + ypos * len] = v;
591 if (ypos >= height) {
622 /* See if an image is contained in a data source */
623 int IMG_isGIF(SDL_RWops *src)
628 /* Load a GIF type image from an SDL datasource */
629 SDL_Surface *IMG_LoadGIF_RW(SDL_RWops *src)
634 #endif /* LOAD_GIF */
636 #endif /* !defined(__APPLE__) || defined(SDL_IMAGE_USE_COMMON_BACKEND) */