mirror of
https://github.com/python-pillow/Pillow.git
synced 2025-02-05 14:10:52 +03:00
Modify GifEncode.c and Gif.h to use LZW encoding
This commit is contained in:
parent
d40211254c
commit
0acf3514a1
|
@ -9,10 +9,10 @@
|
||||||
|
|
||||||
/* Max size for a LZW code word. */
|
/* Max size for a LZW code word. */
|
||||||
|
|
||||||
#define GIFBITS 12
|
#define GIFBITS 12
|
||||||
|
|
||||||
#define GIFTABLE (1 << GIFBITS)
|
#define GIFTABLE (1<<GIFBITS)
|
||||||
#define GIFBUFFER (1 << GIFBITS)
|
#define GIFBUFFER (1<<GIFBITS)
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
/* CONFIGURATION */
|
/* CONFIGURATION */
|
||||||
|
@ -62,11 +62,8 @@ typedef struct {
|
||||||
|
|
||||||
} GIFDECODERSTATE;
|
} GIFDECODERSTATE;
|
||||||
|
|
||||||
typedef struct GIFENCODERBLOCK_T {
|
/* For GIF LZW encoder. */
|
||||||
struct GIFENCODERBLOCK_T *next;
|
#define TABLE_SIZE 8192
|
||||||
int size;
|
|
||||||
UINT8 data[255];
|
|
||||||
} GIFENCODERBLOCK;
|
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
/* CONFIGURATION */
|
/* CONFIGURATION */
|
||||||
|
@ -84,21 +81,17 @@ typedef struct {
|
||||||
/* PRIVATE CONTEXT (set by encoder) */
|
/* PRIVATE CONTEXT (set by encoder) */
|
||||||
|
|
||||||
/* Interlace parameters */
|
/* Interlace parameters */
|
||||||
int step, repeat;
|
int step;
|
||||||
|
|
||||||
/* Output bit buffer */
|
/* For GIF LZW encoder. */
|
||||||
INT32 bitbuffer;
|
UINT32 put_state;
|
||||||
int bitcount;
|
UINT32 entry_state;
|
||||||
|
UINT32 clear_code, end_code, next_code, max_code;
|
||||||
/* Output buffer list (linked list) */
|
UINT32 code_width, code_bits_left, buf_bits_left;
|
||||||
GIFENCODERBLOCK *block; /* current block */
|
UINT32 code_buffer;
|
||||||
GIFENCODERBLOCK *flush; /* output queue */
|
UINT32 head, tail;
|
||||||
GIFENCODERBLOCK *free; /* if not null, use this */
|
int probe;
|
||||||
|
UINT32 code;
|
||||||
/* Fields used for run-length encoding */
|
UINT32 codes[TABLE_SIZE];
|
||||||
int first; /* true if we haven't read the first pixel */
|
|
||||||
int last; /* last byte value seen */
|
|
||||||
int count; /* how many bytes with that value we've seen */
|
|
||||||
int lastcode;
|
|
||||||
|
|
||||||
} GIFENCODERSTATE;
|
} GIFENCODERSTATE;
|
||||||
|
|
|
@ -10,6 +10,7 @@
|
||||||
* 98-07-09 fl added interlace write support
|
* 98-07-09 fl added interlace write support
|
||||||
* 99-02-07 fl rewritten, now uses a run-length encoding strategy
|
* 99-02-07 fl rewritten, now uses a run-length encoding strategy
|
||||||
* 99-02-08 fl improved run-length encoding for long runs
|
* 99-02-08 fl improved run-length encoding for long runs
|
||||||
|
* 2020-12-12 rdg Reworked for LZW compression.
|
||||||
*
|
*
|
||||||
* Copyright (c) Secret Labs AB 1997-99.
|
* Copyright (c) Secret Labs AB 1997-99.
|
||||||
* Copyright (c) Fredrik Lundh 1997.
|
* Copyright (c) Fredrik Lundh 1997.
|
||||||
|
@ -21,136 +22,199 @@
|
||||||
|
|
||||||
#include "Gif.h"
|
#include "Gif.h"
|
||||||
|
|
||||||
/* codes from 0 to 255 are literals */
|
enum { INIT, ENCODE, FINISH };
|
||||||
#define CLEAR_CODE 256
|
|
||||||
#define EOF_CODE 257
|
|
||||||
#define FIRST_CODE 258
|
|
||||||
#define LAST_CODE 511
|
|
||||||
|
|
||||||
enum { INIT, ENCODE, ENCODE_EOF, FLUSH, EXIT };
|
/* GIF LZW encoder by Raymond Gardner. */
|
||||||
|
/* Released here under PIL license. */
|
||||||
|
|
||||||
/* to make things a little less complicated, we use a simple output
|
/* Return values */
|
||||||
queue to hold completed blocks. the following inlined function
|
#define GLZW_OK 0
|
||||||
adds a byte to the current block. it allocates a new block if
|
#define GLZW_NO_INPUT_AVAIL 1
|
||||||
necessary. */
|
#define GLZW_NO_OUTPUT_AVAIL 2
|
||||||
|
#define GLZW_INTERNAL_ERROR 3
|
||||||
|
|
||||||
static inline int
|
#define CODE_LIMIT 4096
|
||||||
emit(GIFENCODERSTATE *context, int byte) {
|
|
||||||
/* write a byte to the output buffer */
|
|
||||||
|
|
||||||
if (!context->block || context->block->size == 255) {
|
/* Values of entry_state */
|
||||||
GIFENCODERBLOCK *block;
|
enum { LZW_INITIAL, LZW_TRY_IN1, LZW_TRY_IN2, LZW_TRY_OUT1, LZW_TRY_OUT2,
|
||||||
|
LZW_FINISHED };
|
||||||
|
|
||||||
/* no room in the current block (or no current block);
|
/* Values of control_state */
|
||||||
allocate a new one */
|
enum { PUT_HEAD, PUT_INIT_CLEAR, PUT_CLEAR, PUT_LAST_HEAD, PUT_END };
|
||||||
|
|
||||||
/* add current block to end of flush queue */
|
static void glzwe_reset(GIFENCODERSTATE *st) {
|
||||||
if (context->block) {
|
st->next_code = st->end_code + 1;
|
||||||
block = context->flush;
|
st->max_code = 2 * st->clear_code - 1;
|
||||||
while (block && block->next) {
|
st->code_width = st->bits + 1;
|
||||||
block = block->next;
|
memset(st->codes, 0, sizeof(st->codes));
|
||||||
}
|
|
||||||
if (block) {
|
|
||||||
block->next = context->block;
|
|
||||||
} else {
|
|
||||||
context->flush = context->block;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* get a new block */
|
|
||||||
if (context->free) {
|
|
||||||
block = context->free;
|
|
||||||
context->free = NULL;
|
|
||||||
} else {
|
|
||||||
/* malloc check ok, small constant allocation */
|
|
||||||
block = malloc(sizeof(GIFENCODERBLOCK));
|
|
||||||
if (!block) {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
block->size = 0;
|
|
||||||
block->next = NULL;
|
|
||||||
|
|
||||||
context->block = block;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* write new byte to block */
|
|
||||||
context->block->data[context->block->size++] = byte;
|
|
||||||
|
|
||||||
return 1;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* write a code word to the current block. this is a macro to make
|
static void glzwe_init(GIFENCODERSTATE *st) {
|
||||||
sure it's inlined on all platforms */
|
st->clear_code = 1 << st->bits;
|
||||||
|
st->end_code = st->clear_code + 1;
|
||||||
|
glzwe_reset(st);
|
||||||
|
st->entry_state = LZW_INITIAL;
|
||||||
|
st->buf_bits_left = 8;
|
||||||
|
st->code_buffer = 0;
|
||||||
|
}
|
||||||
|
|
||||||
#define EMIT(code) \
|
static int glzwe(GIFENCODERSTATE *st, const UINT8 *in_ptr, UINT8 *out_ptr,
|
||||||
{ \
|
UINT32 *in_avail, UINT32 *out_avail,
|
||||||
context->bitbuffer |= ((INT32)(code)) << context->bitcount; \
|
UINT32 end_of_data) {
|
||||||
context->bitcount += 9; \
|
switch (st->entry_state) {
|
||||||
while (context->bitcount >= 8) { \
|
|
||||||
if (!emit(context, (UINT8)context->bitbuffer)) { \
|
case LZW_TRY_IN1:
|
||||||
state->errcode = IMAGING_CODEC_MEMORY; \
|
get_first_byte:
|
||||||
return 0; \
|
if (!*in_avail) {
|
||||||
} \
|
if (end_of_data) {
|
||||||
context->bitbuffer >>= 8; \
|
goto end_of_data;
|
||||||
context->bitcount -= 8; \
|
}
|
||||||
} \
|
st->entry_state = LZW_TRY_IN1;
|
||||||
}
|
return GLZW_NO_INPUT_AVAIL;
|
||||||
|
}
|
||||||
/* write a run. we use a combination of literals and combinations of
|
st->head = *in_ptr++;
|
||||||
literals. this can give quite decent compression for images with
|
(*in_avail)--;
|
||||||
long stretches of identical pixels. but remember: if you want
|
|
||||||
really good compression, use another file format. */
|
case LZW_TRY_IN2:
|
||||||
|
encode_loop:
|
||||||
#define EMIT_RUN(label) \
|
if (!*in_avail) {
|
||||||
{ \
|
if (end_of_data) {
|
||||||
label: \
|
st->code = st->head;
|
||||||
while (context->count > 0) { \
|
st->put_state = PUT_LAST_HEAD;
|
||||||
int run = 2; \
|
goto put_code;
|
||||||
EMIT(context->last); \
|
}
|
||||||
context->count--; \
|
st->entry_state = LZW_TRY_IN2;
|
||||||
if (state->count++ == LAST_CODE) { \
|
return GLZW_NO_INPUT_AVAIL;
|
||||||
EMIT(CLEAR_CODE); \
|
}
|
||||||
state->count = FIRST_CODE; \
|
st->tail = *in_ptr++;
|
||||||
goto label; \
|
(*in_avail)--;
|
||||||
} \
|
|
||||||
while (context->count >= run) { \
|
/* Knuth TAOCP vol 3 sec. 6.4 algorithm D. */
|
||||||
EMIT(state->count - 1); \
|
/* Hash found experimentally to be pretty good. */
|
||||||
context->count -= run; \
|
/* This works ONLY with TABLE_SIZE a power of 2. */
|
||||||
run++; \
|
st->probe = ((st->head ^ (st->tail << 6)) * 31) & (TABLE_SIZE - 1);
|
||||||
if (state->count++ == LAST_CODE) { \
|
while (st->codes[st->probe]) {
|
||||||
EMIT(CLEAR_CODE); \
|
if ((st->codes[st->probe] & 0xFFFFF) ==
|
||||||
state->count = FIRST_CODE; \
|
((st->head << 8) | st->tail)) {
|
||||||
goto label; \
|
st->head = st->codes[st->probe] >> 20;
|
||||||
} \
|
goto encode_loop;
|
||||||
} \
|
} else {
|
||||||
if (context->count > 1) { \
|
/* Reprobe decrement must be nonzero and relatively prime to table
|
||||||
EMIT(state->count - 1 - (run - context->count)); \
|
* size. So, any odd positive number for power-of-2 size. */
|
||||||
context->count = 0; \
|
if ((st->probe -= ((st->tail << 2) | 1)) < 0) {
|
||||||
if (state->count++ == LAST_CODE) { \
|
st->probe += TABLE_SIZE;
|
||||||
EMIT(CLEAR_CODE); \
|
}
|
||||||
state->count = FIRST_CODE; \
|
}
|
||||||
} \
|
}
|
||||||
break; \
|
/* Key not found, probe is at empty slot. */
|
||||||
} \
|
st->code = st->head;
|
||||||
} \
|
st->put_state = PUT_HEAD;
|
||||||
|
goto put_code;
|
||||||
|
insert_code_or_clear: /* jump here after put_code */
|
||||||
|
if (st->next_code < CODE_LIMIT) {
|
||||||
|
st->codes[st->probe] = (st->next_code << 20) |
|
||||||
|
(st->head << 8) | st->tail;
|
||||||
|
if (st->next_code > st->max_code) {
|
||||||
|
st->max_code = st->max_code * 2 + 1;
|
||||||
|
st->code_width++;
|
||||||
|
}
|
||||||
|
st->next_code++;
|
||||||
|
} else {
|
||||||
|
st->code = st->clear_code;
|
||||||
|
st->put_state = PUT_CLEAR;
|
||||||
|
goto put_code;
|
||||||
|
reset_after_clear: /* jump here after put_code */
|
||||||
|
glzwe_reset(st);
|
||||||
|
}
|
||||||
|
st->head = st->tail;
|
||||||
|
goto encode_loop;
|
||||||
|
|
||||||
|
case LZW_INITIAL:
|
||||||
|
glzwe_reset(st);
|
||||||
|
st->code = st->clear_code;
|
||||||
|
st->put_state = PUT_INIT_CLEAR;
|
||||||
|
put_code:
|
||||||
|
st->code_bits_left = st->code_width;
|
||||||
|
check_buf_bits:
|
||||||
|
if (!st->buf_bits_left) { /* out buffer full */
|
||||||
|
|
||||||
|
case LZW_TRY_OUT1:
|
||||||
|
if (!*out_avail) {
|
||||||
|
st->entry_state = LZW_TRY_OUT1;
|
||||||
|
return GLZW_NO_OUTPUT_AVAIL;
|
||||||
|
}
|
||||||
|
*out_ptr++ = st->code_buffer;
|
||||||
|
(*out_avail)--;
|
||||||
|
st->code_buffer = 0;
|
||||||
|
st->buf_bits_left = 8;
|
||||||
|
}
|
||||||
|
/* code bits to pack */
|
||||||
|
UINT32 n = st->buf_bits_left < st->code_bits_left
|
||||||
|
? st->buf_bits_left : st->code_bits_left;
|
||||||
|
st->code_buffer |=
|
||||||
|
(st->code & ((1 << n) - 1)) << (8 - st->buf_bits_left);
|
||||||
|
st->code >>= n;
|
||||||
|
st->buf_bits_left -= n;
|
||||||
|
st->code_bits_left -= n;
|
||||||
|
if (st->code_bits_left)
|
||||||
|
goto check_buf_bits;
|
||||||
|
switch (st->put_state) {
|
||||||
|
case PUT_INIT_CLEAR:
|
||||||
|
goto get_first_byte;
|
||||||
|
case PUT_HEAD:
|
||||||
|
goto insert_code_or_clear;
|
||||||
|
case PUT_CLEAR:
|
||||||
|
goto reset_after_clear;
|
||||||
|
case PUT_LAST_HEAD:
|
||||||
|
goto end_of_data;
|
||||||
|
case PUT_END:
|
||||||
|
goto flush_code_buffer;
|
||||||
|
default:
|
||||||
|
return GLZW_INTERNAL_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
end_of_data:
|
||||||
|
st->code = st->end_code;
|
||||||
|
st->put_state = PUT_END;
|
||||||
|
goto put_code;
|
||||||
|
flush_code_buffer: /* jump here after put_code */
|
||||||
|
if (st->buf_bits_left < 8) {
|
||||||
|
|
||||||
|
case LZW_TRY_OUT2:
|
||||||
|
if (!*out_avail) {
|
||||||
|
st->entry_state = LZW_TRY_OUT2;
|
||||||
|
return GLZW_NO_OUTPUT_AVAIL;
|
||||||
|
}
|
||||||
|
*out_ptr++ = st->code_buffer;
|
||||||
|
(*out_avail)--;
|
||||||
|
}
|
||||||
|
st->entry_state = LZW_FINISHED;
|
||||||
|
return GLZW_OK;
|
||||||
|
|
||||||
|
case LZW_FINISHED:
|
||||||
|
return GLZW_OK;
|
||||||
|
|
||||||
|
default:
|
||||||
|
return GLZW_INTERNAL_ERROR;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
/* -END- GIF LZW encoder. */
|
||||||
|
|
||||||
int
|
int
|
||||||
ImagingGifEncode(Imaging im, ImagingCodecState state, UINT8 *buf, int bytes) {
|
ImagingGifEncode(Imaging im, ImagingCodecState state, UINT8* buf, int bytes) {
|
||||||
UINT8 *ptr;
|
UINT8* ptr;
|
||||||
int this;
|
UINT8* sub_block_ptr;
|
||||||
|
UINT8* sub_block_limit;
|
||||||
|
UINT8* buf_limit;
|
||||||
|
GIFENCODERSTATE *context = (GIFENCODERSTATE*) state->context;
|
||||||
|
int r;
|
||||||
|
|
||||||
GIFENCODERBLOCK *block;
|
UINT32 in_avail, in_used;
|
||||||
GIFENCODERSTATE *context = (GIFENCODERSTATE *)state->context;
|
UINT32 out_avail, out_used;
|
||||||
|
|
||||||
if (!state->state) {
|
if (state->state == INIT) {
|
||||||
/* place a clear code in the output buffer */
|
state->state = ENCODE;
|
||||||
context->bitbuffer = CLEAR_CODE;
|
glzwe_init(context);
|
||||||
context->bitcount = 9;
|
|
||||||
|
|
||||||
state->count = FIRST_CODE;
|
|
||||||
|
|
||||||
if (context->interlace) {
|
if (context->interlace) {
|
||||||
context->interlace = 1;
|
context->interlace = 1;
|
||||||
|
@ -159,166 +223,132 @@ ImagingGifEncode(Imaging im, ImagingCodecState state, UINT8 *buf, int bytes) {
|
||||||
context->step = 1;
|
context->step = 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
context->last = -1;
|
/* Need at least 2 bytes for data sub-block; 5 for empty image */
|
||||||
|
if (bytes < 5) {
|
||||||
|
state->errcode = IMAGING_CODEC_CONFIG;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
/* sanity check */
|
/* sanity check */
|
||||||
if (state->xsize <= 0 || state->ysize <= 0) {
|
if (state->xsize <= 0 || state->ysize <= 0) {
|
||||||
state->state = ENCODE_EOF;
|
/* Is this better than an error return? */
|
||||||
|
/* This will handle any legal "LZW Minimum Code Size" */
|
||||||
|
memset(buf, 0, 5);
|
||||||
|
in_avail = 0;
|
||||||
|
out_avail = 5;
|
||||||
|
r = glzwe(context, (const UINT8 *)"", buf + 1, &in_avail, &out_avail, 1);
|
||||||
|
if (r == GLZW_OK) {
|
||||||
|
r = 5 - out_avail;
|
||||||
|
if (r < 1 || r > 3) {
|
||||||
|
state->errcode = IMAGING_CODEC_BROKEN;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
buf[0] = r;
|
||||||
|
state->errcode = IMAGING_CODEC_END;
|
||||||
|
return r + 2;
|
||||||
|
} else {
|
||||||
|
/* Should not be possible unless something external to this
|
||||||
|
* routine messes with our state data */
|
||||||
|
state->errcode = IMAGING_CODEC_BROKEN;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
/* Init state->x to make if() below true the first time through. */
|
||||||
|
state->x = state->xsize;
|
||||||
}
|
}
|
||||||
|
|
||||||
ptr = buf;
|
buf_limit = buf + bytes;
|
||||||
|
sub_block_limit = sub_block_ptr = ptr = buf;
|
||||||
|
|
||||||
|
/* On entry, buf is output buffer, bytes is space available in buf.
|
||||||
|
* Loop here getting input until buf is full or image is all encoded. */
|
||||||
for (;;) {
|
for (;;) {
|
||||||
switch (state->state) {
|
/* Set up sub-block ptr and limit. sub_block_ptr stays at beginning
|
||||||
case INIT:
|
* of sub-block until it is full. ptr will advance when any data is
|
||||||
case ENCODE:
|
* placed in buf.
|
||||||
|
*/
|
||||||
|
if (ptr >= sub_block_limit) {
|
||||||
|
if (buf_limit - ptr < 2) { /* Need at least 2 for data sub-block */
|
||||||
|
return ptr - buf;
|
||||||
|
}
|
||||||
|
sub_block_ptr = ptr;
|
||||||
|
sub_block_limit = sub_block_ptr +
|
||||||
|
(256 < buf_limit - sub_block_ptr ?
|
||||||
|
256 : buf_limit - sub_block_ptr);
|
||||||
|
*ptr++ = 0;
|
||||||
|
}
|
||||||
|
|
||||||
/* identify and store a run of pixels */
|
/* Get next row of pixels. */
|
||||||
|
/* This if() originally tested state->x==0 for the first time through.
|
||||||
|
* This no longer works, as the loop will not advance state->x if
|
||||||
|
* glzwe() does not consume any input; this would advance the row
|
||||||
|
* spuriously. Now pre-init state->x above for first time, and avoid
|
||||||
|
* entering if() when state->state is FINISH, or it will loop
|
||||||
|
* infinitely.
|
||||||
|
*/
|
||||||
|
if (state->x >= state->xsize && state->state == ENCODE) {
|
||||||
|
if (!context->interlace && state->y >= state->ysize) {
|
||||||
|
state->state = FINISH;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
if (state->x == 0 || state->x >= state->xsize) {
|
/* get another line of data */
|
||||||
if (!context->interlace && state->y >= state->ysize) {
|
state->shuffle(
|
||||||
state->state = ENCODE_EOF;
|
state->buffer,
|
||||||
|
(UINT8*) im->image[state->y + state->yoff] +
|
||||||
|
state->xoff * im->pixelsize, state->xsize
|
||||||
|
);
|
||||||
|
state->x = 0;
|
||||||
|
|
||||||
|
/* step forward, according to the interlace settings */
|
||||||
|
state->y += context->step;
|
||||||
|
while (context->interlace && state->y >= state->ysize) {
|
||||||
|
switch (context->interlace) {
|
||||||
|
case 1:
|
||||||
|
state->y = 4;
|
||||||
|
context->interlace = 2;
|
||||||
break;
|
break;
|
||||||
}
|
case 2:
|
||||||
|
context->step = 4;
|
||||||
if (context->flush) {
|
state->y = 2;
|
||||||
state->state = FLUSH;
|
context->interlace = 3;
|
||||||
break;
|
break;
|
||||||
}
|
case 3:
|
||||||
|
context->step = 2;
|
||||||
/* get another line of data */
|
state->y = 1;
|
||||||
state->shuffle(
|
context->interlace = 0;
|
||||||
state->buffer,
|
break;
|
||||||
(UINT8 *)im->image[state->y + state->yoff] +
|
default:
|
||||||
state->xoff * im->pixelsize,
|
/* just make sure we don't loop forever */
|
||||||
state->xsize);
|
context->interlace = 0;
|
||||||
|
|
||||||
state->x = 0;
|
|
||||||
|
|
||||||
if (state->state == INIT) {
|
|
||||||
/* preload the run-length buffer and get going */
|
|
||||||
context->last = state->buffer[0];
|
|
||||||
context->count = state->x = 1;
|
|
||||||
state->state = ENCODE;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* step forward, according to the interlace settings */
|
|
||||||
state->y += context->step;
|
|
||||||
while (context->interlace && state->y >= state->ysize)
|
|
||||||
switch (context->interlace) {
|
|
||||||
case 1:
|
|
||||||
state->y = 4;
|
|
||||||
context->interlace = 2;
|
|
||||||
break;
|
|
||||||
case 2:
|
|
||||||
context->step = 4;
|
|
||||||
state->y = 2;
|
|
||||||
context->interlace = 3;
|
|
||||||
break;
|
|
||||||
case 3:
|
|
||||||
context->step = 2;
|
|
||||||
state->y = 1;
|
|
||||||
context->interlace = 0;
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
/* just make sure we don't loop forever */
|
|
||||||
context->interlace = 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
/* Potential special case for xsize==1 */
|
|
||||||
if (state->x < state->xsize) {
|
|
||||||
this = state->buffer[state->x++];
|
|
||||||
} else {
|
|
||||||
EMIT_RUN(label0);
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (this == context->last) {
|
in_avail = state->xsize - state->x; /* bytes left in line */
|
||||||
context->count++;
|
out_avail = sub_block_limit - ptr; /* bytes left in sub-block */
|
||||||
} else {
|
r = glzwe(context, &state->buffer[state->x], ptr, &in_avail,
|
||||||
EMIT_RUN(label1);
|
&out_avail, state->state == FINISH);
|
||||||
context->last = this;
|
out_used = sub_block_limit - ptr - out_avail;
|
||||||
context->count = 1;
|
*sub_block_ptr += out_used;
|
||||||
}
|
ptr += out_used;
|
||||||
break;
|
in_used = state->xsize - state->x - in_avail;
|
||||||
|
state->x += in_used;
|
||||||
|
|
||||||
case ENCODE_EOF:
|
if (r == GLZW_OK) {
|
||||||
|
/* Should not be possible when end-of-data flag is false. */
|
||||||
/* write the final run */
|
state->errcode = IMAGING_CODEC_END;
|
||||||
EMIT_RUN(label2);
|
return ptr - buf;
|
||||||
|
} else if (r == GLZW_NO_INPUT_AVAIL) {
|
||||||
/* write an end of image marker */
|
/* Used all the input line; get another line */
|
||||||
EMIT(EOF_CODE);
|
continue;
|
||||||
|
} else if (r == GLZW_NO_OUTPUT_AVAIL) {
|
||||||
/* empty the bit buffer */
|
/* subblock is full */
|
||||||
while (context->bitcount > 0) {
|
continue;
|
||||||
if (!emit(context, (UINT8)context->bitbuffer)) {
|
} else {
|
||||||
state->errcode = IMAGING_CODEC_MEMORY;
|
/* Should not be possible unless something external to this
|
||||||
return 0;
|
* routine messes with our state data */
|
||||||
}
|
state->errcode = IMAGING_CODEC_BROKEN;
|
||||||
context->bitbuffer >>= 8;
|
return 0;
|
||||||
context->bitcount -= 8;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* flush the last block, and exit */
|
|
||||||
if (context->block) {
|
|
||||||
GIFENCODERBLOCK *block;
|
|
||||||
block = context->flush;
|
|
||||||
while (block && block->next) {
|
|
||||||
block = block->next;
|
|
||||||
}
|
|
||||||
if (block) {
|
|
||||||
block->next = context->block;
|
|
||||||
} else {
|
|
||||||
context->flush = context->block;
|
|
||||||
}
|
|
||||||
context->block = NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
state->state = EXIT;
|
|
||||||
|
|
||||||
/* fall through... */
|
|
||||||
|
|
||||||
case EXIT:
|
|
||||||
case FLUSH:
|
|
||||||
|
|
||||||
while (context->flush) {
|
|
||||||
/* get a block from the flush queue */
|
|
||||||
block = context->flush;
|
|
||||||
|
|
||||||
if (block->size > 0) {
|
|
||||||
/* make sure it fits into the output buffer */
|
|
||||||
if (bytes < block->size + 1) {
|
|
||||||
return ptr - buf;
|
|
||||||
}
|
|
||||||
|
|
||||||
ptr[0] = block->size;
|
|
||||||
memcpy(ptr + 1, block->data, block->size);
|
|
||||||
|
|
||||||
ptr += block->size + 1;
|
|
||||||
bytes -= block->size + 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
context->flush = block->next;
|
|
||||||
|
|
||||||
if (context->free) {
|
|
||||||
free(context->free);
|
|
||||||
}
|
|
||||||
context->free = block;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (state->state == EXIT) {
|
|
||||||
/* this was the last block! */
|
|
||||||
if (context->free) {
|
|
||||||
free(context->free);
|
|
||||||
}
|
|
||||||
state->errcode = IMAGING_CODEC_END;
|
|
||||||
return ptr - buf;
|
|
||||||
}
|
|
||||||
|
|
||||||
state->state = ENCODE;
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue
Block a user