From: nhmall Date: Mon, 7 Mar 2016 13:40:03 +0000 (-0500) Subject: giftiles.c X-Git-Tag: NetHack-3.6.1_RC01~883 X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=40f2994ba776df1145c947d04645d35b1609aa1f;p=nethack giftiles.c --- diff --git a/win/share/giftiles.c b/win/share/giftiles.c new file mode 100644 index 000000000..e4e44ba8f --- /dev/null +++ b/win/share/giftiles.c @@ -0,0 +1,512 @@ +/* NetHack 3.6 tileset.h $NHDT-Date: 1457207052 2016/03/05 19:44:12 $ $NHDT-Branch: chasonr $:$NHDT-Revision: 1.0 $ */ +/* Copyright (c) Ray Chason, 2016. */ +/* NetHack may be freely redistributed. See license for details. */ + +/* giftiles.c -- read a tile map in GIF format */ +/* Reference: GIF specification,, retrieved from + * http://www.w3.org/Graphics/GIF/spec-gif89a.txt */ + +#include "config.h" +#include "tileset.h" + +#define MIN_LZW_BITS 3 +#define MAX_LZW_BITS 12 +#define END_OF_DATA 0x7FFF + +/* For use when reading the GIF as a bitstream */ +struct Bitstream { + /* The file */ + FILE *fp; + + /* For unpacking LZW codes */ + unsigned long bits; + unsigned char num_bits; + unsigned char bit_width; + unsigned char initial_bit_width; + unsigned char block_size; + + /* The dictionary */ + struct { + unsigned char byte; + unsigned short next; + } dictionary[1 << MAX_LZW_BITS]; + unsigned short dict_size; + + /* The string currently being decoded */ + unsigned char string[1 << MAX_LZW_BITS]; + unsigned short str_size; + + unsigned short last_code; +}; + +struct DataBlock { + size_t size; + size_t index; + unsigned char *data; +}; + +static boolean FDECL(read_data_block, (struct Bitstream *gif, struct DataBlock *block)); +static void FDECL(free_data_block, (struct DataBlock *block)); +static unsigned short FDECL(read_u16, (const unsigned char buf[2])); +static void FDECL(init_decoder, (struct Bitstream *gif, unsigned bit_width)); +static void FDECL(reset_decoder, (struct Bitstream *gif)); +static int FDECL(decode, (struct Bitstream *gif, struct DataBlock *block)); +static int FDECL(get_code, (struct Bitstream *gif, struct DataBlock *block)); +static unsigned FDECL(interlace_incr, (unsigned y, unsigned height)); + +/* + * GIF specifies a canvas, which may have a palette (the "global color table") + * of up to 256 colors, of which one is a background color, and zero or more + * images, each of which may have its own palette (a "local color table") + * independently of the canvas. We will join all palettes found into a single + * palette, and indicate that the image uses a palette if, and only if, the + * various palettes do not total more than 256 colors. + */ + +boolean +read_gif_tiles(filename, image) +const char *filename; +struct TileSetImage *image; +{ + struct Bitstream gif; + struct DataBlock block; + + unsigned char buf[1024]; + size_t size, num_pixels, i; + + /* Image data not returned to the caller */ + boolean have_gct; /* global color table is present */ + unsigned gct_size; /* global color table size */ + unsigned back_color; /* index for background color */ + unsigned trans_color = 0xFFFF; /* index for transparent palette entry */ + + block.data = NULL; /* custodial */ + gif.fp = NULL; /* custodial */ + + image->width = 0; + image->height = 0; + image->pixels = NULL; /* custodial, returned */ + image->indexes = NULL; /* custodial, returned */ + image->image_desc = NULL; /* custodial, returned */ + image->tile_width = 0; + image->tile_height = 0; + + gif.fp = fopen(filename, "rb"); + if (gif.fp == NULL) goto error; + + /* 17. Header */ + size = fread(buf, 1, 6, gif.fp); + if (size < 6) goto error; + if (memcmp(buf, "GIF87a", 6) != 0 && memcmp(buf, "GIF89a", 6) != 0) + goto error; + + /* 18. Logical screen descriptor */ + size = fread(buf, 1, 7, gif.fp); + if (size < 7) goto error; + image->width = read_u16(buf + 0); + image->height = read_u16(buf + 2); + have_gct = (buf[4] & 0x80) != 0; + gct_size = 1 << ((buf[4] & 0x07) + 1); + back_color = buf[5]; + if (image->width == 0 || image->height == 0) goto error; + + /* 19. Global Color Table */ + for (i = 0; i < SIZE(image->palette); ++i) { + image->palette[i].r = 0; + image->palette[i].g = 0; + image->palette[i].b = 0; + image->palette[i].a = 255; + } + if (have_gct) { + size = fread(buf, 3, gct_size, gif.fp); + if (size < gct_size) goto error; + for (i = 0; i < gct_size; ++i) { + image->palette[i].r = buf[i * 3 + 0]; + image->palette[i].g = buf[i * 3 + 1]; + image->palette[i].b = buf[i * 3 + 2]; + image->palette[i].a = 255; + } + } + + /* Allocate pixel area; watch out for overflow */ + num_pixels = (size_t) image->width * (size_t) image->height; + if (num_pixels / image->width != image->height) goto error; /* overflow */ + size = num_pixels * sizeof(image->pixels[0]); + if (size / sizeof(image->pixels[0]) != num_pixels) goto error; /* overflow */ + image->pixels = (struct Pixel *) alloc(size); + image->indexes = (unsigned char *) alloc(num_pixels); + + /* Fill with the background color */ + for (i = 0; i < num_pixels; ++i) { + image->pixels[i] = image->palette[back_color]; + image->indexes[i] = back_color; + } + + /* Read the image data */ + while (TRUE) { + int b = fgetc(gif.fp); + if (b == EOF) goto error; + + /* 27. Trailer (0x3B) */ + if (b == 0x3B) break; + + switch (b) { + case 0x2C: + /* 20. Image descriptor (0x2C) */ + { + unsigned img_left, img_top, img_width, img_height; + boolean have_lct, interlace; + unsigned lct_start, lct_size; + struct Pixel lct[256]; + int b; + unsigned x, y, x2, y2; + + size = fread(buf, 1, 9, gif.fp); + if (size < 9) goto error; + img_left = read_u16(buf + 0); + img_top = read_u16(buf + 2); + img_width = read_u16(buf + 4); + img_height = read_u16(buf + 6); + have_lct = (buf[8] & 0x80) != 0; + interlace = (buf[8] & 0x40) != 0; + lct_size = 1 << ((buf[8] & 0x07) + 1); + + /* 21. Local color table */ + lct_start = 0; + memcpy(lct, image->palette, sizeof(lct)); + if (have_lct) { + size = fread(buf, 3, lct_size, gif.fp); + if (size < lct_size) goto error; + for (i = 0; i < lct_size; ++i) { + lct[i].r = buf[i * 3 + 0]; + lct[i].g = buf[i * 3 + 1]; + lct[i].b = buf[i * 3 + 2]; + lct[i].a = 255; + } + /* + * The combined palette may exceed 256 colors, in which + * case the indexes array will be discarded, indicating a + * full-color image. + */ + if (lct_size + gct_size <= 256) { + memcpy(image->palette, lct, sizeof(lct[0]) * lct_size); + } + lct_start = gct_size; + gct_size += lct_start; + } + if (trans_color != 0xFFFF) { + lct[trans_color].a = 0; + if (!have_lct) { + /* FIXME: this will affect all images using the global + * color table, not just the current one */ + image->palette[trans_color].a = 0; + } + } + /* 22. Table based image data */ + b = fgetc(gif.fp); + if (b == EOF) goto error; + if (b < MIN_LZW_BITS - 1 || MAX_LZW_BITS - 1 < b) goto error; + init_decoder(&gif, b); + x = 0; + y = 0; + if (!read_data_block(&gif, &block)) goto error; + while (TRUE) { + b = decode(&gif, &block); + if (b == EOF) goto error; + if (b == END_OF_DATA) break; + if (y >= img_height) goto error; + x2 = img_left + x; + y2 = img_top + y; + if (x2 < image->width && y2 < image->height) { + image->pixels[y2 * image->width + x2] = lct[b]; + image->indexes[y2 * image->width + x2] = b + lct_start; + } + ++x; + if (x >= img_width) { + x = 0; + if (interlace) { + y = interlace_incr(y, img_height); + } else { + ++y; + } + } + } + free_data_block(&block); + trans_color = 0xFFFF; + } + break; + + case 0x21: + /* Extension blocks */ + { + int label; + + label = fgetc(gif.fp); + if (label == EOF) goto error; + if (!read_data_block(&gif, &block)) goto error; + switch (label) { + case 0xF9: + /* 23. Graphic control extension (0xF9) */ + if (block.size >= 4 && (block.data[0] & 0x01) != 0) { + /* image has a transparent index */ + trans_color = block.data[3]; + } + break; + +#if 0 + case 0xFE: + /* 24. Comment extension (0xFE) */ + break; + + case 0x01: + /* 25. Plain text extension (0x01) */ + break; +#endif + + case 0xFF: + /* 26. Application extension (0xFF) */ + if (block.size > 11 + && memcmp(block.data, "NETHACK3GIF", 11) == 0 + && image->image_desc == NULL) { + memmove(block.data, block.data + 11, block.size - 11); + block.data[block.size - 11] = '\0'; + image->image_desc = (char *) block.data; + block.data = NULL; + } + break; + + default: + /* Unknown extension type */ + break; + } + free_data_block(&block); + } + break; + + default: + goto error; + } + } + + fclose(gif.fp); + free_data_block(&block); + if (gct_size > 256) { + /* Max palette size exceeded; indexes array is not meaningful */ + free(image->indexes); + image->indexes = NULL; + } + return TRUE; + +error: + if (gif.fp) fclose(gif.fp); + free_data_block(&block); + free(image->pixels); + image->pixels = NULL; + free(image->indexes); + image->indexes = NULL; + free(image->image_desc); + image->image_desc = NULL; + return FALSE; +} + +static void +init_decoder(gif, bit_width) +struct Bitstream *gif; +unsigned bit_width; +{ + unsigned i; + unsigned clear; + + gif->bits = 0; + gif->num_bits = 0; + gif->initial_bit_width = bit_width; + gif->block_size = 0; + + clear = 1 << bit_width; + gif->dict_size = clear + 2; + for (i = 0; i < clear; ++i) { + gif->dictionary[i].byte = i; + gif->dictionary[i].next = 0xFFFF; + } + + gif->str_size = 0; + + reset_decoder(gif); +} + +static void +reset_decoder(gif) +struct Bitstream *gif; +{ + /* Set the bit width */ + gif->bit_width = gif->initial_bit_width + 1; + + /* Reset the dictionary */ + gif->dict_size = (1 << gif->initial_bit_width) + 2; + + /* No last code */ + gif->last_code = 0xFFFF; +} + +static int +decode(gif, block) +struct Bitstream *gif; +struct DataBlock *block; +{ + int code; + unsigned clear = 1 << gif->initial_bit_width; + + /* If a string is being decoded, return the next byte */ + if (gif->str_size != 0) { + return gif->string[--gif->str_size]; + } + + /* Get the next code, until code other than clear */ + while (TRUE) { + code = get_code(gif, block); + if (code != clear) break; + reset_decoder(gif); + } + + if (code == EOF) return EOF; + if (code == clear + 1) return END_OF_DATA; + if (code > gif->dict_size) return EOF; + + /* Add a new string to the dictionary */ + if (gif->last_code != 0xFFFF && gif->dict_size < SIZE(gif->dictionary)) { + unsigned next_code; + if (code < gif->dict_size) { + next_code = code; + } else { + next_code = gif->last_code; + } + while (next_code >= clear) { + next_code = gif->dictionary[next_code].next; + } + gif->dictionary[gif->dict_size].next = gif->last_code; + gif->dictionary[gif->dict_size].byte = next_code; + ++gif->dict_size; + if (gif->dict_size >= 1 << gif->bit_width + && gif->bit_width < MAX_LZW_BITS) { + ++gif->bit_width; + } + } + gif->last_code = code; + + /* code is less than gif->dict_size and not equal to clear or clear + 1 */ + /* Prepare the decoded string for return; note that it is stored in + * reverse order */ + while (code >= clear) { + gif->string[gif->str_size++] = gif->dictionary[code].byte; + code = gif->dictionary[code].next; + } + + return code; +} + +static int +get_code(gif, block) +struct Bitstream *gif; +struct DataBlock *block; +{ + int code; + + while (gif->num_bits < gif->bit_width) { + unsigned char b; + if (block->index >= block->size) return EOF; + b = block->data[block->index++]; + gif->bits |= (unsigned long)b << gif->num_bits; + gif->num_bits += 8; + } + + code = (int) (gif->bits & ((1UL << gif->bit_width) - 1)); + gif->bits >>= gif->bit_width; + gif->num_bits -= gif->bit_width; + return code; +} + +static unsigned +interlace_incr(y, height) +unsigned y; +unsigned height; +{ + static const unsigned char incr[] = { 8, 2, 4, 2 }; + + /* The lower three bits indicate the current pass */ + + /* Advance to the next row of the current pass */ + y += incr[y & 0x3]; + + /* Go to the next pass if y exceeds height */ + /* Might not be the immediately following pass if height is small */ + if (y >= height && (y & 0x7) == 0) { + /* Pass 1 -> Pass 2 */ + y = 4; + } + if (y >= height && (y & 0x7) == 4) { + /* Pass 2 -> Pass 3 */ + y = 2; + } + if (y >= height && (y & 0x3) == 2) { + /* Pass 3 -> Pass 4 */ + y = 1; + } + + return y; +} + +/* Decode an unsigned 16 bit quantity */ +static unsigned short +read_u16(buf) +const unsigned char buf[2]; +{ + return ((unsigned short)buf[0] << 0) + | ((unsigned short)buf[1] << 8); +} + +static boolean +read_data_block(gif, block) +struct Bitstream *gif; +struct DataBlock *block; +{ + long pos = ftell(gif->fp); + int b; + size_t i; + + free_data_block(block); + + /* Get the length of the data block */ + while (TRUE) { + b = fgetc(gif->fp); + if (b == EOF) return FALSE; + if (b == 0) break; + block->size += b; + fseek(gif->fp, b, SEEK_CUR); + } + fseek(gif->fp, pos, SEEK_SET); + + /* Allocate memory */ + block->data = (unsigned char *) alloc(block->size); + + /* Read the data from the file */ + i = 0; + while (TRUE) { + b = fgetc(gif->fp); + if (b == EOF) return FALSE; + if (b == 0) break; + if (fread(block->data + i, 1, b, gif->fp) != b) return FALSE; + i += b; + } + + block->index = 0; + return TRUE; +} + +static void +free_data_block(block) +struct DataBlock *block; +{ + free(block->data); + block->size = 0; + block->data = NULL; +}