summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRichard Wilson <rjw@netsurf-browser.org>2004-04-06 23:13:25 +0000
committerRichard Wilson <rjw@netsurf-browser.org>2004-04-06 23:13:25 +0000
commit8b7d128a570ff615607faa86549707ff3d91e198 (patch)
tree16ab2fa3b534e98158f15e5cfbec090fa021875d
parent48c685bfaab2050bdcc6e1f2504c34a2cef5f0e1 (diff)
downloadnetsurf-8b7d128a570ff615607faa86549707ff3d91e198.tar.gz
netsurf-8b7d128a570ff615607faa86549707ff3d91e198.tar.bz2
[project @ 2004-04-06 23:13:25 by rjw]
New GIF reading code. svn path=/import/netsurf/; revision=732
-rw-r--r--riscos/gif.c549
-rw-r--r--riscos/gif.h38
-rw-r--r--riscos/gifread.c915
-rw-r--r--riscos/gifread.h25
4 files changed, 977 insertions, 550 deletions
diff --git a/riscos/gif.c b/riscos/gif.c
index b559268f8..94fdc6a42 100644
--- a/riscos/gif.c
+++ b/riscos/gif.c
@@ -14,381 +14,90 @@
#include <stdbool.h>
#include <stdlib.h>
#include <swis.h>
-#include "animlib/animlib.h"
-#include "oslib/colourtrans.h"
-#include "oslib/os.h"
#include "oslib/osfile.h"
#include "oslib/osspriteop.h"
#include "netsurf/utils/config.h"
#include "netsurf/content/content.h"
#include "netsurf/riscos/gif.h"
+#include "netsurf/riscos/gifread.h"
#include "netsurf/riscos/gui.h"
#include "netsurf/riscos/options.h"
#include "netsurf/riscos/tinct.h"
#include "netsurf/utils/log.h"
-#include "netsurf/utils/messages.h"
#include "netsurf/utils/utils.h"
-/* REVISED GIF FUNCTIONALITY
- =========================
+/* GIF FUNCTIONALITY
+ =================
- To improve the display quality of the GIFs, Tinct is used when plotting the
- images. This provides a speed gain as the necessary translation tables are
- cached, but comes at the expense that the current frame must be held as a
- 32bpp sprite.
-
- To overcome the problem of memory wastage, each frame of the GIF is held as
- a Nbpp paletted sprite and is converted into a 32bpp sprite suitable for
- plotting following a frame transition. This conversion is performed by using
- Tinct_ConvertSprite.
-
- By using this technique rather than dynamically decompressing the current
- GIF frame we can skip over frames that we can't display and always keep
- pace with the animation simply. If we were dynamically decompressing the
- GIF then it would be necessary to also decompress any intermediate frames as
- the GIF format dictates that each successive frame is plotted on top of any
- previous one.
-
- [rjw] - Thu 18th March 2004
+ All GIFs are dynamically decompressed using the routines that gifread.c
+ provides. Whilst this allows support for progressive decoding, it is
+ not implemented here as NetSurf currently does not provide such support.
+
+ [rjw] - Sun 4th April 2004
*/
#ifdef WITH_GIF
-static bool nsgif_decompress_frame(struct content *c, anim *p_gif_animation,
- unsigned int cur_frame);
static void nsgif_animate(void *p);
-static osspriteop_header *nsgif_get_sprite_address(struct content *c,
- unsigned int frame);
-static void CompressSpriteLine( pixel *dest, const pixel *src, int n, int bpp );
-static void CompressMaskLine( pixel *dest, const pixel *src, int n, int bpp );
void nsgif_init(void) {
}
void nsgif_create(struct content *c, const char *params[]) {
- c->data.gif.sprite_area = 0;
- c->data.gif.buffer_pos = 0;
- c->data.gif.total_frames = 0; // Paranoid
+ /* Initialise our data structure
+ */
+ c->data.gif.gif = (gif_animation *)xcalloc(sizeof(gif_animation), 1);
+ c->data.gif.current_frame = 0;
}
int nsgif_convert(struct content *c, unsigned int iwidth, unsigned int iheight) {
- struct osspriteop_area *sprite_area;
-
- unsigned int sprite_area_size;
- unsigned int frame_count;
- unsigned int cur_frame;
- unsigned int frame_colours;
- unsigned int frame_bpp;
- unsigned int frame_size;
- unsigned int *frame_delays;
-
- anim gif_animation;
- frame gif_frame;
-
-
- /* Get an anim object from our data
- */
- gif_animation = Anim_FromData(c->source_data, c->source_size, NULL, false, false, false);
- if (!gif_animation) {
- LOG(("Error creating anim object"));
- return 1;
- }
-
- /* Check we have some frames
+ struct gif_animation *gif;
+
+ /* Create our animation
*/
- if (gif_animation->nFrames < 1) {
- LOG(("No frames found"));
- Anim_Destroy(&gif_animation);
- return 1;
- }
-
- /* Store the animation details
+ gif = c->data.gif.gif;
+ gif->gif_data = c->source_data;
+ gif->buffer_size = c->source_size;
+ gif->buffer_position = 0; // Paranoid
+
+ /* Initialise the GIF
*/
- c->width = gif_animation->nWidth;
- c->height = gif_animation->nHeight;
- c->data.gif.current_frame = 0;
- c->data.gif.expanded_frame = 0xffffffff; // ie invalid value
- c->data.gif.remainder_time = 0;
- c->data.gif.total_frames = frame_count = gif_animation->nFrames;
- c->data.gif.animate_gif = (frame_count > 1);
- c->data.gif.loop_gif = true;
-
- /* Claim a buffer for the cached 32bpp version of the current frame. By
- doing this now we can use the same memory area as the temporary buffer
- for decoding all the subsequent frames later.
+ gif_initialise(gif);
+
+ /* Store our content width
*/
- struct osspriteop_header *temp_buf = xcalloc(gif_animation->nWidth *
- gif_animation->nHeight + 11, 4);
- c->data.gif.buffer_header = (osspriteop_header*)(temp_buf);
+ c->width = gif->width;
+ c->height = gif->height;
- /* We can store our frame transitions now too
+ /* Schedule the animation if we have one
*/
- if (frame_count > 1) {
- frame_delays = xcalloc(frame_count, sizeof(int));
- c->data.gif.frame_transitions = frame_delays;
+ if (gif->frame_count > 1) {
+ schedule(gif->frame_delays[0], nsgif_animate, c);
}
- /* Now we need to work out the total size of the sprite area. If we are
- doing dynamic decompression then this can simply be an 8bpp paletted
- sprite of the required dimensions as all frames will fit.
- For dynamic decompression, the frame delay buffer must still be filled
- in a similar manner.
- */
- sprite_area_size = sizeof(osspriteop_area);
- for (cur_frame = 0; cur_frame < frame_count; cur_frame++) {
-
- /* Increment by the header size
- */
- sprite_area_size += sizeof(osspriteop_header);
-
- /* Get the frame details
- */
- gif_frame = gif_animation->pFrames + cur_frame;
- frame_colours = gif_frame->pal->nColours;
-
- /* Store our transition time
- */
- if (frame_count > 1) {
- frame_delays[cur_frame] = gif_frame->csDelay;
- }
-
- /* Get the minimum number of bpp for this frame
- */
- frame_bpp = 8;
- if (frame_colours <=16) frame_bpp = 4;
- if (frame_colours <=4) frame_bpp = 2;
- if (frame_colours <=2) frame_bpp = 1;
-
- /* Increase our area by our palette size. Due to legacy flashing
- colour support, RISC OS lumbers all sprites with two words of
- palette data per colour.
- */
- sprite_area_size += (8 << frame_bpp);
-
- /* Now we need to calculate how big each sprite is given the
- current number of bits per pixel.
- */
- frame_size = (((((gif_animation->nWidth * frame_bpp) + 31) & ~31) >> 3) *
- gif_animation->nHeight);
-
- /* Finally we add in our frame size, and add it again if we have
- some mask data.
- */
- if (gif_frame->pMaskData) frame_size *= 2;
- sprite_area_size += frame_size;
- }
-
- /* So, we now have the size needed so we can create our sprite area and
- fill in some data for it.
- */
- sprite_area = xcalloc(sprite_area_size, 1);
- sprite_area->size = sprite_area_size;
- sprite_area->sprite_count = frame_count;
- sprite_area->first = sizeof(osspriteop_area);
- sprite_area->used = sprite_area_size;
- c->data.gif.sprite_area = sprite_area;
-
- /* Now we need to decompress all our frames. This is handled by a
- sub-routine so we can easily modify this object to do dynamic
- decompression if desired.
- */
- for (cur_frame = 0; cur_frame < frame_count; cur_frame++) {
-
- /* Decompress the frame. We don't worry if we failed as
- we'll have an empty sprite that'll just make the animation
- look wrong rather than having no animation at all.
- If we wanted we could stop at this frame and set the maximum
- number of frames as our current frame.
- */
- nsgif_decompress_frame(c, &gif_animation, cur_frame);
-
- }
-
- /* Destroy our animation data. If things are being done dynamically
- then this needs to be done in nsgif_destroy or things will go
- horribly wrong.
- */
- Anim_Destroy(&gif_animation);
-
- /* Finish things off
- */
- c->title = xcalloc(100, sizeof(char));
- sprintf(c->title, messages_get("GIFTitle"), c->width, c->height);
- c->status = CONTENT_STATUS_DONE;
-
- /* Debugging helpers
- */
-/* xosspriteop_save_sprite_file(osspriteop_USER_AREA,
- c->data.gif.sprite_area, "gif");
- if (frame_count > 1) {
- xosfile_save_stamped("gif_frames", 0xffd,
- frame_delays, (unsigned int*)(frame_delays + frame_count));
- }
-*/
-
- if (c->data.gif.animate_gif)
- schedule(frame_delays[0], nsgif_animate, c);
-
/* Exit as a success
*/
+ c->status = CONTENT_STATUS_DONE;
return 0;
}
-/** Decompresses a GIF frame.
- NB: This call uses the current decompressed image as a temporary buffer.
-
- @param c The content store the data back to
- @param p_gif_animation A pointer to the GIF animation to read from
- @param cur_frame The desired frame [0...(max-1)]
- @return <code>true</code> on success, <code>false</code> otherwise
-*/
-bool nsgif_decompress_frame(struct content *c, anim *p_gif_animation, unsigned int cur_frame) {
-
- struct osspriteop_header *sprite_header;
- anim gif_animation = *p_gif_animation;
- frame gif_frame;
- palette frame_palette;
- const unsigned int *palette_entries;
- unsigned int frame_colours;
- unsigned int frame_bpp;
- unsigned int scanline_size;
- unsigned int frame_size;
- unsigned int *sprite_palette;
- unsigned int loop;
- pixel *src;
- pixel *dest;
-
- /* Get the frame details
- */
- gif_frame = gif_animation->pFrames + cur_frame;
- frame_palette = gif_frame->pal;
- palette_entries = frame_palette->pColours;
- frame_colours = frame_palette->nColours;
-
- /* Get the minimum number of bpp for this frame
- */
- frame_bpp = 8;
- if (frame_colours <=16) frame_bpp = 4;
- if (frame_colours <=4) frame_bpp = 2;
- if (frame_colours <=2) frame_bpp = 1;
-
- /* Now we need to calculate how big each sprite is given the
- current number of bits per pixel.
- */
- scanline_size = ((((gif_animation->nWidth * frame_bpp) + 31) & ~31) >> 3);
- frame_size = scanline_size * gif_animation->nHeight;
-
- /* Get our current sprite. For dynamic decompression we should always use 0.
- */
- sprite_header = nsgif_get_sprite_address(c, cur_frame);
-
- /* Set up the sprite header details
- */
- sprite_header->size = frame_size + (8 << frame_bpp) + sizeof(osspriteop_header);
- sprite_header->width = (scanline_size >> 2) - 1;
- sprite_header->height = gif_animation->nHeight - 1;
- sprite_header->left_bit = 0;
- sprite_header->right_bit = (gif_animation->nWidth * frame_bpp - 1 ) & 31;
- sprite_header->image = (8 << frame_bpp) + sizeof(osspriteop_header);
- strcpy(sprite_header->name, "gif");
-
- /* Do the mask stuff if we have one
- */
- if (gif_frame->pMaskData) {
- sprite_header->size += frame_size;
- sprite_header->mask = sprite_header->image + frame_size;
- } else {
- sprite_header->mask = sprite_header->image;
- }
-
- /* Set the mode using old skool values
- */
- switch (frame_bpp) {
- case 1: sprite_header->mode = 18; break;
- case 2: sprite_header->mode = 19; break;
- case 4: sprite_header->mode = 20; break;
- case 8: sprite_header->mode = 21; break;
- }
-
- /* Set up the palette - 2 words per entry.
- */
- sprite_palette = (unsigned int*)(sprite_header + 1);
- memset(sprite_palette, 0, frame_colours);
- for (loop = 0; loop < frame_colours; loop++) {
- *sprite_palette++ = palette_entries[loop];
- *sprite_palette++ = palette_entries[loop];
- }
-
- /* Get the intermediate result place (src) and where it ends up after
- we've changed it to the correct bpp (dest).
- We use our 32bpp sprite buffer as temporary workspace.
- */
- dest = ((pixel*)sprite_header) + sprite_header->image;
- src = (pixel*)c->data.gif.buffer_header;
-
- if (!Anim_Decompress(gif_frame->pImageData, gif_frame->nImageSize,
- gif_animation->nWidth * gif_animation->nHeight, src)) {
- return false;
- }
-
- /* Now we compress each line to the minimum bpp
- */
- for (loop=0; loop < gif_animation->nHeight; loop++) {
- CompressSpriteLine(dest, src, gif_animation->nWidth, frame_bpp );
- dest += scanline_size;
- src += gif_animation->nWidth;
- }
-
- /* As before, but for the mask this time
- */
- if (gif_frame->pMaskData) {
- dest = ((pixel*)sprite_header) + sprite_header->mask;
- src = (pixel*)c->data.gif.buffer_header;
-
- if (!Anim_Decompress(gif_frame->pMaskData, gif_frame->nMaskSize,
- gif_animation->nWidth * gif_animation->nHeight, src)) {
- return false;
- }
-
- /* Now we compress each line to the minimum bpp
- */
- for (loop=0; loop < gif_animation->nHeight; loop++) {
- CompressMaskLine(dest, src, gif_animation->nWidth, frame_bpp);
- dest += scanline_size;
- src += gif_animation->nWidth;
- }
- }
-
- /* Return success
- */
- return true;
-}
-
void nsgif_redraw(struct content *c, long x, long y,
unsigned long width, unsigned long height,
long clip_x0, long clip_y0, long clip_x1, long clip_y1,
float scale) {
-
- /* Hack - animate as if 4cs have passed every redraw
+
+ int previous_frame;
+ unsigned int frame;
+
+ /* Decode from the last frame to the current frame
*/
-/* nsgif_animate(c, 4); */
-
- /* Check if we need to expand the current frame to 32bpp
- */
- if (c->data.gif.current_frame != c->data.gif.expanded_frame) {
-
- /* Convert the sprite
- */
- _swix(Tinct_ConvertSprite, _IN(2) | _IN(3),
- ((char *) nsgif_get_sprite_address(c, c->data.gif.current_frame)),
- ((char *) c->data.gif.buffer_header));
-
- /* Remember we are expanded for future calls
- */
- c->data.gif.expanded_frame = c->data.gif.current_frame;
+ previous_frame = c->data.gif.gif->decoded_frame;
+ if (previous_frame > c->data.gif.current_frame) previous_frame = -1;
+ for (frame = previous_frame + 1; frame <= c->data.gif.current_frame; frame++) {
+ gif_decode_frame(c->data.gif.gif, frame);
}
/* Tinct currently only handles 32bpp sprites that have an embedded alpha mask. Any
@@ -396,7 +105,7 @@ void nsgif_redraw(struct content *c, long x, long y,
documentation for further information.
*/
_swix(Tinct_PlotScaledAlpha, _IN(2) | _IN(3) | _IN(4) | _IN(5) | _IN(6) | _IN(7),
- ((char *) c->data.gif.buffer_header),
+ (char *)c->data.gif.gif->frame_image,
x, (int)(y - height),
width, height,
(option_filter_sprites?(1<<1):0) | (option_dither_sprites?(1<<2):0));
@@ -410,10 +119,8 @@ void nsgif_destroy(struct content *c)
/* Free all the associated memory buffers
*/
schedule_remove(nsgif_animate, c);
- xfree(c->title);
- xfree(c->data.gif.sprite_area);
- xfree(c->data.gif.buffer_header);
- xfree(c->data.gif.frame_transitions);
+ gif_finalise(c->data.gif.gif);
+ xfree(c->data.gif.gif);
}
@@ -428,183 +135,19 @@ void nsgif_animate(void *p)
/* at the moment just advance by one frame */
c->data.gif.current_frame++;
- if (c->data.gif.current_frame == c->data.gif.total_frames) {
- if (!c->data.gif.loop_gif) {
+ if (c->data.gif.current_frame == c->data.gif.gif->frame_count) {
+/* if (!c->data.gif.loop_gif) {
c->data.gif.current_frame--;
c->data.gif.animate_gif = false;
return;
} else
- c->data.gif.current_frame = 0;
+*/ c->data.gif.current_frame = 0;
}
- schedule(c->data.gif.frame_transitions[c->data.gif.current_frame],
+ schedule(c->data.gif.gif->frame_delays[c->data.gif.current_frame],
nsgif_animate, c);
content_broadcast(c, CONTENT_MSG_REDRAW, 0);
}
-
-#if 0
- unsigned int max_frame;
- unsigned int cur_frame;
- unsigned int old_frame;
- unsigned int *delay_values;
-
- /* Abort if we are not animated or cannot animate
- */
- max_frame = c->data.gif.total_frames;
- if ((max_frame < 2) || (!c->data.gif.animate_gif)) return 0;
-
- /* Add in the number of cs we had left over from the last animation
- */
- advance_time += c->data.gif.remainder_time;
-
- /* Get our frame information locally
- */
- cur_frame = c->data.gif.current_frame;
- old_frame = cur_frame;
- delay_values = c->data.gif.frame_transitions;
-
- /* Move through the frames
- */
- while (advance_time >= delay_values[cur_frame]) {
-
- /* Advance a frame
- */
- advance_time -= delay_values[cur_frame++];
-
- /* Handle looping
- */
- if (cur_frame >= max_frame) {
- if (!c->data.gif.loop_gif) {
- c->data.gif.current_frame = max_frame - 1;
- c->data.gif.animate_gif = false;
-
- /* We can't return 0 as it indicates no animation
- has occured, so we return a small value so we
- can be called back and then say that we're done.
- */
- return -1;
- } else {
- cur_frame -= max_frame;
- }
- }
- }
-
- /* Store the leftover time
- */
- c->data.gif.remainder_time = advance_time;
-
- /* Return whether we've changed and when the next update should be
- */
- if (cur_frame == old_frame) {
- return (delay_values[cur_frame] - advance_time);
- } else {
- c->data.gif.current_frame = cur_frame;
- return (advance_time - delay_values[cur_frame]);
- }
-}
-#endif
-
-
-
-/** Provides the address of a frame within the sprite area.
-
- @param c The content to find the frame from
- @param frame The desired frame [0...(max-1)]
- @return The address of the sprite header
-*/
-osspriteop_header *nsgif_get_sprite_address(struct content *c, unsigned int frame) {
-
- struct osspriteop_header *header;
-
- /* Get the header for the first sprite
- */
- header = (osspriteop_header*)((char *)c->data.gif.sprite_area +
- c->data.gif.sprite_area->first);
-
- /* Keep advancing until we get our sprite
- */
- while (frame-- > 0) {
- header = (osspriteop_header*)(((char *)header) + header->size);
- }
-
- /* Return our value
- */
- return header;
-}
-
-
-
-
-/* Shamelessly stolen from AnimLib.savesprite.c
-*/
-static void CompressSpriteLine( pixel *dest, const pixel *src, int n, int bpp )
-{
- int i;
- pixel j;
-
- switch ( bpp )
- {
- case 8:
- if ( src != dest )
- memmove( dest, src, n );
- break;
-
- case 4:
- for ( i=0; i< (n+1)/2; i++ )
- dest[i] = (src[i<<1] & 0xF) + ( src[(i<<1)+1] << 4 ) ;
- break;
-
- case 2:
- for ( i=0; i < (n+3)/4; i++ )
- dest[i] = ( ( src[i<<2 ] ) & 3 )
- | ( ( src[(i<<2)+1] << 2 ) & 0xC )
- | ( ( src[(i<<2)+2] << 4 ) & 0x30 )
- | ( src[(i<<2)+3] << 6 );
- break;
-
- case 1:
- j = 0;
- for ( i=0; i < (n|7)+1; i++ )
- {
- j += (src[i] & 1) << (i&7);
- if ( (i&7) == 7 )
- {
- dest[i>>3] = j;
- j = 0;
- }
- }
- break;
- }
-}
-
-static void CompressMaskLine( pixel *dest, const pixel *src, int n, int bpp )
-{
- int i;
-
- switch ( bpp )
- {
- case 8:
- for ( i=0; i<n; i++ )
- dest[i] = ( src[i] ) ? 0xFF : 0;
- break;
- case 4:
- for ( i=0; i< (n+1)/2; i++ )
- dest[i] = ( src[i<<1] ? 0xF : 0 )
- + ( ( src[(i<<1)+1] ? 0xF : 0 ) << 4 );
- break;
- case 2:
- for ( i=0; i < (n+3)/4; i++ )
- dest[i] = ( src[i<<2 ] ? 0x3 : 0 )
- + ( src[(i<<2)+1] ? 0xC : 0 )
- + ( src[(i<<2)+2] ? 0x30 : 0 )
- + ( src[(i<<2)+3] ? 0xC0 : 0 );
- break;
- case 1:
- CompressSpriteLine( dest, src, n, 1 ); /* It's the same! */
- break;
- }
-}
-
#endif
diff --git a/riscos/gif.h b/riscos/gif.h
index c175cc868..526716dec 100644
--- a/riscos/gif.h
+++ b/riscos/gif.h
@@ -3,56 +3,26 @@
* Licensed under the GNU General Public License,
* http://www.opensource.org/licenses/gpl-license
* Copyright 2003 Philip Pemberton <philpem@users.sourceforge.net>
- * Copyright 2004 Richard Wilson <not_ginger_matt@hotmail.com>
+ * Copyright 2004 Richard Wilson <not_ginger_matt@sourceforge.net>
*/
#ifndef _NETSURF_RISCOS_GIF_H_
#define _NETSURF_RISCOS_GIF_H_
#include "oslib/osspriteop.h"
+#include "netsurf/riscos/gifread.h"
struct content;
struct content_gif_data {
- unsigned long buffer_pos;
- /* The sprite area containing the 8bpp frames.
+ /* The GIF data
*/
- osspriteop_area *sprite_area;
-
- /* The sprite header of the current 32bpp image.
- */
- osspriteop_header *buffer_header;
+ struct gif_animation *gif;
/** The current frame number of the GIF to display, [0...(max-1)]
*/
unsigned int current_frame;
-
- /** The current frame that we hold a 32bpp version of [0...(max-1)]
- */
- unsigned int expanded_frame;
-
- /** Whether the GIF should be animated
- */
- bool animate_gif;
-
- /** Whether the GIF should loop
- */
- bool loop_gif;
-
- /** The number of cs unprocessed as the next transition has
- not yet occurred.
- */
- unsigned int remainder_time;
-
- /** The total number of frames
- */
- unsigned int total_frames;
-
- /** An array of times (in cs) for the frame transitions between each frame
- */
- unsigned int *frame_transitions;
-
};
void nsgif_init(void);
diff --git a/riscos/gifread.c b/riscos/gifread.c
new file mode 100644
index 000000000..77406afd3
--- /dev/null
+++ b/riscos/gifread.c
@@ -0,0 +1,915 @@
+/*
+ * This file is part of NetSurf, http://netsurf.sourceforge.net/
+ * Licensed under the GNU General Public License,
+ * http://www.opensource.org/licenses/gpl-license
+ * Copyright 2004 Richard Wilson <not_ginger_matt@sourceforge.net>
+ */
+
+
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include "gifread.h"
+#include "netsurf/utils/log.h"
+#include "oslib/osspriteop.h"
+#include "oslib/osfile.h"
+
+
+/* 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
+ latter being the number of frames that a partial amount of data is
+ available for. This assists the caller in managing the animation whilst
+ decoding is continuing.
+
+ To decode a frame, the caller must use gif_decode_frame() which updates
+ the current 'frame_image' to reflect the desired frame. The required
+ 'background_action' is also updated to reflect how the frame should be
+ plotted. The caller must not assume that the current 'frame_image' will
+ be valid between calls if initialisation is still occuring, and should
+ either always request that the frame is decoded (no processing will
+ occur if the 'decoded_frame' has not been invalidated by initialisation)
+ or perform the check itself.
+
+ It should be noted that gif_finalise() should always be called, even if
+ no frames were initialised.
+
+ [rjw] - Fri 2nd April 2004
+*/
+
+
+
+/* Internal GIF routines
+*/
+static int gif_initialise_sprite(struct gif_animation *gif, unsigned int width, unsigned int height);
+static int gif_initialise_frame(struct gif_animation *gif);
+static unsigned int gif_interlaced_line(unsigned int height, unsigned int y);
+
+
+
+/* Internal LZW routines
+*/
+static int gif_next_LZW(struct gif_animation *gif);
+static int gif_next_code(struct gif_animation *gif, int code_size);
+static int gif_next_block(struct gif_animation *gif, unsigned char *buf);
+#define gif_read_LZW(gif) ((stack_pointer > stack) ? *--stack_pointer : gif_next_LZW(gif))
+
+/* General LZW values. They are shared for all GIFs being decoded, and
+ thus we can't handle progressive decoding efficiently without having
+ the data for each image which would use an extra 10Kb or so per GIF.
+*/
+static int stack[(1 << GIF_MAX_LZW) * 2];
+static int *stack_pointer;
+static int code_size, set_code_size;
+static int max_code, max_code_size;
+static int clear_code, end_code;
+static int curbit, lastbit, get_done, last_byte;
+static int return_clear;
+static int zero_data_block = 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
+ -2 for GIF error
+ -1 for insufficient data to do anything
+ 0 for successful decoding
+ 1 for successful decoding (all frames completely read)
+*/
+int gif_initialise(struct gif_animation *gif) {
+ unsigned char *gif_data;
+ unsigned int index;
+ int return_value;
+
+ /* Check for sufficient data to be a GIF
+ */
+ if (gif->buffer_size < 13) {
+ return -1;
+ }
+
+ /* Get our current processing position
+ */
+ gif_data = gif->gif_data + gif->buffer_position;
+
+ /* See if we should initialise the GIF
+ */
+ if (gif->buffer_position == 0) {
+
+ /* The 12th byte must be a 0 for the block terminator
+ */
+ if (gif_data[12] != 0x00) return GIF_DATA_ERROR;
+
+ /* We want everything to be NULL before we start so we've no chance
+ of freeing bad pointers (paranoia)
+ */
+ gif->frame_image = NULL;
+ gif->frame_pointers = NULL;
+ gif->frame_delays = 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) 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"));
+ }
+ gif_data += 3;
+
+ /* Get our GIF data. Quite often the width/height are lies, so we don't fill them in
+ */
+ gif->width = 0; // Can't trust the supplied value
+ gif->height = 0; // Can't trust the supplied value
+// gif->width = (gif_data[0] | (gif_data[1] << 8));
+// gif->height = (gif_data[2] | (gif_data[3] << 8));
+ gif->global_colours = (gif_data[4] & 0x80);
+ gif->colour_table_size = (2 << (gif_data[4] & 0x07));
+ gif->background_colour = gif_data[5];
+ gif->aspect_ratio = gif_data[6];
+ 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.
+ */
+ gif->global_colour_table = (unsigned int *)calloc(GIF_MAX_COLOURS, sizeof(int));
+ gif->local_colour_table = (unsigned int *)calloc(GIF_MAX_COLOURS, sizeof(int));
+ if ((gif->global_colour_table == NULL) || (gif->local_colour_table == NULL)) {
+ 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
+ */
+ gif->global_colour_table[0] = 0xaa000000;
+
+ /* Initialise enough workspace for 4 frame initially
+ */
+ if ((gif->frame_delays = (unsigned int *)malloc(sizeof(int))) == NULL) {
+ gif_finalise(gif);
+ return GIF_INSUFFICIENT_MEMORY;
+ }
+ if ((gif->frame_pointers = (unsigned int *)malloc(sizeof(int))) == NULL) {
+ gif_finalise(gif);
+ return GIF_INSUFFICIENT_MEMORY;
+ }
+ gif->frame_holders = 1;
+
+ /* Initialise the sprite header
+ */
+ if ((gif->frame_image = (osspriteop_header *)malloc(sizeof(osspriteop_header))) == NULL) {
+ gif_finalise(gif);
+ return GIF_INSUFFICIENT_MEMORY;
+ }
+ gif->frame_image->size = sizeof(osspriteop_header);
+ strcpy(gif->frame_image->name, "gif");
+ gif->frame_image->left_bit = 0;
+ gif->frame_image->right_bit = 31;
+ gif->frame_image->width = 0;
+ gif->frame_image->height = 0;
+ gif->frame_image->image = sizeof(osspriteop_header);
+ gif->frame_image->mask = sizeof(osspriteop_header);
+ gif->frame_image->mode = (os_mode) 0x301680b5;
+
+ /* Remember we've done this now
+ */
+ gif->buffer_position = gif_data - gif->gif_data;
+ }
+
+ /* Do the colour map if we haven't already. As the top byte is always 0xff or 0x00
+ depending on the transparency we know if it's been filled in.
+ */
+ if (gif->global_colour_table[0] == 0xaa000000) {
+ /* Check for a global colour map signified by bit 7
+ */
+ if (gif->global_colours) {
+ if (gif->buffer_size < (gif->colour_table_size * 3 + 12)) {
+ return GIF_INSUFFICIENT_DATA;
+ }
+// LOG(("Found global colour table with %i entries", gif->colour_table_size));
+ for (index = 0; index < gif->colour_table_size; index++) {
+ gif->global_colour_table[index] = gif_data[0] | (gif_data[1] << 8) |
+ (gif_data[2] << 16) | 0xff000000;
+ gif_data += 3;
+ }
+ gif->buffer_position = (gif_data - gif->gif_data);
+ } else {
+// LOG(("No global colour table"));
+ /* Create a default colour table with the first two colours as black and white
+ */
+ gif->global_colour_table[0] = 0xff000000;
+ 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) ||
+ (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;
+}
+
+
+
+/** Updates the sprite memory size
+
+ @return -3 for a memory error
+ 0 for success
+*/
+static int gif_initialise_sprite(struct gif_animation *gif, unsigned int width, unsigned int height) {
+ struct osspriteop_header *buffer;
+ unsigned int max_width;
+ unsigned int max_height;
+ unsigned int frame_bytes;
+
+ /* Check if we've changed
+ */
+ 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) {
+ return GIF_INSUFFICIENT_MEMORY;
+ }
+ gif->frame_image = buffer;
+
+ /* Update the sizes
+ */
+ gif->width = max_width;
+ gif->height = max_height;
+
+ /* Update our sprite image
+ */
+ buffer->size = frame_bytes;
+ buffer->width = max_width - 1;
+ buffer->height = max_height - 1;
+
+ /* Invalidate our currently decoded image
+ */
+ gif->decoded_frame = 0xffffffff;
+ return 0;
+}
+
+
+/* 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
+ -1 for insufficient data to process anything
+ 0 for success
+ 1 for success (GIF terminator found)
+*/
+int gif_initialise_frame(struct gif_animation *gif) {
+ unsigned int frame;
+ unsigned int *temp_buf;
+
+ unsigned char *gif_data, *gif_end;
+ int gif_bytes;
+ unsigned int flags = 0;
+ unsigned int width, height, offset_x, offset_y;
+ unsigned int extension_size, colour_table_size;
+ unsigned int block_size;
+ unsigned int more_images;
+
+ /* Get the frame to decode and our data position
+ */
+ frame = gif->frame_count;
+
+ /* Get our buffer position etc.
+ */
+ gif_data = (unsigned char *)(gif->gif_data + gif->buffer_position);
+ gif_end = (unsigned char *)(gif->gif_data + gif->buffer_size);
+ gif_bytes = (gif_end - gif_data);
+
+ /* Check we have enough data for at least the header, or if we've finished
+ */
+ if ((gif_bytes > 0) && (gif_data[0] == 0x3b)) return 1;
+ if (gif_bytes < 11) return -1;
+
+ /* We could theoretically get some junk data that gives us millions of frames, so
+ we ensure that we don't have a silly number
+ */
+ if (frame > 4096) return GIF_DATA_ERROR;
+
+ /* Get some memory to store our pointers in etc.
+ */
+ if (gif->frame_holders <= frame) {
+ /* Allocate more memory
+ */
+ if ((temp_buf = (unsigned int *)realloc(gif->frame_delays,
+ (frame + 1) * sizeof(int))) == NULL) {
+ return GIF_INSUFFICIENT_MEMORY;
+ }
+ gif->frame_delays = temp_buf;
+ if ((temp_buf = (unsigned int *)realloc(gif->frame_pointers,
+ (frame + 1) * sizeof(int))) == NULL) {
+ return GIF_INSUFFICIENT_MEMORY;
+ }
+ gif->frame_pointers = temp_buf;
+
+ /* Remember we allocated it
+ */
+ 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->frame_pointers[frame] = gif->buffer_position;
+ gif->frame_delays[frame] = 100; // 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.
+ */
+ more_images = 1;
+ while (more_images != 0) {
+
+ /* Ensure we have some data
+ */
+ if ((gif_end - gif_data) < 10) return GIF_INSUFFICIENT_FRAME_DATA;
+
+ /* Decode the extensions
+ */
+ while (gif_data[0] == 0x21) {
+ /* 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->frame_delays[frame] = gif_data[4] | (gif_data[5] << 8);
+ more_images = ((gif->frame_delays[frame]) == 0);
+ }
+ /* 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;
+ }
+ 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);
+// LOG(("Found image %ix%i with offset (%i,%i)", width, height, offset_x, offset_y));
+
+ /* Boundary checking - shouldn't ever happen except with junk data
+ */
+ 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;
+ while (block_size != 1) {
+ /* Skip the block data
+ */
+ block_size = gif_data[0] + 1;
+ if ((gif_bytes -= block_size) < 0) return GIF_INSUFFICIENT_FRAME_DATA;
+ gif_data += block_size;
+ }
+
+ /* Check for end of data
+ */
+ if ((gif_bytes < 1) || (gif_data[0] == 0x3b)) more_images = 0;
+ }
+
+ /* Check if we've finished
+ */
+ if (gif_bytes < 1) {
+ return GIF_INSUFFICIENT_FRAME_DATA;
+ } else {
+ gif->buffer_position = gif_data - gif->gif_data;
+ gif->frame_count = frame + 1;
+ if (gif_data[0] == 0x3b) return 1;
+ }
+ return 0;
+}
+
+
+/** Decodes a GIF frame.
+
+ @return -5 for GIF frame data error
+ -4 for insufficient data to complete the frame
+ -2 for GIF error (invalid frame header)
+ -1 for insufficient data to do anything
+ 0 for successful decoding
+*/
+int gif_decode_frame(struct gif_animation *gif, unsigned int frame) {
+ unsigned int index = 0;
+ unsigned char *gif_data;
+ unsigned char *gif_end;
+ int gif_bytes;
+ unsigned int width, height, offset_x, offset_y;
+ unsigned int flags, colour_table_size, interlace;
+ unsigned int *colour_table;
+ unsigned int *frame_data = 0; // Set to 0 for no warnings
+ unsigned int *frame_scanline;
+ unsigned int extension_size;
+ int transparency_index = -1;
+ unsigned int transparent_colour = 0; // Set to 0 for no warnings
+ unsigned int save_buffer_position;
+ unsigned int frame_data_size;
+ unsigned int return_value = 0;
+ unsigned int x, y, decode_y;
+ int colour;
+
+ unsigned int more_images;
+
+ /* Ensure we have a frame to decode
+ */
+ if (frame > gif->frame_count_partial) return GIF_INSUFFICIENT_DATA;
+ if (frame == gif->decoded_frame) return 0;
+
+ /* Get the start of our frame data and the end of the GIF data
+ */
+ gif_data = gif->gif_data + gif->frame_pointers[frame];
+ gif_end = gif->gif_data + gif->buffer_size;
+ gif_bytes = (gif_end - gif_data);
+
+ /* Check we have enough data for the header
+ */
+ if (gif_bytes < 9) return GIF_INSUFFICIENT_DATA;
+
+ /* Clear the previous frame totally. We can't just pretend we've got a smaller
+ sprite and clear what we need as some frames have multiple images which would
+ produce errors.
+ */
+ frame_data = (unsigned int*)((char *)gif->frame_image + sizeof(osspriteop_header));
+ if ((frame == 0) || (gif->decoded_frame == 0xffffffff)) {
+ memset((char*)frame_data, 0x00, gif->width * gif->height * sizeof(int));
+ }
+ gif->decoded_frame = frame;
+
+ /* Save the buffer position
+ */
+ 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
+ */
+ gif->background_action = 0;
+ more_images = 1;
+ while (more_images != 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) {
+
+ /* 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) {
+ flags = gif_data[3];
+ if (flags & 0x01) transparency_index = gif_data[6];
+ gif->background_action = ((flags & 0x1c) >> 2);
+ more_images = ((gif_data[4] | (gif_data[5] << 8)) == 0);
+ }
+ /* 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;
+ }
+ gif_data++;
+ }
+
+ /* Decode the header
+ */
+ if (gif_data[0] != 0x2c) return GIF_DATA_ERROR;
+ 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);
+// LOG(("Decoding %ix%i at offset (%i,%i)", width, height, offset_x, offset_y));
+
+ /* Boundary checking - shouldn't ever happen except unless the data has been
+ modified since initialisation.
+ */
+ if ((offset_x + width > gif->width) || (offset_y + height > gif->height)) {
+ return GIF_DATA_ERROR;
+ }
+
+ /* Decode the flags
+ */
+ flags = gif_data[9];
+ colour_table_size = 2 << (flags & 0x07);
+ interlace = flags & 0x40;
+
+ /* Move through our data
+ */
+ gif_data += 10;
+ gif_bytes = (int)(gif_end - gif_data);
+
+ /* Initialise our sprite to be totally transparent if we should
+ */
+// gif->frame_offset_x = offset_x;
+// gif->frame_offset_y = offset_y;
+// gif->frame_image->width = width;
+// gif->frame_image->height = height;
+
+ /* Set up the colour table
+ */
+ if (flags & 0x80) {
+// LOG(("Found local colour table"));
+ if (gif_bytes < (int)(3 * colour_table_size)) return GIF_INSUFFICIENT_FRAME_DATA;
+ colour_table = gif->local_colour_table;
+ for (index = 0; index < colour_table_size; index++) {
+ colour_table[index] = gif_data[0] | (gif_data[1] << 8) |
+ (gif_data[2] << 16) | 0xff000000;
+ gif_data += 3;
+ }
+ gif_bytes = (int)(gif_end - gif_data);
+ } else {
+ colour_table = gif->global_colour_table;
+ }
+ /* Initialise the LZW decoding
+ */
+ set_code_size = gif_data[0];
+ gif->buffer_position = (gif_data - gif->gif_data) + 1;
+
+ /* Set our code variables
+ */
+ code_size = set_code_size + 1;
+ clear_code = (1 << set_code_size);
+ end_code = clear_code + 1;
+ max_code_size = 2 * clear_code;
+ max_code = clear_code + 2;
+ curbit = lastbit = 0;
+ last_byte = 2;
+ get_done = 0;
+ return_clear = 1;
+ stack_pointer = stack;
+
+ /* Set the transparent colour as transparent
+ */
+ if (transparency_index >= 0) {
+ transparent_colour = colour_table[transparency_index];
+ colour_table[transparency_index] = 0x00000000;
+ }
+
+ /* Decompress the data
+ */
+ if ((gif->background_action == 2) || (gif->background_action == 3)) {
+ frame_data_size = width * height;
+ for (y = 0; y < height; y++) {
+ if (interlace) {
+ decode_y = gif_interlaced_line(height, y) + offset_y;
+ } else {
+ decode_y = y + offset_y;
+ }
+ frame_scanline = frame_data + offset_x + (decode_y * gif->width);
+ for (x = 0; x < width; x++) {
+ if ((colour = gif_read_LZW(gif)) >= 0) {
+ *frame_scanline++ = colour_table[colour];
+ } else {
+ LOG(("%i bytes of %i read", index, frame_data_size));
+ return_value = GIF_INSUFFICIENT_FRAME_DATA;
+ goto gif_decode_frame_exit;
+ }
+ }
+ }
+ } else {
+ frame_data_size = width * height;
+ for (y = 0; y < height; y++) {
+ if (interlace) {
+ decode_y = gif_interlaced_line(height, y) + offset_y;
+ } else {
+ decode_y = y + offset_y;
+ }
+ frame_scanline = frame_data + offset_x + (decode_y * gif->width);
+ for (x = 0; x < width; x++) {
+ if ((colour = gif_read_LZW(gif)) >= 0) {
+ if (colour == transparency_index) {
+ *frame_scanline++;
+ } else {
+ *frame_scanline++ = colour_table[colour];
+ }
+ } else {
+ LOG(("%i bytes of %i read", index, frame_data_size));
+ return_value = GIF_INSUFFICIENT_FRAME_DATA;
+ goto gif_decode_frame_exit;
+ }
+ }
+ }
+ }
+gif_decode_frame_exit:
+
+ /* Unset the transparent colour
+ */
+ if (transparency_index >= 0) {
+ colour_table[transparency_index] = transparent_colour;
+ }
+
+ /* Check for end of data
+ */
+ gif_bytes = gif->buffer_size - gif->buffer_position;
+ if ((gif_bytes < 1) || (gif_data[0] == 0x3b)) more_images = 0;
+ gif->buffer_position++;
+ }
+
+ /* Restore the buffer position
+ */
+ gif->buffer_position = save_buffer_position;
+
+ /* Success!
+ */
+ return return_value;
+
+}
+
+static unsigned int gif_interlaced_line(unsigned int height, unsigned int y) {
+ if ((y << 3) < height) return (y << 3);
+ y -= ((height + 7) >> 3);
+ if ((y << 3) < (height - 4)) return (y << 3) + 4;
+ y -= ((height + 3) >> 3);
+ if ((y << 2) < (height - 2)) return (y << 2) + 2;
+ y -= ((height + 1) >> 2);
+ return (y << 1) + 1;
+
+/* if ((y & 7) == 0) return (y >> 3);
+ offset = (height + 7) >> 3;
+ if ((y & 7) == 4) return offset + ((y - 4) >> 3);
+ offset += (height + 3) >> 3;
+ if ((y & 3) == 2) return offset + ((y - 2) >> 2);
+ return offset + ((height + 1) >> 2) + ((y - 1) >> 1);
+*/
+}
+
+/* Releases any workspace held by the animation
+*/
+void gif_finalise(struct gif_animation *gif) {
+ /* Release all our memory blocks
+ */
+ free(gif->frame_image);
+ gif->frame_image = NULL;
+ free(gif->frame_pointers);
+ gif->frame_pointers = NULL;
+ free(gif->frame_delays);
+ gif->frame_delays = NULL;
+ free(gif->local_colour_table);
+ gif->local_colour_table = NULL;
+ free(gif->global_colour_table);
+ gif->global_colour_table = NULL;
+}
+
+
+static int gif_next_LZW(struct gif_animation *gif) {
+ static int table[2][(1<< GIF_MAX_LZW)];
+ static int firstcode, oldcode;
+ int code, incode;
+ unsigned int i, block_size;
+
+ while ((code = gif_next_code(gif, code_size)) >= 0) {
+ if (code == clear_code) {
+ /* Check we have a valid clear code
+ */
+ if (clear_code >= (1 << GIF_MAX_LZW)) return -2;
+
+ /* Initialise our table
+ */
+ for (i = 0; i < clear_code; ++i) {
+ table[0][i] = 0;
+ table[1][i] = i;
+ }
+ for (; i < (1 << GIF_MAX_LZW); ++i) {
+ table[0][i] = table[1][i] = 0;
+ }
+
+ /* Update our LZW parameters
+ */
+ code_size = set_code_size + 1;
+ max_code_size = 2 * clear_code;
+ max_code = clear_code + 2;
+ stack_pointer = stack;
+ do {
+ firstcode = oldcode = gif_next_code(gif, code_size);
+ } while (firstcode == clear_code);
+ return firstcode;
+ }
+
+ if (code == end_code) {
+ /* Skip to the end of our data so multi-image GIFs work
+ */
+ if (zero_data_block) return -2;
+ block_size = 0;
+ while (block_size != 1) {
+ block_size = gif->gif_data[gif->buffer_position] + 1;
+ gif->buffer_position += block_size;
+ }
+ return -2;
+ }
+
+ /* Fill the stack with some data
+ */
+ incode = code;
+
+ if (code >= max_code) {
+ *stack_pointer++ = firstcode;
+ code = oldcode;
+ }
+
+ while (code >= clear_code) {
+ *stack_pointer++ = table[1][code];
+ 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];
+
+ if ((code = max_code) < (1 << GIF_MAX_LZW)) {
+ table[0][code] = oldcode;
+ table[1][code] = firstcode;
+ ++max_code;
+ if ((max_code >= max_code_size) && (max_code_size < (1 << GIF_MAX_LZW))) {
+ max_code_size *= 2;
+ ++code_size;
+ }
+ }
+
+ oldcode = incode;
+
+ if (stack_pointer > stack) return *--stack_pointer;
+ }
+ return code;
+}
+
+static int gif_next_code(struct gif_animation *gif, int code_size) {
+ static unsigned char buf[280];
+ static int maskTbl[16] = {0x0000, 0x0001, 0x0003, 0x0007, 0x000f, 0x001f, 0x003f, 0x007f,
+ 0x00ff, 0x01ff, 0x03ff, 0x07ff, 0x0fff, 0x1fff, 0x3fff, 0x7fff};
+ int i, j, end;
+ long ret;
+
+ if (return_clear) {
+ return_clear = 0;
+ return clear_code;
+ }
+
+ end = curbit + code_size;
+ if (end >= lastbit) {
+ int count;
+ if (get_done) return -1;
+ buf[0] = buf[last_byte - 2];
+ buf[1] = buf[last_byte - 1];
+ if ((count = gif_next_block(gif, &buf[2])) == 0) get_done = 1;
+ if (count < 0) return -1;
+ last_byte = 2 + count;
+ curbit = (curbit - lastbit) + 16;
+ lastbit = (2 + count) * 8;
+ end = curbit + code_size;
+ }
+
+ j = end / 8;
+ i = curbit / 8;
+ if (i == j) {
+ ret = (long)buf[i];
+ } else if (i + 1 == j) {
+ ret = (long)buf[i] | ((long)buf[i+1] << 8);
+ } 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;
+}
+
+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));
+ return -1;
+ }
+ if (block_size > 0) memcpy(buf, gif_data + 1, block_size);
+ gif->buffer_position += block_size + 1;
+ return((int)block_size);
+}
diff --git a/riscos/gifread.h b/riscos/gifread.h
index 5cefede29..e1d11017e 100644
--- a/riscos/gifread.h
+++ b/riscos/gifread.h
@@ -12,11 +12,11 @@
/* Error return values
*/
-#define GIF_INSUFFICIENT_DATA -1
-#define GIF_DATA_ERROR -2
-#define GIF_INSUFFICIENT_MEMORY -3
-#define GIF_INSUFFICIENT_FRAME_DATA -4
-#define GIF_FRAME_DATA_ERROR -5
+#define GIF_INSUFFICIENT_FRAME_DATA -1
+#define GIF_FRAME_DATA_ERROR -2
+#define GIF_INSUFFICIENT_DATA -3
+#define GIF_DATA_ERROR -4
+#define GIF_INSUFFICIENT_MEMORY -5
/* Colour map size constant. Because we don't want to allocate
memory each time we decode a frame we get enough so all frames
@@ -30,7 +30,7 @@
/* A simple hold-all for our GIF data
*/
-struct gif_animation {
+typedef struct gif_animation {
/* Encoded GIF data
*/
unsigned char *gif_data;
@@ -46,7 +46,6 @@ struct gif_animation {
/* Animation data
*/
- unsigned int current_frame;
unsigned int decoded_frame;
unsigned int loop_count;
unsigned int *frame_delays;
@@ -64,18 +63,18 @@ struct gif_animation {
/* Decoded frame data
*/
- unsigned int frame_offset_x;
- unsigned int frame_offset_y;
- unsigned int frame_width;
- unsigned int frame_height;
+// unsigned int frame_offset_x;
+// unsigned int frame_offset_y;
+// unsigned int frame_width;
+// unsigned int frame_height;
unsigned int background_action;
osspriteop_header *frame_image;
-};
+} gif_animation;
/* Function declarations
*/
int gif_initialise(struct gif_animation *gif);
-int gif_decode_frame(struct gif_animation *gif, int frame);
+int gif_decode_frame(struct gif_animation *gif, unsigned int frame);
void gif_finalise(struct gif_animation *gif);
#endif