bug 285872 GIF Decoder: replace gathering buffer with dynamic malloc to fixed

256 bytes hold
patch by Alfred Kayser <alfredkayser@nl.ibm.com> r/sr=tor/pavlov
This commit is contained in:
cbiesinger%web.de 2005-08-22 18:10:23 +00:00
parent 7d86ff9edd
commit eaaf0466e4
3 changed files with 130 additions and 204 deletions

View File

@ -79,57 +79,24 @@ mailing address.
#include "nsGIFDecoder2.h"
/* Gather n characters from the input stream and then enter state s. */
/*
* GETN(n, s) requests at least 'n' bytes available from 'q', at start of state 's'
*
* Note, the hold will never need to be bigger than 256 bytes to gather up in the hold,
* as each GIF block (except colormaps) can never be bigger than 256 bytes.
* Colormaps are directly copied in the resp. global_colormap or dynamically allocated local_colormap.
* So a fixed buffer in gif_struct is good enough.
* This buffer is only needed to copy left-over data from one GifWrite call to the next
*/
#define GETN(n,s) \
PR_BEGIN_MACRO \
gs->state = gif_gather; \
gs->gather_request_size = (n); \
gs->post_gather_state = s; \
gs->bytes_to_consume = (n); \
gs->state = (s); \
PR_END_MACRO
/* Get a 16-bit value stored in little-endian format */
#define GETINT16(p) ((p)[1]<<8|(p)[0])
/* Get a 32-bit value stored in little-endian format */
#define GETINT32(p) (((p)[3]<<24) | ((p)[2]<<16) | ((p)[1]<<8) | ((p)[0]))
//******************************************************************************
/* binary block Allocate and Concatenate
*
* destination_length is the length of the existing block
* source_length is the length of the block being added to the
* destination block
*/
static char *il_BACat (char **destination,
size_t destination_length,
const char *source,
size_t source_length)
{
if (source) {
if (*destination) {
*destination = (char *) PR_REALLOC (*destination,
destination_length + source_length);
if (*destination == nsnull)
return (nsnull);
memmove(*destination + destination_length, source, source_length);
}
else {
*destination = (char *) PR_MALLOC (source_length);
if (*destination == nsnull)
return (nsnull);
memcpy(*destination, source, source_length);
}
}
return *destination;
}
#undef BlockAllocCat
#define BlockAllocCat(dest, dest_length, src, src_length) \
il_BACat(&(dest), dest_length, src, src_length)
//******************************************************************************
// Send the data to the display front-end.
static void output_row(gif_struct *gs)
@ -411,28 +378,13 @@ PRBool GIFInit(gif_struct* gs, void* aClientData)
memset(gs, 0, offsetof(gif_struct, prefix));
gs->clientptr = aClientData;
gs->state = gif_init;
gs->post_gather_state = gif_error;
gs->gathered = 0;
// Start with the version (GIF89a|GIF87a)
gs->state = gif_type;
gs->bytes_to_consume = 6;
return PR_TRUE;
}
/* Maximum # of bytes to read ahead while waiting for delay_time to expire.
We no longer limit this number to remain within WIN16 malloc limitations
of 0xffff */
#define MAX_READ_AHEAD (0xFFFFFFL)
//******************************************************************************
PRBool gif_write_ready(const gif_struct* gs)
{
if (!gs)
return PR_FALSE;
return (gs->gathered < MAX_READ_AHEAD);
}
/******************************************************************************/
/*
* process data arriving from the stream for the gif decoder
@ -440,20 +392,55 @@ PRBool gif_write_ready(const gif_struct* gs)
PRStatus gif_write(gif_struct *gs, const PRUint8 *buf, PRUint32 len)
{
if (!gs)
if (!gs || !len)
return PR_FAILURE;
/* If we fail, some upstream data provider ignored the
zero return value from il_gif_write_ready() which says not to
send any more data to this stream until the delay timeout fires. */
if ((len != 0) && (gs->gathered >= MAX_READ_AHEAD))
return PR_FAILURE;
const PRUint8 *q = buf;
const PRUint8 *q, *p = buf, *ep = buf + len;
// Add what we have sofar to the block
// If previous call to me left something in the hold first complete current block
// Or if we are filling the colormaps, first complete the colormap
PRUint8* p;
if (gs->state == gif_global_colormap)
p = gs->global_colormap;
else if (gs->state == gif_image_colormap)
p = gs->local_colormap;
else if (gs->bytes_in_hold)
p = gs->hold;
else
p = nsnull;
q = nsnull; /* Initialize to shut up gcc warnings */
if (p) {
// Add what we have sofar to the block
PRUint32 l = PR_MIN(len, gs->bytes_to_consume);
memcpy(p+gs->bytes_in_hold, buf, l);
if (l < gs->bytes_to_consume) {
// Not enough in 'buf' to complete current block, get more
gs->bytes_in_hold += l;
gs->bytes_to_consume -= l;
return PR_SUCCESS;
}
// Reset hold buffer count
gs->bytes_in_hold = 0;
// Point 'q' to complete block in hold (or in colormap)
q = p;
}
// Invariant:
// 'q' is start of current to be processed block (hold, colormap or buf)
// 'bytes_to_consume' is number of bytes to consume from 'buf'
// 'buf' points to the bytes to be consumed from the input buffer
// 'len' is number of bytes left in input buffer from position 'buf'.
// At entrance of the for loop will 'buf' will be moved 'bytes_to_consume'
// to point to next buffer, 'len' is adjusted accordingly.
// So that next round in for loop, q gets pointed to the next buffer.
for (;len >= gs->bytes_to_consume; q=buf) {
// Eat the current block from the buffer, q keeps pointed at current block
buf += gs->bytes_to_consume;
len -= gs->bytes_to_consume;
while (p <= ep) {
switch (gs->state)
{
case gif_lzw:
@ -496,29 +483,12 @@ PRStatus gif_write(gif_struct *gs, const PRUint8 *buf, PRUint32 len)
}
break;
/* We're positioned at the very start of the file. */
case gif_init:
{
GETN(3, gif_type);
break;
}
/* All GIF files begin with "GIF87a" or "GIF89a" */
case gif_type:
{
if (strncmp((char*)q, "GIF", 3)) {
gs->state = gif_error;
break;
}
GETN(3, gif_version);
}
break;
case gif_version:
{
if (!strncmp((char*)q, "89a", 3)) {
if (!strncmp((char*)q, "GIF89a", 6)) {
gs->version = 89;
} else if (!strncmp((char*)q, "87a", 3)) {
} else if (!strncmp((char*)q, "GIF87a", 6)) {
gs->version = 87;
} else {
gs->state = gif_error;
@ -551,11 +521,21 @@ PRStatus gif_write(gif_struct *gs, const PRUint8 *buf, PRUint32 len)
gs->screen_height,
gs->screen_bgcolor);
if (q[4] & 0x80) /* global map */
/* 3 bytes for each entry in the global colormap */
GETN(gs->global_colormap_size*3, gif_global_colormap);
else
GETN(1, gif_image_start);
if (q[4] & 0x80) { /* global map */
// Get the global colormap
const PRUint32 size = 3*gs->global_colormap_size;
if (len < size) {
// Use 'hold' pattern to get the global colormap
GETN(size, gif_global_colormap);
break;
}
// Copy everything and directly go to gif_lzw_start
memcpy(gs->global_colormap, buf, size);
buf += size;
len -= size;
}
GETN(1, gif_image_start);
// q[6] = Pixel Aspect Ratio
// Not used
@ -564,11 +544,8 @@ PRStatus gif_write(gif_struct *gs, const PRUint8 *buf, PRUint32 len)
break;
case gif_global_colormap:
{
memcpy(gs->global_colormap, q, 3 * gs->global_colormap_size);
// Everything is already copied into global_colormap
GETN(1, gif_image_start);
}
break;
case gif_image_start:
@ -669,9 +646,8 @@ PRStatus gif_write(gif_struct *gs, const PRUint8 *buf, PRUint32 len)
case gif_comment_extension:
{
gs->count = *q;
if (gs->count)
GETN(gs->count, gif_consume_comment);
if (*q)
GETN(*q, gif_consume_comment);
else
GETN(1, gif_image_start);
}
@ -833,43 +809,46 @@ PRStatus gif_write(gif_struct *gs, const PRUint8 *buf, PRUint32 len)
gs->rowend = gs->rowbuf + gs->width;
gs->rowp = gs->rowbuf;
/* bits per pixel is 1<<((q[8]&0x07) + 1); */
/* bits per pixel is q[8]&0x07 */
if (q[8] & 0x80) /* has a local colormap? */
{
int num_colors = 2 << (q[8] & 0x7);
// If current local_colormap is not big enough, force reallocation
if (num_colors > gs->local_colormap_size)
PR_FREEIF(gs->local_colormap);
gs->local_colormap_size = num_colors;
const PRUint32 size = 3*num_colors;
PRUint8 *map = gs->local_colormap;
if (!map || (num_colors > gs->local_colormap_size)) {
map = (PRUint8*)PR_REALLOC(map, size);
if (!map) {
gs->state = gif_oom;
break;
}
}
/* Switch to the new local palette after it loads */
gs->local_colormap = map;
gs->local_colormap_size = num_colors;
gs->is_local_colormap_defined = PR_TRUE;
GETN(gs->local_colormap_size * 3, gif_image_colormap);
if (len < size) {
// Use 'hold' pattern to get the image colormap
GETN(size, gif_image_colormap);
break;
}
// Copy everything and directly go to gif_lzw_start
memcpy(gs->local_colormap, buf, size);
buf += size;
len -= size;
} else {
/* Switch back to the global palette */
gs->is_local_colormap_defined = PR_FALSE;
GETN(1, gif_lzw_start);
}
GETN(1, gif_lzw_start);
}
break;
case gif_image_colormap:
{
PRUint8 *map = gs->local_colormap;
if (!map) {
map = gs->local_colormap = (PRUint8*)PR_MALLOC(3 * gs->local_colormap_size);
if (!map) {
gs->state = gif_oom;
break;
}
}
memcpy(map, q, 3 * gs->local_colormap_size);
// Everything is already copied into local_colormap
GETN(1, gif_lzw_start);
}
break;
case gif_sub_block:
@ -900,6 +879,7 @@ PRStatus gif_write(gif_struct *gs, const PRUint8 *buf, PRUint32 len)
gs->delay_time);
/* Clear state from this image */
gs->is_local_colormap_defined = PR_FALSE;
gs->is_transparent = PR_FALSE;
/* An image can specify a delay time before which to display
@ -917,59 +897,6 @@ PRStatus gif_write(gif_struct *gs, const PRUint8 *buf, PRUint32 len)
return PR_SUCCESS;
break;
case gif_delay:
case gif_gather:
{
PRInt32 gather_remaining;
PRInt32 request_size = gs->gather_request_size;
{
gather_remaining = request_size - gs->gathered;
/* Do we already have enough data in the accumulation
buffer to satisfy the request ? (This can happen
after we transition from the gif_delay state.) */
if (gather_remaining <= 0) {
gs->gathered -= request_size;
q = gs->gather_head;
gs->gather_head += request_size;
gs->state = gs->post_gather_state;
break;
}
/* Shift remaining data to the head of the buffer */
if (gs->gathered && (gs->gather_head != gs->hold)) {
memmove(gs->hold, gs->gather_head, gs->gathered);
gs->gather_head = gs->hold;
}
/* If we add the data just handed to us by the netlib
to what we've already gathered, is there enough to satisfy
the current request ? */
if ((ep - p) >= gather_remaining) {
if (gs->gathered) { /* finish a prior gather */
char *hold = (char*)gs->hold;
BlockAllocCat(hold, gs->gathered, (char*)p, gather_remaining);
gs->hold = (PRUint8*)hold;
q = gs->gather_head = gs->hold;
gs->gathered = 0;
} else
q = p;
p += gather_remaining;
gs->state = gs->post_gather_state;
} else {
char *hold = (char*)gs->hold;
BlockAllocCat(hold, gs->gathered, (char*)p, ep - p);
gs->hold = (PRUint8*)hold;
gs->gather_head = gs->hold;
gs->gathered += ep-p;
return PR_SUCCESS;
}
}
}
break;
// Handle out of memory errors
case gif_oom:
return PR_FAILURE;
@ -979,15 +906,27 @@ PRStatus gif_write(gif_struct *gs, const PRUint8 *buf, PRUint32 len)
nsGIFDecoder2::EndGIF(gs->clientptr, gs->loop_count);
return PR_SUCCESS;
case gif_stop_animating:
return PR_SUCCESS;
// We shouldn't ever get here.
default:
break;
}
}
// Copy the leftover into gs->hold
gs->bytes_in_hold = len;
if (len) {
// Add what we have sofar to the block
PRUint8* p;
if (gs->state == gif_global_colormap)
p = gs->global_colormap;
else if (gs->state == gif_image_colormap)
p = gs->local_colormap;
else
p = gs->hold;
memcpy(p, buf, len);
gs->bytes_to_consume -= len;
}
return PR_SUCCESS;
}
@ -1003,8 +942,6 @@ void gif_destroy(gif_struct *gs)
gs->delay_time = 0;
PR_FREEIF(gs->rowbuf);
PR_FREEIF(gs->hold);
PR_FREEIF(gs->local_colormap);
}

