From bedeee808ce4d8113ccd01d1fd0d066ce9821587 Mon Sep 17 00:00:00 2001 From: Philip Pemberton Date: Thu, 5 Jun 2003 13:24:28 +0000 Subject: [project @ 2003-06-05 13:24:28 by philpem] Added GIF decode support svn path=/import/netsurf/; revision=161 --- riscos/gif.c | 329 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ riscos/gif.h | 19 ++++ 2 files changed, 348 insertions(+) create mode 100644 riscos/gif.c create mode 100644 riscos/gif.h diff --git a/riscos/gif.c b/riscos/gif.c new file mode 100644 index 000000000..b2a9768b3 --- /dev/null +++ b/riscos/gif.c @@ -0,0 +1,329 @@ +/******************* + * 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. + * + * $Id: gif.c,v 1.1 2003/06/05 13:24:28 philpem Exp $ + */ + +#include +#include +#include +#include "libungif/gif_lib.h" +#include "oslib/colourtrans.h" +#include "oslib/os.h" +#include "oslib/osspriteop.h" +#include "netsurf/content/content.h" +#include "netsurf/riscos/gif.h" +#include "netsurf/utils/log.h" +#include "netsurf/utils/utils.h" + +/* maps colours to 256 mode colour numbers */ +static os_colour_number colour_table[4096]; + +static int + InterlacedOffset[] = { 0, 4, 2, 1 }, /* The way Interlaced image should. */ + InterlacedJumps[] = { 8, 8, 4, 2 }; /* be read - offsets and jumps... */ + + +void nsgif_init(void) +{ + // Generate a colour lookup table + // TODO: Use the PNG colour table instead of the GIF table... + unsigned int red, green, blue; + for (red = 0; red != 0xf; red++) + for (green = 0; green != 0xf; green++) + for (blue = 0; blue != 0xf; blue++) + colour_table[red << 8 | green << 4 | blue] = + colourtrans_return_colour_number_for_mode( + blue << 28 | blue << 24 | + green << 20 | green << 16 | + red << 12 | red << 8, 21, 0); +} + +// Called when Netsurf wants us to prepare to decode a GIF +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; + + LOG(("gif object created")); +} + +// 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], 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 width, unsigned int height) +{ + char *row, **row_pointers; + int i, j, bit_depth, color_type, log2bpp, interlace; + unsigned int rowbytes, sprite_size; + unsigned long width, height; + os_palette *palette; + 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 + int recordtype, cur_row, extcode, count, got_image_data; + GifByteType *extension; + GifColorType *colormap; + + LOG(("Opening GIF file")); + + 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); +// assert(c->data.gif.giffile != NULL); + if (c->data.gif.giffile == NULL) + { + LOG(("ERROR: giffile is null! error %d", GifLastError())); +// assert(1 == 2); + return 0; // more graceful exit + } + // TODO: ^^^ Error checking + + // Start creating the sprite + width = c->data.gif.giffile->SWidth; + height = c->data.gif.giffile->SHeight; + + LOG(("gif image width = %d, height = %d", width, height)); + + sprite_size = sizeof(*sprite_area) + sizeof(*sprite); + + // GIFs can't be more than 256 colours (8 bits). + sprite_size += 8 * 256 + height * ((width + 3) & ~3u); + + 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->mask = sprite->image = sizeof(*sprite) + 8 * 256; + 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; + + do + { + if (DGifGetRecordType(c->data.gif.giffile, &recordtype) == GIF_ERROR) + { + LOG(("gif error %d", GifLastError())); +// assert(1 == 2); + return(0); // 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 + + c->data.gif.sprite_area = sprite_area; + + 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=InterlacedOffset[i]; j < height; j += InterlacedJumps[i]) + { + LOG(("gif line %d", count)); + count++; + assert (DGifGetLine(c->data.gif.giffile, c->data.gif.sprite_image + j * ((c->width + 3) & ~3u), width) != GIF_ERROR); + // TODO: ^^^ better error checking + } + } + } + else + { + LOG(("running DGifGetLine")); + for (i=0; idata.gif.giffile, c->data.gif.sprite_image + i * ((c->width + 3) & ~3u), width) != GIF_ERROR); + if (DGifGetLine(c->data.gif.giffile, c->data.gif.sprite_image + i * ((c->width + 3) & ~3u), width) == GIF_ERROR) + { + LOG(("error: gif line %d - error %d", i, GifLastError())); + LOG(("exp height = %d, width = %d", height, width)); + assert(1 == 2); + } + // TODO: ^^^ better error checking + LOG(("gif line %d", i)); + } + } + got_image_data = -1; + } else recordtype = TERMINATE_RECORD_TYPE; + break; + case EXTENSION_RECORD_TYPE: + // Skip any extension blocks in the file + assert (DGifGetExtension(c->data.gif.giffile, &extcode, &extension) != GIF_ERROR); + // TODO: ^^^ better error checking + while (extension != NULL) + { + DGifGetExtensionNext(c->data.gif.giffile, &extension); + // TODO: ^^^ better error checking + } + break; + case TERMINATE_RECORD_TYPE: + break; + default: // Should be trapped by DGifGetRecordType + break; + } + } while (recordtype != TERMINATE_RECORD_TYPE); + + LOG(("all done")); + +/* + 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." + */ + +// for (i = 0; i != palette_size; i++) +// sprite_palette->entries[i].on = +// sprite_palette->entries[i].off = +// png_palette[i].blue << 24 | +// png_palette[i].green << 16 | +// png_palette[i].red << 8 | 16; + + // FIXME: 255 is the size of the GIF palette + // FIXME: Load the palette instead of creating a greyscale palette + + colormap = (c->data.gif.giffile->Image.ColorMap ? + c->data.gif.giffile->Image.ColorMap->Colors : + c->data.gif.giffile->SColorMap->Colors); + + for (i=0; i != 255; 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); + sprintf(c->title, "GIF image (%ux%u)", c->width, c->height); + +// 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) +{ +} + + +void nsgif_reformat(struct content *c, unsigned int width, unsigned int height) +{ +} + +// Called when Netsurf is finished with us +void nsgif_destroy(struct content *c) +{ + xfree(c->title); + xfree(c->data.gif.sprite_area); + 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) +{ + /* TODO: scale to width, height */ + int size; + osspriteop_trans_tab *table; + + LOG(("Redraw fired")); + + 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, 0, 0, table); + + xfree(table); +} diff --git a/riscos/gif.h b/riscos/gif.h new file mode 100644 index 000000000..4f98ecded --- /dev/null +++ b/riscos/gif.h @@ -0,0 +1,19 @@ +/** + * $Id: gif.h,v 1.1 2003/06/05 13:24:28 philpem Exp $ + */ + +#ifndef _NETSURF_RISCOS_GIF_H_ +#define _NETSURF_RISCOS_GIF_H_ + +#include "netsurf/content/content.h" + +void nsgif_init(void); +void nsgif_create(struct content *c); +void nsgif_process_data(struct content *c, char *data, unsigned long size); +int nsgif_convert(struct content *c, unsigned int width, unsigned int height); +void nsgif_revive(struct content *c, unsigned int width, unsigned int height); +void nsgif_reformat(struct content *c, unsigned int width, unsigned int height); +void nsgif_destroy(struct content *c); +void nsgif_redraw(struct content *c, long x, long y, + unsigned long width, unsigned long height); +#endif -- cgit v1.2.3