[project @ 2004-05-10 22:14:33 by rjw]

Added support for NETSCAPE2.0 application extension for limited looping.
GIF decoding attempts all images, irrespective of whether they adhere to the GIF87/GIF89a spec.
Disabled logging.

svn path=/import/netsurf/; revision=849
This commit is contained in:
Richard Wilson 2004-05-10 22:14:33 +00:00
parent 7fa938d09c
commit 47ab606edf
2 changed files with 92 additions and 71 deletions

View File

@ -161,15 +161,30 @@ void nsgif_animate(void *p)
{
struct content *c = p;
union content_msg_data data;
/* at the moment just advance by one frame */
/* Advance by a frame, updating the loop count accordingly
*/
c->data.gif.current_frame++;
if (c->data.gif.current_frame == c->data.gif.gif->frame_count) {
c->data.gif.current_frame = 0;
/* A loop count of 0 has a special meaning of infinite
*/
if (c->data.gif.gif->loop_count != 0) {
c->data.gif.gif->loop_count--;
if (c->data.gif.gif->loop_count == 0) {
c->data.gif.gif->loop_count = -1;
}
}
}
schedule(c->data.gif.gif->frames[c->data.gif.current_frame].frame_delay,
nsgif_animate, c);
/* Continue animating if we should
*/
if (c->data.gif.gif->loop_count >= 0) {
schedule(c->data.gif.gif->frames[c->data.gif.current_frame].frame_delay,
nsgif_animate, c);
}
/* area within gif to redraw */
data.redraw.x = c->data.gif.gif->frames[c->data.gif.current_frame].redraw_x;

View File

@ -17,21 +17,21 @@
/* READING GIF FILES
=================
The functions provided by this file allow for efficient progressive GIF
decoding. Whilst the initialisation does not ensure that there is
sufficient image data to complete the entire frame, it does ensure that
the information provided is valid. Any subsequent attempts to decode an
initialised GIF are guaranteed to succeed, and any bytes of the image
not present are assumed to be totally transparent.
To begin decoding a GIF, the 'gif' structure must be initialised with
the 'gif_data' and 'buffer_size' set to their initial values. The
'buffer_position' should initially be 0, and will be internally updated
as the decoding commences. The caller should then repeatedly call
gif_initialise() with the structure until the function returns 1, or
no more data is avaliable.
Once the initialisation has begun, the decoder completes the variables
'frame_count' and 'frame_count_partial'. The former being the total
number of frames that have been successfully initialised, and the
@ -92,7 +92,7 @@ static int clear_image = FALSE;
/* Initialises any workspace held by the animation and attempts to decode
any information that hasn't already been decoded.
If an error occurs, all previously decoded frames are retained.
@return -5 for GIF frame data error
-4 for insufficient data to process any more frames
-3 for memory error
@ -118,10 +118,6 @@ int gif_initialise(struct gif_animation *gif) {
*/
if (gif->buffer_position == 0) {
/* The 12th byte must be a 0 for the block terminator
*/
// if (gif_data[11] != 0x00) return GIF_DATA_ERROR;
/* We want everything to be NULL before we start so we've no chance
of freeing bad pointers (paranoia)
*/
@ -129,26 +125,26 @@ int gif_initialise(struct gif_animation *gif) {
gif->frames = NULL;
gif->local_colour_table = NULL;
gif->global_colour_table = NULL;
/* The caller may have been lazy and not reset any values
*/
gif->frame_count = 0;
gif->frame_count_partial = 0;
gif->decoded_frame = 0xffffffff;
/* Check we are a GIF
*/
if (strncmp(gif_data, "GIF", 3) != 0) {
LOG(("Invalid GIF header - should be 'GIF'"));
// LOG(("Invalid GIF header - should be 'GIF'"));
return GIF_DATA_ERROR;
}
gif_data += 3;
/* Check we are a GIF type 87a or 89a
*/
if ((strncmp(gif_data, "87a", 3) != 0) &&
(strncmp(gif_data, "89a", 3) != 0)) {
LOG(("Unknown GIF format - proceeding anyway"));
// LOG(("Unknown GIF format - proceeding anyway"));
}
gif_data += 3;
@ -161,8 +157,9 @@ int gif_initialise(struct gif_animation *gif) {
gif->background_colour = gif_data[5];
gif->aspect_ratio = gif_data[6];
gif->dirty_frame = -1;
gif->loop_count = 0;
gif_data += 7;
/* Allocate some data irrespective of whether we've got any colour tables. We
always get the maximum size in case a GIF is lying to us. It's far better
to give the wrong colours than to trample over some memory somewhere.
@ -173,7 +170,7 @@ int gif_initialise(struct gif_animation *gif) {
gif_finalise(gif);
return GIF_INSUFFICIENT_MEMORY;
}
/* Set the first colour to a value that will never occur in reality so we
know if we've processed it
*/
@ -233,25 +230,25 @@ int gif_initialise(struct gif_animation *gif) {
gif->global_colour_table[1] = 0xffffffff;
}
}
/* Repeatedly try to decode frames
*/
while ((return_value = gif_initialise_frame(gif)) == 0);
/* If there was a memory error tell the caller
*/
if ((return_value == GIF_INSUFFICIENT_MEMORY) ||
if ((return_value == GIF_INSUFFICIENT_MEMORY) ||
(return_value == GIF_DATA_ERROR)) {
return return_value;
}
/* If we didn't have some frames then a GIF_INSUFFICIENT_DATA becomes a
GIF_INSUFFICIENT_FRAME_DATA
*/
if ((return_value == GIF_INSUFFICIENT_DATA) && (gif->frame_count_partial > 0)) {
return_value = GIF_INSUFFICIENT_FRAME_DATA;
}
/* Return how many we got
*/
return return_value;
@ -260,7 +257,7 @@ int gif_initialise(struct gif_animation *gif) {
/** Updates the sprite memory size
@return -3 for a memory error
0 for success
*/
@ -275,11 +272,11 @@ static int gif_initialise_sprite(struct gif_animation *gif, unsigned int width,
if ((width <= gif->width) && (height <= gif->height)) return 0;
/* Get our maximum values
*/
*/
max_width = (width > gif->width) ? width : gif->width;
max_height = (height > gif->height) ? height : gif->height;
frame_bytes = max_width * max_height * 4 + sizeof(osspriteop_header);
/* Allocate some more memory
*/
if ((buffer = (osspriteop_header *)realloc(gif->frame_image, frame_bytes)) == NULL) {
@ -291,7 +288,7 @@ static int gif_initialise_sprite(struct gif_animation *gif, unsigned int width,
*/
gif->width = max_width;
gif->height = max_height;
/* Update our sprite image
*/
buffer->size = frame_bytes;
@ -306,7 +303,7 @@ static int gif_initialise_sprite(struct gif_animation *gif, unsigned int width,
/* Attempts to initialise the next frame
@return -4 for insufficient data to process the entire frame
-3 for a memory error
-2 for a data error
@ -363,19 +360,18 @@ int gif_initialise_frame(struct gif_animation *gif) {
*/
gif->frame_holders = frame + 1;
}
/* Store our frame pointer. We would do it when allocating except we
start off with one frame allocated so we can always use realloc.
*/
// LOG(("Set frame number %i to offset %i", frame, gif->buffer_position));
gif->frames[frame].frame_pointer = gif->buffer_position;
gif->frames[frame].frame_delay = 100; // Paranoia
gif->frames[frame].redraw_required = 0; // Paranoia
/* Invalidate any previous decoding we have of this frame
*/
if (gif->decoded_frame == frame) gif->decoded_frame = 0xffffffff;
/* We pretend to initialise the frames, but really we just skip over all
the data contained within. This is all basically a cut down version of
gif_decode_frame that doesn't have any of the LZW bits in it.
@ -383,11 +379,11 @@ int gif_initialise_frame(struct gif_animation *gif) {
more_images = 1;
first_image = 1;
while (more_images != 0) {
/* Ensure we have some data
*/
if ((gif_end - gif_data) < 10) return GIF_INSUFFICIENT_FRAME_DATA;
/* Decode the extensions
*/
background_action = 0;
@ -395,41 +391,52 @@ int gif_initialise_frame(struct gif_animation *gif) {
/* Get the extension size
*/
extension_size = gif_data[2];
/* Check we've enough data for the extension then header
*/
if ((gif_end - gif_data) < (int)(extension_size + 13)) return GIF_INSUFFICIENT_FRAME_DATA;
/* Graphic control extension - store the frame delay.
*/
if (gif_data[1] == 0xf9) {
gif->frames[frame].frame_delay = gif_data[4] | (gif_data[5] << 8);
background_action = ((gif_data[3] & 0x1c) >> 2);
more_images = ((gif->frames[frame].frame_delay) == 0);
/* Application extension - handle NETSCAPE2.0 looping
*/
} else if ((gif_data[1] == 0xff) &&
(gif_data[2] == 0x0b) &&
(strncmp(gif_data + 3, "NETSCAPE2.0", 11) == 0) &&
(gif_data[14] == 0x03) &&
(gif_data[15] == 0x01)) {
gif->loop_count = gif_data[16] | (gif_data[17] << 8);
}
/* Move to the first sub-block
*/
gif_data += 2;
/* Skip all the sub-blocks
*/
while (gif_data[0] != 0x00) {
gif_data += gif_data[0] + 1;
if ((gif_end - gif_data) < 10) return GIF_INSUFFICIENT_FRAME_DATA;
if ((gif_end - gif_data) < 10) return GIF_INSUFFICIENT_FRAME_DATA;
}
gif_data++;
}
/* We must have at least one image descriptor
*/
if (gif_data[0] != 0x2c) return GIF_FRAME_DATA_ERROR;
/* Do some simple boundary checking
*/
offset_x = gif_data[1] | (gif_data[2] << 8);
offset_y = gif_data[3] | (gif_data[4] << 8);
width = gif_data[5] | (gif_data[6] << 8);
height = gif_data[7] | (gif_data[8] << 8);
/* Set up the redraw characteristics. We have to check for extending the area
due to multi-image frames.
*/
@ -462,34 +469,34 @@ int gif_initialise_frame(struct gif_animation *gif) {
if (gif_initialise_sprite(gif, (offset_x + width), (offset_y + height))) {
return GIF_INSUFFICIENT_MEMORY;
}
/* Decode the flags
*/
flags = gif_data[9];
colour_table_size = 2 << (flags & 0x07);
/* Move our data onwards and remember we've got a bit of this frame
*/
gif_data += 10;
gif_bytes = (gif_end - gif_data);
gif->frame_count_partial = frame + 1;
/* Skip the local colour table
*/
if (flags & 0x80) {
gif_data += 3 * colour_table_size;
if ((gif_bytes = (gif_end - gif_data)) < 0) return GIF_INSUFFICIENT_FRAME_DATA;
}
/* Ensure we have a correct code size
*/
if (gif_data[0] > GIF_MAX_LZW) return GIF_DATA_ERROR;
/* Move our data onwards
*/
gif_data++;
if (--gif_bytes < 0) return GIF_INSUFFICIENT_FRAME_DATA;
/* Repeatedly skip blocks until we get a zero block or run out of data
*/
block_size = 0;
@ -512,7 +519,7 @@ int gif_initialise_frame(struct gif_animation *gif) {
return GIF_INSUFFICIENT_FRAME_DATA;
} else {
gif->buffer_position = gif_data - gif->gif_data;
gif->frame_count = frame + 1;
gif->frame_count = frame + 1;
if (gif_data[0] == 0x3b) return 1;
}
return 0;
@ -551,7 +558,7 @@ int gif_decode_frame(struct gif_animation *gif, unsigned int frame) {
*/
if (frame > gif->frame_count_partial) return GIF_INSUFFICIENT_DATA;
if ((!clear_image) && (frame == gif->decoded_frame)) return 0;
/* If the previous frame was dirty, remove it
*/
if (!clear_image) {
@ -562,7 +569,7 @@ int gif_decode_frame(struct gif_animation *gif, unsigned int frame) {
}
gif->dirty_frame = -1;
}
/* Get the start of our frame data and the end of the GIF data
*/
gif_data = gif->gif_data + gif->frames[frame].frame_pointer;
@ -589,18 +596,18 @@ int gif_decode_frame(struct gif_animation *gif, unsigned int frame) {
*/
save_buffer_position = gif->buffer_position;
gif->buffer_position = gif_data - gif->gif_data;
/* We've got to do this more than one time if we've got multiple images
*/
more_images = 1;
while (more_images != 0) {
background_action = 0;
/* Ensure we have some data
*/
gif_data = gif->gif_data + gif->buffer_position;
if ((gif_end - gif_data) < 10) return GIF_INSUFFICIENT_FRAME_DATA;
/* Decode the extensions
*/
while (gif_data[0] == 0x21) {
@ -608,7 +615,7 @@ int gif_decode_frame(struct gif_animation *gif, unsigned int frame) {
/* Get the extension size
*/
extension_size = gif_data[2];
/* Check we've enough data for the extension then header
*/
if ((gif_end - gif_data) < (int)(extension_size + 13)) return GIF_INSUFFICIENT_FRAME_DATA;
@ -624,12 +631,12 @@ int gif_decode_frame(struct gif_animation *gif, unsigned int frame) {
/* Move to the first sub-block
*/
gif_data += 2;
/* Skip all the sub-blocks
*/
while (gif_data[0] != 0x00) {
gif_data += gif_data[0] + 1;
if ((gif_end - gif_data) < 10) return GIF_INSUFFICIENT_FRAME_DATA;
if ((gif_end - gif_data) < 10) return GIF_INSUFFICIENT_FRAME_DATA;
}
gif_data++;
}
@ -648,7 +655,7 @@ int gif_decode_frame(struct gif_animation *gif, unsigned int frame) {
if ((offset_x + width > gif->width) || (offset_y + height > gif->height)) {
return GIF_DATA_ERROR;
}
/* Decode the flags
*/
flags = gif_data[9];
@ -659,7 +666,7 @@ int gif_decode_frame(struct gif_animation *gif, unsigned int frame) {
*/
gif_data += 10;
gif_bytes = (int)(gif_end - gif_data);
/* Set up the colour table
*/
if (flags & 0x80) {
@ -687,7 +694,7 @@ int gif_decode_frame(struct gif_animation *gif, unsigned int frame) {
if ((background_action == 2) || (background_action == 3)) {
gif->dirty_frame = frame;
}
/* Initialise the LZW decoding
*/
set_code_size = gif_data[0];
@ -725,7 +732,7 @@ int gif_decode_frame(struct gif_animation *gif, unsigned int frame) {
} else {
return_value = GIF_INSUFFICIENT_FRAME_DATA;
goto gif_decode_frame_exit;
}
}
}
}
} else {
@ -737,7 +744,7 @@ int gif_decode_frame(struct gif_animation *gif, unsigned int frame) {
memset(frame_scanline, 0x00, width * 4);
}
}
/* Repeatedly skip blocks until we get a zero block or run out of data
*/
gif_bytes = gif->buffer_size - gif->buffer_position;
@ -786,14 +793,13 @@ static unsigned int gif_interlaced_line(unsigned int height, unsigned int y) {
/* Releases any workspace held by the animation
*/
void gif_finalise(struct gif_animation *gif) {
LOG(("Finalising image"));
/* Release all our memory blocks
*/
free(gif->frame_image);
gif->frame_image = NULL;
free(gif->frames);
gif->frames = NULL;
free(gif->local_colour_table);
free(gif->local_colour_table);
gif->local_colour_table = NULL;
free(gif->global_colour_table);
gif->global_colour_table = NULL;
@ -811,7 +817,7 @@ static int gif_next_LZW(struct gif_animation *gif) {
/* Check we have a valid clear code
*/
if (clear_code >= (1 << GIF_MAX_LZW)) return -2;
/* Initialise our table
*/
for (i = 0; i < (unsigned int)clear_code; ++i) {
@ -821,7 +827,7 @@ static int gif_next_LZW(struct gif_animation *gif) {
for (; i < (1 << GIF_MAX_LZW); ++i) {
table[0][i] = table[1][i] = 0;
}
/* Update our LZW parameters
*/
code_size = set_code_size + 1;
@ -860,7 +866,7 @@ static int gif_next_LZW(struct gif_animation *gif) {
if (code == table[0][code]) return(code);
if (((char *)stack_pointer - (char *)stack) >= (int)sizeof(stack)) return(code);
code = table[0][code];
}
}
*stack_pointer++ = firstcode = table[1][code];
@ -872,7 +878,7 @@ static int gif_next_LZW(struct gif_animation *gif) {
max_code_size *= 2;
++code_size;
}
}
}
oldcode = incode;
@ -906,7 +912,7 @@ static int gif_next_code(struct gif_animation *gif, int code_size) {
lastbit = (2 + count) * 8;
end = curbit + code_size;
}
j = end / 8;
i = curbit / 8;
if (i == j) {
@ -916,7 +922,7 @@ static int gif_next_code(struct gif_animation *gif, int code_size) {
} else {
ret = (long)buf[i] | ((long)buf[i+1] << 8) | ((long)buf[i+2] << 16);
}
ret = (ret >> (curbit % 8)) & maskTbl[code_size];
curbit += code_size;
return (int)ret;
@ -925,12 +931,12 @@ static int gif_next_code(struct gif_animation *gif, int code_size) {
static int gif_next_block(struct gif_animation *gif, unsigned char *buf) {
unsigned int block_size;
unsigned char *gif_data;
gif_data = gif->gif_data + gif->buffer_position;
zero_data_block = ((block_size = gif_data[0]) == 0);
if ((gif->buffer_position + block_size) >= gif->buffer_size) {
LOG(("Insufficient data to read %i bytes", block_size));
// LOG(("Insufficient data to read %i bytes", block_size));
return -1;
}
if (block_size > 0) memcpy(buf, gif_data + 1, block_size);