View File

@ -41,39 +41,35 @@
#define MAX_BITS 4097 /* 2^MAX_LZW_BITS+1 */
#define MINIMUM_DELAY_TIME 100
#define MAX_COLORS 256
#define MAX_HOLD_SIZE 256
/* gif2.h
The interface for the GIF87/89a decoder.
*/
// List of possible parsing states
typedef enum {
gif_gather,
gif_init, //1
gif_type,
gif_version,
gif_global_header,
gif_global_colormap,
gif_image_start, //6
gif_image_start,
gif_image_header,
gif_image_colormap,
gif_image_body,
gif_lzw_start,
gif_lzw, //11
gif_lzw,
gif_sub_block,
gif_extension,
gif_control_extension,
gif_consume_block,
gif_skip_block,
gif_done, //17
gif_done,
gif_oom,
gif_error,
gif_comment_extension,
gif_application_extension,
gif_netscape_extension_block,
gif_consume_netscape_extension,
gif_consume_comment,
gif_delay,
gif_stop_animating //added for animation stop
gif_consume_comment
} gstate;
/* "Disposal" method indicates how the image should be handled in the
@ -90,12 +86,9 @@ typedef enum
typedef struct gif_struct {
void* clientptr;
/* Parsing state machine */
gstate state; /* Curent decoder master state */
PRUint8 *hold; /* Accumulation buffer */
PRUint8 *gather_head; /* Next byte to read in accumulation buffer */
int32 gather_request_size; /* Number of bytes to accumulate */
int32 gathered; /* bytes accumulated so far*/
gstate post_gather_state; /* State after requested bytes accumulated */
gstate state; /* Curent decoder master state */
PRUint32 bytes_to_consume; /* Number of bytes to accumulate */
PRUint32 bytes_in_hold; /* bytes accumulated so far*/
/* LZW decoder state machine */
PRUint8 *stackp; /* Current stack pointer */
@ -144,6 +137,7 @@ typedef struct gif_struct {
PRPackedBool is_local_colormap_defined;
PRUint16 prefix[MAX_BITS]; /* LZW decoding tables */
PRUint8 hold[MAX_HOLD_SIZE]; /* Accumulation buffer */
PRUint8 global_colormap[3*MAX_COLORS]; /* Default colormap if local not supplied, 3 bytes for each color */
PRUint8 suffix[MAX_BITS]; /* LZW decoding tables */
PRUint8 stack[MAX_BITS]; /* Base of LZW decoder stack */

View File

@ -223,14 +223,9 @@ nsresult nsGIFDecoder2::ProcessData(unsigned char *data, PRUint32 count, PRUint3
{
// Push the data to the GIF decoder
// First we ask if the gif decoder is ready for more data, and if so, push it.
// In the new decoder, we should always be able to process more data since
// we don't wait to decode each frame in an animation now.
if (gif_write_ready(mGIFStruct)) {
PRStatus result = gif_write(mGIFStruct, data, count);
if (result != PR_SUCCESS)
return NS_ERROR_FAILURE;
}
PRStatus result = gif_write(mGIFStruct, data, count);
if (result != PR_SUCCESS)
return NS_ERROR_FAILURE;
if (mImageFrame && mObserver) {
FlushImageData();