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 * $NHDT-Date: 1432512803 2015/05/25 00:13:23 $ $NHDT-Branch: master $:$NHDT-Revision: 1.5 $
23 extern long *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 GetDataBlock(FILE * fd
, unsigned char *buf
);
69 static void DoExtension(FILE * fd
, int label
);
70 static boolean
ReadColorMap(FILE * fd
, int number
);
71 static void read_header(FILE * fd
);
72 static int GetCode(FILE * fd
, int code_size
, int flag
);
73 static int LWZReadByte(FILE * fd
, int flag
); /*, int input_code_size);*/
74 static void ReadInterleavedImage(FILE * fd
, int len
, int height
);
75 static void ReadTileStrip(FILE * fd
, int len
);
77 /* These should be in gif.h, but there isn't one. */
78 boolean
fopen_gif_file(const char *, const char *);
79 boolean
read_gif_tile(pixel(*) [TILE_X
]);
80 int fclose_gif_file(void);
83 GetDataBlock(FILE *fd
, unsigned char *buf
)
87 if (!ReadOK(fd
, &count
, 1)) {
88 Fprintf(stderr
, "error in getting DataBlock size\n");
92 ZeroDataBlock
= (count
== 0);
94 if ((count
!= 0) && (!ReadOK(fd
, buf
, count
))) {
95 Fprintf(stderr
, "error in reading DataBlock\n");
103 DoExtension(FILE *fd
, int label
)
105 static char buf
[256];
109 case 0x01: /* Plain Text Extension */
110 str
= "Plain Text Extension";
112 if (GetDataBlock(fd
, (unsigned char *) buf
) == 0)
115 lpos
= LM_to_uint(buf
[0], buf
[1]);
116 tpos
= LM_to_uint(buf
[2], buf
[3]);
117 width
= LM_to_uint(buf
[4], buf
[5]);
118 height
= LM_to_uint(buf
[6], buf
[7]);
121 foreground
= buf
[10];
122 background
= buf
[11];
124 while (GetDataBlock(fd
, (unsigned char *) buf
) != 0) {
125 PPM_ASSIGN(image
[ypos
][xpos
], cmap
[CM_RED
][v
], cmap
[CM_GREEN
][v
],
134 case 0xff: /* Application Extension */
135 str
= "Application Extension";
137 case 0xfe: /* Comment Extension */
138 str
= "Comment Extension";
139 while (GetDataBlock(fd
, (unsigned char *) buf
) != 0) {
140 Fprintf(stderr
, "gif comment: %s\n", buf
);
143 case 0xf9: /* Graphic Control Extension */
144 str
= "Graphic Control Extension";
145 (void) GetDataBlock(fd
, (unsigned char *) buf
);
146 Gif89
.disposal
= (buf
[0] >> 2) & 0x7;
147 Gif89
.inputFlag
= (buf
[0] >> 1) & 0x1;
148 Gif89
.delayTime
= LM_to_uint(buf
[1], buf
[2]);
149 if ((buf
[0] & 0x1) != 0)
150 Gif89
.transparent
= buf
[3];
152 while (GetDataBlock(fd
, (unsigned char *) buf
) != 0)
157 Sprintf(buf
, "UNKNOWN (0x%02x)", label
);
161 Fprintf(stderr
, "got a '%s' extension\n", str
);
163 while (GetDataBlock(fd
, (unsigned char *) buf
) != 0)
168 ReadColorMap(FILE *fd
, int number
)
171 unsigned char rgb
[3];
173 for (i
= 0; i
< number
; ++i
) {
174 if (!ReadOK(fd
, rgb
, sizeof(rgb
))) {
178 ColorMap
[CM_RED
][i
] = rgb
[0];
179 ColorMap
[CM_GREEN
][i
] = rgb
[1];
180 ColorMap
[CM_BLUE
][i
] = rgb
[2];
182 colorsinmap
= number
;
187 * Read gif header, including colormaps. We expect only one image per
188 * file, so if that image has a local colormap, overwrite the global one.
191 read_header(FILE *fd
)
193 unsigned char buf
[16];
197 if (!ReadOK(fd
, buf
, 6)) {
198 Fprintf(stderr
, "error reading magic number\n");
202 if (strncmp((genericptr_t
) buf
, "GIF", 3) != 0) {
203 Fprintf(stderr
, "not a GIF file\n");
207 (void) strncpy(version
, (char *) buf
+ 3, 3);
210 if ((strcmp(version
, "87a") != 0) && (strcmp(version
, "89a") != 0)) {
211 Fprintf(stderr
, "bad version number, not '87a' or '89a'\n");
215 if (!ReadOK(fd
, buf
, 7)) {
216 Fprintf(stderr
, "failed to read screen descriptor\n");
220 GifScreen
.Width
= LM_to_uint(buf
[0], buf
[1]);
221 GifScreen
.Height
= LM_to_uint(buf
[2], buf
[3]);
222 GifScreen
.Colors
= 2 << (buf
[4] & 0x07);
223 GifScreen
.ColorResolution
= (((buf
[4] & 0x70) >> 3) + 1);
224 GifScreen
.Background
= buf
[5];
225 GifScreen
.AspectRatio
= buf
[6];
227 if (BitSet(buf
[4], LOCALCOLORMAP
)) { /* Global Colormap */
228 if (!ReadColorMap(fd
, GifScreen
.Colors
)) {
229 Fprintf(stderr
, "error reading global colormap\n");
234 if (GifScreen
.AspectRatio
!= 0 && GifScreen
.AspectRatio
!= 49) {
235 Fprintf(stderr
, "warning - non-square pixels\n");
239 if (!ReadOK(fd
, &c
, 1)) {
240 Fprintf(stderr
, "EOF / read error on image data\n");
244 if (c
== ';') { /* GIF terminator */
248 if (c
== '!') { /* Extension */
249 if (!ReadOK(fd
, &c
, 1)) {
251 "EOF / read error on extension function code\n");
254 DoExtension(fd
, (int) c
);
258 if (c
!= ',') { /* Not a valid start character */
259 Fprintf(stderr
, "bogus character 0x%02x, ignoring\n", (int) c
);
263 if (!ReadOK(fd
, buf
, 9)) {
264 Fprintf(stderr
, "couldn't read left/top/width/height\n");
268 if (BitSet(buf
[8], LOCALCOLORMAP
)) {
269 /* replace global color map with local */
270 GifScreen
.Colors
= 1 << ((buf
[8] & 0x07) + 1);
271 if (!ReadColorMap(fd
, GifScreen
.Colors
)) {
272 Fprintf(stderr
, "error reading local colormap\n");
276 if (GifScreen
.Width
!= LM_to_uint(buf
[4], buf
[5])) {
277 Fprintf(stderr
, "warning: widths don't match\n");
278 GifScreen
.Width
= LM_to_uint(buf
[4], buf
[5]);
280 if (GifScreen
.Height
!= LM_to_uint(buf
[6], buf
[7])) {
281 Fprintf(stderr
, "warning: heights don't match\n");
282 GifScreen
.Height
= LM_to_uint(buf
[6], buf
[7]);
284 GifScreen
.Interlace
= BitSet(buf
[8], INTERLACE
);
290 GetCode(FILE *fd
, int code_size
, int flag
)
292 static unsigned char buf
[280];
293 static int curbit
, lastbit
, done
, last_byte
;
304 if ((curbit
+ code_size
) >= lastbit
) {
306 if (curbit
>= lastbit
)
307 Fprintf(stderr
, "ran off the end of my bits\n");
310 buf
[0] = buf
[last_byte
- 2];
311 buf
[1] = buf
[last_byte
- 1];
313 if ((count
= GetDataBlock(fd
, &buf
[2])) == 0)
316 last_byte
= 2 + count
;
317 curbit
= (curbit
- lastbit
) + 16;
318 lastbit
= (2 + count
) * 8;
322 for (i
= curbit
, j
= 0; j
< code_size
; ++i
, ++j
)
323 ret
|= ((buf
[i
/ 8] & (1 << (i
% 8))) != 0) << j
;
331 LWZReadByte(FILE *fd
, int flag
) /*, int input_code_size)*/
333 static int fresh
= FALSE
;
335 static int code_size
, set_code_size
;
336 static int max_code
, max_code_size
;
337 static int firstcode
, oldcode
;
338 static int clear_code
, end_code
;
339 static int table
[2][(1 << MAX_LWZ_BITS
)];
340 static int stack
[(1 << (MAX_LWZ_BITS
)) * 2], *sp
;
344 set_code_size
= input_code_size
;
345 code_size
= set_code_size
+ 1;
346 clear_code
= 1 << set_code_size
;
347 end_code
= clear_code
+ 1;
348 max_code_size
= 2 * clear_code
;
349 max_code
= clear_code
+ 2;
351 (void) GetCode(fd
, 0, TRUE
);
355 for (i
= 0; i
< clear_code
; ++i
) {
359 for (; i
< (1 << MAX_LWZ_BITS
); ++i
)
360 table
[0][i
] = table
[1][0] = 0;
368 firstcode
= oldcode
= GetCode(fd
, code_size
, FALSE
);
369 } while (firstcode
== clear_code
);
376 while ((code
= GetCode(fd
, code_size
, FALSE
)) >= 0) {
377 if (code
== clear_code
) {
378 for (i
= 0; i
< clear_code
; ++i
) {
382 for (; i
< (1 << MAX_LWZ_BITS
); ++i
)
383 table
[0][i
] = table
[1][i
] = 0;
384 code_size
= set_code_size
+ 1;
385 max_code_size
= 2 * clear_code
;
386 max_code
= clear_code
+ 2;
388 firstcode
= oldcode
= GetCode(fd
, code_size
, FALSE
);
390 } else if (code
== end_code
) {
392 unsigned char buf
[260];
397 while ((count
= GetDataBlock(fd
, buf
)) > 0)
402 "missing EOD in data stream (common occurrence)\n");
408 if (code
>= max_code
) {
413 while (code
>= clear_code
) {
414 *sp
++ = table
[1][code
];
415 if (code
== table
[0][code
]) {
416 Fprintf(stderr
, "circular table entry BIG ERROR\n");
419 code
= table
[0][code
];
422 *sp
++ = firstcode
= table
[1][code
];
424 if ((code
= max_code
) < (1 << MAX_LWZ_BITS
)) {
425 table
[0][code
] = oldcode
;
426 table
[1][code
] = firstcode
;
428 if ((max_code
>= max_code_size
)
429 && (max_code_size
< (1 << MAX_LWZ_BITS
))) {
444 ReadInterleavedImage(FILE *fd
, int len
, int height
)
447 int xpos
= 0, ypos
= 0, pass
= 0;
449 while ((v
= LWZReadByte(fd
, FALSE
/*, (int) input_code_size*/ )) >= 0) {
450 PPM_ASSIGN(image
[ypos
][xpos
], ColorMap
[CM_RED
][v
],
451 ColorMap
[CM_GREEN
][v
], ColorMap
[CM_BLUE
][v
]);
469 if (ypos
>= height
) {
491 if (LWZReadByte(fd
, FALSE
/*, (int) input_code_size*/ ) >= 0)
492 Fprintf(stderr
, "too much input data, ignoring extra...\n");
496 ReadTileStrip(FILE *fd
, int len
)
499 int xpos
= 0, ypos
= 0;
501 while ((v
= LWZReadByte(fd
, FALSE
/*, (int) input_code_size*/ )) >= 0) {
502 PPM_ASSIGN(image
[ypos
][xpos
], ColorMap
[CM_RED
][v
],
503 ColorMap
[CM_GREEN
][v
], ColorMap
[CM_BLUE
][v
]);
516 fopen_gif_file(const char *filename
, const char *type
)
520 if (strcmp(type
, RDBMODE
)) {
521 Fprintf(stderr
, "using reading routine for non-reading?\n");
524 gif_file
= fopen(filename
, type
);
525 if (gif_file
== (FILE *) 0) {
526 Fprintf(stderr
, "cannot open gif file %s\n", filename
);
530 read_header(gif_file
);
531 if (GifScreen
.Width
% TILE_X
) {
532 Fprintf(stderr
, "error: width %d not divisible by %d\n",
533 GifScreen
.Width
, TILE_X
);
536 tiles_across
= GifScreen
.Width
/ TILE_X
;
537 curr_tiles_across
= 0;
538 if (GifScreen
.Height
% TILE_Y
) {
539 Fprintf(stderr
, "error: height %d not divisible by %d\n",
540 GifScreen
.Height
, TILE_Y
);
541 /* exit(EXIT_FAILURE) */;
543 tiles_down
= GifScreen
.Height
/ TILE_Y
;
546 if (GifScreen
.Interlace
) {
547 /* sigh -- hope this doesn't happen on micros */
548 image
= (pixel
**) alloc(GifScreen
.Height
* sizeof(pixel
*));
549 for (i
= 0; i
< GifScreen
.Height
; i
++) {
550 image
[i
] = (pixel
*) alloc(GifScreen
.Width
* sizeof(pixel
));
553 image
= (pixel
**) alloc(TILE_Y
* sizeof(pixel
*));
554 for (i
= 0; i
< TILE_Y
; i
++) {
555 image
[i
] = (pixel
*) alloc(GifScreen
.Width
* sizeof(pixel
));
560 ** Initialize the Compression routines
562 if (!ReadOK(gif_file
, &input_code_size
, 1)) {
563 Fprintf(stderr
, "EOF / read error on image data\n");
567 if (LWZReadByte(gif_file
, TRUE
/*, (int) input_code_size*/ ) < 0) {
568 Fprintf(stderr
, "error reading image\n");
572 /* read first section */
573 if (GifScreen
.Interlace
) {
574 ReadInterleavedImage(gif_file
, GifScreen
.Width
, GifScreen
.Height
);
576 ReadTileStrip(gif_file
, GifScreen
.Width
);
581 /* Read a tile. Returns FALSE when there are no more tiles */
583 read_gif_tile(pixel (*pixels
)[TILE_X
])
587 if (curr_tiles_down
>= tiles_down
)
589 if (curr_tiles_across
== tiles_across
) {
590 curr_tiles_across
= 0;
592 if (curr_tiles_down
>= tiles_down
)
594 if (!GifScreen
.Interlace
)
595 ReadTileStrip(gif_file
, GifScreen
.Width
);
597 if (GifScreen
.Interlace
) {
598 for (j
= 0; j
< TILE_Y
; j
++) {
599 for (i
= 0; i
< TILE_X
; i
++) {
600 pixels
[j
][i
] = image
[curr_tiles_down
* TILE_Y
601 + j
][curr_tiles_across
* TILE_X
+ i
];
605 for (j
= 0; j
< TILE_Y
; j
++) {
606 for (i
= 0; i
< TILE_X
; i
++) {
607 pixels
[j
][i
] = image
[j
][curr_tiles_across
* TILE_X
+ i
];
613 /* check for "filler" tile */
614 for (j
= 0; j
< TILE_Y
; j
++) {
615 for (i
= 0; i
< TILE_X
&& i
< 4; i
+= 2) {
616 if (pixels
[j
][i
].r
!= ColorMap
[CM_RED
][0]
617 || pixels
[j
][i
].g
!= ColorMap
[CM_GREEN
][0]
618 || pixels
[j
][i
].b
!= ColorMap
[CM_BLUE
][0]
619 || pixels
[j
][i
+ 1].r
!= ColorMap
[CM_RED
][1]
620 || pixels
[j
][i
+ 1].g
!= ColorMap
[CM_GREEN
][1]
621 || pixels
[j
][i
+ 1].b
!= ColorMap
[CM_BLUE
][1])
629 fclose_gif_file(void)
633 if (GifScreen
.Interlace
) {
634 for (i
= 0; i
< GifScreen
.Height
; i
++) {
635 free((genericptr_t
) image
[i
]);
637 free((genericptr_t
) image
);
639 for (i
= 0; i
< TILE_Y
; i
++) {
640 free((genericptr_t
) image
[i
]);
642 free((genericptr_t
) image
);
644 return (fclose(gif_file
));
648 static const char *const std_args
[] = {
649 "tilemap", /* dummy argv[0] */
650 "monsters.gif", "monsters.txt",
651 "objects.gif", "objects.txt",
652 "other.gif", "other.txt",
656 main(int argc
, char *argv
[])
658 pixel pixels
[TILE_Y
][TILE_X
];
661 argc
= SIZE(std_args
);
662 argv
= (char **) std_args
;
663 } else if (argc
!= 3) {
664 Fprintf(stderr
, "usage: gif2txt giffile txtfile\n");
669 if (!fopen_gif_file(argv
[1], RDBMODE
))
674 if (!fopen_text_file(argv
[2], WRTMODE
)) {
675 (void) fclose_gif_file();
679 while (read_gif_tile(pixels
))
680 (void) write_text_tile(pixels
);
682 (void) fclose_gif_file();
683 (void) fclose_text_file();