summaryrefslogtreecommitdiff
path: root/riscos
diff options
context:
space:
mode:
authorRichard Wilson <rjw@netsurf-browser.org>2004-03-19 18:14:50 +0000
committerRichard Wilson <rjw@netsurf-browser.org>2004-03-19 18:14:50 +0000
commitc28b257d9d5a7b2ae139dca60379adb3958b828d (patch)
tree3d4cf4db9657a541ea9195bad72473fd45dd81c7 /riscos
parent5df9d191e88ac7ce2959e3295777cf157c93d05d (diff)
downloadnetsurf-c28b257d9d5a7b2ae139dca60379adb3958b828d.tar.gz
netsurf-c28b257d9d5a7b2ae139dca60379adb3958b828d.tar.bz2
[project @ 2004-03-19 18:14:50 by rjw]
Animated GIF support. svn path=/import/netsurf/; revision=635
Diffstat (limited to 'riscos')
-rw-r--r--riscos/gif.c501
-rw-r--r--riscos/gif.h6
2 files changed, 371 insertions, 136 deletions
diff --git a/riscos/gif.c b/riscos/gif.c
index cd63801f6..980462523 100644
--- a/riscos/gif.c
+++ b/riscos/gif.c
@@ -1,12 +1,12 @@
/*
* This file is part of NetSurf, http://netsurf.sourceforge.net/
* Licensed under the GNU General Public License,
- * http://www.opensource.org/licenses/gpl-license
+ * http://www.opensource.org/licenses/gpl-license
* Copyright 2003 John M Bell <jmb202@ecs.soton.ac.uk>
* Copyright 2004 Richard Wilson <not_ginger_matt@sourceforge.net>
*
* Parts modified from IGviewer source by Peter Hartley
- * http://utter.chaos.org/~pdh/software/intergif.htm
+ * http://utter.chaos.org/~pdh/software/intergif.htm
*/
#include <assert.h>
@@ -17,6 +17,7 @@
#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"
@@ -37,9 +38,9 @@
32bpp sprite.
To overcome the problem of memory wastage, each frame of the GIF is held as
- an 8bpp sprite with a 256 colour entry palette and is converted into a 32bpp
- sprite suitable for plotting following a frame transition. This conversion is
- performed by using Tinct_ConvertSprite.
+ 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
@@ -47,18 +48,15 @@
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.
-
- N.B. Future implementations may of store each frame at the lowest possible
- colour depth to reduce memory usage. To ensure this forwards compatibility
- nsgif_get_sprite_address should always be used to obtain the sprite for the
- various animation frames.
- [rjw] - Wed 17th March 2004
+ [rjw] - Thu 18th March 2004
*/
#ifdef WITH_GIF
-static osspriteop_area *create_buffer_sprite(struct content *c, anim a);
+
+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) {
}
@@ -67,93 +65,297 @@ 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
+}
+
+
+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
+ */
+ if (gif_animation->nFrames < 1) {
+ LOG(("No frames found"));
+ Anim_Destroy(&gif_animation);
+ return 1;
+ }
+ /* Store the animation details
+ */
+ 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.
+ */
+ struct osspriteop_header *temp_buf = xcalloc(gif_animation->nWidth *
+ gif_animation->nHeight + 11, 4);
+ c->data.gif.buffer_header = (osspriteop_header*)(temp_buf);
+
+ /* We can store our frame transitions now too
+ */
+ if (frame_count > 1) {
+ frame_delays = xcalloc(frame_count, sizeof(int));
+ c->data.gif.frame_transitions = frame_delays;
+ }
+
+ /* 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));
+ }
+*/
+
+ /* Exit as a success
+ */
+ return 0;
}
+/** Decompresses a GIF frame.
+ NB: This call uses the current decompressed image as a temporary buffer.
-int nsgif_convert(struct content *c, unsigned int iwidth, unsigned int iheight)
-{
- anim a;
- frame f;
- pixel *img, *mask;
- struct osspriteop_header *header;
- struct osspriteop_area *area;
+ @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;
- a = Anim_FromData(c->source_data, c->source_size, NULL, false, false, false);
- if (!a) {
+ /* 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;
- LOG(("Error creating anim object"));
- return 1;
- }
+ /* Get our current sprite. For dynamic decompression we should always use 0.
+ */
+ sprite_header = nsgif_get_sprite_address(c, cur_frame);
- if(!Anim_CommonPalette(a)) {
+ /* 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;
+ }
- LOG(("bad palette"));
- Anim_Destroy(&a);
- return 1;
- }
- /* Claim a buffer [temporary code]
+ /* Set the mode using old skool values
*/
- struct osspriteop_header *temp_buf = xcalloc(1, a->nWidth * a->nHeight * 4 + 44);
- c->data.gif.buffer_header = (osspriteop_header*)(temp_buf);
+ 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;
+ }
- area = create_buffer_sprite(c, a);
- if(!area) {
-
- LOG(("Failed to create sprite"));
- Anim_Destroy(&a);
- return 1;
- }
- c->data.gif.sprite_area = area;
-
- header = (osspriteop_header*)((char*)c->data.gif.sprite_area +
- c->data.gif.sprite_area->first);
- f = a->pFrames + 0;
- img = (pixel*)header + header->image;
- mask = (pixel*)header + header->mask;
-
- if (!Anim_DecompressAligned(f->pImageData, f->nImageSize,
- a->nWidth, a->nHeight, img)) {
- LOG(("Anim_DecompressAligned image failed"));
- Anim_Destroy(&a);
- xfree(area);
- return 1;
- }
-
- if(f->pMaskData) {
-
- int i,n = header->mask - header->image;
-
- if (!Anim_DecompressAligned(f->pMaskData, f->nMaskSize,
- a->nWidth, a->nHeight, mask)) {
- LOG(("Anim_DecompressAligned mask failed"));
- Anim_Destroy(&a);
- xfree(area);
- return 1;
- }
+ /* 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];
+ }
- for(i=0; i<n; i++)
- if(!mask[i]) {
+ /* 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;
+ }
- img[i] = 255;
- mask[i] = 0;
- }
- }
- else
- memset(mask, 255, (unsigned int)(header->mask - header->image));
+ /* 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;
- c->title = xcalloc(100, sizeof(char));
- sprintf(c->title, messages_get("GIFTitle"), c->width, c->height);
- c->status = CONTENT_STATUS_DONE;
+ if (!Anim_Decompress(gif_frame->pMaskData, gif_frame->nMaskSize,
+ gif_animation->nWidth * gif_animation->nHeight, src)) {
+ return false;
+ }
-/* xosspriteop_save_sprite_file(osspriteop_USER_AREA,
- c->data.gif.sprite_area, "gif"); */
+ /* 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 0;
+ /* Return success
+ */
+ return true;
}
@@ -162,21 +364,23 @@ void nsgif_redraw(struct content *c, long x, long y,
long clip_x0, long clip_y0, long clip_x1, long clip_y1,
float scale) {
+ /* Hack - animate as if 4cs have passed every redraw
+ */
+ 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
- */
+ /* 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));
- LOG(("Converted GIF frame %i.", c->data.gif.current_frame));
-
- /* Remember we are expanded for future calls
- */
- c->data.gif.current_frame = c->data.gif.expanded_frame;
+ /* Remember we are expanded for future calls
+ */
+ c->data.gif.expanded_frame = c->data.gif.current_frame;
}
/* Tinct currently only handles 32bpp sprites that have an embedded alpha mask. Any
@@ -230,7 +434,8 @@ int nsgif_animate(struct content *c, unsigned int advance_time) {
/* Get our frame information locally
*/
- cur_frame = old_frame = c->data.gif.current_frame;
+ cur_frame = c->data.gif.current_frame;
+ old_frame = cur_frame;
delay_values = c->data.gif.frame_transitions;
/* Move through the frames
@@ -239,16 +444,15 @@ int nsgif_animate(struct content *c, unsigned int advance_time) {
/* Advance a frame
*/
- advance_time -= delay_values[cur_frame];
- cur_frame++;
+ advance_time -= delay_values[cur_frame++];
/* Handle looping
*/
if (cur_frame >= max_frame) {
- if (!c->data.gif.loop_gif) {
+ 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.
@@ -280,9 +484,10 @@ int nsgif_animate(struct content *c, unsigned int advance_time) {
@param c The content to find the frame from
@param frame The desired frame [0...(max-1)]
- @return The address of the sprite header
+ @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
@@ -292,54 +497,86 @@ osspriteop_header *nsgif_get_sprite_address(struct content *c, unsigned int fram
/* Keep advancing until we get our sprite
*/
- while (frame-- > 0) header += header->size;
+ while (frame-- > 0) {
+ header = (osspriteop_header*)(((char *)header) + header->size);
+ }
/* Return our value
*/
- return header;
+ return header;
}
-static osspriteop_area *create_buffer_sprite( struct content *c, anim a )
+
+
+/* Shamelessly stolen from AnimLib.savesprite.c
+*/
+static void CompressSpriteLine( pixel *dest, const pixel *src, int n, int bpp )
{
- unsigned int abw = ((a->nWidth + 3 ) & ~3u) * a->nHeight;
- unsigned int nBytes = abw*2 + 44 + 16 + 256*8;
- struct osspriteop_area *result = xcalloc(1, nBytes);
- struct osspriteop_header *spr = (osspriteop_header*)(result+1);
- int i,n;
- unsigned int *pPalDest = (unsigned int*)(spr+1);
- unsigned int *pPalSrc;
-
- if ( !result )
- return NULL;
-
- result->size = nBytes;
- result->sprite_count = 1;
- result->first = sizeof(*result);
- result->used = nBytes;
-
- spr->size = nBytes-sizeof(*result);
- strncpy( spr->name, "gif", 12 );
- spr->width = ((a->nWidth+3)>>2)-1;
- spr->height = a->nHeight-1;
- spr->left_bit = 0;
- spr->right_bit = ((a->nWidth & 3) * 8 - 1) & 31;
- spr->image = sizeof(*spr) + 256*8;
- spr->mask = sizeof(*spr) + 256*8 + abw;
- spr->mode = os_MODE8BPP90X90; /* 28 */
-
- c->data.gif.sprite_image = ((char*)spr) + spr->image;
- c->width = a->nWidth;
- c->height = a->nHeight;
-
- n = a->pFrames->pal->nColours;
- pPalSrc = a->pFrames->pal->pColours;
- for ( i=0; i<n; i++ )
+ int i;
+ pixel j;
+
+ switch ( bpp )
{
- *pPalDest++ = *pPalSrc;
- *pPalDest++ = *pPalSrc++;
+ 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;
- return result;
+ 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 107ceaed1..a1823241e 100644
--- a/riscos/gif.h
+++ b/riscos/gif.h
@@ -20,10 +20,6 @@ struct content_gif_data {
*/
osspriteop_area *sprite_area;
- /* The sprite image of the current 8bpp frame
- */
- char *sprite_image;
-
/* The sprite header of the current 32bpp image.
*/
osspriteop_header *buffer_header;
@@ -69,4 +65,6 @@ void nsgif_redraw(struct content *c, long x, long y,
long clip_x0, long clip_y0, long clip_x1, long clip_y1,
float scale);
osspriteop_header *nsgif_get_sprite_address(struct content *c, unsigned int frame);
+bool nsgif_decompress_frame(struct content *c, anim *p_gif_animation, unsigned int cur_frame);
+
#endif