mirror of
https://github.com/netsurf-browser/netsurf
synced 2024-12-22 20:16:54 +03:00
[project @ 2003-07-18 21:02:29 by bursa]
New GIF renderer using animlib (jmb). svn path=/import/netsurf/; revision=235
This commit is contained in:
parent
d6053c3cb0
commit
c3c1fb916b
@ -12,7 +12,6 @@
|
||||
#include "libxml/HTMLparser.h"
|
||||
#ifdef riscos
|
||||
#include "libpng/png.h"
|
||||
#include "libungif/gif_lib.h"
|
||||
#include "oslib/osspriteop.h"
|
||||
#endif
|
||||
#include "netsurf/content/cache.h"
|
||||
@ -149,7 +148,6 @@ struct content
|
||||
// Structure for the GIF handler
|
||||
struct
|
||||
{
|
||||
GifFileType *giffile; // GIF file handler
|
||||
char *data; // GIF data
|
||||
unsigned long length; // Length of GIF data
|
||||
unsigned long buffer_pos; // Position in the buffer
|
||||
|
2
makefile
2
makefile
@ -28,7 +28,7 @@ CFLAGS = -std=c9x -D_BSD_SOURCE -Driscos -DBOOL_DEFINED -O $(WARNFLAGS) -I.. -I/
|
||||
CFLAGS_DEBUG = -std=c9x -D_BSD_SOURCE -O $(WARNFLAGS) -I.. -I/usr/include/libxml2 \
|
||||
-Dfd_set=long -g
|
||||
LDFLAGS = \
|
||||
/usr/local/riscoslibs/libungif/libungif.ro \
|
||||
/usr/local/riscoslibs/animlib/animlib.ro \
|
||||
/usr/local/riscoslibs/libxml2/libxml2.ro \
|
||||
/usr/local/riscoslibs/OSLib/OSLib32.ro \
|
||||
/usr/local/riscoslibs/curl/libcurl.ro \
|
||||
|
402
riscos/gif.c
402
riscos/gif.c
@ -2,25 +2,16 @@
|
||||
* 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 2003 Philip Pemberton <philpem@users.sourceforge.net>
|
||||
*/
|
||||
/*******************
|
||||
* GIF loader for Netsurf
|
||||
* Developed by Philip Pemberton for the Netsurf project
|
||||
*
|
||||
* Yes, this is an evil hack. You will not be tested on your understanding of
|
||||
* this code. Beware - dragons lurketh here.
|
||||
*
|
||||
* TO-DO:
|
||||
* Add support for GIF transparency
|
||||
* Add better error handling
|
||||
* - especially where bad GIFs are concerned.
|
||||
* Copyright 2003 John M Bell <jmb202@ecs.soton.ac.uk>
|
||||
* Parts modified from IGviewer source by Peter Hartley
|
||||
* http://utter.chaos.org/~pdh/software/intergif.htm
|
||||
*/
|
||||
|
||||
#include <assert.h>
|
||||
#include <string.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdlib.h>
|
||||
#include "libungif/gif_lib.h"
|
||||
#include "animlib/animlib.h"
|
||||
#include "oslib/colourtrans.h"
|
||||
#include "oslib/os.h"
|
||||
#include "oslib/osspriteop.h"
|
||||
@ -29,265 +20,96 @@
|
||||
#include "netsurf/utils/log.h"
|
||||
#include "netsurf/utils/utils.h"
|
||||
|
||||
// Constants for GIF interlacing
|
||||
const int
|
||||
InterlacedOffset[] = { 0, 4, 2, 1 }, /* The way Interlaced image should. */
|
||||
InterlacedJumps[] = { 8, 8, 4, 2 }; /* be read - offsets and jumps... */
|
||||
|
||||
static osspriteop_area *create_buffer_sprite(struct content *c, anim a, int *bgc);
|
||||
|
||||
void nsgif_init(void)
|
||||
{
|
||||
}
|
||||
|
||||
// Called when Netsurf wants us to prepare to decode a GIF
|
||||
void nsgif_create(struct content *c)
|
||||
void nsgif_create(struct content*c)
|
||||
{
|
||||
// Clear the sprite area
|
||||
c->data.gif.sprite_area = 0;
|
||||
|
||||
// Allocate some memory for the GIF file
|
||||
c->data.gif.data = xcalloc(0, 1);
|
||||
// Set the length and buffer position to zero (we haven't loaded any data
|
||||
// yet)
|
||||
c->data.gif.length = 0;
|
||||
c->data.gif.buffer_pos = 0;
|
||||
}
|
||||
|
||||
// Called when Netsurf has got some more data for us
|
||||
void nsgif_process_data(struct content *c, char *data, unsigned long size)
|
||||
{
|
||||
// We've just been given some more data!
|
||||
// Reallocate the memory block
|
||||
c->data.gif.data = xrealloc(c->data.gif.data, c->data.gif.length + size);
|
||||
// Copy the new data into our buffer
|
||||
memcpy(c->data.gif.data + c->data.gif.length, data, size);
|
||||
// Update the data length variables
|
||||
c->data.gif.length += size;
|
||||
c->size += size;
|
||||
}
|
||||
|
||||
// This is the callback for the GIF loader. It grabs some data from the buffer
|
||||
// and hands it over to Libungif.
|
||||
// Returns the number of bytes successfully read from the buffer
|
||||
int nsgif_input_callback(GifFileType *giffile, GifByteType *data, int length)
|
||||
{
|
||||
struct content *c;
|
||||
|
||||
// We need to set up the content block pointer first
|
||||
c = (struct content *)giffile->UserData;
|
||||
|
||||
// Now check that there's enough data in the buffer
|
||||
if ((c->data.gif.buffer_pos + length) > c->data.gif.length)
|
||||
{
|
||||
// We don't have enough data in the buffer. Give libungif the data we've
|
||||
// got.
|
||||
memcpy(data, &c->data.gif.data[c->data.gif.buffer_pos], (c->data.gif.length - c->data.gif.buffer_pos));
|
||||
c->data.gif.buffer_pos += length;
|
||||
return (c->data.gif.length - c->data.gif.buffer_pos);
|
||||
}
|
||||
|
||||
// Well, we've got enough data. Give libungif as much as it wants.
|
||||
memcpy(data, &c->data.gif.data[c->data.gif.buffer_pos], (unsigned int) length);
|
||||
c->data.gif.buffer_pos += length;
|
||||
|
||||
return length;
|
||||
}
|
||||
|
||||
// Called when Netsurf wants us to convert the image
|
||||
int nsgif_convert(struct content *c, unsigned int iwidth, unsigned int iheight)
|
||||
{
|
||||
unsigned long i, j;
|
||||
unsigned int sprite_size;
|
||||
unsigned long width, height, left, top;
|
||||
os_sprite_palette *sprite_palette;
|
||||
osspriteop_area *sprite_area;
|
||||
osspriteop_header *sprite;
|
||||
// The next three lines are for vars. used by the gif decoding engine
|
||||
GifRecordType recordtype;
|
||||
int extcode, count, got_image_data;
|
||||
int trancol = -1;
|
||||
GifByteType *extension;
|
||||
GifColorType *colormap;
|
||||
anim a;
|
||||
frame f;
|
||||
pixel *img, *mask;
|
||||
int bg;
|
||||
struct osspriteop_header *header;
|
||||
struct osspriteop_area *area;
|
||||
|
||||
LOG(("Opening GIF file"));
|
||||
a = Anim_FromData(c->data.gif.data, c->data.gif.length, NULL, false, false, false);
|
||||
if (!a) {
|
||||
|
||||
got_image_data = 0; // we haven't read any image data yet
|
||||
|
||||
// Now decode the GIF
|
||||
c->data.gif.giffile = DGifOpen(c, &nsgif_input_callback);
|
||||
if (c->data.gif.giffile == NULL)
|
||||
{
|
||||
LOG(("ERROR: giffile is null! error %d", GifLastError()));
|
||||
return 1; // more graceful exit
|
||||
LOG(("Error creating anim object"));
|
||||
return 1;
|
||||
}
|
||||
|
||||
// Start creating the sprite
|
||||
width = c->data.gif.giffile->SWidth;
|
||||
height = c->data.gif.giffile->SHeight;
|
||||
if(!Anim_CommonPalette(a)) {
|
||||
|
||||
LOG(("gif image width = %lu, height = %lu", width, height));
|
||||
LOG(("bad palette"));
|
||||
Anim_Destroy(&a);
|
||||
return 1;
|
||||
}
|
||||
|
||||
sprite_size = sizeof(*sprite_area) + sizeof(*sprite);
|
||||
area = create_buffer_sprite(c, a, &bg);
|
||||
if(!area) {
|
||||
|
||||
// GIFs can't be more than 256 colours (8 bits).
|
||||
sprite_size += (8 * 256) + (height * ((width + 3) & ~3u) * 2);
|
||||
LOG(("Failed to create sprite"));
|
||||
Anim_Destroy(&a);
|
||||
xfree(area);
|
||||
return 1;
|
||||
}
|
||||
c->data.gif.sprite_area = area;
|
||||
|
||||
sprite_area = xcalloc((sprite_size) + 1000, 1);
|
||||
sprite_area->size = sprite_size;
|
||||
sprite_area->sprite_count = 1;
|
||||
sprite_area->first = sizeof(*sprite_area);
|
||||
sprite_area->used = sprite_size;
|
||||
sprite = (osspriteop_header *) (sprite_area + 1);
|
||||
sprite->size = sprite_size - sizeof(*sprite_area);
|
||||
strcpy(sprite->name, "gif");
|
||||
sprite->height = height -1;
|
||||
c->data.gif.sprite_area = sprite_area;
|
||||
sprite->width = ((width + 3) & ~3u) / 4 - 1;
|
||||
sprite->left_bit = 0;
|
||||
sprite->right_bit = (8 * (((width - 1) % 4) + 1)) -1;
|
||||
sprite->image = sizeof(*sprite) + 8 * 256;
|
||||
sprite->mask = sizeof(*sprite) + (8 * 256) + (height * ((width + 3) & ~3u));
|
||||
LOG(("image = %d, mask = %d", sprite->image, sprite->mask));
|
||||
sprite->mode = (os_mode) 21;
|
||||
sprite_palette = (os_sprite_palette *) (sprite + 1);
|
||||
c->data.gif.sprite_image = ((char *) sprite) + sprite->image;
|
||||
c->width = width;
|
||||
c->height = height;
|
||||
header = (osspriteop_header*)(c->data.gif.sprite_area + 1);
|
||||
f = a->pFrames + 0;
|
||||
img = (pixel*)header + header->image;
|
||||
mask = (pixel*)header + header->mask;
|
||||
|
||||
do
|
||||
{
|
||||
if (DGifGetRecordType(c->data.gif.giffile, &recordtype) == GIF_ERROR)
|
||||
{
|
||||
LOG(("gif error %d", GifLastError()));
|
||||
return(1); // more graceful exit
|
||||
}
|
||||
// TODO: ^^^ better error checking
|
||||
switch (recordtype)
|
||||
{
|
||||
case IMAGE_DESC_RECORD_TYPE:
|
||||
// The next line is used to handle animated GIFs
|
||||
if (!got_image_data)
|
||||
{
|
||||
assert(DGifGetImageDesc(c->data.gif.giffile) != GIF_ERROR);
|
||||
// TODO: ^^^ better error checking
|
||||
Anim_DecompressAligned(f->pImageData, f->nImageSize,
|
||||
a->nWidth, a->nHeight, img);
|
||||
|
||||
c->data.gif.sprite_area = sprite_area;
|
||||
height = c->data.gif.giffile->Image.Height;
|
||||
width = c->data.gif.giffile->Image.Width;
|
||||
left = c->data.gif.giffile->Image.Left;
|
||||
top = c->data.gif.giffile->Image.Top;
|
||||
if(f->pMaskData) {
|
||||
|
||||
if (c->data.gif.giffile->Image.Interlace)
|
||||
{
|
||||
// The image is interlaced, therefore we need to perform four
|
||||
// passes over the image
|
||||
for (count = i = 0; i < 4; i++)
|
||||
{
|
||||
LOG(("Interlaced GIF file"));
|
||||
for (j=(top+InterlacedOffset[i]); j < (top+c->data.gif.giffile->Image.Height); j += InterlacedJumps[i])
|
||||
{
|
||||
count++;
|
||||
assert (DGifGetLine(c->data.gif.giffile, c->data.gif.sprite_image + (j * ((c->width + 3) & ~3u)) + left, (int)width) != GIF_ERROR);
|
||||
// TODO: ^^^ better error checking
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
LOG(("running DGifGetLine"));
|
||||
for (i=top; i<(height+top); i++)
|
||||
{
|
||||
if (DGifGetLine(c->data.gif.giffile, c->data.gif.sprite_image + (i * ((c->width + 3) & ~3u))+left, (int) width) == GIF_ERROR)
|
||||
{
|
||||
LOG(("error: gif line %lu - error %d", i, GifLastError()));
|
||||
LOG(("exp height = %lu, width = %lu", height, width));
|
||||
return 1;
|
||||
}
|
||||
// TODO: ^^^ better error checking
|
||||
}
|
||||
}
|
||||
got_image_data = -1;
|
||||
} else recordtype = TERMINATE_RECORD_TYPE;
|
||||
break;
|
||||
case EXTENSION_RECORD_TYPE:
|
||||
assert (DGifGetExtension(c->data.gif.giffile, &extcode, &extension) != GIF_ERROR);
|
||||
int i,n = header->mask - header->image;
|
||||
|
||||
if (extension == NULL)
|
||||
break;
|
||||
Anim_DecompressAligned(f->pMaskData, f->nMaskSize,
|
||||
a->nWidth, a->nHeight, mask);
|
||||
|
||||
do
|
||||
{
|
||||
// Is the extension block a Graphics Rendering Info block?
|
||||
if (extcode == GRAPHICS_EXT_FUNC_CODE)
|
||||
{
|
||||
// Yep. Grab the transparency data
|
||||
if ((extension[1] & 1) == 1)
|
||||
trancol = extension[4];
|
||||
else
|
||||
trancol = -1;
|
||||
}
|
||||
for(i=0; i<n; i++)
|
||||
if(!mask[i]) {
|
||||
|
||||
DGifGetExtensionNext(c->data.gif.giffile, &extension);
|
||||
} while (extension != NULL);
|
||||
break;
|
||||
case TERMINATE_RECORD_TYPE:
|
||||
break;
|
||||
default: // Should be trapped by DGifGetRecordType
|
||||
break;
|
||||
}
|
||||
} while (recordtype != TERMINATE_RECORD_TYPE);
|
||||
img[i] = 255;
|
||||
mask[i] = bg;
|
||||
}
|
||||
}
|
||||
else
|
||||
memset(mask, 255, header->mask - header->image);
|
||||
|
||||
LOG(("creating image transparency mask, trancol %i", trancol));
|
||||
|
||||
// This is one evil piece of code. Still, it works...
|
||||
for (j=0; j<height; j++)
|
||||
for (i=0; i<width; i++)
|
||||
{
|
||||
unsigned char *x = ((char *)sprite) + sprite->mask;
|
||||
unsigned int pixofs = (j * ((c->width + 3) & ~3u)) + i;
|
||||
|
||||
if (c->data.gif.sprite_image[pixofs] == trancol)
|
||||
x[pixofs] = 0x00;
|
||||
else
|
||||
x[pixofs] = 0xFF;
|
||||
}
|
||||
|
||||
/*
|
||||
And now for the obligatory quote from "The Matrix"
|
||||
"Before you can bend the spoon, you must realise the truth."
|
||||
"And what is the truth?"
|
||||
"There is no spoon."
|
||||
*/
|
||||
|
||||
// Load the colour map
|
||||
colormap = (c->data.gif.giffile->Image.ColorMap ?
|
||||
c->data.gif.giffile->Image.ColorMap->Colors :
|
||||
c->data.gif.giffile->SColorMap->Colors);
|
||||
|
||||
for (i=0; i < 256; i++)
|
||||
sprite_palette->entries[i].on =
|
||||
sprite_palette->entries[i].off =
|
||||
(colormap[i].Blue << 24) |
|
||||
(colormap[i].Green << 16) |
|
||||
(colormap[i].Red << 8) |
|
||||
16;
|
||||
|
||||
DGifCloseFile(c->data.gif.giffile);
|
||||
|
||||
c->width = width;
|
||||
c->height = height;
|
||||
|
||||
c->title = xcalloc(100, 1);
|
||||
c->title = xcalloc(100, sizeof(char));
|
||||
sprintf(c->title, "GIF image (%lux%lu)", c->width, c->height);
|
||||
c->status = CONTENT_STATUS_DONE;
|
||||
|
||||
// Enable this if you want to debug the GIF loader
|
||||
// xosspriteop_save_sprite_file(osspriteop_USER_AREA, c->data.gif.sprite_area,
|
||||
// "gif");
|
||||
/* xosspriteop_save_sprite_file(osspriteop_USER_AREA,
|
||||
c->data.gif.sprite_area, "gif"); */
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
void nsgif_revive(struct content *c, unsigned int width, unsigned int height)
|
||||
{
|
||||
}
|
||||
@ -297,7 +119,37 @@ void nsgif_reformat(struct content *c, unsigned int width, unsigned int height)
|
||||
{
|
||||
}
|
||||
|
||||
// Called when Netsurf is finished with us
|
||||
void nsgif_redraw(struct content *c, long x, long y,
|
||||
unsigned long width, unsigned long height)
|
||||
{
|
||||
unsigned int size;
|
||||
osspriteop_trans_tab *table;
|
||||
|
||||
|
||||
xcolourtrans_generate_table_for_sprite(c->data.gif.sprite_area,
|
||||
(osspriteop_id) (c->data.gif.sprite_area + 1),
|
||||
colourtrans_CURRENT_MODE, colourtrans_CURRENT_PALETTE,
|
||||
0, colourtrans_GIVEN_SPRITE, 0, 0, &size);
|
||||
|
||||
table = xcalloc(size, 1);
|
||||
|
||||
xcolourtrans_generate_table_for_sprite(c->data.gif.sprite_area,
|
||||
(osspriteop_id) (c->data.gif.sprite_area + 1),
|
||||
colourtrans_CURRENT_MODE, colourtrans_CURRENT_PALETTE,
|
||||
table, colourtrans_GIVEN_SPRITE, 0, 0, 0);
|
||||
|
||||
xosspriteop_put_sprite_scaled(osspriteop_PTR,
|
||||
c->data.gif.sprite_area,
|
||||
(osspriteop_id) (c->data.gif.sprite_area + 1),
|
||||
x, y - height,
|
||||
/* osspriteop_USE_PALETTE is RO 3.5+ only.
|
||||
* behaviour on RO < 3.5 is unknown...
|
||||
*/
|
||||
osspriteop_USE_MASK | osspriteop_USE_PALETTE, 0, table);
|
||||
|
||||
xfree(table);
|
||||
}
|
||||
|
||||
void nsgif_destroy(struct content *c)
|
||||
{
|
||||
xfree(c->title);
|
||||
@ -305,35 +157,75 @@ void nsgif_destroy(struct content *c)
|
||||
xfree(c->data.gif.data);
|
||||
}
|
||||
|
||||
// Called when Netsurf wants us to draw the image
|
||||
void nsgif_redraw(struct content *c, long x, long y,
|
||||
unsigned long width, unsigned long height)
|
||||
|
||||
static osspriteop_area *create_buffer_sprite( struct content *c, anim a, int *bgc )
|
||||
{
|
||||
/* TODO: scale to width, height */
|
||||
unsigned int size;
|
||||
osspriteop_trans_tab *table;
|
||||
unsigned int abw = ((a->nWidth + 3 ) & ~3) * 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;
|
||||
|
||||
/* JMB - lose this, it's filling my error log up too quickly ;)
|
||||
if ( !result )
|
||||
return NULL;
|
||||
|
||||
LOG(("Redraw fired"));
|
||||
result->size = nBytes;
|
||||
result->sprite_count = 1;
|
||||
result->first = sizeof(*result);
|
||||
result->used = nBytes;
|
||||
|
||||
philpem - Oops. Sorry about that!
|
||||
*/
|
||||
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 */
|
||||
|
||||
xcolourtrans_generate_table_for_sprite(c->data.gif.sprite_area,
|
||||
(osspriteop_id) (c->data.gif.sprite_area + 1),
|
||||
colourtrans_CURRENT_MODE, colourtrans_CURRENT_PALETTE,
|
||||
0, colourtrans_GIVEN_SPRITE, 0, 0, &size);
|
||||
table = xcalloc(size, 1);
|
||||
xcolourtrans_generate_table_for_sprite(c->data.gif.sprite_area,
|
||||
(osspriteop_id) (c->data.gif.sprite_area + 1),
|
||||
colourtrans_CURRENT_MODE, colourtrans_CURRENT_PALETTE,
|
||||
table, colourtrans_GIVEN_SPRITE, 0, 0, 0);
|
||||
c->data.gif.sprite_image = ((char*)spr) + spr->image;
|
||||
c->width = a->nWidth;
|
||||
c->height = a->nHeight;
|
||||
|
||||
xosspriteop_put_sprite_scaled(osspriteop_PTR,
|
||||
c->data.gif.sprite_area,
|
||||
(osspriteop_id) (c->data.gif.sprite_area + 1),
|
||||
x, y - c->height * 2, 8, 0, table);
|
||||
n = a->pFrames->pal->nColours;
|
||||
pPalSrc = a->pFrames->pal->pColours;
|
||||
for ( i=0; i<n; i++ )
|
||||
{
|
||||
*pPalDest++ = *pPalSrc;
|
||||
*pPalDest++ = *pPalSrc++;
|
||||
}
|
||||
|
||||
xfree(table);
|
||||
if ( !bgc )
|
||||
return result;
|
||||
|
||||
/* A bit of faff here to return a useful (near-white) background colour */
|
||||
|
||||
pPalDest = (unsigned int*)(spr+1);
|
||||
|
||||
if ( n < 256 )
|
||||
{
|
||||
pPalDest[255*2] = 0xFFFFFF00;
|
||||
pPalDest[255*2+1] = 0xFFFFFF00;
|
||||
*bgc = 255;
|
||||
}
|
||||
else
|
||||
{
|
||||
unsigned int max = 0;
|
||||
for ( i=0; i<256; i++ )
|
||||
{
|
||||
unsigned int dark = pPalDest[i*2];
|
||||
dark = (dark>>24) + ((dark>>16)&0xFF) + ((dark>>8)&0xFF);
|
||||
if ( dark > max )
|
||||
{
|
||||
max = dark;
|
||||
n = i;
|
||||
}
|
||||
}
|
||||
*bgc = n;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user