#include "gif.h" #include // Para fprintf, stderr #include // Para exit, malloc, calloc, free, realloc #include // Para vector void Gif::uncompress(int code_length, const uint8_t *input, int input_length, uint8_t *out) { int i, bit; int code, prev = -1; std::vector dictionary; int dictionary_ind; unsigned int mask = 0x01; int reset_code_length; int clear_code; int stop_code; int match_len; clear_code = 1 << (code_length); stop_code = clear_code + 1; reset_code_length = code_length; dictionary.resize(1 << (code_length + 1)); for (dictionary_ind = 0; dictionary_ind < (1 << code_length); dictionary_ind++) { dictionary[dictionary_ind].byte = dictionary_ind; dictionary[dictionary_ind].prev = -1; dictionary[dictionary_ind].len = 1; } dictionary_ind += 2; while (input_length) { code = 0x0; for (i = 0; i < (code_length + 1); i++) { bit = (*input & mask) ? 1 : 0; mask <<= 1; if (mask == 0x100) { mask = 0x01; input++; input_length--; } code = code | (bit << i); } if (code == clear_code) { code_length = reset_code_length; dictionary.resize(1 << (code_length + 1)); for (dictionary_ind = 0; dictionary_ind < (1 << code_length); dictionary_ind++) { dictionary[dictionary_ind].byte = dictionary_ind; dictionary[dictionary_ind].prev = -1; dictionary[dictionary_ind].len = 1; } dictionary_ind += 2; prev = -1; continue; } else if (code == stop_code) { break; } if ((prev > -1) && (code_length < 12)) { if (code > dictionary_ind) { fprintf(stderr, "code = %.02x, but dictionary_ind = %.02x\n", code, dictionary_ind); exit(0); } if (code == dictionary_ind) { int ptr = prev; while (dictionary[ptr].prev != -1) { ptr = dictionary[ptr].prev; } dictionary[dictionary_ind].byte = dictionary[ptr].byte; } else { int ptr = code; while (dictionary[ptr].prev != -1) { ptr = dictionary[ptr].prev; } dictionary[dictionary_ind].byte = dictionary[ptr].byte; } dictionary[dictionary_ind].prev = prev; dictionary[dictionary_ind].len = dictionary[prev].len + 1; dictionary_ind++; if ((dictionary_ind == (1 << (code_length + 1))) && (code_length < 11)) { code_length++; dictionary.resize(1 << (code_length + 1)); } } prev = code; match_len = dictionary[code].len; while (code != -1) { out[dictionary[code].len - 1] = dictionary[code].byte; if (dictionary[code].prev == code) { fprintf(stderr, "Internal error; self-reference."); exit(0); } code = dictionary[code].prev; } out += match_len; } } int Gif::read_sub_blocks(uint8_t *buffer, uint8_t **data) { int data_length = 0; int index = 0; uint8_t block_size; *data = nullptr; while (true) { READ(&block_size, 1); if (block_size == 0) { break; } data_length += block_size; *data = (uint8_t *)realloc(*data, data_length); READ(*data + index, block_size); index += block_size; } return data_length; } uint8_t *Gif::process_image_descriptor(uint8_t *buffer, RGB *gct, int gct_size, int resolution_bits) { ImageDescriptor image_descriptor; int compressed_data_length; uint8_t *compressed_data = nullptr; uint8_t lzw_code_size; int uncompressed_data_length = 0; uint8_t *uncompressed_data = nullptr; READ(&image_descriptor, 9); READ(&lzw_code_size, 1); compressed_data_length = read_sub_blocks(buffer, &compressed_data); uncompressed_data_length = image_descriptor.image_width * image_descriptor.image_height; uncompressed_data = (uint8_t *)malloc(uncompressed_data_length); uncompress(lzw_code_size, compressed_data, compressed_data_length, uncompressed_data); if (compressed_data) { free(compressed_data); } return uncompressed_data; } uint32_t *Gif::LoadPalette(uint8_t *buffer) { uint8_t header[7]; ScreenDescriptor screen_descriptor; int global_color_table_size = 0; uint32_t *global_color_table = nullptr; READ(header, 6); READ(&screen_descriptor, 7); global_color_table = (uint32_t *)calloc(1, 1024); if (screen_descriptor.fields & 0x80) { global_color_table_size = 1 << (((screen_descriptor.fields & 0x07) + 1)); for (int i = 0; i < global_color_table_size; ++i) { global_color_table[i] = (buffer[0] << 16) + (buffer[1] << 8) + buffer[2]; buffer += 3; } } return global_color_table; } uint8_t *Gif::process_gif_stream(uint8_t *buffer, uint16_t *w, uint16_t *h) { uint8_t header[7]; ScreenDescriptor screen_descriptor; int color_resolution_bits; int global_color_table_size = 0; RGB *global_color_table = nullptr; uint8_t block_type = 0x0; READ(header, 6); header[6] = 0x0; READ(&screen_descriptor, 7); *w = screen_descriptor.width; *h = screen_descriptor.height; color_resolution_bits = ((screen_descriptor.fields & 0x70) >> 4) + 1; if (screen_descriptor.fields & 0x80) { global_color_table_size = 1 << (((screen_descriptor.fields & 0x07) + 1)); global_color_table = (RGB *)malloc(3 * global_color_table_size); READ(global_color_table, 3 * global_color_table_size); } while (block_type != TRAILER) { READ(&block_type, 1); uint8_t size; switch (block_type) { case IMAGE_DESCRIPTOR: return process_image_descriptor(buffer, global_color_table, global_color_table_size, color_resolution_bits); case EXTENSION_INTRODUCER: buffer++; size = *(buffer++); buffer += size; do { size = *(buffer++); buffer += size; } while (size != 0); break; case TRAILER: break; default: fprintf(stderr, "Bailing on unrecognized block type %.02x\n", block_type); return nullptr; } } return nullptr; } uint8_t *Gif::LoadGif(uint8_t *buffer, uint16_t *w, uint16_t *h) { return process_gif_stream(buffer, w, h); }