1 /*
2 SDL_image: An example image loading library for use with SDL
3 Copyright (C) 1997-2019 Sam Lantinga <slouken@libsdl.org>
4
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.
8
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:
12
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.
20 */
21
22 #if !defined(__APPLE__) || defined(SDL_IMAGE_USE_COMMON_BACKEND)
23
24 /* This is a GIF image file loading framework */
25
26 #include "SDL_image.h"
27
28 #ifdef LOAD_GIF
29
30 /* See if an image is contained in a data source */
IMG_isGIF(SDL_RWops * src)31 int IMG_isGIF(SDL_RWops *src)
32 {
33 Sint64 start;
34 int is_GIF;
35 char magic[6];
36
37 if ( !src )
38 return 0;
39 start = SDL_RWtell(src);
40 is_GIF = 0;
41 if ( SDL_RWread(src, magic, sizeof(magic), 1) ) {
42 if ( (SDL_strncmp(magic, "GIF", 3) == 0) &&
43 ((SDL_memcmp(magic + 3, "87a", 3) == 0) ||
44 (SDL_memcmp(magic + 3, "89a", 3) == 0)) ) {
45 is_GIF = 1;
46 }
47 }
48 SDL_RWseek(src, start, RW_SEEK_SET);
49 return(is_GIF);
50 }
51
52 /* Code from here to end of file has been adapted from XPaint: */
53 /* +-------------------------------------------------------------------+ */
54 /* | Copyright 1990, 1991, 1993 David Koblas. | */
55 /* | Copyright 1996 Torsten Martinsen. | */
56 /* | Permission to use, copy, modify, and distribute this software | */
57 /* | and its documentation for any purpose and without fee is hereby | */
58 /* | granted, provided that the above copyright notice appear in all | */
59 /* | copies and that both that copyright notice and this permission | */
60 /* | notice appear in supporting documentation. This software is | */
61 /* | provided "as is" without express or implied warranty. | */
62 /* +-------------------------------------------------------------------+ */
63
64 /* Adapted for use in SDL by Sam Lantinga -- 7/20/98 */
65 #define USED_BY_SDL
66
67 #include <stdio.h>
68 #include <string.h>
69
70 #ifdef USED_BY_SDL
71 /* Changes to work with SDL:
72
73 Include SDL header file
74 Use SDL_Surface rather than xpaint Image structure
75 Define SDL versions of RWSetMsg(), ImageNewCmap() and ImageSetCmap()
76 */
77 #include "SDL.h"
78
79 #define Image SDL_Surface
80 #define RWSetMsg IMG_SetError
81 #define ImageNewCmap(w, h, s) SDL_CreateRGBSurface(SDL_SWSURFACE,w,h,8,0,0,0,0)
82 #define ImageSetCmap(s, i, R, G, B) do { \
83 s->format->palette->colors[i].r = R; \
84 s->format->palette->colors[i].g = G; \
85 s->format->palette->colors[i].b = B; \
86 } while (0)
87 /* * * * * */
88
89 #else
90
91 /* Original XPaint sources */
92
93 #include "image.h"
94 #include "rwTable.h"
95
96 #define SDL_RWops FILE
97 #define SDL_RWclose fclose
98
99 #endif /* USED_BY_SDL */
100
101
102 #define MAXCOLORMAPSIZE 256
103
104 #define TRUE 1
105 #define FALSE 0
106
107 #define CM_RED 0
108 #define CM_GREEN 1
109 #define CM_BLUE 2
110
111 #define MAX_LWZ_BITS 12
112
113 #define INTERLACE 0x40
114 #define LOCALCOLORMAP 0x80
115 #define BitSet(byte, bit) (((byte) & (bit)) == (bit))
116
117 #define ReadOK(file,buffer,len) SDL_RWread(file, buffer, len, 1)
118
119 #define LM_to_uint(a,b) (((b)<<8)|(a))
120
121 typedef struct {
122 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;
132
133 struct {
134 int transparent;
135 int delayTime;
136 int inputFlag;
137 int disposal;
138 } Gif89;
139
140 unsigned char buf[280];
141 int curbit, lastbit, done, last_byte;
142
143 int fresh;
144 int code_size, set_code_size;
145 int max_code, max_code_size;
146 int firstcode, oldcode;
147 int clear_code, end_code;
148 int table[2][(1 << MAX_LWZ_BITS)];
149 int stack[(1 << (MAX_LWZ_BITS)) * 2], *sp;
150
151 int ZeroDataBlock;
152 } State_t;
153
154 static int ReadColorMap(SDL_RWops * src, int number,
155 unsigned char buffer[3][MAXCOLORMAPSIZE], int *flag);
156 static int DoExtension(SDL_RWops * src, int label, State_t * state);
157 static int GetDataBlock(SDL_RWops * src, unsigned char *buf, State_t * state);
158 static int GetCode(SDL_RWops * src, int code_size, int flag, State_t * state);
159 static int LWZReadByte(SDL_RWops * src, int flag, int input_code_size, State_t * state);
160 static Image *ReadImage(SDL_RWops * src, int len, int height, int,
161 unsigned char cmap[3][MAXCOLORMAPSIZE],
162 int gray, int interlace, int ignore, State_t * state);
163
164 Image *
IMG_LoadGIF_RW(SDL_RWops * src)165 IMG_LoadGIF_RW(SDL_RWops *src)
166 {
167 Sint64 start;
168 unsigned char buf[16];
169 unsigned char c;
170 unsigned char localColorMap[3][MAXCOLORMAPSIZE];
171 int grayScale;
172 int useGlobalColormap;
173 int bitPixel;
174 int imageCount = 0;
175 char version[4];
176 int imageNumber = 1;
177 Image *image = NULL;
178 State_t state;
179 state.ZeroDataBlock = FALSE;
180 state.fresh = FALSE;
181 state.last_byte = 0;
182
183 if ( src == NULL ) {
184 return NULL;
185 }
186 start = SDL_RWtell(src);
187
188 if (!ReadOK(src, buf, 6)) {
189 RWSetMsg("error reading magic number");
190 goto done;
191 }
192 if (SDL_strncmp((char *) buf, "GIF", 3) != 0) {
193 RWSetMsg("not a GIF file");
194 goto done;
195 }
196 SDL_memcpy(version, (char *) buf + 3, 3);
197 version[3] = '\0';
198
199 if ((SDL_strcmp(version, "87a") != 0) && (SDL_strcmp(version, "89a") != 0)) {
200 RWSetMsg("bad version number, not '87a' or '89a'");
201 goto done;
202 }
203 state.Gif89.transparent = -1;
204 state.Gif89.delayTime = -1;
205 state.Gif89.inputFlag = -1;
206 state.Gif89.disposal = 0;
207
208 if (!ReadOK(src, buf, 7)) {
209 RWSetMsg("failed to read screen descriptor");
210 goto done;
211 }
212 state.GifScreen.Width = LM_to_uint(buf[0], buf[1]);
213 state.GifScreen.Height = LM_to_uint(buf[2], buf[3]);
214 state.GifScreen.BitPixel = 2 << (buf[4] & 0x07);
215 state.GifScreen.ColorResolution = (((buf[4] & 0x70) >> 3) + 1);
216 state.GifScreen.Background = buf[5];
217 state.GifScreen.AspectRatio = buf[6];
218
219 if (BitSet(buf[4], LOCALCOLORMAP)) { /* Global Colormap */
220 if (ReadColorMap(src, state.GifScreen.BitPixel,
221 state.GifScreen.ColorMap, &state.GifScreen.GrayScale)) {
222 RWSetMsg("error reading global colormap");
223 goto done;
224 }
225 }
226 do {
227 if (!ReadOK(src, &c, 1)) {
228 RWSetMsg("EOF / read error on image data");
229 goto done;
230 }
231 if (c == ';') { /* GIF terminator */
232 if (imageCount < imageNumber) {
233 RWSetMsg("only %d image%s found in file",
234 imageCount, imageCount > 1 ? "s" : "");
235 goto done;
236 }
237 }
238 if (c == '!') { /* Extension */
239 if (!ReadOK(src, &c, 1)) {
240 RWSetMsg("EOF / read error on extention function code");
241 goto done;
242 }
243 DoExtension(src, c, &state);
244 continue;
245 }
246 if (c != ',') { /* Not a valid start character */
247 continue;
248 }
249 ++imageCount;
250
251 if (!ReadOK(src, buf, 9)) {
252 RWSetMsg("couldn't read left/top/width/height");
253 goto done;
254 }
255 useGlobalColormap = !BitSet(buf[8], LOCALCOLORMAP);
256
257 bitPixel = 1 << ((buf[8] & 0x07) + 1);
258
259 if (!useGlobalColormap) {
260 if (ReadColorMap(src, bitPixel, localColorMap, &grayScale)) {
261 RWSetMsg("error reading local colormap");
262 goto done;
263 }
264 image = ReadImage(src, LM_to_uint(buf[4], buf[5]),
265 LM_to_uint(buf[6], buf[7]),
266 bitPixel, localColorMap, grayScale,
267 BitSet(buf[8], INTERLACE),
268 imageCount != imageNumber, &state);
269 } else {
270 image = ReadImage(src, LM_to_uint(buf[4], buf[5]),
271 LM_to_uint(buf[6], buf[7]),
272 state.GifScreen.BitPixel, state.GifScreen.ColorMap,
273 state.GifScreen.GrayScale, BitSet(buf[8], INTERLACE),
274 imageCount != imageNumber, &state);
275 }
276 } while (image == NULL);
277
278 #ifdef USED_BY_SDL
279 if ( state.Gif89.transparent >= 0 ) {
280 SDL_SetColorKey(image, SDL_TRUE, state.Gif89.transparent);
281 }
282 #endif
283
284 done:
285 if ( image == NULL ) {
286 SDL_RWseek(src, start, RW_SEEK_SET);
287 }
288 return image;
289 }
290
291 static int
ReadColorMap(SDL_RWops * src,int number,unsigned char buffer[3][MAXCOLORMAPSIZE],int * gray)292 ReadColorMap(SDL_RWops *src, int number,
293 unsigned char buffer[3][MAXCOLORMAPSIZE], int *gray)
294 {
295 int i;
296 unsigned char rgb[3];
297 int flag;
298
299 flag = TRUE;
300
301 for (i = 0; i < number; ++i) {
302 if (!ReadOK(src, rgb, sizeof(rgb))) {
303 RWSetMsg("bad colormap");
304 return 1;
305 }
306 buffer[CM_RED][i] = rgb[0];
307 buffer[CM_GREEN][i] = rgb[1];
308 buffer[CM_BLUE][i] = rgb[2];
309 flag &= (rgb[0] == rgb[1] && rgb[1] == rgb[2]);
310 }
311
312 #if 0
313 if (flag)
314 *gray = (number == 2) ? PBM_TYPE : PGM_TYPE;
315 else
316 *gray = PPM_TYPE;
317 #else
318 *gray = 0;
319 #endif
320
321 return FALSE;
322 }
323
324 static int
DoExtension(SDL_RWops * src,int label,State_t * state)325 DoExtension(SDL_RWops *src, int label, State_t * state)
326 {
327 unsigned char buf[256];
328 char *str;
329
330 switch (label) {
331 case 0x01: /* Plain Text Extension */
332 str = "Plain Text Extension";
333 break;
334 case 0xff: /* Application Extension */
335 str = "Application Extension";
336 break;
337 case 0xfe: /* Comment Extension */
338 str = "Comment Extension";
339 while (GetDataBlock(src, (unsigned char *) buf, state) > 0)
340 ;
341 return FALSE;
342 case 0xf9: /* Graphic Control Extension */
343 str = "Graphic Control Extension";
344 (void) GetDataBlock(src, (unsigned char *) buf, state);
345 state->Gif89.disposal = (buf[0] >> 2) & 0x7;
346 state->Gif89.inputFlag = (buf[0] >> 1) & 0x1;
347 state->Gif89.delayTime = LM_to_uint(buf[1], buf[2]);
348 if ((buf[0] & 0x1) != 0)
349 state->Gif89.transparent = buf[3];
350
351 while (GetDataBlock(src, (unsigned char *) buf, state) > 0)
352 ;
353 return FALSE;
354 default:
355 str = (char *)buf;
356 SDL_snprintf(str, 256, "UNKNOWN (0x%02x)", label);
357 break;
358 }
359
360 while (GetDataBlock(src, (unsigned char *) buf, state) > 0)
361 ;
362
363 return FALSE;
364 }
365
366 static int
GetDataBlock(SDL_RWops * src,unsigned char * buf,State_t * state)367 GetDataBlock(SDL_RWops *src, unsigned char *buf, State_t * state)
368 {
369 unsigned char count;
370
371 if (!ReadOK(src, &count, 1)) {
372 /* pm_message("error in getting DataBlock size" ); */
373 return -1;
374 }
375 state->ZeroDataBlock = count == 0;
376
377 if ((count != 0) && (!ReadOK(src, buf, count))) {
378 /* pm_message("error in reading DataBlock" ); */
379 return -1;
380 }
381 return count;
382 }
383
384 static int
GetCode(SDL_RWops * src,int code_size,int flag,State_t * state)385 GetCode(SDL_RWops *src, int code_size, int flag, State_t * state)
386 {
387 int i, j, ret;
388 unsigned char count;
389
390 if (flag) {
391 state->curbit = 0;
392 state->lastbit = 0;
393 state->done = FALSE;
394 return 0;
395 }
396 if ((state->curbit + code_size) >= state->lastbit) {
397 if (state->done) {
398 if (state->curbit >= state->lastbit)
399 RWSetMsg("ran off the end of my bits");
400 return -1;
401 }
402 state->buf[0] = state->buf[state->last_byte - 2];
403 state->buf[1] = state->buf[state->last_byte - 1];
404
405 if ((count = GetDataBlock(src, &state->buf[2], state)) <= 0)
406 state->done = TRUE;
407
408 state->last_byte = 2 + count;
409 state->curbit = (state->curbit - state->lastbit) + 16;
410 state->lastbit = (2 + count) * 8;
411 }
412 ret = 0;
413 for (i = state->curbit, j = 0; j < code_size; ++i, ++j)
414 ret |= ((state->buf[i / 8] & (1 << (i % 8))) != 0) << j;
415
416 state->curbit += code_size;
417
418 return ret;
419 }
420
421 static int
LWZReadByte(SDL_RWops * src,int flag,int input_code_size,State_t * state)422 LWZReadByte(SDL_RWops *src, int flag, int input_code_size, State_t * state)
423 {
424 int code, incode;
425 register int i;
426
427 /* Fixed buffer overflow found by Michael Skladnikiewicz */
428 if (input_code_size > MAX_LWZ_BITS)
429 return -1;
430
431 if (flag) {
432 state->set_code_size = input_code_size;
433 state->code_size = state->set_code_size + 1;
434 state->clear_code = 1 << state->set_code_size;
435 state->end_code = state->clear_code + 1;
436 state->max_code_size = 2 * state->clear_code;
437 state->max_code = state->clear_code + 2;
438
439 GetCode(src, 0, TRUE, state);
440
441 state->fresh = TRUE;
442
443 for (i = 0; i < state->clear_code; ++i) {
444 state->table[0][i] = 0;
445 state->table[1][i] = i;
446 }
447 state->table[1][0] = 0;
448 for (; i < (1 << MAX_LWZ_BITS); ++i)
449 state->table[0][i] = 0;
450
451 state->sp = state->stack;
452
453 return 0;
454 } else if (state->fresh) {
455 state->fresh = FALSE;
456 do {
457 state->firstcode = state->oldcode = GetCode(src, state->code_size, FALSE, state);
458 } while (state->firstcode == state->clear_code);
459 return state->firstcode;
460 }
461 if (state->sp > state->stack)
462 return *--state->sp;
463
464 while ((code = GetCode(src, state->code_size, FALSE, state)) >= 0) {
465 if (code == state->clear_code) {
466 for (i = 0; i < state->clear_code; ++i) {
467 state->table[0][i] = 0;
468 state->table[1][i] = i;
469 }
470 for (; i < (1 << MAX_LWZ_BITS); ++i)
471 state->table[0][i] = state->table[1][i] = 0;
472 state->code_size = state->set_code_size + 1;
473 state->max_code_size = 2 * state->clear_code;
474 state->max_code = state->clear_code + 2;
475 state->sp = state->stack;
476 state->firstcode = state->oldcode = GetCode(src, state->code_size, FALSE, state);
477 return state->firstcode;
478 } else if (code == state->end_code) {
479 int count;
480 unsigned char buf[260];
481
482 if (state->ZeroDataBlock)
483 return -2;
484
485 while ((count = GetDataBlock(src, buf, state)) > 0)
486 ;
487
488 if (count != 0) {
489 /*
490 * pm_message("missing EOD in data stream (common occurence)");
491 */
492 }
493 return -2;
494 }
495 incode = code;
496
497 if (code >= state->max_code) {
498 *state->sp++ = state->firstcode;
499 code = state->oldcode;
500 }
501 while (code >= state->clear_code) {
502 /* Guard against buffer overruns */
503 if (code < 0 || code >= (1 << MAX_LWZ_BITS)) {
504 RWSetMsg("invalid LWZ data");
505 return -3;
506 }
507 *state->sp++ = state->table[1][code];
508 if (code == state->table[0][code]) {
509 RWSetMsg("circular table entry BIG ERROR");
510 return -3;
511 }
512 code = state->table[0][code];
513 }
514
515 /* Guard against buffer overruns */
516 if (code < 0 || code >= (1 << MAX_LWZ_BITS)) {
517 RWSetMsg("invalid LWZ data");
518 return -4;
519 }
520 *state->sp++ = state->firstcode = state->table[1][code];
521
522 if ((code = state->max_code) < (1 << MAX_LWZ_BITS)) {
523 state->table[0][code] = state->oldcode;
524 state->table[1][code] = state->firstcode;
525 ++state->max_code;
526 if ((state->max_code >= state->max_code_size) &&
527 (state->max_code_size < (1 << MAX_LWZ_BITS))) {
528 state->max_code_size *= 2;
529 ++state->code_size;
530 }
531 }
532 state->oldcode = incode;
533
534 if (state->sp > state->stack)
535 return *--state->sp;
536 }
537 return code;
538 }
539
540 static Image *
ReadImage(SDL_RWops * src,int len,int height,int cmapSize,unsigned char cmap[3][MAXCOLORMAPSIZE],int gray,int interlace,int ignore,State_t * state)541 ReadImage(SDL_RWops * src, int len, int height, int cmapSize,
542 unsigned char cmap[3][MAXCOLORMAPSIZE],
543 int gray, int interlace, int ignore, State_t * state)
544 {
545 Image *image;
546 unsigned char c;
547 int i, v;
548 int xpos = 0, ypos = 0, pass = 0;
549
550 /*
551 ** Initialize the compression routines
552 */
553 if (!ReadOK(src, &c, 1)) {
554 RWSetMsg("EOF / read error on image data");
555 return NULL;
556 }
557 if (LWZReadByte(src, TRUE, c, state) < 0) {
558 RWSetMsg("error reading image");
559 return NULL;
560 }
561 /*
562 ** If this is an "uninteresting picture" ignore it.
563 */
564 if (ignore) {
565 while (LWZReadByte(src, FALSE, c, state) >= 0)
566 ;
567 return NULL;
568 }
569 image = ImageNewCmap(len, height, cmapSize);
570
571 for (i = 0; i < cmapSize; i++)
572 ImageSetCmap(image, i, cmap[CM_RED][i],
573 cmap[CM_GREEN][i], cmap[CM_BLUE][i]);
574
575 while ((v = LWZReadByte(src, FALSE, c, state)) >= 0) {
576 #ifdef USED_BY_SDL
577 ((Uint8 *)image->pixels)[xpos + ypos * image->pitch] = v;
578 #else
579 image->data[xpos + ypos * len] = v;
580 #endif
581 ++xpos;
582 if (xpos == len) {
583 xpos = 0;
584 if (interlace) {
585 switch (pass) {
586 case 0:
587 case 1:
588 ypos += 8;
589 break;
590 case 2:
591 ypos += 4;
592 break;
593 case 3:
594 ypos += 2;
595 break;
596 }
597
598 if (ypos >= height) {
599 ++pass;
600 switch (pass) {
601 case 1:
602 ypos = 4;
603 break;
604 case 2:
605 ypos = 2;
606 break;
607 case 3:
608 ypos = 1;
609 break;
610 default:
611 goto fini;
612 }
613 }
614 } else {
615 ++ypos;
616 }
617 }
618 if (ypos >= height)
619 break;
620 }
621
622 fini:
623
624 return image;
625 }
626
627 #else
628
629 /* See if an image is contained in a data source */
IMG_isGIF(SDL_RWops * src)630 int IMG_isGIF(SDL_RWops *src)
631 {
632 return(0);
633 }
634
635 /* Load a GIF type image from an SDL datasource */
IMG_LoadGIF_RW(SDL_RWops * src)636 SDL_Surface *IMG_LoadGIF_RW(SDL_RWops *src)
637 {
638 return(NULL);
639 }
640
641 #endif /* LOAD_GIF */
642
643 #endif /* !defined(__APPLE__) || defined(SDL_IMAGE_USE_COMMON_BACKEND) */
644