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