diff --git a/gifenc.h b/gifenc.h index a12659d..e76e69e 100644 --- a/gifenc.h +++ b/gifenc.h @@ -3,226 +3,174 @@ #include #include +#define MAX_DICT_SIZE 4096 + namespace gif { - struct gif_t { - uint16_t w, h; - int depth; - int bgindex; - FILE *fd; - int offset; - int nframes; - uint8_t *frame, *back; - uint32_t partial; - uint8_t buffer[0xFF]; - }; + typedef struct { + int prefix; + uint8_t character; + } DictEntry; - struct node_t { - uint16_t key; - node_t *children[]; - }; + typedef struct { + uint8_t *data; + size_t size; + size_t capacity; + int bit_pos; + uint32_t bit_buffer; + } BitStream; - static node_t *new_node(uint16_t key, int degree) - { - node_t *node = (node_t*)calloc(1, sizeof(*node) + degree * sizeof(node_t *)); - if (node) node->key = key; - return node; + void bitstream_init(BitStream *bs) { + bs->capacity = 256; + bs->size = 0; + bs->data = (uint8_t*)malloc(bs->capacity); + bs->bit_pos = 0; + bs->bit_buffer = 0; } - static node_t *new_trie(int degree, int *nkeys) - { - node_t *root = new_node(0, degree); - /* Create nodes for single pixels. */ - for (*nkeys = 0; *nkeys < degree; (*nkeys)++) root->children[*nkeys] = new_node(*nkeys, degree); - *nkeys += 2; /* skip clear code and stop code */ - return root; - } + void bitstream_write(BitStream *bs, uint16_t code, int code_size) { + bs->bit_buffer |= ((uint32_t)code) << bs->bit_pos; + bs->bit_pos += code_size; - static void del_trie(node_t *root, int degree) - { - if (!root) return; - for (int i = 0; i < degree; i++) del_trie(root->children[i], degree); - free(root); - } - - static void put_loop(gif_t *gif, uint16_t loop); - - gif_t *create(const char *fname, uint16_t width, uint16_t height, uint8_t *palette, uint8_t depth, int16_t bgindex, int loop) - { - gif_t *gif = (gif_t*)calloc(1, sizeof(*gif) + (bgindex < 0 ? 2 : 1)*width*height); - gif->w = width; gif->h = height; - gif->bgindex = bgindex; - gif->frame = (uint8_t *) &gif[1]; - gif->back = &gif->frame[width*height]; - gif->fd = fopen(fname, "wb"); - if (!gif->fd) { free(gif); return NULL; } - fwrite("GIF89a", 6, 1, gif->fd); - fwrite(&width, 2, 1, gif->fd); - fwrite(&height, 2, 1, gif->fd); - gif->depth = depth; - fputc((!palette?0x70:0xF0|(depth-1)), gif->fd); - fputc(bgindex, gif->fd); fputc(0, gif->fd); - if (palette) fwrite(palette, 3 << depth, 1, gif->fd); - if (loop >= 0 && loop <= 0xFFFF) put_loop(gif, (uint16_t)loop); - return gif; - } - - static void put_loop(gif_t *gif, uint16_t loop) - { - fputc('!', gif->fd); fputc(0xFF, gif->fd); fputc(0x0B, gif->fd); - fwrite("NETSCAPE2.0", 11, 1, gif->fd); - fputc(0x03, gif->fd); fputc(0x01, gif->fd); - fwrite(&loop, 2, 1, gif->fd); - fputc(0, gif->fd); - } - - /* Add packed key to buffer, updating offset and partial. - * gif->offset holds position to put next *bit* - * gif->partial holds bits to include in next byte */ - static void put_key(gif_t *gif, uint16_t key, int key_size) - { - int byte_offset, bit_offset, bits_to_write; - byte_offset = gif->offset / 8; - bit_offset = gif->offset % 8; - gif->partial |= ((uint32_t) key) << bit_offset; - bits_to_write = bit_offset + key_size; - while (bits_to_write >= 8) { - gif->buffer[byte_offset++] = gif->partial & 0xFF; - if (byte_offset == 0xFF) { - fputc(0xFF, gif->fd); - fwrite(gif->buffer, 0xFF, 1, gif->fd); - byte_offset = 0; + while (bs->bit_pos >= 8) { + if (bs->size >= bs->capacity) { + bs->capacity *= 2; + bs->data = (uint8_t*)realloc(bs->data, bs->capacity); } - gif->partial >>= 8; - bits_to_write -= 8; + bs->data[bs->size++] = bs->bit_buffer & 0xFF; + bs->bit_buffer >>= 8; + bs->bit_pos -= 8; } - gif->offset = (gif->offset + key_size) % (0xFF * 8); } - static void end_key(gif_t *gif) - { - uint8_t byte_offset; - byte_offset = gif->offset >> 3; - if (gif->offset & 0x07) gif->buffer[byte_offset++] = gif->partial & 0xFF; - if (byte_offset) - { - fputc(byte_offset, gif->fd); - fwrite(gif->buffer, byte_offset, 1, gif->fd); + void bitstream_flush(BitStream *bs) { + if (bs->bit_pos > 0) { + if (bs->size >= bs->capacity) { + bs->capacity *= 2; + bs->data = (uint8_t*)realloc(bs->data, bs->capacity); + } + bs->data[bs->size++] = bs->bit_buffer & 0xFF; } - fputc(0, gif->fd); - gif->offset = gif->partial = 0; } - static void put_image(gif_t *gif, uint16_t w, uint16_t h, uint16_t x, uint16_t y) - { - int nkeys, key_size, i, j; - node_t *node, *child, *root; - int degree = 1 << gif->depth; + uint8_t *lzw_compress(uint8_t *input, int width, int height, int min_code_size, size_t *out_size) { + int clear_code = 1 << min_code_size; + int end_code = clear_code + 1; + int next_code = end_code + 1; + int code_size = min_code_size + 1; - fputc(',', gif->fd); - fwrite(&x, 2, 1, gif->fd); - fwrite(&y, 2, 1, gif->fd); - fwrite(&w, 2, 1, gif->fd); - fwrite(&h, 2, 1, gif->fd); - fputc(0, gif->fd); fputc(gif->depth, gif->fd); - root = node = new_trie(degree, &nkeys); - key_size = gif->depth + 1; - put_key(gif, degree, key_size); /* clear code */ - for (i = y; i < y+h; i++) { - for (j = x; j < x+w; j++) { - uint8_t pixel = gif->frame[i*gif->w+j] & (degree - 1); - child = node->children[pixel]; - if (child) { - node = child; - } else { - put_key(gif, node->key, key_size); - if (nkeys < 0x1000) { - if (nkeys == (1 << key_size)) - key_size++; - node->children[pixel] = new_node(nkeys++, degree); - } else { - put_key(gif, degree, key_size); /* clear code */ - del_trie(root, degree); - root = node = new_trie(degree, &nkeys); - key_size = gif->depth + 1; + DictEntry dict[MAX_DICT_SIZE]; + int dict_len = next_code; + + BitStream bs; + bitstream_init(&bs); + bitstream_write(&bs, clear_code, code_size); + + int prefix = input[0]; + for (int i = 1; i < width * height; i++) { + uint8_t c = input[i]; + + // Search for prefix + c in dictionary + int found = -1; + for (int j = end_code + 1; j < dict_len; j++) { + if (dict[j].prefix == prefix && dict[j].character == c) { + found = j; + break; + } + } + + if (found != -1) { + prefix = found; // Extend prefix + } else { + bitstream_write(&bs, prefix, code_size); // Emit current prefix + if (dict_len < MAX_DICT_SIZE) { + dict[dict_len].prefix = prefix; + dict[dict_len].character = c; + dict_len++; + if (dict_len == (1 << code_size) && code_size < 12) { + code_size++; } - node = root->children[pixel]; + } else { + bitstream_write(&bs, clear_code, code_size); + dict_len = end_code + 1; + code_size = min_code_size + 1; } + prefix = c; // Start new prefix } } - put_key(gif, node->key, key_size); - put_key(gif, degree + 1, key_size); /* stop code */ - end_key(gif); - del_trie(root, degree); + + // Emit final prefix and end code + bitstream_write(&bs, prefix, code_size); + bitstream_write(&bs, end_code, code_size); + bitstream_flush(&bs); + + *out_size = bs.size; + return bs.data; } - static int get_bbox(gif_t *gif, uint16_t *w, uint16_t *h, uint16_t *x, uint16_t *y) - { - int i, j, k; - int left, right, top, bottom; - uint8_t back; - left = gif->w; right = 0; - top = gif->h; bottom = 0; - k = 0; - for (i = 0; i < gif->h; i++) { - for (j = 0; j < gif->w; j++, k++) { - back = gif->bgindex >= 0 ? gif->bgindex : gif->back[k]; - if (gif->frame[k] != back) { - if (j < left) left = j; - if (j > right) right = j; - if (i < top) top = i; - if (i > bottom) bottom = i; - } - } + void write_gif(const char *filename, uint8_t *pixels, int width, int height, uint8_t *palette, int palette_size) { + FILE *f = fopen(filename, "wb"); + if (!f) { + perror("Failed to open file"); + return; } - if (left != gif->w && top != gif->h) { - *x = left; *y = top; - *w = right - left + 1; - *h = bottom - top + 1; - return 1; - } else { - return 0; + + // Header + fwrite("GIF89a", 1, 6, f); + + // Determine min_code_size from palette_size + int palette_depth = 0; + while ((1 << palette_depth) < palette_size) palette_depth++; + const int min_code_size = palette_depth < 2 ? 2 : palette_depth; // GIF spec requires at least 2 + + printf("min_code_size: %i\n", palette_depth); + + // Logical Screen Descriptor + uint8_t packed_field = palette ? (0x80 | ((palette_depth - 1) << 4) | (palette_depth - 1)) : 0x00; + uint8_t screen_desc[] = { + uint8_t(width & 0xFF), uint8_t((width >> 8) & 0xFF), + uint8_t(height & 0xFF), uint8_t((height >> 8) & 0xFF), + packed_field, // GCT flag + color resolution + size + 0x00, // Background color index + 0x00 // Pixel aspect ratio + }; + fwrite(screen_desc, 1, sizeof(screen_desc), f); + + // Global Color Table (if provided) + if (palette) { + int gct_size = 1 << palette_depth; + fwrite(palette, 1, gct_size * 3, f); } - } - static void add_graphics_control_extension(gif_t *gif, uint16_t d) - { - uint8_t flags = ((gif->bgindex >= 0 ? 2 : 1) << 2) + 1; - fputc('!', gif->fd); fputc(0xF9, gif->fd); fputc(0x04, gif->fd); fputc(flags, gif->fd); - fwrite(&d, 2, 1, gif->fd); - fputc(gif->bgindex, gif->fd); fputc(0, gif->fd); - } + // Image Descriptor + uint8_t image_desc[] = { + 0x2C, 0, 0, 0, 0, + uint8_t(width & 0xFF), uint8_t((width >> 8) & 0xFF), + uint8_t(height & 0xFF), uint8_t((height >> 8) & 0xFF), + 0x00 // No local color table + }; + fwrite(image_desc, 1, sizeof(image_desc), f); - void addFrame(gif_t *gif, uint16_t delay) - { - uint16_t w, h, x, y; - uint8_t *tmp; + // LZW-compressed image data + fwrite(&min_code_size, 1, 1, f); - if (delay || (gif->bgindex >= 0)) - add_graphics_control_extension(gif, delay); - if (gif->nframes == 0) { - w = gif->w; - h = gif->h; - x = y = 0; - } else if (!get_bbox(gif, &w, &h, &x, &y)) { - /* image's not changed; save one pixel just to add delay */ - w = h = 1; - x = y = 0; + size_t compressed_size; + uint8_t *compressed = lzw_compress(pixels, width, height, min_code_size, &compressed_size); + + // Write as sub-blocks + size_t offset = 0; + while (offset < compressed_size) { + uint8_t block_size = (compressed_size - offset > 255) ? 255 : (uint8_t)(compressed_size - offset); + fwrite(&block_size, 1, 1, f); + fwrite(compressed + offset, 1, block_size, f); + offset += block_size; } - put_image(gif, w, h, x, y); - gif->nframes++; - if (gif->bgindex < 0) { - tmp = gif->back; - gif->back = gif->frame; - gif->frame = tmp; - } - } + fputc(0x00, f); // Block terminator - void close(gif_t* gif) - { - fputc(';', gif->fd); - fclose(gif->fd); - free(gif); + // Trailer + fputc(0x3B, f); + + free(compressed); + fclose(f); } - } diff --git a/gifenc_old.h b/gifenc_old.h new file mode 100644 index 0000000..a12659d --- /dev/null +++ b/gifenc_old.h @@ -0,0 +1,228 @@ +#include +#include +#include +#include + +namespace gif +{ + struct gif_t { + uint16_t w, h; + int depth; + int bgindex; + FILE *fd; + int offset; + int nframes; + uint8_t *frame, *back; + uint32_t partial; + uint8_t buffer[0xFF]; + }; + + struct node_t { + uint16_t key; + node_t *children[]; + }; + + static node_t *new_node(uint16_t key, int degree) + { + node_t *node = (node_t*)calloc(1, sizeof(*node) + degree * sizeof(node_t *)); + if (node) node->key = key; + return node; + } + + static node_t *new_trie(int degree, int *nkeys) + { + node_t *root = new_node(0, degree); + /* Create nodes for single pixels. */ + for (*nkeys = 0; *nkeys < degree; (*nkeys)++) root->children[*nkeys] = new_node(*nkeys, degree); + *nkeys += 2; /* skip clear code and stop code */ + return root; + } + + static void del_trie(node_t *root, int degree) + { + if (!root) return; + for (int i = 0; i < degree; i++) del_trie(root->children[i], degree); + free(root); + } + + static void put_loop(gif_t *gif, uint16_t loop); + + gif_t *create(const char *fname, uint16_t width, uint16_t height, uint8_t *palette, uint8_t depth, int16_t bgindex, int loop) + { + gif_t *gif = (gif_t*)calloc(1, sizeof(*gif) + (bgindex < 0 ? 2 : 1)*width*height); + gif->w = width; gif->h = height; + gif->bgindex = bgindex; + gif->frame = (uint8_t *) &gif[1]; + gif->back = &gif->frame[width*height]; + gif->fd = fopen(fname, "wb"); + if (!gif->fd) { free(gif); return NULL; } + fwrite("GIF89a", 6, 1, gif->fd); + fwrite(&width, 2, 1, gif->fd); + fwrite(&height, 2, 1, gif->fd); + gif->depth = depth; + fputc((!palette?0x70:0xF0|(depth-1)), gif->fd); + fputc(bgindex, gif->fd); fputc(0, gif->fd); + if (palette) fwrite(palette, 3 << depth, 1, gif->fd); + if (loop >= 0 && loop <= 0xFFFF) put_loop(gif, (uint16_t)loop); + return gif; + } + + static void put_loop(gif_t *gif, uint16_t loop) + { + fputc('!', gif->fd); fputc(0xFF, gif->fd); fputc(0x0B, gif->fd); + fwrite("NETSCAPE2.0", 11, 1, gif->fd); + fputc(0x03, gif->fd); fputc(0x01, gif->fd); + fwrite(&loop, 2, 1, gif->fd); + fputc(0, gif->fd); + } + + /* Add packed key to buffer, updating offset and partial. + * gif->offset holds position to put next *bit* + * gif->partial holds bits to include in next byte */ + static void put_key(gif_t *gif, uint16_t key, int key_size) + { + int byte_offset, bit_offset, bits_to_write; + byte_offset = gif->offset / 8; + bit_offset = gif->offset % 8; + gif->partial |= ((uint32_t) key) << bit_offset; + bits_to_write = bit_offset + key_size; + while (bits_to_write >= 8) { + gif->buffer[byte_offset++] = gif->partial & 0xFF; + if (byte_offset == 0xFF) { + fputc(0xFF, gif->fd); + fwrite(gif->buffer, 0xFF, 1, gif->fd); + byte_offset = 0; + } + gif->partial >>= 8; + bits_to_write -= 8; + } + gif->offset = (gif->offset + key_size) % (0xFF * 8); + } + + static void end_key(gif_t *gif) + { + uint8_t byte_offset; + byte_offset = gif->offset >> 3; + if (gif->offset & 0x07) gif->buffer[byte_offset++] = gif->partial & 0xFF; + if (byte_offset) + { + fputc(byte_offset, gif->fd); + fwrite(gif->buffer, byte_offset, 1, gif->fd); + } + fputc(0, gif->fd); + gif->offset = gif->partial = 0; + } + + static void put_image(gif_t *gif, uint16_t w, uint16_t h, uint16_t x, uint16_t y) + { + int nkeys, key_size, i, j; + node_t *node, *child, *root; + int degree = 1 << gif->depth; + + fputc(',', gif->fd); + fwrite(&x, 2, 1, gif->fd); + fwrite(&y, 2, 1, gif->fd); + fwrite(&w, 2, 1, gif->fd); + fwrite(&h, 2, 1, gif->fd); + fputc(0, gif->fd); fputc(gif->depth, gif->fd); + root = node = new_trie(degree, &nkeys); + key_size = gif->depth + 1; + put_key(gif, degree, key_size); /* clear code */ + for (i = y; i < y+h; i++) { + for (j = x; j < x+w; j++) { + uint8_t pixel = gif->frame[i*gif->w+j] & (degree - 1); + child = node->children[pixel]; + if (child) { + node = child; + } else { + put_key(gif, node->key, key_size); + if (nkeys < 0x1000) { + if (nkeys == (1 << key_size)) + key_size++; + node->children[pixel] = new_node(nkeys++, degree); + } else { + put_key(gif, degree, key_size); /* clear code */ + del_trie(root, degree); + root = node = new_trie(degree, &nkeys); + key_size = gif->depth + 1; + } + node = root->children[pixel]; + } + } + } + put_key(gif, node->key, key_size); + put_key(gif, degree + 1, key_size); /* stop code */ + end_key(gif); + del_trie(root, degree); + } + + static int get_bbox(gif_t *gif, uint16_t *w, uint16_t *h, uint16_t *x, uint16_t *y) + { + int i, j, k; + int left, right, top, bottom; + uint8_t back; + left = gif->w; right = 0; + top = gif->h; bottom = 0; + k = 0; + for (i = 0; i < gif->h; i++) { + for (j = 0; j < gif->w; j++, k++) { + back = gif->bgindex >= 0 ? gif->bgindex : gif->back[k]; + if (gif->frame[k] != back) { + if (j < left) left = j; + if (j > right) right = j; + if (i < top) top = i; + if (i > bottom) bottom = i; + } + } + } + if (left != gif->w && top != gif->h) { + *x = left; *y = top; + *w = right - left + 1; + *h = bottom - top + 1; + return 1; + } else { + return 0; + } + } + + static void add_graphics_control_extension(gif_t *gif, uint16_t d) + { + uint8_t flags = ((gif->bgindex >= 0 ? 2 : 1) << 2) + 1; + fputc('!', gif->fd); fputc(0xF9, gif->fd); fputc(0x04, gif->fd); fputc(flags, gif->fd); + fwrite(&d, 2, 1, gif->fd); + fputc(gif->bgindex, gif->fd); fputc(0, gif->fd); + } + + void addFrame(gif_t *gif, uint16_t delay) + { + uint16_t w, h, x, y; + uint8_t *tmp; + + if (delay || (gif->bgindex >= 0)) + add_graphics_control_extension(gif, delay); + if (gif->nframes == 0) { + w = gif->w; + h = gif->h; + x = y = 0; + } else if (!get_bbox(gif, &w, &h, &x, &y)) { + /* image's not changed; save one pixel just to add delay */ + w = h = 1; + x = y = 0; + } + put_image(gif, w, h, x, y); + gif->nframes++; + if (gif->bgindex < 0) { + tmp = gif->back; + gif->back = gif->frame; + gif->frame = tmp; + } + } + + void close(gif_t* gif) + { + fputc(';', gif->fd); + fclose(gif->fd); + free(gif); + } + +} diff --git a/lua.cpp b/lua.cpp index 63f31a3..714be3c 100644 --- a/lua.cpp +++ b/lua.cpp @@ -48,7 +48,7 @@ extern "C" { lua_pop(L, 1); //pal[i-1] = (r<<16)+(g<<8)+b; } - savesurf(surface, str, pal, len-1); + savesurf(surface, str, pal, len); } else { savesurf(surface, str, nullptr); } diff --git a/mini.cpp b/mini.cpp index dfe0a5d..983f894 100644 --- a/mini.cpp +++ b/mini.cpp @@ -242,12 +242,14 @@ uint8_t loadsurf(const char* filename) { void savesurf(uint8_t surface, const char* filename, uint8_t *pal, uint8_t colors) { - uint8_t depth=0; + gif::write_gif(filename, surfaces[surface].p, surfaces[surface].w, surfaces[surface].h, pal, colors); + /*uint8_t depth=0; do { colors = colors >> 1; depth++; } while (colors!=0); + printf("pal depth: %i\n", depth); gif::gif_t *file = gif::create(filename, surfaces[surface].w, surfaces[surface].h, pal, (pal?depth:8), -1, -1); memcpy(file->frame, surfaces[surface].p, surfaces[surface].w*surfaces[surface].h); gif::addFrame(file, 0); - gif::close(file); + gif::close(file);*/ } void freesurf(uint8_t surface) {