1 /* GIF reading routines based on those in pbmplus:ppm/giftoppm.c, bearing
2 * following copyright notice:
5 /* +-------------------------------------------------------------------+ */
6 /* | Copyright 1990, David Koblas. | */
7 /* | Permission to use, copy, modify, and distribute this software | */
8 /* | and its documentation for any purpose and without fee is hereby | */
9 /* | granted, provided that the above copyright notice appear in all | */
10 /* | copies and that both that copyright notice and this permission | */
11 /* | notice appear in supporting documentation. This software is | */
12 /* | provided "as is" without express or implied warranty. | */
13 /* +-------------------------------------------------------------------+ */
16 * $ANH-Date: 1432512803 2015/05/25 00:13:23 $ $ANH-Branch: master $:$ANH-Revision: 1.5 $
23 extern long *FDECL(alloc
, (unsigned int));
26 #define PPM_ASSIGN(p, red, grn, blu) \
33 #define MAX_LWZ_BITS 12
35 #define INTERLACE 0x40
36 #define LOCALCOLORMAP 0x80
37 #define BitSet(byte, bit) (((byte) & (bit)) == (bit))
39 #define ReadOK(file, buffer, len) \
40 (fread((genericptr_t) buffer, (int) len, 1, file) != 0)
42 #define LM_to_uint(a, b) (((b) << 8) | (a))
59 } Gif89
= { -1, -1, -1, 0 };
61 int ZeroDataBlock
= FALSE
;
63 static FILE *gif_file
;
64 static int tiles_across
, tiles_down
, curr_tiles_across
, curr_tiles_down
;
66 static unsigned char input_code_size
;
68 static int FDECL(GetDataBlock
, (FILE * fd
, unsigned char *buf
));
69 static void FDECL(DoExtension
, (FILE * fd
, int label
));
70 static boolean
FDECL(ReadColorMap
, (FILE * fd
, int number
));
71 static void FDECL(read_header
, (FILE * fd
));
72 static int FDECL(GetCode
, (FILE * fd
, int code_size
, int flag
));
73 static int FDECL(LWZReadByte
, (FILE * fd
, int flag
, int input_code_size
));
74 static void FDECL(ReadInterleavedImage
, (FILE * fd
, int len
, int height
));
75 static void FDECL(ReadTileStrip
, (FILE * fd
, int len
));
77 /* These should be in gif.h, but there isn't one. */
78 boolean
FDECL(fopen_gif_file
, (const char *, const char *));
79 boolean
FDECL(read_gif_tile
, (pixel(*) [TILE_X
]));
80 int NDECL(fclose_gif_file
);
89 if (!ReadOK(fd
, &count
, 1)) {
90 Fprintf(stderr
, "error in getting DataBlock size\n");
94 ZeroDataBlock
= (count
== 0);
96 if ((count
!= 0) && (!ReadOK(fd
, buf
, count
))) {
97 Fprintf(stderr
, "error in reading DataBlock\n");
105 DoExtension(fd
, label
)
109 static char buf
[256];
113 case 0x01: /* Plain Text Extension */
114 str
= "Plain Text Extension";
116 if (GetDataBlock(fd
, (unsigned char *) buf
) == 0)
119 lpos
= LM_to_uint(buf
[0], buf
[1]);
120 tpos
= LM_to_uint(buf
[2], buf
[3]);
121 width
= LM_to_uint(buf
[4], buf
[5]);
122 height
= LM_to_uint(buf
[6], buf
[7]);
125 foreground
= buf
[10];
126 background
= buf
[11];
128 while (GetDataBlock(fd
, (unsigned char *) buf
) != 0) {
129 PPM_ASSIGN(image
[ypos
][xpos
], cmap
[CM_RED
][v
], cmap
[CM_GREEN
][v
],
138 case 0xff: /* Application Extension */
139 str
= "Application Extension";
141 case 0xfe: /* Comment Extension */
142 str
= "Comment Extension";
143 while (GetDataBlock(fd
, (unsigned char *) buf
) != 0) {
144 Fprintf(stderr
, "gif comment: %s\n", buf
);
147 case 0xf9: /* Graphic Control Extension */
148 str
= "Graphic Control Extension";
149 (void) GetDataBlock(fd
, (unsigned char *) buf
);
150 Gif89
.disposal
= (buf
[0] >> 2) & 0x7;
151 Gif89
.inputFlag
= (buf
[0] >> 1) & 0x1;
152 Gif89
.delayTime
= LM_to_uint(buf
[1], buf
[2]);
153 if ((buf
[0] & 0x1) != 0)
154 Gif89
.transparent
= buf
[3];
156 while (GetDataBlock(fd
, (unsigned char *) buf
) != 0)
161 Sprintf(buf
, "UNKNOWN (0x%02x)", label
);
165 Fprintf(stderr
, "got a '%s' extension\n", str
);
167 while (GetDataBlock(fd
, (unsigned char *) buf
) != 0)
172 ReadColorMap(fd
, number
)
177 unsigned char rgb
[3];
179 for (i
= 0; i
< number
; ++i
) {
180 if (!ReadOK(fd
, rgb
, sizeof(rgb
))) {
184 ColorMap
[CM_RED
][i
] = rgb
[0];
185 ColorMap
[CM_GREEN
][i
] = rgb
[1];
186 ColorMap
[CM_BLUE
][i
] = rgb
[2];
188 colorsinmap
= number
;
193 * Read gif header, including colormaps. We expect only one image per
194 * file, so if that image has a local colormap, overwrite the global one.
200 unsigned char buf
[16];
204 if (!ReadOK(fd
, buf
, 6)) {
205 Fprintf(stderr
, "error reading magic number\n");
209 if (strncmp((genericptr_t
) buf
, "GIF", 3) != 0) {
210 Fprintf(stderr
, "not a GIF file\n");
214 (void) strncpy(version
, (char *) buf
+ 3, 3);
217 if ((strcmp(version
, "87a") != 0) && (strcmp(version
, "89a") != 0)) {
218 Fprintf(stderr
, "bad version number, not '87a' or '89a'\n");
222 if (!ReadOK(fd
, buf
, 7)) {
223 Fprintf(stderr
, "failed to read screen descriptor\n");
227 GifScreen
.Width
= LM_to_uint(buf
[0], buf
[1]);
228 GifScreen
.Height
= LM_to_uint(buf
[2], buf
[3]);
229 GifScreen
.Colors
= 2 << (buf
[4] & 0x07);
230 GifScreen
.ColorResolution
= (((buf
[4] & 0x70) >> 3) + 1);
231 GifScreen
.Background
= buf
[5];
232 GifScreen
.AspectRatio
= buf
[6];
234 if (BitSet(buf
[4], LOCALCOLORMAP
)) { /* Global Colormap */
235 if (!ReadColorMap(fd
, GifScreen
.Colors
)) {
236 Fprintf(stderr
, "error reading global colormap\n");
241 if (GifScreen
.AspectRatio
!= 0 && GifScreen
.AspectRatio
!= 49) {
242 Fprintf(stderr
, "warning - non-square pixels\n");
246 if (!ReadOK(fd
, &c
, 1)) {
247 Fprintf(stderr
, "EOF / read error on image data\n");
251 if (c
== ';') { /* GIF terminator */
255 if (c
== '!') { /* Extension */
256 if (!ReadOK(fd
, &c
, 1)) {
258 "EOF / read error on extension function code\n");
261 DoExtension(fd
, (int) c
);
265 if (c
!= ',') { /* Not a valid start character */
266 Fprintf(stderr
, "bogus character 0x%02x, ignoring\n", (int) c
);
270 if (!ReadOK(fd
, buf
, 9)) {
271 Fprintf(stderr
, "couldn't read left/top/width/height\n");
275 if (BitSet(buf
[8], LOCALCOLORMAP
)) {
276 /* replace global color map with local */
277 GifScreen
.Colors
= 1 << ((buf
[8] & 0x07) + 1);
278 if (!ReadColorMap(fd
, GifScreen
.Colors
)) {
279 Fprintf(stderr
, "error reading local colormap\n");
283 if (GifScreen
.Width
!= LM_to_uint(buf
[4], buf
[5])) {
284 Fprintf(stderr
, "warning: widths don't match\n");
285 GifScreen
.Width
= LM_to_uint(buf
[4], buf
[5]);
287 if (GifScreen
.Height
!= LM_to_uint(buf
[6], buf
[7])) {
288 Fprintf(stderr
, "warning: heights don't match\n");
289 GifScreen
.Height
= LM_to_uint(buf
[6], buf
[7]);
291 GifScreen
.Interlace
= BitSet(buf
[8], INTERLACE
);
297 GetCode(fd
, code_size
, flag
)
302 static unsigned char buf
[280];
303 static int curbit
, lastbit
, done
, last_byte
;
314 if ((curbit
+ code_size
) >= lastbit
) {
316 if (curbit
>= lastbit
)
317 Fprintf(stderr
, "ran off the end of my bits\n");
320 buf
[0] = buf
[last_byte
- 2];
321 buf
[1] = buf
[last_byte
- 1];
323 if ((count
= GetDataBlock(fd
, &buf
[2])) == 0)
326 last_byte
= 2 + count
;
327 curbit
= (curbit
- lastbit
) + 16;
328 lastbit
= (2 + count
) * 8;
332 for (i
= curbit
, j
= 0; j
< code_size
; ++i
, ++j
)
333 ret
|= ((buf
[i
/ 8] & (1 << (i
% 8))) != 0) << j
;
341 LWZReadByte(fd
, flag
, input_code_size
)
346 static int fresh
= FALSE
;
348 static int code_size
, set_code_size
;
349 static int max_code
, max_code_size
;
350 static int firstcode
, oldcode
;
351 static int clear_code
, end_code
;
352 static int table
[2][(1 << MAX_LWZ_BITS
)];
353 static int stack
[(1 << (MAX_LWZ_BITS
)) * 2], *sp
;
357 set_code_size
= input_code_size
;
358 code_size
= set_code_size
+ 1;
359 clear_code
= 1 << set_code_size
;
360 end_code
= clear_code
+ 1;
361 max_code_size
= 2 * clear_code
;
362 max_code
= clear_code
+ 2;
364 (void) GetCode(fd
, 0, TRUE
);
368 for (i
= 0; i
< clear_code
; ++i
) {
372 for (; i
< (1 << MAX_LWZ_BITS
); ++i
)
373 table
[0][i
] = table
[1][0] = 0;
381 firstcode
= oldcode
= GetCode(fd
, code_size
, FALSE
);
382 } while (firstcode
== clear_code
);
389 while ((code
= GetCode(fd
, code_size
, FALSE
)) >= 0) {
390 if (code
== clear_code
) {
391 for (i
= 0; i
< clear_code
; ++i
) {
395 for (; i
< (1 << MAX_LWZ_BITS
); ++i
)
396 table
[0][i
] = table
[1][i
] = 0;
397 code_size
= set_code_size
+ 1;
398 max_code_size
= 2 * clear_code
;
399 max_code
= clear_code
+ 2;
401 firstcode
= oldcode
= GetCode(fd
, code_size
, FALSE
);
403 } else if (code
== end_code
) {
405 unsigned char buf
[260];
410 while ((count
= GetDataBlock(fd
, buf
)) > 0)
415 "missing EOD in data stream (common occurrence)\n");
421 if (code
>= max_code
) {
426 while (code
>= clear_code
) {
427 *sp
++ = table
[1][code
];
428 if (code
== table
[0][code
]) {
429 Fprintf(stderr
, "circular table entry BIG ERROR\n");
432 code
= table
[0][code
];
435 *sp
++ = firstcode
= table
[1][code
];
437 if ((code
= max_code
) < (1 << MAX_LWZ_BITS
)) {
438 table
[0][code
] = oldcode
;
439 table
[1][code
] = firstcode
;
441 if ((max_code
>= max_code_size
)
442 && (max_code_size
< (1 << MAX_LWZ_BITS
))) {
457 ReadInterleavedImage(fd
, len
, height
)
462 int xpos
= 0, ypos
= 0, pass
= 0;
464 while ((v
= LWZReadByte(fd
, FALSE
, (int) input_code_size
)) >= 0) {
465 PPM_ASSIGN(image
[ypos
][xpos
], ColorMap
[CM_RED
][v
],
466 ColorMap
[CM_GREEN
][v
], ColorMap
[CM_BLUE
][v
]);
484 if (ypos
>= height
) {
506 if (LWZReadByte(fd
, FALSE
, (int) input_code_size
) >= 0)
507 Fprintf(stderr
, "too much input data, ignoring extra...\n");
511 ReadTileStrip(fd
, len
)
516 int xpos
= 0, ypos
= 0;
518 while ((v
= LWZReadByte(fd
, FALSE
, (int) input_code_size
)) >= 0) {
519 PPM_ASSIGN(image
[ypos
][xpos
], ColorMap
[CM_RED
][v
],
520 ColorMap
[CM_GREEN
][v
], ColorMap
[CM_BLUE
][v
]);
533 fopen_gif_file(filename
, type
)
534 const char *filename
;
539 if (strcmp(type
, RDBMODE
)) {
540 Fprintf(stderr
, "using reading routine for non-reading?\n");
543 gif_file
= fopen(filename
, type
);
544 if (gif_file
== (FILE *) 0) {
545 Fprintf(stderr
, "cannot open gif file %s\n", filename
);
549 read_header(gif_file
);
550 if (GifScreen
.Width
% TILE_X
) {
551 Fprintf(stderr
, "error: width %d not divisible by %d\n",
552 GifScreen
.Width
, TILE_X
);
555 tiles_across
= GifScreen
.Width
/ TILE_X
;
556 curr_tiles_across
= 0;
557 if (GifScreen
.Height
% TILE_Y
) {
558 Fprintf(stderr
, "error: height %d not divisible by %d\n",
559 GifScreen
.Height
, TILE_Y
);
560 /* exit(EXIT_FAILURE) */;
562 tiles_down
= GifScreen
.Height
/ TILE_Y
;
565 if (GifScreen
.Interlace
) {
566 /* sigh -- hope this doesn't happen on micros */
567 image
= (pixel
**) alloc(GifScreen
.Height
* sizeof(pixel
*));
568 for (i
= 0; i
< GifScreen
.Height
; i
++) {
569 image
[i
] = (pixel
*) alloc(GifScreen
.Width
* sizeof(pixel
));
572 image
= (pixel
**) alloc(TILE_Y
* sizeof(pixel
*));
573 for (i
= 0; i
< TILE_Y
; i
++) {
574 image
[i
] = (pixel
*) alloc(GifScreen
.Width
* sizeof(pixel
));
579 ** Initialize the Compression routines
581 if (!ReadOK(gif_file
, &input_code_size
, 1)) {
582 Fprintf(stderr
, "EOF / read error on image data\n");
586 if (LWZReadByte(gif_file
, TRUE
, (int) input_code_size
) < 0) {
587 Fprintf(stderr
, "error reading image\n");
591 /* read first section */
592 if (GifScreen
.Interlace
) {
593 ReadInterleavedImage(gif_file
, GifScreen
.Width
, GifScreen
.Height
);
595 ReadTileStrip(gif_file
, GifScreen
.Width
);
600 /* Read a tile. Returns FALSE when there are no more tiles */
602 read_gif_tile(pixels
)
603 pixel (*pixels
)[TILE_X
];
607 if (curr_tiles_down
>= tiles_down
)
609 if (curr_tiles_across
== tiles_across
) {
610 curr_tiles_across
= 0;
612 if (curr_tiles_down
>= tiles_down
)
614 if (!GifScreen
.Interlace
)
615 ReadTileStrip(gif_file
, GifScreen
.Width
);
617 if (GifScreen
.Interlace
) {
618 for (j
= 0; j
< TILE_Y
; j
++) {
619 for (i
= 0; i
< TILE_X
; i
++) {
620 pixels
[j
][i
] = image
[curr_tiles_down
* TILE_Y
621 + j
][curr_tiles_across
* TILE_X
+ i
];
625 for (j
= 0; j
< TILE_Y
; j
++) {
626 for (i
= 0; i
< TILE_X
; i
++) {
627 pixels
[j
][i
] = image
[j
][curr_tiles_across
* TILE_X
+ i
];
633 /* check for "filler" tile */
634 for (j
= 0; j
< TILE_Y
; j
++) {
635 for (i
= 0; i
< TILE_X
&& i
< 4; i
+= 2) {
636 if (pixels
[j
][i
].r
!= ColorMap
[CM_RED
][0]
637 || pixels
[j
][i
].g
!= ColorMap
[CM_GREEN
][0]
638 || pixels
[j
][i
].b
!= ColorMap
[CM_BLUE
][0]
639 || pixels
[j
][i
+ 1].r
!= ColorMap
[CM_RED
][1]
640 || pixels
[j
][i
+ 1].g
!= ColorMap
[CM_GREEN
][1]
641 || pixels
[j
][i
+ 1].b
!= ColorMap
[CM_BLUE
][1])
653 if (GifScreen
.Interlace
) {
654 for (i
= 0; i
< GifScreen
.Height
; i
++) {
655 free((genericptr_t
) image
[i
]);
657 free((genericptr_t
) image
);
659 for (i
= 0; i
< TILE_Y
; i
++) {
660 free((genericptr_t
) image
[i
]);
662 free((genericptr_t
) image
);
664 return (fclose(gif_file
));
668 static char *std_args
[] = { "tilemap", /* dummy argv[0] */
669 "monsters.gif", "monsters.txt", "objects.gif",
670 "objects.txt", "other.gif", "other.txt" };
677 pixel pixels
[TILE_Y
][TILE_X
];
680 argc
= SIZE(std_args
);
682 } else if (argc
!= 3) {
683 Fprintf(stderr
, "usage: gif2txt giffile txtfile\n");
688 if (!fopen_gif_file(argv
[1], RDBMODE
))
693 if (!fopen_text_file(argv
[2], WRTMODE
)) {
694 (void) fclose_gif_file();
698 while (read_gif_tile(pixels
))
699 (void) write_text_tile(pixels
);
701 (void) fclose_gif_file();
702 (void) fclose_text_file();