original 1.0.1 release
[xwelltris.git] / src / image / sublib / IMG_gif.c
blob99907be49e4b4697cbe236a87f5c80ea7b5eec94
1 /*
2 SDL_image: An example image loading library for use with SDL
3 Copyright (C) 1999, 2000, 2001 Sam Lantinga
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.
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.
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
19 Sam Lantinga
20 slouken@libsdl.org
23 /* $Id: IMG_gif.c,v 1.1.1.1 2003/01/04 11:37:22 leo Exp $ */
25 /* This is a GIF image file loading framework */
27 #include <stdio.h>
28 #include <string.h>
30 #include "SDL_image.h"
32 #define LOAD_GIF
34 #ifdef LOAD_GIF
36 /* See if an image is contained in a data source */
37 int IMG_isGIF(SDL_RWops *src)
39 int is_GIF;
40 char magic[6];
42 is_GIF = 0;
43 if ( SDL_RWread(src, magic, 6, 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;
50 return(is_GIF);
53 /* Code from here to end of file has been adapted from XPaint: */
54 /* +-------------------------------------------------------------------+ */
55 /* | Copyright 1990, 1991, 1993 David Koblas. | */
56 /* | Copyright 1996 Torsten Martinsen. | */
57 /* | Permission to use, copy, modify, and distribute this software | */
58 /* | and its documentation for any purpose and without fee is hereby | */
59 /* | granted, provided that the above copyright notice appear in all | */
60 /* | copies and that both that copyright notice and this permission | */
61 /* | notice appear in supporting documentation. This software is | */
62 /* | provided "as is" without express or implied warranty. | */
63 /* +-------------------------------------------------------------------+ */
65 /* Adapted for use in SDL by Sam Lantinga -- 7/20/98 */
66 #define USED_BY_SDL
68 #include <stdio.h>
69 #include <string.h>
71 #ifdef USED_BY_SDL
72 /* Changes to work with SDL:
74 Include SDL header file
75 Use SDL_Surface rather than xpaint Image structure
76 Define SDL versions of RWSetMsg(), ImageNewCmap() and ImageSetCmap()
78 #include "SDL.h"
80 #define Image SDL_Surface
81 #define RWSetMsg IMG_SetError
82 #define ImageNewCmap(w, h, s) SDL_AllocSurface(SDL_SWSURFACE,w,h,8,0,0,0,0)
83 #define ImageSetCmap(s, i, R, G, B) do { \
84 s->format->palette->colors[i].r = R; \
85 s->format->palette->colors[i].g = G; \
86 s->format->palette->colors[i].b = B; \
87 } while (0)
88 /* * * * * */
90 #else
92 /* Original XPaint sources */
94 #include "image.h"
95 #include "rwTable.h"
97 #define SDL_RWops FILE
98 #define SDL_RWclose fclose
100 #endif /* USED_BY_SDL */
103 #define MAXCOLORMAPSIZE 256
105 #define TRUE 1
106 #define FALSE 0
108 #define CM_RED 0
109 #define CM_GREEN 1
110 #define CM_BLUE 2
112 #define MAX_LWZ_BITS 12
114 #define INTERLACE 0x40
115 #define LOCALCOLORMAP 0x80
116 #define BitSet(byte, bit) (((byte) & (bit)) == (bit))
118 #define ReadOK(file,buffer,len) SDL_RWread(file, buffer, len, 1)
120 #define LM_to_uint(a,b) (((b)<<8)|(a))
122 static struct {
123 unsigned int Width;
124 unsigned int Height;
125 unsigned char ColorMap[3][MAXCOLORMAPSIZE];
126 unsigned int BitPixel;
127 unsigned int ColorResolution;
128 unsigned int Background;
129 unsigned int AspectRatio;
130 int GrayScale;
131 } GifScreen;
133 static struct {
134 int transparent;
135 int delayTime;
136 int inputFlag;
137 int disposal;
138 } Gif89;
140 static int ReadColorMap(SDL_RWops * src, int number,
141 unsigned char buffer[3][MAXCOLORMAPSIZE], int *flag);
142 static int DoExtension(SDL_RWops * src, int label);
143 static int GetDataBlock(SDL_RWops * src, unsigned char *buf);
144 static int GetCode(SDL_RWops * src, int code_size, int flag);
145 static int LWZReadByte(SDL_RWops * src, int flag, int input_code_size);
146 static Image *ReadImage(SDL_RWops * src, int len, int height, int,
147 unsigned char cmap[3][MAXCOLORMAPSIZE],
148 int gray, int interlace, int ignore);
150 Image *
151 IMG_LoadGIF_RW(SDL_RWops *src)
153 unsigned char buf[16];
154 unsigned char c;
155 unsigned char localColorMap[3][MAXCOLORMAPSIZE];
156 int grayScale;
157 int useGlobalColormap;
158 int bitPixel;
159 int imageCount = 0;
160 char version[4];
161 int imageNumber = 1;
162 Image *image = NULL;
164 if ( src == NULL ) {
165 goto done;
167 if (!ReadOK(src, buf, 6)) {
168 RWSetMsg("error reading magic number");
169 goto done;
171 if (strncmp((char *) buf, "GIF", 3) != 0) {
172 RWSetMsg("not a GIF file");
173 goto done;
175 strncpy(version, (char *) buf + 3, 3);
176 version[3] = '\0';
178 if ((strcmp(version, "87a") != 0) && (strcmp(version, "89a") != 0)) {
179 RWSetMsg("bad version number, not '87a' or '89a'");
180 goto done;
182 Gif89.transparent = -1;
183 Gif89.delayTime = -1;
184 Gif89.inputFlag = -1;
185 Gif89.disposal = 0;
187 if (!ReadOK(src, buf, 7)) {
188 RWSetMsg("failed to read screen descriptor");
189 goto done;
191 GifScreen.Width = LM_to_uint(buf[0], buf[1]);
192 GifScreen.Height = LM_to_uint(buf[2], buf[3]);
193 GifScreen.BitPixel = 2 << (buf[4] & 0x07);
194 GifScreen.ColorResolution = (((buf[4] & 0x70) >> 3) + 1);
195 GifScreen.Background = buf[5];
196 GifScreen.AspectRatio = buf[6];
198 if (BitSet(buf[4], LOCALCOLORMAP)) { /* Global Colormap */
199 if (ReadColorMap(src, GifScreen.BitPixel, GifScreen.ColorMap,
200 &GifScreen.GrayScale)) {
201 RWSetMsg("error reading global colormap");
202 goto done;
205 do {
206 if (!ReadOK(src, &c, 1)) {
207 RWSetMsg("EOF / read error on image data");
208 goto done;
210 if (c == ';') { /* GIF terminator */
211 if (imageCount < imageNumber) {
212 RWSetMsg("only %d image%s found in file",
213 imageCount, imageCount > 1 ? "s" : "");
214 goto done;
217 if (c == '!') { /* Extension */
218 if (!ReadOK(src, &c, 1)) {
219 RWSetMsg("EOF / read error on extention function code");
220 goto done;
222 DoExtension(src, c);
223 continue;
225 if (c != ',') { /* Not a valid start character */
226 continue;
228 ++imageCount;
230 if (!ReadOK(src, buf, 9)) {
231 RWSetMsg("couldn't read left/top/width/height");
232 goto done;
234 useGlobalColormap = !BitSet(buf[8], LOCALCOLORMAP);
236 bitPixel = 1 << ((buf[8] & 0x07) + 1);
238 if (!useGlobalColormap) {
239 if (ReadColorMap(src, bitPixel, localColorMap, &grayScale)) {
240 RWSetMsg("error reading local colormap");
241 goto done;
243 image = ReadImage(src, LM_to_uint(buf[4], buf[5]),
244 LM_to_uint(buf[6], buf[7]),
245 bitPixel, localColorMap, grayScale,
246 BitSet(buf[8], INTERLACE),
247 imageCount != imageNumber);
248 } else {
249 image = ReadImage(src, LM_to_uint(buf[4], buf[5]),
250 LM_to_uint(buf[6], buf[7]),
251 GifScreen.BitPixel, GifScreen.ColorMap,
252 GifScreen.GrayScale, BitSet(buf[8], INTERLACE),
253 imageCount != imageNumber);
255 } while (image == NULL);
257 #ifdef USED_BY_SDL
258 if ( Gif89.transparent >= 0 ) {
259 SDL_SetColorKey(image, SDL_SRCCOLORKEY, Gif89.transparent);
261 #endif
263 done:
264 return image;
267 static int
268 ReadColorMap(SDL_RWops *src, int number,
269 unsigned char buffer[3][MAXCOLORMAPSIZE], int *gray)
271 int i;
272 unsigned char rgb[3];
273 int flag;
275 flag = TRUE;
277 for (i = 0; i < number; ++i) {
278 if (!ReadOK(src, rgb, sizeof(rgb))) {
279 RWSetMsg("bad colormap");
280 return 1;
282 buffer[CM_RED][i] = rgb[0];
283 buffer[CM_GREEN][i] = rgb[1];
284 buffer[CM_BLUE][i] = rgb[2];
285 flag &= (rgb[0] == rgb[1] && rgb[1] == rgb[2]);
288 #if 0
289 if (flag)
290 *gray = (number == 2) ? PBM_TYPE : PGM_TYPE;
291 else
292 *gray = PPM_TYPE;
293 #else
294 *gray = 0;
295 #endif
297 return FALSE;
300 static int
301 DoExtension(SDL_RWops *src, int label)
303 static unsigned char buf[256];
304 char *str;
306 switch (label) {
307 case 0x01: /* Plain Text Extension */
308 str = "Plain Text Extension";
309 break;
310 case 0xff: /* Application Extension */
311 str = "Application Extension";
312 break;
313 case 0xfe: /* Comment Extension */
314 str = "Comment Extension";
315 while (GetDataBlock(src, (unsigned char *) buf) != 0);
316 return FALSE;
317 case 0xf9: /* Graphic Control Extension */
318 str = "Graphic Control Extension";
319 (void) GetDataBlock(src, (unsigned char *) buf);
320 Gif89.disposal = (buf[0] >> 2) & 0x7;
321 Gif89.inputFlag = (buf[0] >> 1) & 0x1;
322 Gif89.delayTime = LM_to_uint(buf[1], buf[2]);
323 if ((buf[0] & 0x1) != 0)
324 Gif89.transparent = buf[3];
326 while (GetDataBlock(src, (unsigned char *) buf) != 0);
327 return FALSE;
328 default:
329 str = (char *)buf;
330 sprintf(str, "UNKNOWN (0x%02x)", label);
331 break;
334 while (GetDataBlock(src, (unsigned char *) buf) != 0);
336 return FALSE;
339 static int ZeroDataBlock = FALSE;
341 static int
342 GetDataBlock(SDL_RWops *src, unsigned char *buf)
344 unsigned char count;
346 if (!ReadOK(src, &count, 1)) {
347 /* pm_message("error in getting DataBlock size" ); */
348 return -1;
350 ZeroDataBlock = count == 0;
352 if ((count != 0) && (!ReadOK(src, buf, count))) {
353 /* pm_message("error in reading DataBlock" ); */
354 return -1;
356 return count;
359 static int
360 GetCode(SDL_RWops *src, int code_size, int flag)
362 static unsigned char buf[280];
363 static int curbit, lastbit, done, last_byte;
364 int i, j, ret;
365 unsigned char count;
367 if (flag) {
368 curbit = 0;
369 lastbit = 0;
370 done = FALSE;
371 return 0;
373 if ((curbit + code_size) >= lastbit) {
374 if (done) {
375 if (curbit >= lastbit)
376 RWSetMsg("ran off the end of my bits");
377 return -1;
379 buf[0] = buf[last_byte - 2];
380 buf[1] = buf[last_byte - 1];
382 if ((count = GetDataBlock(src, &buf[2])) == 0)
383 done = TRUE;
385 last_byte = 2 + count;
386 curbit = (curbit - lastbit) + 16;
387 lastbit = (2 + count) * 8;
389 ret = 0;
390 for (i = curbit, j = 0; j < code_size; ++i, ++j)
391 ret |= ((buf[i / 8] & (1 << (i % 8))) != 0) << j;
393 curbit += code_size;
395 return ret;
398 static int
399 LWZReadByte(SDL_RWops *src, int flag, int input_code_size)
401 static int fresh = FALSE;
402 int code, incode;
403 static int code_size, set_code_size;
404 static int max_code, max_code_size;
405 static int firstcode, oldcode;
406 static int clear_code, end_code;
407 static int table[2][(1 << MAX_LWZ_BITS)];
408 static int stack[(1 << (MAX_LWZ_BITS)) * 2], *sp;
409 register int i;
411 if (flag) {
412 set_code_size = input_code_size;
413 code_size = set_code_size + 1;
414 clear_code = 1 << set_code_size;
415 end_code = clear_code + 1;
416 max_code_size = 2 * clear_code;
417 max_code = clear_code + 2;
419 GetCode(src, 0, TRUE);
421 fresh = TRUE;
423 for (i = 0; i < clear_code; ++i) {
424 table[0][i] = 0;
425 table[1][i] = i;
427 for (; i < (1 << MAX_LWZ_BITS); ++i)
428 table[0][i] = table[1][0] = 0;
430 sp = stack;
432 return 0;
433 } else if (fresh) {
434 fresh = FALSE;
435 do {
436 firstcode = oldcode = GetCode(src, code_size, FALSE);
437 } while (firstcode == clear_code);
438 return firstcode;
440 if (sp > stack)
441 return *--sp;
443 while ((code = GetCode(src, code_size, FALSE)) >= 0) {
444 if (code == clear_code) {
445 for (i = 0; i < clear_code; ++i) {
446 table[0][i] = 0;
447 table[1][i] = i;
449 for (; i < (1 << MAX_LWZ_BITS); ++i)
450 table[0][i] = table[1][i] = 0;
451 code_size = set_code_size + 1;
452 max_code_size = 2 * clear_code;
453 max_code = clear_code + 2;
454 sp = stack;
455 firstcode = oldcode = GetCode(src, code_size, FALSE);
456 return firstcode;
457 } else if (code == end_code) {
458 int count;
459 unsigned char buf[260];
461 if (ZeroDataBlock)
462 return -2;
464 while ((count = GetDataBlock(src, buf)) > 0);
466 if (count != 0) {
468 * pm_message("missing EOD in data stream (common occurence)");
471 return -2;
473 incode = code;
475 if (code >= max_code) {
476 *sp++ = firstcode;
477 code = oldcode;
479 while (code >= clear_code) {
480 *sp++ = table[1][code];
481 if (code == table[0][code])
482 RWSetMsg("circular table entry BIG ERROR");
483 code = table[0][code];
486 *sp++ = firstcode = table[1][code];
488 if ((code = max_code) < (1 << MAX_LWZ_BITS)) {
489 table[0][code] = oldcode;
490 table[1][code] = firstcode;
491 ++max_code;
492 if ((max_code >= max_code_size) &&
493 (max_code_size < (1 << MAX_LWZ_BITS))) {
494 max_code_size *= 2;
495 ++code_size;
498 oldcode = incode;
500 if (sp > stack)
501 return *--sp;
503 return code;
506 static Image *
507 ReadImage(SDL_RWops * src, int len, int height, int cmapSize,
508 unsigned char cmap[3][MAXCOLORMAPSIZE],
509 int gray, int interlace, int ignore)
511 Image *image;
512 unsigned char c;
513 int i, v;
514 int xpos = 0, ypos = 0, pass = 0;
517 ** Initialize the compression routines
519 if (!ReadOK(src, &c, 1)) {
520 RWSetMsg("EOF / read error on image data");
521 return NULL;
523 if (LWZReadByte(src, TRUE, c) < 0) {
524 RWSetMsg("error reading image");
525 return NULL;
528 ** If this is an "uninteresting picture" ignore it.
530 if (ignore) {
531 while (LWZReadByte(src, FALSE, c) >= 0);
532 return NULL;
534 image = ImageNewCmap(len, height, cmapSize);
536 for (i = 0; i < cmapSize; i++)
537 ImageSetCmap(image, i, cmap[CM_RED][i],
538 cmap[CM_GREEN][i], cmap[CM_BLUE][i]);
540 while ((v = LWZReadByte(src, FALSE, c)) >= 0) {
541 #ifdef USED_BY_SDL
542 ((Uint8 *)image->pixels)[xpos + ypos * image->pitch] = v;
543 #else
544 image->data[xpos + ypos * len] = v;
545 #endif
546 ++xpos;
547 if (xpos == len) {
548 xpos = 0;
549 if (interlace) {
550 switch (pass) {
551 case 0:
552 case 1:
553 ypos += 8;
554 break;
555 case 2:
556 ypos += 4;
557 break;
558 case 3:
559 ypos += 2;
560 break;
563 if (ypos >= height) {
564 ++pass;
565 switch (pass) {
566 case 1:
567 ypos = 4;
568 break;
569 case 2:
570 ypos = 2;
571 break;
572 case 3:
573 ypos = 1;
574 break;
575 default:
576 goto fini;
579 } else {
580 ++ypos;
583 if (ypos >= height)
584 break;
587 fini:
589 return image;
592 #else
594 /* See if an image is contained in a data source */
595 int IMG_isGIF(SDL_RWops *src)
597 return(0);
600 /* Load a GIF type image from an SDL datasource */
601 SDL_Surface *IMG_LoadGIF_RW(SDL_RWops *src)
603 return(NULL);
606 #endif /* LOAD_GIF */