228 lines
6.6 KiB
C
228 lines
6.6 KiB
C
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <fcntl.h>
|
|
#include <stdint.h>
|
|
|
|
#define READ(dst, size) memcpy(dst, buffer, size); buffer += size
|
|
|
|
struct rgb { uint8_t r, g, b; };
|
|
|
|
struct image_descriptor_t
|
|
{
|
|
uint16_t left, top, width, height;
|
|
uint8_t fields;
|
|
};
|
|
|
|
struct dictionary_entry_t
|
|
{
|
|
unsigned char byte;
|
|
int prev, len;
|
|
};
|
|
|
|
void uncompress( int code_length, const uint8_t *input, int input_length, uint8_t *out )
|
|
{
|
|
dictionary_entry_t *dictionary;
|
|
int dictionary_ind;
|
|
|
|
const int clear_code = 1 << ( code_length );
|
|
const int stop_code = clear_code + 1;
|
|
const int reset_code_length = code_length;
|
|
|
|
dictionary = ( dictionary_entry_t * )
|
|
malloc( sizeof( dictionary_entry_t ) * ( 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;
|
|
|
|
int prev = -1;
|
|
uint32_t mask = 0x01;
|
|
while ( input_length )
|
|
{
|
|
int code = 0x0;
|
|
|
|
for (int i=0; i<(code_length + 1); i++)
|
|
{
|
|
const int 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 = ( dictionary_entry_t * ) realloc( dictionary, sizeof( dictionary_entry_t ) * ( 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 );
|
|
}
|
|
|
|
int ptr = (code == dictionary_ind) ? prev : 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 = ( dictionary_entry_t * ) realloc( dictionary, sizeof( dictionary_entry_t ) * ( 1 << ( code_length + 1 ) ) );
|
|
}
|
|
}
|
|
|
|
prev = code;
|
|
|
|
const int 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;
|
|
}
|
|
}
|
|
|
|
static int read_sub_blocks( uint8_t* buffer, uint8_t **data )
|
|
{
|
|
int data_length = 0;
|
|
*data = NULL;
|
|
int index = 0;
|
|
|
|
while (1) {
|
|
uint8_t block_size = *(buffer++);
|
|
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* process_image_descriptor( uint8_t* buffer, rgb *gct, int gct_size, int resolution_bits )
|
|
{
|
|
image_descriptor_t image_descriptor;
|
|
READ(&image_descriptor, 9);
|
|
|
|
uint8_t lzw_code_size;
|
|
READ(&lzw_code_size, 1);
|
|
|
|
uint8_t *compressed_data = NULL;
|
|
const int compressed_data_length = read_sub_blocks( buffer, &compressed_data );
|
|
|
|
const int uncompressed_data_length = image_descriptor.width * image_descriptor.height;
|
|
uint8_t *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* LoadPalette(uint8_t *buffer, uint16_t *size = NULL )
|
|
{
|
|
buffer += 10;
|
|
const uint8_t fields = *buffer;
|
|
|
|
uint32_t *global_color_table = (uint32_t *)calloc(1, 1024);;
|
|
|
|
if (size) *size = 0;
|
|
if (fields & 0x80) {
|
|
int global_color_table_size = 1 << (((fields & 0x07) + 1));
|
|
if (size) *size = global_color_table_size;
|
|
|
|
for (int i=0; i<global_color_table_size;++i) {
|
|
buffer+=3;
|
|
global_color_table[i] = (buffer[0]<<16) + (buffer[1]<<8) + buffer[2];
|
|
}
|
|
}
|
|
return global_color_table;
|
|
}
|
|
|
|
static uint8_t* LoadGif(uint8_t *buffer, uint16_t* w, uint16_t* h)
|
|
{
|
|
int global_color_table_size = 0; // number of entries in global_color_table
|
|
rgb *global_color_table = NULL;
|
|
|
|
buffer += 6; // Ignore header
|
|
*w = *((uint16_t*)buffer); buffer+=2;
|
|
*h = *((uint16_t*)buffer); buffer+=2;
|
|
const uint8_t fields = *buffer; buffer+=3;
|
|
|
|
const int color_resolution_bits = ( ( fields & 0x70 ) >> 4 ) + 1;
|
|
|
|
if ( fields & 0x80 )
|
|
{
|
|
global_color_table_size = 1 << ( ( ( fields & 0x07 ) + 1 ) );
|
|
global_color_table = (rgb*)malloc( 3 * global_color_table_size );
|
|
READ(global_color_table, 3 * global_color_table_size);
|
|
}
|
|
|
|
uint8_t block_type = 0x0;
|
|
while ( block_type != 0x3B )
|
|
{
|
|
READ(&block_type, 1);
|
|
|
|
uint8_t size;
|
|
switch ( block_type )
|
|
{
|
|
case 0x2C:
|
|
return process_image_descriptor(buffer, global_color_table, global_color_table_size, color_resolution_bits);
|
|
case 0x21:
|
|
buffer++; //size = *(buffer++); buffer += size;
|
|
do { size = *(buffer++); buffer += size; } while (size != 0);
|
|
break;
|
|
case 0x3B:
|
|
break;
|
|
default:
|
|
fprintf( stderr, "Bailing on unrecognized block type %.02x\n", block_type );
|
|
return NULL;
|
|
}
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
|
|
/*unsigned char* LoadGif(unsigned char *buffer, unsigned short* w, unsigned short* h) {
|
|
return process_gif_stream(buffer, w, h);
|
|
}*/
|