summaryrefslogtreecommitdiff
path: root/content
diff options
context:
space:
mode:
authorVincent Sanders <vince@kyllikki.org>2016-05-23 23:32:16 +0100
committerVincent Sanders <vince@kyllikki.org>2016-05-23 23:32:16 +0100
commit3224d7121aff91ab8d888831dc493ef621e3dd39 (patch)
tree5e4585fd78b6b247a5e602ea1a1d55e0dfe206fb /content
parent93be8d805e7e1f32638015770446476fef22ceac (diff)
downloadnetsurf-3224d7121aff91ab8d888831dc493ef621e3dd39.tar.gz
netsurf-3224d7121aff91ab8d888831dc493ef621e3dd39.tar.bz2
move image content handlers to accomodate core build changes
Diffstat (limited to 'content')
-rw-r--r--content/Makefile6
-rw-r--r--content/content.c2
-rw-r--r--content/fetchers/about.c2
-rw-r--r--content/handlers/Makefile4
-rw-r--r--content/handlers/image/Makefile15
-rw-r--r--content/handlers/image/bitmap.h177
-rw-r--r--content/handlers/image/bmp.c282
-rw-r--r--content/handlers/image/bmp.h33
-rw-r--r--content/handlers/image/gif.c455
-rw-r--r--content/handlers/image/gif.h29
-rw-r--r--content/handlers/image/ico.c300
-rw-r--r--content/handlers/image/ico.h28
-rw-r--r--content/handlers/image/image.c159
-rw-r--r--content/handlers/image/image.h43
-rw-r--r--content/handlers/image/image_cache.c797
-rw-r--r--content/handlers/image/image_cache.h189
-rw-r--r--content/handlers/image/jpeg.c393
-rw-r--r--content/handlers/image/jpeg.h28
-rw-r--r--content/handlers/image/nssprite.c259
-rw-r--r--content/handlers/image/nssprite.h28
-rw-r--r--content/handlers/image/png.c614
-rw-r--r--content/handlers/image/png.h25
-rw-r--r--content/handlers/image/rsvg.c326
-rw-r--r--content/handlers/image/rsvg.h28
-rw-r--r--content/handlers/image/svg.c353
-rw-r--r--content/handlers/image/svg.h28
-rw-r--r--content/handlers/image/video.c202
-rw-r--r--content/handlers/image/video.h26
-rw-r--r--content/urldb.c2
29 files changed, 4830 insertions, 3 deletions
diff --git a/content/Makefile b/content/Makefile
index ab257eaea..02490618a 100644
--- a/content/Makefile
+++ b/content/Makefile
@@ -9,3 +9,9 @@ ifeq ($(NETSURF_FS_BACKING_STORE),YES)
endif
S_CONTENT := $(addprefix content/,$(S_CONTENT))
+
+# Content fetchers sources
+include content/fetchers/Makefile
+
+# Content handlers
+include content/handlers/Makefile
diff --git a/content/content.c b/content/content.c
index e112f3844..9b53a4971 100644
--- a/content/content.c
+++ b/content/content.c
@@ -32,8 +32,8 @@
#include "desktop/knockout.h"
#include "desktop/gui_internal.h"
#include "desktop/browser.h"
-#include "image/bitmap.h"
+#include "content/handlers/image/bitmap.h"
#include "content/content_protected.h"
#include "content/content_debug.h"
#include "content/hlcache.h"
diff --git a/content/fetchers/about.c b/content/fetchers/about.c
index 605d3a8d9..69ca0aed2 100644
--- a/content/fetchers/about.c
+++ b/content/fetchers/about.c
@@ -41,7 +41,7 @@
#include "content/fetchers.h"
#include "content/fetchers/about.h"
#include "content/content_type.h"
-#include "image/image_cache.h"
+#include "content/handlers/image/image_cache.h"
struct fetch_about_context;
diff --git a/content/handlers/Makefile b/content/handlers/Makefile
new file mode 100644
index 000000000..97d2813b4
--- /dev/null
+++ b/content/handlers/Makefile
@@ -0,0 +1,4 @@
+# Image content handler sources
+include content/handlers/image/Makefile
+
+S_IMAGE := $(addprefix content/handlers/,$(S_IMAGE))
diff --git a/content/handlers/image/Makefile b/content/handlers/image/Makefile
new file mode 100644
index 000000000..5851a1c43
--- /dev/null
+++ b/content/handlers/image/Makefile
@@ -0,0 +1,15 @@
+# Image content handlers sources
+
+# S_IMAGE are sources related to image management
+S_IMAGE_YES := image.c image_cache.c
+S_IMAGE_NO :=
+S_IMAGE_$(NETSURF_USE_BMP) += bmp.c ico.c
+S_IMAGE_$(NETSURF_USE_GIF) += gif.c
+S_IMAGE_$(NETSURF_USE_JPEG) += jpeg.c
+S_IMAGE_$(NETSURF_USE_ROSPRITE) += nssprite.c
+S_IMAGE_$(NETSURF_USE_PNG) += png.c
+S_IMAGE_$(NETSURF_USE_NSSVG) += svg.c
+S_IMAGE_$(NETSURF_USE_RSVG) += rsvg.c
+S_IMAGE_$(NETSURF_USE_VIDEO) += video.c
+
+S_IMAGE := $(addprefix image/,$(S_IMAGE_YES))
diff --git a/content/handlers/image/bitmap.h b/content/handlers/image/bitmap.h
new file mode 100644
index 000000000..ef6c1b49b
--- /dev/null
+++ b/content/handlers/image/bitmap.h
@@ -0,0 +1,177 @@
+/*
+ * Copyright 2004 James Bursa <bursa@users.sourceforge.net>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * NetSurf is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+/**
+ * \file
+ * Generic bitmap handling interface.
+ *
+ * This interface wraps the native platform-specific image format, so that
+ * portable image convertors can be written.
+ *
+ * Bitmaps are required to be 32bpp with components in the order RR GG BB AA.
+ *
+ * For example, an opaque 1x1 pixel image would yield the following bitmap
+ * data:
+ *
+ * > Red : 0xff 0x00 0x00 0x00
+ * > Green: 0x00 0xff 0x00 0x00
+ * > Blue : 0x00 0x00 0xff 0x00
+ *
+ * Any attempt to read pixels by casting bitmap data to uint32_t or similar
+ * will need to cater for the order of bytes in a word being different on
+ * big and little endian systems. To avoid confusion, it is recommended
+ * that pixel data is loaded as follows:
+ *
+ * uint32_t read_pixel(const uint8_t *bmp)
+ * {
+ * // red green blue alpha
+ * return bmp[0] | (bmp[1] << 8) | (bmp[2] << 16) | (bmp[3] << 24);
+ * }
+ *
+ * and *not* as follows:
+ *
+ * uint32_t read_pixel(const uint8_t *bmp)
+ * {
+ * return *((uint32_t *) bmp);
+ * }
+ */
+
+#ifndef _NETSURF_IMAGE_BITMAP_H_
+#define _NETSURF_IMAGE_BITMAP_H_
+
+#define BITMAP_NEW 0
+#define BITMAP_OPAQUE (1 << 0) /**< image is opaque */
+#define BITMAP_MODIFIED (1 << 1) /**< buffer has been modified */
+#define BITMAP_CLEAR_MEMORY (1 << 2) /**< memory should be wiped */
+
+struct content;
+struct bitmap;
+struct hlcache_handle;
+
+/**
+ * Bitmap operations.
+ */
+struct gui_bitmap_table {
+ /* Mandantory entries */
+
+ /**
+ * Create a new bitmap.
+ *
+ * \param width width of image in pixels
+ * \param height width of image in pixels
+ * \param state The state to create the bitmap in.
+ * \return A bitmap structure or NULL on error.
+ */
+ void *(*create)(int width, int height, unsigned int state);
+
+ /**
+ * Destroy a bitmap.
+ *
+ * \param bitmap The bitmap to destroy.
+ */
+ void (*destroy)(void *bitmap);
+
+ /**
+ * Set the opacity of a bitmap.
+ *
+ * \param bitmap The bitmap to set opacity on.
+ * \param opaque The bitmap opacity to set.
+ */
+ void (*set_opaque)(void *bitmap, bool opaque);
+
+ /**
+ * Get the opacity of a bitmap.
+ *
+ * \param bitmap The bitmap to examine.
+ * \return The bitmap opacity.
+ */
+ bool (*get_opaque)(void *bitmap);
+
+ /**
+ * Test if a bitmap is opaque.
+ *
+ * \param bitmap The bitmap to examine.
+ * \return The bitmap opacity.
+ */
+ bool (*test_opaque)(void *bitmap);
+
+ /**
+ * Get the image buffer from a bitmap
+ *
+ * \param bitmap The bitmap to get the buffer from.
+ * \return The image buffer or NULL if there is none.
+ */
+ unsigned char *(*get_buffer)(void *bitmap);
+
+ /**
+ * Get the number of bytes per row of the image
+ *
+ * \param bitmap The bitmap
+ * \return The number of bytes for a row of the bitmap.
+ */
+ size_t (*get_rowstride)(void *bitmap);
+
+ /**
+ * Get the bitmap width
+ *
+ * \param bitmap The bitmap
+ * \return The bitmap width in pixels.
+ */
+ int (*get_width)(void *bitmap);
+
+ /**
+ * Get the bitmap height
+ *
+ * \param bitmap The bitmap
+ * \return The bitmap height in pixels.
+ */
+ int (*get_height)(void *bitmap);
+
+ /**
+ * The the *bytes* per pixel.
+ *
+ * \param bitmap The bitmap
+ */
+ size_t (*get_bpp)(void *bitmap);
+
+ /**
+ * Savde a bitmap to disc.
+ *
+ * \param bitmap The bitmap to save
+ * \param path The path to save the bitmap to.
+ * \param flags Flags affectin the save.
+ */
+ bool (*save)(void *bitmap, const char *path, unsigned flags);
+
+ /**
+ * Marks a bitmap as modified.
+ *
+ * \param bitmap The bitmap set as modified.
+ */
+ void (*modified)(void *bitmap);
+
+ /**
+ * Render content into a bitmap.
+ *
+ * \param bitmap The bitmap to render into.
+ * \param content The content to render.
+ */
+ nserror (*render)(struct bitmap *bitmap, struct hlcache_handle *content);
+};
+
+#endif
diff --git a/content/handlers/image/bmp.c b/content/handlers/image/bmp.c
new file mode 100644
index 000000000..f622f6cb0
--- /dev/null
+++ b/content/handlers/image/bmp.c
@@ -0,0 +1,282 @@
+/*
+ * Copyright 2006 Richard Wilson <info@tinct.net>
+ * Copyright 2008 Sean Fox <dyntryx@gmail.com>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * NetSurf is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+/**
+ * \file
+ * implementation of content handler for BMP images.
+ */
+
+#include <stdbool.h>
+#include <stdlib.h>
+#include <libnsbmp.h>
+
+#include "utils/utils.h"
+#include "utils/messages.h"
+#include "content/content_protected.h"
+#include "desktop/gui_internal.h"
+#include "desktop/plotters.h"
+
+#include "bitmap.h"
+#include "bmp.h"
+
+/** bmp context. */
+typedef struct nsbmp_content {
+ struct content base;
+
+ bmp_image *bmp; /** BMP image data */
+
+ struct bitmap *bitmap; /**< Created NetSurf bitmap */
+} nsbmp_content;
+
+/**
+ * Callback for libnsbmp; forwards the call to bitmap_create()
+ *
+ * \param width width of image in pixels
+ * \param height width of image in pixels
+ * \param bmp_state A flag word indicating the initial state
+ * \return An opaque struct bitmap, or NULL on memory exhaustion
+ */
+static void *nsbmp_bitmap_create(int width, int height, unsigned int bmp_state)
+{
+ unsigned int bitmap_state = BITMAP_NEW;
+
+ /* set bitmap state based on bmp state */
+ bitmap_state |= (bmp_state & BMP_OPAQUE) ? BITMAP_OPAQUE : 0;
+ bitmap_state |= (bmp_state & BMP_CLEAR_MEMORY) ?
+ BITMAP_CLEAR_MEMORY : 0;
+
+ /* return the created bitmap */
+ return guit->bitmap->create(width, height, bitmap_state);
+}
+
+static nserror nsbmp_create_bmp_data(nsbmp_content *bmp)
+{
+ union content_msg_data msg_data;
+ bmp_bitmap_callback_vt bmp_bitmap_callbacks = {
+ .bitmap_create = nsbmp_bitmap_create,
+ .bitmap_destroy = guit->bitmap->destroy,
+ .bitmap_get_buffer = guit->bitmap->get_buffer,
+ .bitmap_get_bpp = guit->bitmap->get_bpp
+ };
+
+ bmp->bmp = calloc(sizeof(struct bmp_image), 1);
+ if (bmp->bmp == NULL) {
+ msg_data.error = messages_get("NoMemory");
+ content_broadcast(&bmp->base, CONTENT_MSG_ERROR, msg_data);
+ return NSERROR_NOMEM;
+ }
+
+ bmp_create(bmp->bmp, &bmp_bitmap_callbacks);
+
+ return NSERROR_OK;
+}
+
+static nserror nsbmp_create(const content_handler *handler,
+ lwc_string *imime_type, const struct http_parameter *params,
+ llcache_handle *llcache, const char *fallback_charset,
+ bool quirks, struct content **c)
+{
+ nsbmp_content *bmp;
+ nserror error;
+
+ bmp = calloc(1, sizeof(nsbmp_content));
+ if (bmp == NULL)
+ return NSERROR_NOMEM;
+
+ error = content__init(&bmp->base, handler, imime_type, params,
+ llcache, fallback_charset, quirks);
+ if (error != NSERROR_OK) {
+ free(bmp);
+ return error;
+ }
+
+ error = nsbmp_create_bmp_data(bmp);
+ if (error != NSERROR_OK) {
+ free(bmp);
+ return error;
+ }
+
+ *c = (struct content *) bmp;
+
+ return NSERROR_OK;
+}
+
+static bool nsbmp_convert(struct content *c)
+{
+ nsbmp_content *bmp = (nsbmp_content *) c;
+ bmp_result res;
+ union content_msg_data msg_data;
+ uint32_t swidth;
+ const char *data;
+ unsigned long size;
+ char *title;
+
+ /* set the bmp data */
+ data = content__get_source_data(c, &size);
+
+ /* analyse the BMP */
+ res = bmp_analyse(bmp->bmp, size, (unsigned char *) data);
+ switch (res) {
+ case BMP_OK:
+ break;
+ case BMP_INSUFFICIENT_MEMORY:
+ msg_data.error = messages_get("NoMemory");
+ content_broadcast(c, CONTENT_MSG_ERROR, msg_data);
+ return false;
+ case BMP_INSUFFICIENT_DATA:
+ case BMP_DATA_ERROR:
+ msg_data.error = messages_get("BadBMP");
+ content_broadcast(c, CONTENT_MSG_ERROR, msg_data);
+ return false;
+ }
+
+ /* Store our content width and description */
+ c->width = bmp->bmp->width;
+ c->height = bmp->bmp->height;
+ swidth = bmp->bmp->bitmap_callbacks.bitmap_get_bpp(bmp->bmp->bitmap) *
+ bmp->bmp->width;
+ c->size += (swidth * bmp->bmp->height) + 16 + 44;
+
+ /* set title text */
+ title = messages_get_buff("BMPTitle",
+ nsurl_access_leaf(llcache_handle_get_url(c->llcache)),
+ c->width, c->height);
+ if (title != NULL) {
+ content__set_title(c, title);
+ free(title);
+ }
+
+ /* exit as a success */
+ bmp->bitmap = bmp->bmp->bitmap;
+ guit->bitmap->modified(bmp->bitmap);
+
+ content_set_ready(c);
+ content_set_done(c);
+
+ /* Done: update status bar */
+ content_set_status(c, "");
+ return true;
+}
+
+static bool nsbmp_redraw(struct content *c, struct content_redraw_data *data,
+ const struct rect *clip, const struct redraw_context *ctx)
+{
+ nsbmp_content *bmp = (nsbmp_content *) c;
+ bitmap_flags_t flags = BITMAPF_NONE;
+
+ if (bmp->bmp->decoded == false)
+ if (bmp_decode(bmp->bmp) != BMP_OK)
+ return false;
+
+ bmp->bitmap = bmp->bmp->bitmap;
+
+ if (data->repeat_x)
+ flags |= BITMAPF_REPEAT_X;
+ if (data->repeat_y)
+ flags |= BITMAPF_REPEAT_Y;
+
+ return ctx->plot->bitmap(data->x, data->y, data->width, data->height,
+ bmp->bitmap, data->background_colour, flags);
+}
+
+
+static void nsbmp_destroy(struct content *c)
+{
+ nsbmp_content *bmp = (nsbmp_content *) c;
+
+ bmp_finalise(bmp->bmp);
+ free(bmp->bmp);
+}
+
+
+static nserror nsbmp_clone(const struct content *old, struct content **newc)
+{
+ nsbmp_content *new_bmp;
+ nserror error;
+
+ new_bmp = calloc(1, sizeof(nsbmp_content));
+ if (new_bmp == NULL)
+ return NSERROR_NOMEM;
+
+ error = content__clone(old, &new_bmp->base);
+ if (error != NSERROR_OK) {
+ content_destroy(&new_bmp->base);
+ return error;
+ }
+
+ /* We "clone" the old content by replaying creation and conversion */
+ error = nsbmp_create_bmp_data(new_bmp);
+ if (error != NSERROR_OK) {
+ content_destroy(&new_bmp->base);
+ return error;
+ }
+
+ if (old->status == CONTENT_STATUS_READY ||
+ old->status == CONTENT_STATUS_DONE) {
+ if (nsbmp_convert(&new_bmp->base) == false) {
+ content_destroy(&new_bmp->base);
+ return NSERROR_CLONE_FAILED;
+ }
+ }
+
+ *newc = (struct content *) new_bmp;
+
+ return NSERROR_OK;
+}
+
+static void *nsbmp_get_internal(const struct content *c, void *context)
+{
+ nsbmp_content *bmp = (nsbmp_content *)c;
+
+ return bmp->bitmap;
+}
+
+static content_type nsbmp_content_type(void)
+{
+ return CONTENT_IMAGE;
+}
+
+
+static const content_handler nsbmp_content_handler = {
+ .create = nsbmp_create,
+ .data_complete = nsbmp_convert,
+ .destroy = nsbmp_destroy,
+ .redraw = nsbmp_redraw,
+ .clone = nsbmp_clone,
+ .get_internal = nsbmp_get_internal,
+ .type = nsbmp_content_type,
+ .no_share = false,
+};
+
+static const char *nsbmp_types[] = {
+ "application/bmp",
+ "application/preview",
+ "application/x-bmp",
+ "application/x-win-bitmap",
+ "image/bmp",
+ "image/ms-bmp",
+ "image/x-bitmap",
+ "image/x-bmp",
+ "image/x-ms-bmp",
+ "image/x-win-bitmap",
+ "image/x-windows-bmp",
+ "image/x-xbitmap"
+};
+
+CONTENT_FACTORY_REGISTER_TYPES(nsbmp, nsbmp_types, nsbmp_content_handler);
diff --git a/content/handlers/image/bmp.h b/content/handlers/image/bmp.h
new file mode 100644
index 000000000..f3b398584
--- /dev/null
+++ b/content/handlers/image/bmp.h
@@ -0,0 +1,33 @@
+/*
+ * Copyright 2006 Richard Wilson <info@tinct.net>
+ * Copyright 2008 Sean Fox <dyntryx@gmail.com>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * NetSurf is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+/** \file
+ * Content for image/bmp (interface).
+ */
+
+#ifndef _NETSURF_IMAGE_BMP_H_
+#define _NETSURF_IMAGE_BMP_H_
+
+#include <libnsbmp.h>
+
+extern bmp_bitmap_callback_vt bmp_bitmap_callbacks; /** Only to be used by ICO code. */
+
+nserror nsbmp_init(void);
+
+#endif
diff --git a/content/handlers/image/gif.c b/content/handlers/image/gif.c
new file mode 100644
index 000000000..d8d968e7d
--- /dev/null
+++ b/content/handlers/image/gif.c
@@ -0,0 +1,455 @@
+/*
+ * Copyright 2003 John M Bell <jmb202@ecs.soton.ac.uk>
+ * Copyright 2004 Richard Wilson <not_ginger_matt@users.sourceforge.net>
+ * Copyright 2008 Sean Fox <dyntryx@gmail.com>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * NetSurf is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+/**
+ * \file
+ *
+ * Content for image/gif implementation
+ *
+ * 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
+ */
+
+#include <assert.h>
+#include <string.h>
+#include <stdbool.h>
+#include <stdlib.h>
+#include <libnsgif.h>
+
+#include "utils/utils.h"
+#include "utils/messages.h"
+#include "utils/nsoption.h"
+#include "content/content_protected.h"
+#include "desktop/gui_misc.h"
+#include "desktop/gui_internal.h"
+
+#include "image.h"
+#include "bitmap.h"
+#include "gif.h"
+
+typedef struct nsgif_content {
+ struct content base;
+
+ struct gif_animation *gif; /**< GIF animation data */
+ int current_frame; /**< current frame to display [0...(max-1)] */
+} nsgif_content;
+
+
+/**
+ * Callback for libnsgif; forwards the call to bitmap_create()
+ *
+ * \param width width of image in pixels
+ * \param height width of image in pixels
+ * \return an opaque struct bitmap, or NULL on memory exhaustion
+ */
+static void *nsgif_bitmap_create(int width, int height)
+{
+ return guit->bitmap->create(width, height, BITMAP_NEW);
+}
+
+
+static nserror nsgif_create_gif_data(nsgif_content *c)
+{
+ union content_msg_data msg_data;
+ gif_bitmap_callback_vt gif_bitmap_callbacks = {
+ .bitmap_create = nsgif_bitmap_create,
+ .bitmap_destroy = guit->bitmap->destroy,
+ .bitmap_get_buffer = guit->bitmap->get_buffer,
+ .bitmap_set_opaque = guit->bitmap->set_opaque,
+ .bitmap_test_opaque = guit->bitmap->test_opaque,
+ .bitmap_modified = guit->bitmap->modified
+ };
+
+ /* Initialise our data structure */
+ c->gif = calloc(sizeof(gif_animation), 1);
+ if (c->gif == NULL) {
+ msg_data.error = messages_get("NoMemory");
+ content_broadcast(&c->base, CONTENT_MSG_ERROR, msg_data);
+ return NSERROR_NOMEM;
+ }
+ gif_create(c->gif, &gif_bitmap_callbacks);
+ return NSERROR_OK;
+}
+
+
+
+static nserror nsgif_create(const content_handler *handler,
+ lwc_string *imime_type, const struct http_parameter *params,
+ llcache_handle *llcache, const char *fallback_charset,
+ bool quirks, struct content **c)
+{
+ nsgif_content *result;
+ nserror error;
+
+ result = calloc(1, sizeof(nsgif_content));
+ if (result == NULL)
+ return NSERROR_NOMEM;
+
+ error = content__init(&result->base, handler, imime_type, params,
+ llcache, fallback_charset, quirks);
+ if (error != NSERROR_OK) {
+ free(result);
+ return error;
+ }
+
+ error = nsgif_create_gif_data(result);
+ if (error != NSERROR_OK) {
+ free(result);
+ return error;
+ }
+
+ *c = (struct content *) result;
+
+ return NSERROR_OK;
+}
+
+/**
+ * Performs any necessary animation.
+ *
+ * \param p The content to animate
+*/
+static void nsgif_animate(void *p)
+{
+ nsgif_content *gif = p;
+ union content_msg_data data;
+ int delay;
+ int f;
+
+ /* Advance by a frame, updating the loop count accordingly */
+ gif->current_frame++;
+ if (gif->current_frame == (int)gif->gif->frame_count_partial) {
+ gif->current_frame = 0;
+
+ /* A loop count of 0 has a special meaning of infinite */
+ if (gif->gif->loop_count != 0) {
+ gif->gif->loop_count--;
+ if (gif->gif->loop_count == 0) {
+ gif->current_frame =
+ gif->gif->frame_count_partial - 1;
+ gif->gif->loop_count = -1;
+ }
+ }
+ }
+
+ /* Continue animating if we should */
+ if (gif->gif->loop_count >= 0) {
+ delay = gif->gif->frames[gif->current_frame].frame_delay;
+ if (delay < nsoption_int(minimum_gif_delay))
+ delay = nsoption_int(minimum_gif_delay);
+ guit->misc->schedule(delay * 10, nsgif_animate, gif);
+ }
+
+ if ((!nsoption_bool(animate_images)) ||
+ (!gif->gif->frames[gif->current_frame].display)) {
+ return;
+ }
+
+ /* area within gif to redraw */
+ f = gif->current_frame;
+ data.redraw.x = gif->gif->frames[f].redraw_x;
+ data.redraw.y = gif->gif->frames[f].redraw_y;
+ data.redraw.width = gif->gif->frames[f].redraw_width;
+ data.redraw.height = gif->gif->frames[f].redraw_height;
+
+ /* redraw background (true) or plot on top (false) */
+ if (gif->current_frame > 0) {
+ data.redraw.full_redraw =
+ gif->gif->frames[f - 1].redraw_required;
+ /* previous frame needed clearing: expand the redraw area to
+ * cover it */
+ if (data.redraw.full_redraw) {
+ if (data.redraw.x >
+ (int)(gif->gif->frames[f - 1].redraw_x)) {
+ data.redraw.width += data.redraw.x -
+ gif->gif->frames[f - 1].redraw_x;
+ data.redraw.x =
+ gif->gif->frames[f - 1].redraw_x;
+ }
+ if (data.redraw.y >
+ (int)(gif->gif->frames[f - 1].redraw_y)) {
+ data.redraw.height += (data.redraw.y -
+ gif->gif->frames[f - 1].redraw_y);
+ data.redraw.y =
+ gif->gif->frames[f - 1].redraw_y;
+ }
+ if ((int)(gif->gif->frames[f - 1].redraw_x +
+ gif->gif->frames[f - 1].redraw_width) >
+ (data.redraw.x + data.redraw.width))
+ data.redraw.width =
+ gif->gif->frames[f - 1].redraw_x -
+ data.redraw.x +
+ gif->gif->frames[f - 1].redraw_width;
+ if ((int)(gif->gif->frames[f - 1].redraw_y +
+ gif->gif->frames[f - 1].redraw_height) >
+ (data.redraw.y + data.redraw.height))
+ data.redraw.height =
+ gif->gif->frames[f - 1].redraw_y -
+ data.redraw.y +
+ gif->gif->frames[f - 1].redraw_height;
+ }
+ } else {
+ /* do advanced check */
+ if ((data.redraw.x == 0) && (data.redraw.y == 0) &&
+ (data.redraw.width == (int)(gif->gif->width)) &&
+ (data.redraw.height == (int)(gif->gif->height))) {
+ data.redraw.full_redraw = !gif->gif->frames[f].opaque;
+ } else {
+ data.redraw.full_redraw = true;
+ data.redraw.x = 0;
+ data.redraw.y = 0;
+ data.redraw.width = gif->gif->width;
+ data.redraw.height = gif->gif->height;
+ }
+ }
+
+ /* other data */
+ data.redraw.object = (struct content *) gif;
+ data.redraw.object_x = 0;
+ data.redraw.object_y = 0;
+ data.redraw.object_width = gif->base.width;
+ data.redraw.object_height = gif->base.height;
+
+ content_broadcast(&gif->base, CONTENT_MSG_REDRAW, data);
+}
+
+static bool nsgif_convert(struct content *c)
+{
+ nsgif_content *gif = (nsgif_content *) c;
+ int res;
+ union content_msg_data msg_data;
+ const char *data;
+ unsigned long size;
+ char *title;
+
+ /* Get the animation */
+ data = content__get_source_data(c, &size);
+
+ /* Initialise the GIF */
+ do {
+ res = gif_initialise(gif->gif, size, (unsigned char *) data);
+ if (res != GIF_OK && res != GIF_WORKING &&
+ res != GIF_INSUFFICIENT_FRAME_DATA) {
+ switch (res) {
+ case GIF_FRAME_DATA_ERROR:
+ case GIF_INSUFFICIENT_DATA:
+ case GIF_DATA_ERROR:
+ msg_data.error = messages_get("BadGIF");
+ break;
+ case GIF_INSUFFICIENT_MEMORY:
+ msg_data.error = messages_get("NoMemory");
+ break;
+ }
+ content_broadcast(c, CONTENT_MSG_ERROR, msg_data);
+ return false;
+ }
+ } while (res != GIF_OK && res != GIF_INSUFFICIENT_FRAME_DATA);
+
+ /* Abort on bad GIFs */
+ if ((gif->gif->frame_count_partial == 0) || (gif->gif->width == 0) ||
+ (gif->gif->height == 0)) {
+ msg_data.error = messages_get("BadGIF");
+ content_broadcast(c, CONTENT_MSG_ERROR, msg_data);
+ return false;
+ }
+
+ /* Store our content width, height and calculate size */
+ c->width = gif->gif->width;
+ c->height = gif->gif->height;
+ c->size += (gif->gif->width * gif->gif->height * 4) + 16 + 44;
+
+ /* set title text */
+ title = messages_get_buff("GIFTitle",
+ nsurl_access_leaf(llcache_handle_get_url(c->llcache)),
+ c->width, c->height);
+ if (title != NULL) {
+ content__set_title(c, title);
+ free(title);
+ }
+
+ /* Schedule the animation if we have one */
+ gif->current_frame = 0;
+ if (gif->gif->frame_count_partial > 1)
+ guit->misc->schedule(gif->gif->frames[0].frame_delay * 10,
+ nsgif_animate,
+ c);
+
+ /* Exit as a success */
+ content_set_ready(c);
+ content_set_done(c);
+
+ /* Done: update status bar */
+ content_set_status(c, "");
+ return true;
+}
+
+
+/**
+ * Updates the GIF bitmap to display the current frame
+ *
+ * \param gif The gif context to update.
+ * \return GIF_OK on success else apropriate error code.
+ */
+static gif_result nsgif_get_frame(nsgif_content *gif)
+{
+ int previous_frame, current_frame, frame;
+ gif_result res = GIF_OK;
+
+ current_frame = gif->current_frame;
+ if (!nsoption_bool(animate_images)) {
+ current_frame = 0;
+ }
+
+ if (current_frame < gif->gif->decoded_frame) {
+ previous_frame = 0;
+ } else {
+ previous_frame = gif->gif->decoded_frame + 1;
+ }
+
+ for (frame = previous_frame; frame <= current_frame; frame++) {
+ res = gif_decode_frame(gif->gif, frame);
+ }
+
+ return res;
+}
+
+static bool nsgif_redraw(struct content *c, struct content_redraw_data *data,
+ const struct rect *clip, const struct redraw_context *ctx)
+{
+ nsgif_content *gif = (nsgif_content *) c;
+
+ if (gif->current_frame != gif->gif->decoded_frame) {
+ if (nsgif_get_frame(gif) != GIF_OK) {
+ return false;
+ }
+ }
+
+ return image_bitmap_plot(gif->gif->frame_image, data, clip, ctx);
+}
+
+
+static void nsgif_destroy(struct content *c)
+{
+ nsgif_content *gif = (nsgif_content *) c;
+
+ /* Free all the associated memory buffers */
+ guit->misc->schedule(-1, nsgif_animate, c);
+ gif_finalise(gif->gif);
+ free(gif->gif);
+}
+
+
+static nserror nsgif_clone(const struct content *old, struct content **newc)
+{
+ nsgif_content *gif;
+ nserror error;
+
+ gif = calloc(1, sizeof(nsgif_content));
+ if (gif == NULL)
+ return NSERROR_NOMEM;
+
+ error = content__clone(old, &gif->base);
+ if (error != NSERROR_OK) {
+ content_destroy(&gif->base);
+ return error;
+ }
+
+ /* Simply replay creation and conversion of content */
+ error = nsgif_create_gif_data(gif);
+ if (error != NSERROR_OK) {
+ content_destroy(&gif->base);
+ return error;
+ }
+
+ if (old->status == CONTENT_STATUS_READY ||
+ old->status == CONTENT_STATUS_DONE) {
+ if (nsgif_convert(&gif->base) == false) {
+ content_destroy(&gif->base);
+ return NSERROR_CLONE_FAILED;
+ }
+ }
+
+ *newc = (struct content *) gif;
+
+ return NSERROR_OK;
+}
+
+static void nsgif_add_user(struct content *c)
+{
+ nsgif_content *gif = (nsgif_content *) c;
+
+ /* Ensure this content has already been converted.
+ * If it hasn't, the animation will start at the conversion phase instead. */
+ if (gif->gif == NULL) return;
+
+ if (content_count_users(c) == 1) {
+ /* First user, and content already converted, so start the animation. */
+ if (gif->gif->frame_count_partial > 1) {
+ guit->misc->schedule(gif->gif->frames[0].frame_delay * 10,
+ nsgif_animate, c);
+ }
+ }
+}
+
+static void nsgif_remove_user(struct content *c)
+{
+ if (content_count_users(c) == 1) {
+ /* Last user is about to be removed from this content, so stop the animation. */
+ guit->misc->schedule(-1, nsgif_animate, c);
+ }
+}
+
+static void *nsgif_get_internal(const struct content *c, void *context)
+{
+ nsgif_content *gif = (nsgif_content *) c;
+
+ if (gif->current_frame != gif->gif->decoded_frame) {
+ if (nsgif_get_frame(gif) != GIF_OK)
+ return NULL;
+ }
+
+ return gif->gif->frame_image;
+}
+
+static content_type nsgif_content_type(void)
+{
+ return CONTENT_IMAGE;
+}
+
+static const content_handler nsgif_content_handler = {
+ .create = nsgif_create,
+ .data_complete = nsgif_convert,
+ .destroy = nsgif_destroy,
+ .redraw = nsgif_redraw,
+ .clone = nsgif_clone,
+ .add_user = nsgif_add_user,
+ .remove_user = nsgif_remove_user,
+ .get_internal = nsgif_get_internal,
+ .type = nsgif_content_type,
+ .no_share = false,
+};
+
+static const char *nsgif_types[] = {
+ "image/gif"
+};
+
+CONTENT_FACTORY_REGISTER_TYPES(nsgif, nsgif_types, nsgif_content_handler);
diff --git a/content/handlers/image/gif.h b/content/handlers/image/gif.h
new file mode 100644
index 000000000..a75821a96
--- /dev/null
+++ b/content/handlers/image/gif.h
@@ -0,0 +1,29 @@
+/*
+ * Copyright 2004 Richard Wilson <not_ginger_matt@users.sourceforge.net>
+ * Copyright 2008 Sean Fox <dyntryx@gmail.com>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * NetSurf is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+/** \file
+ * Content for image/gif (interface).
+ */
+
+#ifndef _NETSURF_IMAGE_GIF_H_
+#define _NETSURF_IMAGE_GIF_H_
+
+nserror nsgif_init(void);
+
+#endif
diff --git a/content/handlers/image/ico.c b/content/handlers/image/ico.c
new file mode 100644
index 000000000..d364fdff5
--- /dev/null
+++ b/content/handlers/image/ico.c
@@ -0,0 +1,300 @@
+/*
+ * Copyright 2006 Richard Wilson <info@tinct.net>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * NetSurf is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+/** \file
+ * Content for image/ico (implementation)
+ */
+
+#include <stdbool.h>
+#include <stdlib.h>
+#include <libnsbmp.h>
+
+#include "utils/utils.h"
+#include "utils/log.h"
+#include "utils/messages.h"
+#include "content/content_protected.h"
+#include "desktop/gui_internal.h"
+
+#include "image.h"
+#include "bitmap.h"
+#include "ico.h"
+
+typedef struct nsico_content {
+ struct content base;
+
+ struct ico_collection *ico; /** ICO collection data */
+
+} nsico_content;
+
+/**
+ * Callback for libnsbmp; forwards the call to bitmap_create()
+ *
+ * \param width width of image in pixels
+ * \param height width of image in pixels
+ * \param bmp_state A flag word indicating the initial state
+ * \return an opaque struct bitmap, or NULL on memory exhaustion
+ */
+static void *nsico_bitmap_create(int width, int height, unsigned int bmp_state)
+{
+ unsigned int bitmap_state = BITMAP_NEW;
+
+ /* set bitmap state based on bmp state */
+ bitmap_state |= (bmp_state & BMP_OPAQUE) ? BITMAP_OPAQUE : 0;
+ bitmap_state |= (bmp_state & BMP_CLEAR_MEMORY) ?
+ BITMAP_CLEAR_MEMORY : 0;
+
+ /* return the created bitmap */
+ return guit->bitmap->create(width, height, bitmap_state);
+}
+
+static nserror nsico_create_ico_data(nsico_content *c)
+{
+ union content_msg_data msg_data;
+ bmp_bitmap_callback_vt bmp_bitmap_callbacks = {
+ .bitmap_create = nsico_bitmap_create,
+ .bitmap_destroy = guit->bitmap->destroy,
+ .bitmap_get_buffer = guit->bitmap->get_buffer,
+ .bitmap_get_bpp = guit->bitmap->get_bpp
+ };
+
+ c->ico = calloc(sizeof(ico_collection), 1);
+ if (c->ico == NULL) {
+ msg_data.error = messages_get("NoMemory");
+ content_broadcast(&c->base, CONTENT_MSG_ERROR, msg_data);
+ return NSERROR_NOMEM;
+ }
+ ico_collection_create(c->ico, &bmp_bitmap_callbacks);
+ return NSERROR_OK;
+}
+
+
+static nserror nsico_create(const content_handler *handler,
+ lwc_string *imime_type, const struct http_parameter *params,
+ llcache_handle *llcache, const char *fallback_charset,
+ bool quirks, struct content **c)
+{
+ nsico_content *result;
+ nserror error;
+
+ result = calloc(1, sizeof(nsico_content));
+ if (result == NULL)
+ return NSERROR_NOMEM;
+
+ error = content__init(&result->base, handler, imime_type, params,
+ llcache, fallback_charset, quirks);
+ if (error != NSERROR_OK) {
+ free(result);
+ return error;
+ }
+
+ error = nsico_create_ico_data(result);
+ if (error != NSERROR_OK) {
+ free(result);
+ return error;
+ }
+
+ *c = (struct content *) result;
+
+ return NSERROR_OK;
+}
+
+
+
+static bool nsico_convert(struct content *c)
+{
+ nsico_content *ico = (nsico_content *) c;
+ struct bmp_image *bmp;
+ bmp_result res;
+ union content_msg_data msg_data;
+ const char *data;
+ unsigned long size;
+ char *title;
+
+ /* set the ico data */
+ data = content__get_source_data(c, &size);
+
+ /* analyse the ico */
+ res = ico_analyse(ico->ico, size, (unsigned char *) data);
+
+ switch (res) {
+ case BMP_OK:
+ break;
+ case BMP_INSUFFICIENT_MEMORY:
+ msg_data.error = messages_get("NoMemory");
+ content_broadcast(c, CONTENT_MSG_ERROR, msg_data);
+ return false;
+ case BMP_INSUFFICIENT_DATA:
+ case BMP_DATA_ERROR:
+ msg_data.error = messages_get("BadICO");
+ content_broadcast(c, CONTENT_MSG_ERROR, msg_data);
+ return false;
+ }
+
+ /* Store our content width, height and calculate size */
+ c->width = ico->ico->width;
+ c->height = ico->ico->height;
+ c->size += (ico->ico->width * ico->ico->height * 4) + 16 + 44;
+
+ /* set title text */
+ title = messages_get_buff("ICOTitle",
+ nsurl_access_leaf(llcache_handle_get_url(c->llcache)),
+ c->width, c->height);
+ if (title != NULL) {
+ content__set_title(c, title);
+ free(title);
+ }
+
+ /* select largest icon to ensure one can be selected */
+ bmp = ico_find(ico->ico, 255, 255);
+ if (bmp == NULL) {
+ /* return error */
+ LOG("Failed to select icon");
+ return false;
+ }
+
+ content_set_ready(c);
+ content_set_done(c);
+
+ /* Done: update status bar */
+ content_set_status(c, "");
+ return true;
+}
+
+
+static bool nsico_redraw(struct content *c, struct content_redraw_data *data,
+ const struct rect *clip, const struct redraw_context *ctx)
+{
+ nsico_content *ico = (nsico_content *)c;
+ struct bmp_image *bmp;
+
+ /* select most appropriate sized icon for size */
+ bmp = ico_find(ico->ico, data->width, data->height);
+ if (bmp == NULL) {
+ /* return error */
+ LOG("Failed to select icon");
+ return false;
+ }
+
+ /* ensure its decided */
+ if (bmp->decoded == false) {
+ if (bmp_decode(bmp) != BMP_OK) {
+ return false;
+ } else {
+ LOG("Decoding bitmap");
+ guit->bitmap->modified(bmp->bitmap);
+ }
+
+ }
+
+ return image_bitmap_plot(bmp->bitmap, data, clip, ctx);
+}
+
+
+static void nsico_destroy(struct content *c)
+{
+ nsico_content *ico = (nsico_content *) c;
+
+ ico_finalise(ico->ico);
+ free(ico->ico);
+}
+
+static nserror nsico_clone(const struct content *old, struct content **newc)
+{
+ nsico_content *ico;
+ nserror error;
+
+ ico = calloc(1, sizeof(nsico_content));
+ if (ico == NULL)
+ return NSERROR_NOMEM;
+
+ error = content__clone(old, &ico->base);
+ if (error != NSERROR_OK) {
+ content_destroy(&ico->base);
+ return error;
+ }
+
+ /* Simply replay creation and conversion */
+ error = nsico_create_ico_data(ico);
+ if (error != NSERROR_OK) {
+ content_destroy(&ico->base);
+ return error;
+ }
+
+ if (old->status == CONTENT_STATUS_READY ||
+ old->status == CONTENT_STATUS_DONE) {
+ if (nsico_convert(&ico->base) == false) {
+ content_destroy(&ico->base);
+ return NSERROR_CLONE_FAILED;
+ }
+ }
+
+ *newc = (struct content *) ico;
+
+ return NSERROR_OK;
+}
+
+static void *nsico_get_internal(const struct content *c, void *context)
+{
+ nsico_content *ico = (nsico_content *) c;
+ /* TODO: Pick best size for purpose.
+ * Currently assumes it's for a URL bar. */
+ struct bmp_image *bmp;
+
+ bmp = ico_find(ico->ico, 16, 16);
+ if (bmp == NULL) {
+ /* return error */
+ LOG("Failed to select icon");
+ return NULL;
+ }
+
+ if (bmp->decoded == false) {
+ if (bmp_decode(bmp) != BMP_OK) {
+ return NULL;
+ } else {
+ guit->bitmap->modified(bmp->bitmap);
+ }
+ }
+
+ return bmp->bitmap;
+}
+
+static content_type nsico_content_type(void)
+{
+ return CONTENT_IMAGE;
+}
+
+static const content_handler nsico_content_handler = {
+ .create = nsico_create,
+ .data_complete = nsico_convert,
+ .destroy = nsico_destroy,
+ .redraw = nsico_redraw,
+ .clone = nsico_clone,
+ .get_internal = nsico_get_internal,
+ .type = nsico_content_type,
+ .no_share = false,
+};
+
+static const char *nsico_types[] = {
+ "application/ico",
+ "application/x-ico",
+ "image/ico",
+ "image/vnd.microsoft.icon",
+ "image/x-icon"
+};
+
+CONTENT_FACTORY_REGISTER_TYPES(nsico, nsico_types, nsico_content_handler);
diff --git a/content/handlers/image/ico.h b/content/handlers/image/ico.h
new file mode 100644
index 000000000..21e9bdb6e
--- /dev/null
+++ b/content/handlers/image/ico.h
@@ -0,0 +1,28 @@
+/*
+ * Copyright 2006 Richard Wilson <info@tinct.net>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * NetSurf is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+/** \file
+ * Content for image/ico (interface).
+ */
+
+#ifndef _NETSURF_IMAGE_ICO_H_
+#define _NETSURF_IMAGE_ICO_H_
+
+nserror nsico_init(void);
+
+#endif
diff --git a/content/handlers/image/image.c b/content/handlers/image/image.c
new file mode 100644
index 000000000..d1743697d
--- /dev/null
+++ b/content/handlers/image/image.c
@@ -0,0 +1,159 @@
+/*
+ * Copyright 2011 John-Mark Bell <jmb@netsurf-browser.org>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * NetSurf is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <stdbool.h>
+#include <stdlib.h>
+
+#include "utils/utils.h"
+#include "utils/log.h"
+#include "utils/messages.h"
+#include "content/content.h"
+#include "desktop/plotters.h"
+#include "desktop/gui_internal.h"
+
+#include "bitmap.h"
+#include "bmp.h"
+#include "gif.h"
+#include "ico.h"
+#include "jpeg.h"
+#include "nssprite.h"
+#include "png.h"
+#include "rsvg.h"
+#include "svg.h"
+#include "image.h"
+
+/**
+ * Initialise image content handlers
+ *
+ * \return NSERROR_OK on success, appropriate error otherwise.
+ */
+nserror image_init(void)
+{
+ nserror error = NSERROR_OK;
+
+#ifdef WITH_BMP
+ error = nsbmp_init();
+ if (error != NSERROR_OK)
+ return error;
+#endif
+
+#ifdef WITH_GIF
+ error = nsgif_init();
+ if (error != NSERROR_OK)
+ return error;
+#endif
+
+#ifdef WITH_BMP
+ error = nsico_init();
+ if (error != NSERROR_OK)
+ return error;
+#endif
+
+#ifdef WITH_JPEG
+ error = nsjpeg_init();
+ if (error != NSERROR_OK)
+ return error;
+#endif
+
+#ifdef WITH_PNG
+ error = nspng_init();
+ if (error != NSERROR_OK)
+ return error;
+#endif
+
+#ifdef WITH_NSSPRITE
+ error = nssprite_init();
+ if (error != NSERROR_OK)
+ return error;
+#endif
+
+ /* Prefer rsvg over libsvgtiny for svgs */
+#ifdef WITH_NS_SVG
+ error = svg_init();
+ if (error != NSERROR_OK)
+ return error;
+#endif
+#ifdef WITH_RSVG
+ error = nsrsvg_init();
+ if (error != NSERROR_OK)
+ return error;
+#endif
+
+ return error;
+}
+
+
+bool image_bitmap_plot(struct bitmap *bitmap,
+ struct content_redraw_data *data,
+ const struct rect *clip,
+ const struct redraw_context *ctx)
+{
+ bitmap_flags_t flags = BITMAPF_NONE;
+
+ int width;
+ int height;
+ unsigned char *pixel;
+ plot_style_t fill_style;
+ struct rect area;
+
+ width = guit->bitmap->get_width(bitmap);
+ if (width == 1) {
+ height = guit->bitmap->get_height(bitmap);
+ if (height == 1) {
+ /* optimise 1x1 bitmap plot */
+ pixel = guit->bitmap->get_buffer(bitmap);
+ fill_style.fill_colour = pixel_to_colour(pixel);
+
+ if (guit->bitmap->get_opaque(bitmap) ||
+ ((fill_style.fill_colour & 0xff000000) == 0xff000000)) {
+
+ area = *clip;
+
+ if (data->repeat_x != true) {
+ area.x0 = data->x;
+ area.x1 = data->x + data->width;
+ }
+
+ if (data->repeat_y != true) {
+ area.y0 = data->y;
+ area.y1 = data->y + data->height;
+ }
+
+ fill_style.stroke_type = PLOT_OP_TYPE_NONE;
+ fill_style.fill_type = PLOT_OP_TYPE_SOLID;
+
+ return ctx->plot->rectangle(area.x0, area.y0,
+ area.x1, area.y1,
+ &fill_style);
+
+ } else if ((fill_style.fill_colour & 0xff000000) == 0) {
+ /* transparent pixel used as spacer, skip it */
+ return true;
+ }
+ }
+ }
+
+ /* do the plot */
+ if (data->repeat_x)
+ flags |= BITMAPF_REPEAT_X;
+ if (data->repeat_y)
+ flags |= BITMAPF_REPEAT_Y;
+
+ return ctx->plot->bitmap(data->x, data->y, data->width, data->height,
+ bitmap, data->background_colour, flags);
+}
diff --git a/content/handlers/image/image.h b/content/handlers/image/image.h
new file mode 100644
index 000000000..eb9482583
--- /dev/null
+++ b/content/handlers/image/image.h
@@ -0,0 +1,43 @@
+/*
+ * Copyright 2011 John-Mark Bell <jmb@netsurf-browser.org>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * NetSurf is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+/** \file
+ * Initialisation/finalisation of image handlers.
+ */
+
+#ifndef NETSURF_IMAGE_IMAGE_H_
+#define NETSURF_IMAGE_IMAGE_H_
+
+#include "utils/errors.h"
+
+/** Initialise the content handlers for image types.
+ */
+nserror image_init(void);
+
+/** Common image content handler bitmap plot call.
+ *
+ * This plots the specified bitmap controlled by the redraw context
+ * and specific content redraw data. It is a helper specifically
+ * provided for image content handlers redraw callback.
+ */
+bool image_bitmap_plot(struct bitmap *bitmap,
+ struct content_redraw_data *data,
+ const struct rect *clip,
+ const struct redraw_context *ctx);
+
+#endif
diff --git a/content/handlers/image/image_cache.c b/content/handlers/image/image_cache.c
new file mode 100644
index 000000000..49370ad6e
--- /dev/null
+++ b/content/handlers/image/image_cache.c
@@ -0,0 +1,797 @@
+/*
+ * Copyright 2011 Vincent Sanders <vince@netsurf-browser.org>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * NetSurf is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <assert.h>
+#include <inttypes.h>
+#include <stdint.h>
+#include <stdbool.h>
+#include <string.h>
+#include <stdlib.h>
+
+#include "utils/utils.h"
+#include "utils/log.h"
+#include "content/content_protected.h"
+#include "desktop/gui_misc.h"
+#include "desktop/gui_internal.h"
+
+#include "bitmap.h"
+#include "image_cache.h"
+#include "image.h"
+
+/** Age of an entry within the cache
+ *
+ * type deffed away so it can be readily changed later perhaps to a
+ * wallclock time structure.
+ */
+typedef unsigned int cache_age;
+
+/** Image cache entry
+ */
+struct image_cache_entry_s {
+ struct image_cache_entry_s *next; /* next cache entry in list */
+ struct image_cache_entry_s *prev; /* previous cache entry in list */
+
+ struct content *content; /** content is used as a key */
+ struct bitmap *bitmap; /** associated bitmap entry */
+ /** Conversion routine */
+ image_cache_convert_fn *convert;
+
+ /* Statistics for replacement algorithm */
+
+ unsigned int redraw_count; /**< number of times object has been drawn */
+ cache_age redraw_age; /**< Age of last redraw */
+ size_t bitmap_size; /**< size if storage occupied by bitmap */
+ cache_age bitmap_age; /**< Age of last conversion to a bitmap by cache*/
+
+ int conversion_count; /**< Number of times image has been converted */
+};
+
+/** Current state of the cache.
+ *
+ * Global state of the cache. entries "age" is determined based on a
+ * monotonically incrementing operation count. This avoids issues with
+ * using wall clock time while allowing the LRU algorithm to work
+ * sensibly.
+ */
+struct image_cache_s {
+ /** Cache parameters */
+ struct image_cache_parameters params;
+
+ /** The "age" of the current operation */
+ cache_age current_age;
+
+ /* The objects the cache holds */
+ struct image_cache_entry_s *entries;
+
+
+ /* Statistics for management algorithm */
+
+ /** total size of bitmaps currently allocated */
+ size_t total_bitmap_size;
+
+ /** Total count of bitmaps currently allocated */
+ int bitmap_count;
+
+ /** Maximum size of bitmaps allocated at any one time */
+ size_t max_bitmap_size;
+ /** The number of objects when maximum bitmap usage occoured */
+ int max_bitmap_size_count;
+
+ /** Maximum count of bitmaps allocated at any one time */
+ int max_bitmap_count;
+ /** The size of the bitmaps when the max count occoured */
+ size_t max_bitmap_count_size;
+
+ /** Bitmap was not available at plot time required conversion */
+ int miss_count;
+ uint64_t miss_size;
+ /** Bitmap was available at plot time required no conversion */
+ int hit_count;
+ uint64_t hit_size;
+ /** Bitmap was not available at plot time and required
+ * conversion which failed.
+ */
+ int fail_count;
+ uint64_t fail_size;
+
+ /* Cache entry freed without ever being redrawn */
+ int total_unrendered;
+ /** Bitmap was available but never required - wasted conversions */
+ int specultive_miss_count;
+
+ /** Total number of additional (after the first) conversions */
+ int total_extra_conversions;
+ /** counts total number of images with more than one conversion */
+ int total_extra_conversions_count;
+
+ /** Bitmap with most conversions was converted this many times */
+ int peak_conversions;
+ /** Size of bitmap with most conversions */
+ unsigned int peak_conversions_size;
+};
+
+/** image cache state */
+static struct image_cache_s *image_cache = NULL;
+
+
+/** Find the nth cache entry
+ */
+static struct image_cache_entry_s *image_cache__findn(int entryn)
+{
+ struct image_cache_entry_s *found;
+
+ found = image_cache->entries;
+ while ((found != NULL) && (entryn > 0)) {
+ entryn--;
+ found = found->next;
+ }
+ return found;
+}
+
+/** Find the cache entry for a content
+ */
+static struct image_cache_entry_s *image_cache__find(const struct content *c)
+{
+ struct image_cache_entry_s *found;
+
+ found = image_cache->entries;
+ while ((found != NULL) && (found->content != c)) {
+ found = found->next;
+ }
+ return found;
+}
+
+static void image_cache_stats_bitmap_add(struct image_cache_entry_s *centry)
+{
+ centry->bitmap_age = image_cache->current_age;
+ centry->conversion_count++;
+
+ image_cache->total_bitmap_size += centry->bitmap_size;
+ image_cache->bitmap_count++;
+
+ if (image_cache->total_bitmap_size > image_cache->max_bitmap_size) {
+ image_cache->max_bitmap_size = image_cache->total_bitmap_size;
+ image_cache->max_bitmap_size_count = image_cache->bitmap_count;
+
+ }
+
+ if (image_cache->bitmap_count > image_cache->max_bitmap_count) {
+ image_cache->max_bitmap_count = image_cache->bitmap_count;
+ image_cache->max_bitmap_count_size = image_cache->total_bitmap_size;
+ }
+
+ if (centry->conversion_count == 2) {
+ image_cache->total_extra_conversions_count++;
+ }
+
+ if (centry->conversion_count > 1) {
+ image_cache->total_extra_conversions++;
+ }
+
+ if ((centry->conversion_count > image_cache->peak_conversions) ||
+ (centry->conversion_count == image_cache->peak_conversions &&
+ centry->bitmap_size > image_cache->peak_conversions_size)) {
+ image_cache->peak_conversions = centry->conversion_count;
+ image_cache->peak_conversions_size = centry->bitmap_size;
+ }
+}
+
+static void image_cache__link(struct image_cache_entry_s *centry)
+{
+ centry->next = image_cache->entries;
+ centry->prev = NULL;
+ if (centry->next != NULL) {
+ centry->next->prev = centry;
+ }
+ image_cache->entries = centry;
+}
+
+static void image_cache__unlink(struct image_cache_entry_s *centry)
+{
+ /* unlink entry */
+ if (centry->prev == NULL) {
+ /* first in list */
+ if (centry->next != NULL) {
+ centry->next->prev = centry->prev;
+ image_cache->entries = centry->next;
+ } else {
+ /* empty list */
+ image_cache->entries = NULL;
+ }
+ } else {
+ centry->prev->next = centry->next;
+
+ if (centry->next != NULL) {
+ centry->next->prev = centry->prev;
+ }
+ }
+}
+
+static void image_cache__free_bitmap(struct image_cache_entry_s *centry)
+{
+ if (centry->bitmap != NULL) {
+#ifdef IMAGE_CACHE_VERBOSE
+ LOG("Freeing bitmap %p size %d age %d redraw count %d", centry->bitmap, centry->bitmap_size, image_cache->current_age - centry->bitmap_age, centry->redraw_count);
+#endif
+ guit->bitmap->destroy(centry->bitmap);
+ centry->bitmap = NULL;
+ image_cache->total_bitmap_size -= centry->bitmap_size;
+ image_cache->bitmap_count--;
+ if (centry->redraw_count == 0) {
+ image_cache->specultive_miss_count++;
+ }
+ }
+
+}
+
+/* free cache entry */
+static void image_cache__free_entry(struct image_cache_entry_s *centry)
+{
+#ifdef IMAGE_CACHE_VERBOSE
+ LOG("freeing %p ", centry);
+#endif
+
+ if (centry->redraw_count == 0) {
+ image_cache->total_unrendered++;
+ }
+
+ image_cache__free_bitmap(centry);
+
+ image_cache__unlink(centry);
+
+ free(centry);
+}
+
+/** Cache cleaner */
+static void image_cache__clean(struct image_cache_s *icache)
+{
+ struct image_cache_entry_s *centry = icache->entries;
+
+ while (centry != NULL) {
+ if ((icache->current_age - centry->redraw_age) >
+ icache->params.bg_clean_time) {
+ /* only consider older entries, avoids active entries */
+ if ((icache->total_bitmap_size >
+ (icache->params.limit - icache->params.hysteresis)) &&
+ (rand() > (RAND_MAX / 2))) {
+ image_cache__free_bitmap(centry);
+ }
+ }
+ centry=centry->next;
+ }
+}
+
+/** Cache background scheduled callback. */
+static void image_cache__background_update(void *p)
+{
+ struct image_cache_s *icache = p;
+
+ /* increment current cache age */
+ icache->current_age += icache->params.bg_clean_time;
+
+#ifdef IMAGE_CACHE_VERBOSE
+ LOG("Cache age %ds", icache->current_age / 1000);
+#endif
+
+ image_cache__clean(icache);
+
+ guit->misc->schedule(icache->params.bg_clean_time,
+ image_cache__background_update,
+ icache);
+}
+
+/* exported interface documented in image_cache.h */
+struct bitmap *image_cache_get_bitmap(const struct content *c)
+{
+ struct image_cache_entry_s *centry;
+
+ centry = image_cache__find(c);
+ if (centry == NULL) {
+ return NULL;
+ }
+
+ if (centry->bitmap == NULL) {
+ if (centry->convert != NULL) {
+ centry->bitmap = centry->convert(centry->content);
+ }
+
+ if (centry->bitmap != NULL) {
+ image_cache_stats_bitmap_add(centry);
+ image_cache->miss_count++;
+ image_cache->miss_size += centry->bitmap_size;
+ } else {
+ image_cache->fail_count++;
+ image_cache->fail_size += centry->bitmap_size;
+ }
+ } else {
+ image_cache->hit_count++;
+ image_cache->hit_size += centry->bitmap_size;
+ }
+
+ return centry->bitmap;
+}
+
+/* exported interface documented in image_cache.h */
+bool image_cache_speculate(struct content *c)
+{
+ bool decision = false;
+
+ /* If the cache is below its target usage and the bitmap is
+ * small enough speculate.
+ */
+ if ((image_cache->total_bitmap_size < image_cache->params.limit) &&
+ (c->size <= image_cache->params.speculative_small)) {
+#ifdef IMAGE_CACHE_VERBOSE
+ LOG("content size (%d) is smaller than minimum (%d)", c->size, SPECULATE_SMALL);
+#endif
+ decision = true;
+ }
+
+#ifdef IMAGE_CACHE_VERBOSE
+ LOG("returning %d", decision);
+#endif
+ return decision;
+}
+
+/* exported interface documented in image_cache.h */
+struct bitmap *image_cache_find_bitmap(struct content *c)
+{
+ struct image_cache_entry_s *centry;
+
+ centry = image_cache__find(c);
+ if (centry == NULL) {
+ return NULL;
+ }
+
+ return centry->bitmap;
+}
+
+/* exported interface documented in image_cache.h */
+nserror
+image_cache_init(const struct image_cache_parameters *image_cache_parameters)
+{
+ image_cache = calloc(1, sizeof(struct image_cache_s));
+ if (image_cache == NULL) {
+ return NSERROR_NOMEM;
+ }
+
+ image_cache->params = *image_cache_parameters;
+
+ guit->misc->schedule(image_cache->params.bg_clean_time,
+ image_cache__background_update,
+ image_cache);
+
+ LOG("Image cache initilised with a limit of %" PRIsizet " hysteresis of %"PRIsizet,
+ image_cache->params.limit, image_cache->params.hysteresis);
+
+ return NSERROR_OK;
+}
+
+/* exported interface documented in image_cache.h */
+nserror image_cache_fini(void)
+{
+ unsigned int op_count;
+
+ guit->misc->schedule(-1, image_cache__background_update, image_cache);
+
+ LOG("Size at finish %" PRIsizet " (in %d)",
+ image_cache->total_bitmap_size, image_cache->bitmap_count);
+
+ while (image_cache->entries != NULL) {
+ image_cache__free_entry(image_cache->entries);
+ }
+
+ op_count = image_cache->hit_count +
+ image_cache->miss_count +
+ image_cache->fail_count;
+
+ LOG("Age %ds", image_cache->current_age / 1000);
+ LOG("Peak size %" PRIsizet " (in %d)",
+ image_cache->max_bitmap_size, image_cache->max_bitmap_size_count);
+ LOG("Peak image count %d (size %" PRIsizet ")",
+ image_cache->max_bitmap_count, image_cache->max_bitmap_count_size);
+
+ if (op_count > 0) {
+ uint64_t op_size;
+
+ op_size = image_cache->hit_size +
+ image_cache->miss_size +
+ image_cache->fail_size;
+
+ LOG("Cache total/hit/miss/fail (counts) %d/%d/%d/%d (100%%/%d%%/%d%%/%d%%)",
+ op_count,
+ image_cache->hit_count,
+ image_cache->miss_count,
+ image_cache->fail_count,
+ (image_cache->hit_count * 100) / op_count,
+ (image_cache->miss_count * 100) / op_count,
+ (image_cache->fail_count * 100) / op_count);
+ LOG("Cache total/hit/miss/fail (size) %"PRIu64"/%"PRIu64"/%"PRIu64"/%"PRIu64" (100%%/%"PRId64"%%/%"PRId64"%%/%"PRId64"%%)",
+ op_size,
+ image_cache->hit_size,
+ image_cache->miss_size,
+ image_cache->fail_size,
+ (image_cache->hit_size * 100) / op_size,
+ (image_cache->miss_size * 100) / op_size,
+ (image_cache->fail_size * 100) / op_size);
+ }
+
+ LOG("Total images never rendered: %d (includes %d that were converted)",
+ image_cache->total_unrendered,
+ image_cache->specultive_miss_count);
+
+ LOG("Total number of excessive conversions: %d (from %d images converted more than once)",
+ image_cache->total_extra_conversions,
+ image_cache->total_extra_conversions_count);
+
+ LOG("Bitmap of size %d had most (%d) conversions",
+ image_cache->peak_conversions_size,
+ image_cache->peak_conversions);
+
+ free(image_cache);
+
+ return NSERROR_OK;
+}
+
+/* exported interface documented in image_cache.h */
+nserror image_cache_add(struct content *content,
+ struct bitmap *bitmap,
+ image_cache_convert_fn *convert)
+{
+ struct image_cache_entry_s *centry;
+
+ /* bump the cache age by a ms to ensure multiple items are not
+ * added at exactly the same time
+ */
+ image_cache->current_age++;
+
+ centry = image_cache__find(content);
+ if (centry == NULL) {
+ /* new cache entry, content not previously added */
+ centry = calloc(1, sizeof(struct image_cache_entry_s));
+ if (centry == NULL) {
+ return NSERROR_NOMEM;
+ }
+ image_cache__link(centry);
+ centry->content = content;
+
+ centry->bitmap_size = content->width * content->height * 4;
+ }
+
+ LOG("centry %p, content %p, bitmap %p", centry, content, bitmap);
+
+ centry->convert = convert;
+
+ /* set bitmap entry if one is passed, free extant one if present */
+ if (bitmap != NULL) {
+ if (centry->bitmap != NULL) {
+ guit->bitmap->destroy(centry->bitmap);
+ } else {
+ image_cache_stats_bitmap_add(centry);
+ }
+ centry->bitmap = bitmap;
+ } else {
+ /* no bitmap, check to see if we should speculatively convert */
+ if ((centry->convert != NULL) &&
+ (image_cache_speculate(content) == true)) {
+ centry->bitmap = centry->convert(centry->content);
+
+ if (centry->bitmap != NULL) {
+ image_cache_stats_bitmap_add(centry);
+ } else {
+ image_cache->fail_count++;
+ }
+ }
+ }
+
+
+
+ return NSERROR_OK;
+}
+
+/* exported interface documented in image_cache.h */
+nserror image_cache_remove(struct content *content)
+{
+ struct image_cache_entry_s *centry;
+
+ /* get the cache entry */
+ centry = image_cache__find(content);
+ if (centry == NULL) {
+ LOG("Could not find cache entry for content (%p)", content);
+ return NSERROR_NOT_FOUND;
+ }
+
+ image_cache__free_entry(centry);
+
+ return NSERROR_OK;
+}
+
+/* exported interface documented in image_cache.h */
+int image_cache_snsummaryf(char *string, size_t size, const char *fmt)
+{
+ size_t slen = 0; /* current output string length */
+ int fmtc = 0; /* current index into format string */
+ bool pct;
+ unsigned int op_count;
+ uint64_t op_size;
+
+ op_count = image_cache->hit_count +
+ image_cache->miss_count +
+ image_cache->fail_count;
+
+ op_size = image_cache->hit_size +
+ image_cache->miss_size +
+ image_cache->fail_size;
+
+ while((slen < size) && (fmt[fmtc] != 0)) {
+ if (fmt[fmtc] == '%') {
+ fmtc++;
+
+ /* check for percentage modifier */
+ if (fmt[fmtc] == 'p') {
+ fmtc++;
+ pct = true;
+ } else {
+ pct = false;
+ }
+
+#define FMTCHR(chr,fmt,var) case chr : \
+slen += snprintf(string + slen, size - slen, "%"fmt, image_cache->var); break
+
+#define FMTPCHR(chr,fmt,var,div) \
+case chr : \
+ if (pct) { \
+ if (div > 0) { \
+ slen += snprintf(string + slen, size - slen, "%"PRId64, (uint64_t)((image_cache->var * 100) / div)); \
+ } else { \
+ slen += snprintf(string + slen, size - slen, "100"); \
+ } \
+ } else { \
+ slen += snprintf(string + slen, size - slen, "%"fmt, image_cache->var); \
+ } break
+
+
+ switch (fmt[fmtc]) {
+ case '%':
+ string[slen] = '%';
+ slen++;
+ break;
+
+ FMTCHR('a', PRIssizet, params.limit);
+ FMTCHR('b', PRIssizet, params.hysteresis);
+ FMTCHR('c', PRIssizet, total_bitmap_size);
+ FMTCHR('d', "d", bitmap_count);
+ FMTCHR('e', "d", current_age / 1000);
+ FMTCHR('f', PRIssizet, max_bitmap_size);
+ FMTCHR('g', "d", max_bitmap_size_count);
+ FMTCHR('h', "d", max_bitmap_count);
+ FMTCHR('i', PRIssizet, max_bitmap_count_size);
+
+
+ case 'j':
+ slen += snprintf(string + slen, size - slen,
+ "%d", pct?100:op_count);
+ break;
+
+ FMTPCHR('k', "d", hit_count, op_count);
+ FMTPCHR('l', "d", miss_count, op_count);
+ FMTPCHR('m', "d", fail_count, op_count);
+
+ case 'n':
+ slen += snprintf(string + slen, size - slen,
+ "%"PRId64, pct?100:op_size);
+ break;
+
+ FMTPCHR('o', PRId64, hit_size, op_size);
+ FMTPCHR('q', PRId64, miss_size, op_size);
+ FMTPCHR('r', PRId64, fail_size, op_size);
+
+ FMTCHR('s', "d", total_unrendered);
+ FMTCHR('t', "d", specultive_miss_count);
+ FMTCHR('u', "d", total_extra_conversions);
+ FMTCHR('v', "d", total_extra_conversions_count);
+ FMTCHR('w', "d", peak_conversions_size);
+ FMTCHR('x', "d", peak_conversions);
+
+
+ }
+#undef FMTCHR
+#undef FMTPCHR
+
+ fmtc++;
+ } else {
+ string[slen] = fmt[fmtc];
+ slen++;
+ fmtc++;
+ }
+ }
+
+ /* Ensure that we NUL-terminate the output */
+ string[min(slen, size - 1)] = '\0';
+
+ return slen;
+}
+
+/* exported interface documented in image_cache.h */
+int image_cache_snentryf(char *string, size_t size, unsigned int entryn,
+ const char *fmt)
+{
+ struct image_cache_entry_s *centry;
+ size_t slen = 0; /* current output string length */
+ int fmtc = 0; /* current index into format string */
+ lwc_string *origin; /* current entry's origin */
+
+ centry = image_cache__findn(entryn);
+ if (centry == NULL)
+ return -1;
+
+ while((slen < size) && (fmt[fmtc] != 0)) {
+ if (fmt[fmtc] == '%') {
+ fmtc++;
+ switch (fmt[fmtc]) {
+ case 'e':
+ slen += snprintf(string + slen, size - slen,
+ "%d", entryn);
+ break;
+
+ case 'r':
+ slen += snprintf(string + slen, size - slen,
+ "%u", centry->redraw_count);
+ break;
+
+ case 'a':
+ slen += snprintf(string + slen, size - slen,
+ "%.2f", (float)((image_cache->current_age - centry->redraw_age)) / 1000);
+ break;
+
+
+ case 'c':
+ slen += snprintf(string + slen, size - slen,
+ "%d", centry->conversion_count);
+ break;
+
+ case 'g':
+ slen += snprintf(string + slen, size - slen,
+ "%.2f", (float)((image_cache->current_age - centry->bitmap_age)) / 1000);
+ break;
+
+ case 'k':
+ slen += snprintf(string + slen, size - slen,
+ "%p", centry->content);
+ break;
+
+ case 'U':
+ slen += snprintf(string + slen, size - slen,
+ "%s", nsurl_access(llcache_handle_get_url(centry->content->llcache)));
+ break;
+
+ case 'o':
+ if (nsurl_has_component(llcache_handle_get_url(
+ centry->content->llcache),
+ NSURL_HOST)) {
+ origin = nsurl_get_component(
+ llcache_handle_get_url(
+ centry->content->
+ llcache),
+ NSURL_HOST);
+
+ slen += snprintf(string + slen,
+ size - slen, "%s",
+ lwc_string_data(
+ origin));
+
+ lwc_string_unref(origin);
+ } else {
+ slen += snprintf(string + slen,
+ size - slen, "%s",
+ "localhost");
+ }
+ break;
+
+ case 's':
+ if (centry->bitmap != NULL) {
+ slen += snprintf(string + slen,
+ size - slen,
+ "%" PRIssizet,
+ centry->bitmap_size);
+ } else {
+ slen += snprintf(string + slen,
+ size - slen,
+ "0");
+ }
+ break;
+ }
+ fmtc++;
+ } else {
+ string[slen] = fmt[fmtc];
+ slen++;
+ fmtc++;
+ }
+ }
+
+ /* Ensure that we NUL-terminate the output */
+ string[min(slen, size - 1)] = '\0';
+
+ return slen;
+}
+
+
+/* exported interface documented in image_cache.h */
+bool image_cache_redraw(struct content *c,
+ struct content_redraw_data *data,
+ const struct rect *clip,
+ const struct redraw_context *ctx)
+{
+ struct image_cache_entry_s *centry;
+
+ /* get the cache entry */
+ centry = image_cache__find(c);
+ if (centry == NULL) {
+ LOG("Could not find cache entry for content (%p)", c);
+ return false;
+ }
+
+ if (centry->bitmap == NULL) {
+ if (centry->convert != NULL) {
+ centry->bitmap = centry->convert(centry->content);
+ }
+
+ if (centry->bitmap != NULL) {
+ image_cache_stats_bitmap_add(centry);
+ image_cache->miss_count++;
+ image_cache->miss_size += centry->bitmap_size;
+ } else {
+ image_cache->fail_count++;
+ image_cache->fail_size += centry->bitmap_size;
+ return false;
+ }
+ } else {
+ image_cache->hit_count++;
+ image_cache->hit_size += centry->bitmap_size;
+ }
+
+
+ /* update statistics */
+ centry->redraw_count++;
+ centry->redraw_age = image_cache->current_age;
+
+ return image_bitmap_plot(centry->bitmap, data, clip, ctx);
+}
+
+void image_cache_destroy(struct content *content)
+{
+ struct image_cache_entry_s *centry;
+
+ /* get the cache entry */
+ centry = image_cache__find(content);
+ if (centry == NULL) {
+ LOG("Could not find cache entry for content (%p)", content);
+ } else {
+ image_cache__free_entry(centry);
+ }
+}
+
+void *image_cache_get_internal(const struct content *c, void *context)
+{
+ return image_cache_get_bitmap(c);
+}
+
+content_type image_cache_content_type(void)
+{
+ return CONTENT_IMAGE;
+}
diff --git a/content/handlers/image/image_cache.h b/content/handlers/image/image_cache.h
new file mode 100644
index 000000000..2f1a5caee
--- /dev/null
+++ b/content/handlers/image/image_cache.h
@@ -0,0 +1,189 @@
+/*
+ * Copyright 2011 John-Mark Bell <jmb@netsurf-browser.org>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * NetSurf is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+/** \file
+ * The image content handler intermediate image cache.
+ *
+ * This cache allows netsurf to use a generic intermediate bitmap
+ * format without keeping the
+ * intermediate representation in memory.
+ *
+ * The bitmap structure is opaque to the rest of netsurf and is
+ * controlled by the platform-specific code (see image/bitmap.h for
+ * detials). All image content handlers convert into this format and
+ * pass it to the plot functions for display,
+ *
+ * This cache maintains a link between the underlying original content
+ * and the intermediate representation. It is intended to be flexable
+ * and either manage the bitmap plotting completely or give the image
+ * content handler complete control.
+ */
+
+#ifndef NETSURF_IMAGE_IMAGE_CACHE_H_
+#define NETSURF_IMAGE_IMAGE_CACHE_H_
+
+#include "utils/errors.h"
+
+struct content_redraw_data;
+struct redraw_context;
+
+typedef struct bitmap * (image_cache_convert_fn) (struct content *content);
+
+struct image_cache_parameters {
+ /** How frequently the background cache clean process is run (ms) */
+ unsigned int bg_clean_time;
+
+ /** The target upper bound for the image cache size */
+ size_t limit;
+
+ /** The hysteresis allowed round the target size */
+ size_t hysteresis;
+
+ /** The speculative conversion "small" size */
+ size_t speculative_small;
+};
+
+/** Initialise the image cache
+ *
+ * @param image_cache_parameters The control parameters for the image cache
+ */
+nserror image_cache_init(const struct image_cache_parameters *image_cache_parameters);
+nserror image_cache_fini(void);
+
+/** adds an image content to be cached.
+ *
+ * @param content The content handle used as a key
+ * @param bitmap A bitmap representing the already converted content or NULL.
+ * @param convert A function pointer to convert the content into a bitmap or NULL.
+ * @return A netsurf error code.
+ */
+nserror image_cache_add(struct content *content,
+ struct bitmap *bitmap,
+ image_cache_convert_fn *convert);
+
+nserror image_cache_remove(struct content *content);
+
+
+/** Obtain a bitmap from a content converting from source if neccessary. */
+struct bitmap *image_cache_get_bitmap(const struct content *c);
+
+/** Obtain a bitmap from a content with no conversion */
+struct bitmap *image_cache_find_bitmap(struct content *c);
+
+/** Decide if a content should be speculatively converted.
+ *
+ * This allows for image content handlers to ask the cache if a bitmap
+ * should be generated before it is added to the cache. This is the
+ * same decision logic used to decide to perform an immediate
+ * conversion when a content is initially added to the cache.
+ *
+ * @param c The content to be considered.
+ * @return true if a speculative conversion is desired false otherwise.
+ */
+bool image_cache_speculate(struct content *c);
+
+/**
+ * Fill a buffer with information about a cache entry using a format.
+ *
+ * The format string is copied into the output buffer with the
+ * following replaced:
+ * %e - The entry number
+ * %k - The content key
+ * %r - The number of redraws of this bitmap
+ * %c - The number of times this bitmap has been converted
+ * %s - The size of the current bitmap allocation
+ *
+ * \param string The buffer in which to place the results.
+ * \param size The size of the string buffer.
+ * \param entryn The opaque entry number.
+ * \param fmt The format string.
+ * \return The number of bytes written to \a string or -1 on error
+ */
+int image_cache_snentryf(char *string, size_t size, unsigned int entryn,
+ const char *fmt);
+
+/**
+ * Fill a buffer with information about the image cache using a format.
+ *
+ * The format string is copied into the output buffer with the
+ * following replaced:
+ *
+ * a Configured cache limit size
+ * b Configured cache hysteresis size
+ * c Current caches total consumed size
+ * d Number of images currently in the cache
+ * e The age of the cache
+ * f The largest amount of space the cache has occupied since initialisation
+ * g The number of objetcs when the cache was at its largest
+ * h The largest number of images in the cache since initialisation
+ * i The size of the cache when the largest number of objects occoured
+ * j The total number of read operations performed on the cache
+ * k The total number of read operations satisfied from the cache without
+ * conversion.
+ * l The total number of read operations satisfied from the cache which
+ * required a conversion.
+ * m The total number of read operations which could not be sucessfully
+ * returned. ie. not available in cache and conversion failed.
+ * n The total size of read operations performed on the cache
+ * o The total size of read operations satisfied from the cache without
+ * conversion.
+ * q The total size of read operations satisfied from the cache which
+ * required a conversion.
+ * r The total size of read operations which could not be sucessfully
+ * returned. ie. not available in cache and conversion failed.
+ * s The number of images which were placed in the cache but never read.
+ * t The number of images that were converted on insertion into the cache which were subsequently never used.
+ * u The number of times an image was converted after the first
+ * v The number of images that had extra conversions performed.
+ * w Size of the image that was converted (read missed cache) highest number
+ * of times.
+ * x The number of times the image that was converted (read missed cache)
+ * highest number of times.
+ *
+ * format modifiers:
+ * A p before the value modifies the replacement to be a percentage.
+ *
+ *
+ * \param string The buffer in which to place the results.
+ * \param size The size of the string buffer.
+ * \param fmt The format string.
+ * \return The number of bytes written to \a string or -1 on error
+ */
+
+int image_cache_snsummaryf(char *string, size_t size, const char *fmt);
+
+/********* Image content handler generic cache callbacks ************/
+
+/** Generic content redraw callback
+ *
+ * May be used by image content handlers as their redraw
+ * callback. Performs all neccissary cache lookups and conversions and
+ * calls the bitmap plot function in the redraw context.
+ */
+bool image_cache_redraw(struct content *c,
+ struct content_redraw_data *data,
+ const struct rect *clip,
+ const struct redraw_context *ctx);
+
+void image_cache_destroy(struct content *c);
+
+void *image_cache_get_internal(const struct content *c, void *context);
+
+content_type image_cache_content_type(void);
+
+#endif
diff --git a/content/handlers/image/jpeg.c b/content/handlers/image/jpeg.c
new file mode 100644
index 000000000..b5eade306
--- /dev/null
+++ b/content/handlers/image/jpeg.c
@@ -0,0 +1,393 @@
+/*
+ * Copyright 2004 James Bursa <bursa@users.sourceforge.net>
+ * Copyright 2004 John M Bell <jmb202@ecs.soton.ac.uk>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * NetSurf is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+/** \file
+ * Content for image/jpeg (implementation).
+ *
+ * This implementation uses the IJG JPEG library.
+ */
+
+#include <stdbool.h>
+#include <stdlib.h>
+#include <setjmp.h>
+
+#include "utils/utils.h"
+#include "utils/log.h"
+#include "utils/messages.h"
+#include "content/content_protected.h"
+#include "desktop/gui_internal.h"
+
+#include "image_cache.h"
+#include "bitmap.h"
+
+#define JPEG_INTERNAL_OPTIONS
+#include "jpeglib.h"
+#include "jpeg.h"
+
+/** absolute minimum size of a jpeg below which it is not even worth
+ * trying to read header data
+ */
+#define MIN_JPEG_SIZE 20
+
+#ifdef riscos
+/* We prefer the library to be configured with these options to save
+ * copying data during decoding. */
+#if RGB_RED != 0 || RGB_GREEN != 1 || RGB_BLUE != 2 || RGB_PIXELSIZE != 4
+#warning JPEG library not optimally configured. Decoding will be slower.
+#endif
+/* but we don't care if we're not on RISC OS */
+#endif
+
+static char nsjpeg_error_buffer[JMSG_LENGTH_MAX];
+
+static unsigned char nsjpeg_eoi[] = { 0xff, JPEG_EOI };
+
+/**
+ * Content create entry point.
+ */
+static nserror nsjpeg_create(const content_handler *handler,
+ lwc_string *imime_type, const struct http_parameter *params,
+ llcache_handle *llcache, const char *fallback_charset,
+ bool quirks, struct content **c)
+{
+ struct content *jpeg;
+ nserror error;
+
+ jpeg = calloc(1, sizeof(struct content));
+ if (jpeg == NULL)
+ return NSERROR_NOMEM;
+
+ error = content__init(jpeg, handler, imime_type, params,
+ llcache, fallback_charset, quirks);
+ if (error != NSERROR_OK) {
+ free(jpeg);
+ return error;
+ }
+
+ *c = jpeg;
+
+ return NSERROR_OK;
+}
+
+/**
+ * JPEG data source manager: initialize source.
+ */
+static void nsjpeg_init_source(j_decompress_ptr cinfo)
+{
+}
+
+
+/**
+ * JPEG data source manager: fill the input buffer.
+ *
+ * This can only occur if the JPEG data was truncated or corrupted. Insert a
+ * fake EOI marker to allow the decompressor to output as much as possible.
+ */
+static boolean nsjpeg_fill_input_buffer(j_decompress_ptr cinfo)
+{
+ cinfo->src->next_input_byte = nsjpeg_eoi;
+ cinfo->src->bytes_in_buffer = 2;
+ return TRUE;
+}
+
+
+/**
+ * JPEG data source manager: skip num_bytes worth of data.
+ */
+
+static void nsjpeg_skip_input_data(j_decompress_ptr cinfo, long num_bytes)
+{
+ if ((long) cinfo->src->bytes_in_buffer < num_bytes) {
+ cinfo->src->next_input_byte = 0;
+ cinfo->src->bytes_in_buffer = 0;
+ } else {
+ cinfo->src->next_input_byte += num_bytes;
+ cinfo->src->bytes_in_buffer -= num_bytes;
+ }
+}
+
+
+/**
+ * JPEG data source manager: terminate source.
+ */
+static void nsjpeg_term_source(j_decompress_ptr cinfo)
+{
+}
+
+
+/**
+ * Error output handler for JPEG library.
+ *
+ * This logs to NetSurf log instead of stderr.
+ * Warnings only - fatal errors are trapped by nsjpeg_error_exit
+ * and do not call the output handler.
+ */
+static void nsjpeg_error_log(j_common_ptr cinfo)
+{
+ cinfo->err->format_message(cinfo, nsjpeg_error_buffer);
+ LOG("%s", nsjpeg_error_buffer);
+}
+
+
+/**
+ * Fatal error handler for JPEG library.
+ *
+ * This prevents jpeglib calling exit() on a fatal error.
+ */
+static void nsjpeg_error_exit(j_common_ptr cinfo)
+{
+ jmp_buf *setjmp_buffer = (jmp_buf *) cinfo->client_data;
+
+ cinfo->err->format_message(cinfo, nsjpeg_error_buffer);
+ LOG("%s", nsjpeg_error_buffer);
+
+ longjmp(*setjmp_buffer, 1);
+}
+
+static struct bitmap *
+jpeg_cache_convert(struct content *c)
+{
+ uint8_t *source_data; /* Jpeg source data */
+ unsigned long source_size; /* length of Jpeg source data */
+ struct jpeg_decompress_struct cinfo;
+ struct jpeg_error_mgr jerr;
+ jmp_buf setjmp_buffer;
+ unsigned int height;
+ unsigned int width;
+ struct bitmap * volatile bitmap = NULL;
+ uint8_t * volatile pixels = NULL;
+ size_t rowstride;
+ struct jpeg_source_mgr source_mgr = {
+ 0,
+ 0,
+ nsjpeg_init_source,
+ nsjpeg_fill_input_buffer,
+ nsjpeg_skip_input_data,
+ jpeg_resync_to_restart,
+ nsjpeg_term_source };
+
+ /* obtain jpeg source data and perfom minimal sanity checks */
+ source_data = (uint8_t *)content__get_source_data(c, &source_size);
+
+ if ((source_data == NULL) ||
+ (source_size < MIN_JPEG_SIZE)) {
+ return NULL;
+ }
+
+ /* setup a JPEG library error handler */
+ cinfo.err = jpeg_std_error(&jerr);
+ jerr.error_exit = nsjpeg_error_exit;
+ jerr.output_message = nsjpeg_error_log;
+
+ /* handler for fatal errors during decompression */
+ if (setjmp(setjmp_buffer)) {
+ jpeg_destroy_decompress(&cinfo);
+ return bitmap;
+ }
+
+ jpeg_create_decompress(&cinfo);
+ cinfo.client_data = &setjmp_buffer;
+
+ /* setup data source */
+ source_mgr.next_input_byte = source_data;
+ source_mgr.bytes_in_buffer = source_size;
+ cinfo.src = &source_mgr;
+
+ /* read JPEG header information */
+ jpeg_read_header(&cinfo, TRUE);
+
+ /* set output processing parameters */
+ cinfo.out_color_space = JCS_RGB;
+ cinfo.dct_method = JDCT_ISLOW;
+
+ /* commence the decompression, output parameters now valid */
+ jpeg_start_decompress(&cinfo);
+
+ width = cinfo.output_width;
+ height = cinfo.output_height;
+
+ /* create opaque bitmap (jpegs cannot be transparent) */
+ bitmap = guit->bitmap->create(width, height, BITMAP_NEW | BITMAP_OPAQUE);
+ if (bitmap == NULL) {
+ /* empty bitmap could not be created */
+ jpeg_destroy_decompress(&cinfo);
+ return NULL;
+ }
+
+ pixels = guit->bitmap->get_buffer(bitmap);
+ if (pixels == NULL) {
+ /* bitmap with no buffer available */
+ guit->bitmap->destroy(bitmap);
+ jpeg_destroy_decompress(&cinfo);
+ return NULL;
+ }
+
+ /* Convert scanlines from jpeg into bitmap */
+ rowstride = guit->bitmap->get_rowstride(bitmap);
+ do {
+ JSAMPROW scanlines[1];
+
+ scanlines[0] = (JSAMPROW) (pixels +
+ rowstride * cinfo.output_scanline);
+ jpeg_read_scanlines(&cinfo, scanlines, 1);
+
+#if RGB_RED != 0 || RGB_GREEN != 1 || RGB_BLUE != 2 || RGB_PIXELSIZE != 4
+{
+ /* Missmatch between configured libjpeg pixel format and
+ * NetSurf pixel format. Convert to RGBA */
+ int i;
+ for (i = width - 1; 0 <= i; i--) {
+ int r = scanlines[0][i * RGB_PIXELSIZE + RGB_RED];
+ int g = scanlines[0][i * RGB_PIXELSIZE + RGB_GREEN];
+ int b = scanlines[0][i * RGB_PIXELSIZE + RGB_BLUE];
+ scanlines[0][i * 4 + 0] = r;
+ scanlines[0][i * 4 + 1] = g;
+ scanlines[0][i * 4 + 2] = b;
+ scanlines[0][i * 4 + 3] = 0xff;
+ }
+}
+#endif
+ } while (cinfo.output_scanline != cinfo.output_height);
+ guit->bitmap->modified(bitmap);
+
+ jpeg_finish_decompress(&cinfo);
+ jpeg_destroy_decompress(&cinfo);
+
+ return bitmap;
+}
+
+/**
+ * Convert a CONTENT_JPEG for display.
+ */
+static bool nsjpeg_convert(struct content *c)
+{
+ struct jpeg_decompress_struct cinfo;
+ struct jpeg_error_mgr jerr;
+ jmp_buf setjmp_buffer;
+ struct jpeg_source_mgr source_mgr = { 0, 0,
+ nsjpeg_init_source, nsjpeg_fill_input_buffer,
+ nsjpeg_skip_input_data, jpeg_resync_to_restart,
+ nsjpeg_term_source };
+ union content_msg_data msg_data;
+ const char *data;
+ unsigned long size;
+ char *title;
+
+ /* check image header is valid and get width/height */
+ data = content__get_source_data(c, &size);
+
+ cinfo.err = jpeg_std_error(&jerr);
+ jerr.error_exit = nsjpeg_error_exit;
+ jerr.output_message = nsjpeg_error_log;
+
+ if (setjmp(setjmp_buffer)) {
+ jpeg_destroy_decompress(&cinfo);
+
+ msg_data.error = nsjpeg_error_buffer;
+ content_broadcast(c, CONTENT_MSG_ERROR, msg_data);
+ return false;
+ }
+
+ jpeg_create_decompress(&cinfo);
+ cinfo.client_data = &setjmp_buffer;
+ source_mgr.next_input_byte = (unsigned char *) data;
+ source_mgr.bytes_in_buffer = size;
+ cinfo.src = &source_mgr;
+ jpeg_read_header(&cinfo, TRUE);
+ cinfo.out_color_space = JCS_RGB;
+ cinfo.dct_method = JDCT_ISLOW;
+
+ jpeg_calc_output_dimensions(&cinfo);
+
+ c->width = cinfo.output_width;
+ c->height = cinfo.output_height;
+ c->size = c->width * c->height * 4;
+
+ jpeg_destroy_decompress(&cinfo);
+
+ image_cache_add(c, NULL, jpeg_cache_convert);
+
+ /* set title text */
+ title = messages_get_buff("JPEGTitle",
+ nsurl_access_leaf(llcache_handle_get_url(c->llcache)),
+ c->width, c->height);
+ if (title != NULL) {
+ content__set_title(c, title);
+ free(title);
+ }
+
+ content_set_ready(c);
+ content_set_done(c);
+ content_set_status(c, ""); /* Done: update status bar */
+
+ return true;
+}
+
+
+
+/**
+ * Clone content.
+ */
+static nserror nsjpeg_clone(const struct content *old, struct content **newc)
+{
+ struct content *jpeg_c;
+ nserror error;
+
+ jpeg_c = calloc(1, sizeof(struct content));
+ if (jpeg_c == NULL)
+ return NSERROR_NOMEM;
+
+ error = content__clone(old, jpeg_c);
+ if (error != NSERROR_OK) {
+ content_destroy(jpeg_c);
+ return error;
+ }
+
+ /* re-convert if the content is ready */
+ if ((old->status == CONTENT_STATUS_READY) ||
+ (old->status == CONTENT_STATUS_DONE)) {
+ if (nsjpeg_convert(jpeg_c) == false) {
+ content_destroy(jpeg_c);
+ return NSERROR_CLONE_FAILED;
+ }
+ }
+
+ *newc = jpeg_c;
+
+ return NSERROR_OK;
+}
+
+static const content_handler nsjpeg_content_handler = {
+ .create = nsjpeg_create,
+ .data_complete = nsjpeg_convert,
+ .destroy = image_cache_destroy,
+ .redraw = image_cache_redraw,
+ .clone = nsjpeg_clone,
+ .get_internal = image_cache_get_internal,
+ .type = image_cache_content_type,
+ .no_share = false,
+};
+
+static const char *nsjpeg_types[] = {
+ "image/jpeg",
+ "image/jpg",
+ "image/pjpeg"
+};
+
+CONTENT_FACTORY_REGISTER_TYPES(nsjpeg, nsjpeg_types, nsjpeg_content_handler);
diff --git a/content/handlers/image/jpeg.h b/content/handlers/image/jpeg.h
new file mode 100644
index 000000000..8c054732b
--- /dev/null
+++ b/content/handlers/image/jpeg.h
@@ -0,0 +1,28 @@
+/*
+ * Copyright 2004 James Bursa <bursa@users.sourceforge.net>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * NetSurf is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+/** \file
+ * Content for image/jpeg (interface).
+ */
+
+#ifndef _NETSURF_IMAGE_JPEG_H_
+#define _NETSURF_IMAGE_JPEG_H_
+
+nserror nsjpeg_init(void);
+
+#endif
diff --git a/content/handlers/image/nssprite.c b/content/handlers/image/nssprite.c
new file mode 100644
index 000000000..fd651e0b0
--- /dev/null
+++ b/content/handlers/image/nssprite.c
@@ -0,0 +1,259 @@
+ /*
+ * Copyright 2008 James Shaw <js102@zepler.net>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * NetSurf is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+/** \file
+ * Content for image/x-riscos-sprite (librosprite implementation).
+ *
+ */
+
+#include <stdbool.h>
+#include <stdlib.h>
+#include <librosprite.h>
+
+#include "utils/utils.h"
+#include "utils/log.h"
+#include "utils/messages.h"
+#include "content/content_protected.h"
+#include "desktop/gui_internal.h"
+#include "desktop/plotters.h"
+
+#include "bitmap.h"
+#include "nssprite.h"
+
+typedef struct nssprite_content {
+ struct content base;
+ struct bitmap *bitmap; /**< Created NetSurf bitmap */
+
+ struct rosprite_area* sprite_area;
+} nssprite_content;
+
+
+#define ERRCHK(x) do { \
+ rosprite_error err = x; \
+ if (err == ROSPRITE_EOF) { \
+ LOG("Got ROSPRITE_EOF when loading sprite file"); \
+ return false; \
+ } else if (err == ROSPRITE_BADMODE) { \
+ LOG("Got ROSPRITE_BADMODE when loading sprite file"); \
+ return false; \
+ } else if (err == ROSPRITE_OK) { \
+ } else { \
+ return false; \
+ } \
+} while(0)
+
+
+
+
+static nserror nssprite_create(const content_handler *handler,
+ lwc_string *imime_type, const struct http_parameter *params,
+ llcache_handle *llcache, const char *fallback_charset,
+ bool quirks, struct content **c)
+{
+ nssprite_content *sprite;
+ nserror error;
+
+ sprite = calloc(1, sizeof(nssprite_content));
+ if (sprite == NULL)
+ return NSERROR_NOMEM;
+
+ error = content__init(&sprite->base, handler, imime_type, params,
+ llcache, fallback_charset, quirks);
+ if (error != NSERROR_OK) {
+ free(sprite);
+ return error;
+ }
+
+ *c = (struct content *) sprite;
+
+ return NSERROR_OK;
+}
+
+/**
+ * Convert a CONTENT_SPRITE for display.
+ *
+ * No conversion is necessary. We merely read the sprite dimensions.
+ */
+
+static bool nssprite_convert(struct content *c)
+{
+ nssprite_content *nssprite = (nssprite_content *) c;
+ union content_msg_data msg_data;
+
+ struct rosprite_mem_context* ctx;
+
+ const char *data;
+ unsigned long size;
+ char *title;
+
+ data = content__get_source_data(c, &size);
+
+ ERRCHK(rosprite_create_mem_context((uint8_t *) data, size, &ctx));
+
+ struct rosprite_area* sprite_area;
+ ERRCHK(rosprite_load(rosprite_mem_reader, ctx, &sprite_area));
+ rosprite_destroy_mem_context(ctx);
+ nssprite->sprite_area = sprite_area;
+
+ assert(sprite_area->sprite_count > 0);
+
+ struct rosprite* sprite = sprite_area->sprites[0];
+
+ nssprite->bitmap = guit->bitmap->create(sprite->width, sprite->height, BITMAP_NEW);
+ if (!nssprite->bitmap) {
+ msg_data.error = messages_get("NoMemory");
+ content_broadcast(c, CONTENT_MSG_ERROR, msg_data);
+ return false;
+ }
+ uint32_t* imagebuf = (uint32_t *)guit->bitmap->get_buffer(nssprite->bitmap);
+ if (!imagebuf) {
+ msg_data.error = messages_get("NoMemory");
+ content_broadcast(c, CONTENT_MSG_ERROR, msg_data);
+ return false;
+ }
+ unsigned char *spritebuf = (unsigned char *)sprite->image;
+
+ /* reverse byte order of each word */
+ for (uint32_t y = 0; y < sprite->height; y++) {
+ for (uint32_t x = 0; x < sprite->width; x++) {
+ int offset = 4 * (y * sprite->width + x);
+
+ *imagebuf = (spritebuf[offset] << 24) |
+ (spritebuf[offset + 1] << 16) |
+ (spritebuf[offset + 2] << 8) |
+ (spritebuf[offset + 3]);
+
+ imagebuf++;
+ }
+ }
+
+ c->width = sprite->width;
+ c->height = sprite->height;
+
+ /* set title text */
+ title = messages_get_buff("SpriteTitle",
+ nsurl_access_leaf(llcache_handle_get_url(c->llcache)),
+ c->width, c->height);
+ if (title != NULL) {
+ content__set_title(c, title);
+ free(title);
+ }
+
+ guit->bitmap->modified(nssprite->bitmap);
+
+ content_set_ready(c);
+ content_set_done(c);
+ content_set_status(c, ""); /* Done: update status bar */
+
+ return true;
+}
+
+
+/**
+ * Destroy a CONTENT_SPRITE and free all resources it owns.
+ */
+
+static void nssprite_destroy(struct content *c)
+{
+ nssprite_content *nssprite = (nssprite_content *) c;
+
+ if (nssprite->sprite_area != NULL)
+ rosprite_destroy_sprite_area(nssprite->sprite_area);
+ if (nssprite->bitmap != NULL)
+ guit->bitmap->destroy(nssprite->bitmap);
+}
+
+
+/**
+ * Redraw a CONTENT_SPRITE.
+ */
+
+static bool nssprite_redraw(struct content *c, struct content_redraw_data *data,
+ const struct rect *clip, const struct redraw_context *ctx)
+{
+ nssprite_content *nssprite = (nssprite_content *) c;
+ bitmap_flags_t flags = BITMAPF_NONE;
+
+ if (data->repeat_x)
+ flags |= BITMAPF_REPEAT_X;
+ if (data->repeat_y)
+ flags |= BITMAPF_REPEAT_Y;
+
+ return ctx->plot->bitmap(data->x, data->y, data->width, data->height,
+ nssprite->bitmap, data->background_colour, flags);
+}
+
+
+static nserror nssprite_clone(const struct content *old, struct content **newc)
+{
+ nssprite_content *sprite;
+ nserror error;
+
+ sprite = calloc(1, sizeof(nssprite_content));
+ if (sprite == NULL)
+ return NSERROR_NOMEM;
+
+ error = content__clone(old, &sprite->base);
+ if (error != NSERROR_OK) {
+ content_destroy(&sprite->base);
+ return error;
+ }
+
+ /* Simply replay convert */
+ if (old->status == CONTENT_STATUS_READY ||
+ old->status == CONTENT_STATUS_DONE) {
+ if (nssprite_convert(&sprite->base) == false) {
+ content_destroy(&sprite->base);
+ return NSERROR_CLONE_FAILED;
+ }
+ }
+
+ *newc = (struct content *) sprite;
+
+ return NSERROR_OK;
+}
+
+static void *nssprite_get_internal(const struct content *c, void *context)
+{
+ nssprite_content *nssprite = (nssprite_content *) c;
+
+ return nssprite->bitmap;
+}
+
+static content_type nssprite_content_type(void)
+{
+ return CONTENT_IMAGE;
+}
+
+
+static const content_handler nssprite_content_handler = {
+ .create = nssprite_create,
+ .data_complete = nssprite_convert,
+ .destroy = nssprite_destroy,
+ .redraw = nssprite_redraw,
+ .clone = nssprite_clone,
+ .get_internal = nssprite_get_internal,
+ .type = nssprite_content_type,
+ .no_share = false,
+};
+
+static const char *nssprite_types[] = {
+ "image/x-riscos-sprite"
+};
+
+CONTENT_FACTORY_REGISTER_TYPES(nssprite, nssprite_types, nssprite_content_handler);
diff --git a/content/handlers/image/nssprite.h b/content/handlers/image/nssprite.h
new file mode 100644
index 000000000..38226f84e
--- /dev/null
+++ b/content/handlers/image/nssprite.h
@@ -0,0 +1,28 @@
+/*
+ * Copyright 2008 James Shaw <js102@zepler.net>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * NetSurf is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+/** \file
+ * Content for image/x-riscos-sprite (librosprite interface).
+ */
+
+#ifndef _NETSURF_NS_SPRITE_H_
+#define _NETSURF_NS_SPRITE_H_
+
+nserror nssprite_init(void);
+
+#endif
diff --git a/content/handlers/image/png.c b/content/handlers/image/png.c
new file mode 100644
index 000000000..4f6f17d58
--- /dev/null
+++ b/content/handlers/image/png.c
@@ -0,0 +1,614 @@
+/*
+ * Copyright 2004 James Bursa <bursa@users.sourceforge.net>
+ * Copyright 2004 Richard Wilson <not_ginger_matt@hotmail.com>
+ * Copyright 2008 Daniel Silverstone <dsilvers@netsurf-browser.org>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * NetSurf is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <stdbool.h>
+#include <stdlib.h>
+#include <png.h>
+
+#include "utils/utils.h"
+#include "utils/log.h"
+#include "utils/messages.h"
+#include "content/content_protected.h"
+#include "desktop/gui_internal.h"
+
+#include "image_cache.h"
+#include "bitmap.h"
+#include "png.h"
+
+/* accommodate for old versions of libpng (beware security holes!) */
+
+#ifndef png_jmpbuf
+#warning you have an antique libpng
+#define png_jmpbuf(png_ptr) ((png_ptr)->jmpbuf)
+#endif
+
+#if PNG_LIBPNG_VER < 10209
+#define png_set_expand_gray_1_2_4_to_8(png) png_set_gray_1_2_4_to_8(png)
+#endif
+
+typedef struct nspng_content {
+ struct content base; /**< base content type */
+
+ bool no_process_data; /**< Do not continue to process data as it arrives */
+ png_structp png;
+ png_infop info;
+ int interlace;
+ struct bitmap *bitmap; /**< Created NetSurf bitmap */
+ size_t rowstride, bpp; /**< Bitmap rowstride and bpp */
+ size_t rowbytes; /**< Number of bytes per row */
+} nspng_content;
+
+static unsigned int interlace_start[8] = {0, 16, 0, 8, 0, 4, 0};
+static unsigned int interlace_step[8] = {28, 28, 12, 12, 4, 4, 0};
+static unsigned int interlace_row_start[8] = {0, 0, 4, 0, 2, 0, 1};
+static unsigned int interlace_row_step[8] = {8, 8, 8, 4, 4, 2, 2};
+
+/** Callbak error numbers*/
+enum nspng_cberr {
+ CBERR_NONE = 0, /* no error */
+ CBERR_LIBPNG, /* error from png library */
+ CBERR_NOPRE, /* no pre-conversion performed */
+};
+
+/**
+ * nspng_warning -- callback for libpng warnings
+ */
+static void nspng_warning(png_structp png_ptr, png_const_charp warning_message)
+{
+ LOG("%s", warning_message);
+}
+
+/**
+ * nspng_error -- callback for libpng errors
+ */
+static void nspng_error(png_structp png_ptr, png_const_charp error_message)
+{
+ LOG("%s", error_message);
+ longjmp(png_jmpbuf(png_ptr), CBERR_LIBPNG);
+}
+
+static void nspng_setup_transforms(png_structp png_ptr, png_infop info_ptr)
+{
+ int bit_depth, color_type, intent;
+ double gamma;
+
+ bit_depth = png_get_bit_depth(png_ptr, info_ptr);
+ color_type = png_get_color_type(png_ptr, info_ptr);
+
+ /* Set up our transformations */
+ if (color_type == PNG_COLOR_TYPE_PALETTE) {
+ png_set_palette_to_rgb(png_ptr);
+ }
+
+ if ((color_type == PNG_COLOR_TYPE_GRAY) && (bit_depth < 8)) {
+ png_set_expand_gray_1_2_4_to_8(png_ptr);
+ }
+
+ if (png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS)) {
+ png_set_tRNS_to_alpha(png_ptr);
+ }
+
+ if (bit_depth == 16) {
+ png_set_strip_16(png_ptr);
+ }
+
+ if (color_type == PNG_COLOR_TYPE_GRAY ||
+ color_type == PNG_COLOR_TYPE_GRAY_ALPHA) {
+ png_set_gray_to_rgb(png_ptr);
+ }
+
+ if (!(color_type & PNG_COLOR_MASK_ALPHA)) {
+ png_set_filler(png_ptr, 0xff, PNG_FILLER_AFTER);
+ }
+
+ /* gamma correction - we use 2.2 as our screen gamma
+ * this appears to be correct (at least in respect to !Browse)
+ * see http://www.w3.org/Graphics/PNG/all_seven.html for a test case
+ */
+ if (png_get_sRGB(png_ptr, info_ptr, &intent)) {
+ png_set_gamma(png_ptr, 2.2, 0.45455);
+ } else {
+ if (png_get_gAMA(png_ptr, info_ptr, &gamma)) {
+ png_set_gamma(png_ptr, 2.2, gamma);
+ } else {
+ png_set_gamma(png_ptr, 2.2, 0.45455);
+ }
+ }
+
+ png_read_update_info(png_ptr, info_ptr);
+}
+
+/**
+ * info_callback -- PNG header has been completely received, prepare to process
+ * image data
+ */
+static void info_callback(png_structp png_s, png_infop info)
+{
+ int interlace;
+ png_uint_32 width, height;
+ nspng_content *png_c = png_get_progressive_ptr(png_s);
+
+ width = png_get_image_width(png_s, info);
+ height = png_get_image_height(png_s, info);
+ interlace = png_get_interlace_type(png_s, info);
+
+ png_c->base.width = width;
+ png_c->base.height = height;
+ png_c->base.size += width * height * 4;
+
+ /* see if progressive-conversion should continue */
+ if (image_cache_speculate((struct content *)png_c) == false) {
+ longjmp(png_jmpbuf(png_s), CBERR_NOPRE);
+ }
+
+ /* Claim the required memory for the converted PNG */
+ png_c->bitmap = guit->bitmap->create(width, height, BITMAP_NEW);
+ if (png_c->bitmap == NULL) {
+ /* Failed to create bitmap skip pre-conversion */
+ longjmp(png_jmpbuf(png_s), CBERR_NOPRE);
+ }
+
+ png_c->rowstride = guit->bitmap->get_rowstride(png_c->bitmap);
+ png_c->bpp = guit->bitmap->get_bpp(png_c->bitmap);
+
+ nspng_setup_transforms(png_s, info);
+
+ png_c->rowbytes = png_get_rowbytes(png_s, info);
+ png_c->interlace = (interlace == PNG_INTERLACE_ADAM7);
+
+ LOG("size %li * %li, rowbytes %" PRIsizet,
+ (unsigned long)width,
+ (unsigned long)height,
+ png_c->rowbytes);
+}
+
+static void row_callback(png_structp png_s, png_bytep new_row,
+ png_uint_32 row_num, int pass)
+{
+ nspng_content *png_c = png_get_progressive_ptr(png_s);
+ unsigned long rowbytes = png_c->rowbytes;
+ unsigned char *buffer, *row;
+
+ /* Give up if there's no bitmap */
+ if (png_c->bitmap == NULL)
+ return;
+
+ /* Abort if we've not got any data */
+ if (new_row == NULL)
+ return;
+
+ /* Get bitmap buffer */
+ buffer = guit->bitmap->get_buffer(png_c->bitmap);
+ if (buffer == NULL) {
+ /* No buffer, bail out */
+ longjmp(png_jmpbuf(png_s), 1);
+ }
+
+ /* Calculate address of row start */
+ row = buffer + (png_c->rowstride * row_num);
+
+ /* Handle interlaced sprites using the Adam7 algorithm */
+ if (png_c->interlace) {
+ unsigned long dst_off;
+ unsigned long src_off = 0;
+ unsigned int start, step;
+
+ start = interlace_start[pass];
+ step = interlace_step[pass];
+ row_num = interlace_row_start[pass] +
+ interlace_row_step[pass] * row_num;
+
+ /* Copy the data to our current row taking interlacing
+ * into consideration */
+ row = buffer + (png_c->rowstride * row_num);
+
+ for (dst_off = start; dst_off < rowbytes; dst_off += step) {
+ row[dst_off++] = new_row[src_off++];
+ row[dst_off++] = new_row[src_off++];
+ row[dst_off++] = new_row[src_off++];
+ row[dst_off++] = new_row[src_off++];
+ }
+ } else {
+ /* Do a fast memcpy of the row data */
+ memcpy(row, new_row, rowbytes);
+ }
+}
+
+
+static void end_callback(png_structp png_s, png_infop info)
+{
+}
+
+static nserror nspng_create_png_data(nspng_content *png_c)
+{
+ union content_msg_data msg_data;
+
+ png_c->bitmap = NULL;
+
+ png_c->png = png_create_read_struct(PNG_LIBPNG_VER_STRING, 0, 0, 0);
+ if (png_c->png == NULL) {
+ msg_data.error = messages_get("NoMemory");
+ content_broadcast(&png_c->base, CONTENT_MSG_ERROR, msg_data);
+ return NSERROR_NOMEM;
+ }
+
+ png_set_error_fn(png_c->png, NULL, nspng_error, nspng_warning);
+
+ png_c->info = png_create_info_struct(png_c->png);
+ if (png_c->info == NULL) {
+ png_destroy_read_struct(&png_c->png, &png_c->info, 0);
+
+ msg_data.error = messages_get("NoMemory");
+ content_broadcast(&png_c->base, CONTENT_MSG_ERROR, msg_data);
+ return NSERROR_NOMEM;
+ }
+
+ if (setjmp(png_jmpbuf(png_c->png))) {
+ png_destroy_read_struct(&png_c->png, &png_c->info, 0);
+ LOG("Failed to set callbacks");
+ png_c->png = NULL;
+ png_c->info = NULL;
+
+ msg_data.error = messages_get("PNGError");
+ content_broadcast(&png_c->base, CONTENT_MSG_ERROR, msg_data);
+ return NSERROR_NOMEM;
+ }
+
+ png_set_progressive_read_fn(png_c->png, png_c,
+ info_callback, row_callback, end_callback);
+
+ return NSERROR_OK;
+}
+
+static nserror nspng_create(const content_handler *handler,
+ lwc_string *imime_type, const struct http_parameter *params,
+ llcache_handle *llcache, const char *fallback_charset,
+ bool quirks, struct content **c)
+{
+ nspng_content *png_c;
+ nserror error;
+
+ png_c = calloc(1, sizeof(nspng_content));
+ if (png_c == NULL)
+ return NSERROR_NOMEM;
+
+ error = content__init(&png_c->base,
+ handler,
+ imime_type,
+ params,
+ llcache,
+ fallback_charset,
+ quirks);
+ if (error != NSERROR_OK) {
+ free(png_c);
+ return error;
+ }
+
+ error = nspng_create_png_data(png_c);
+ if (error != NSERROR_OK) {
+ free(png_c);
+ return error;
+ }
+
+ *c = (struct content *)png_c;
+
+ return NSERROR_OK;
+}
+
+
+static bool nspng_process_data(struct content *c, const char *data,
+ unsigned int size)
+{
+ nspng_content *png_c = (nspng_content *)c;
+ union content_msg_data msg_data;
+ volatile bool ret = true;
+
+ if (png_c->no_process_data) {
+ return ret;
+ }
+
+ switch (setjmp(png_jmpbuf(png_c->png))) {
+ case CBERR_NONE: /* direct return */
+ png_process_data(png_c->png, png_c->info, (uint8_t *)data, size);
+ break;
+
+ case CBERR_NOPRE: /* not going to progressive convert */
+ png_c->no_process_data = true;
+ break;
+
+ default: /* fatal error from library processing png */
+ if (png_c->bitmap != NULL) {
+ /* A bitmap managed to get created so
+ * operation is past header and possibly some
+ * conversion happened before faliure.
+ *
+ * In this case keep the partial
+ * conversion. This is usually seen if a png
+ * has been truncated (often jsut lost its
+ * last byte and hence end of image marker)
+ */
+ png_c->no_process_data = true;
+ } else {
+ /* not managed to progress past header, clean
+ * up png conversion and signal the content
+ * error
+ */
+ LOG("Fatal PNG error during header, error content");
+
+ png_destroy_read_struct(&png_c->png, &png_c->info, 0);
+ png_c->png = NULL;
+ png_c->info = NULL;
+
+ msg_data.error = messages_get("PNGError");
+ content_broadcast(c, CONTENT_MSG_ERROR, msg_data);
+
+ ret = false;
+
+ }
+ break;
+ }
+
+ return ret;
+}
+
+struct png_cache_read_data_s {
+ const char *data;
+ unsigned long size;
+};
+
+/** PNG library read fucntion to read data from a memory array
+ */
+static void
+png_cache_read_fn(png_structp png_ptr, png_bytep data, png_size_t length)
+{
+ struct png_cache_read_data_s *png_cache_read_data;
+ png_cache_read_data = png_get_io_ptr(png_ptr);
+
+ if (length > png_cache_read_data->size) {
+ length = png_cache_read_data->size;
+ }
+
+ if (length == 0) {
+ png_error(png_ptr, "Read Error");
+ }
+
+ memcpy(data, png_cache_read_data->data, length);
+
+ png_cache_read_data->data += length;
+ png_cache_read_data->size -= length;
+}
+
+/** calculate an array of row pointers into a bitmap data area
+ */
+static png_bytep *calc_row_pointers(struct bitmap *bitmap)
+{
+ int height = guit->bitmap->get_height(bitmap);
+ unsigned char *buffer= guit->bitmap->get_buffer(bitmap);
+ size_t rowstride = guit->bitmap->get_rowstride(bitmap);
+ png_bytep *row_ptrs;
+ int hloop;
+
+ /* The buffer allocation may occour when the buffer is aquired
+ * and therefore may fail.
+ */
+ if (buffer == NULL) {
+ return NULL;
+ }
+
+ row_ptrs = malloc(sizeof(png_bytep) * height);
+
+ if (row_ptrs != NULL) {
+ for (hloop = 0; hloop < height; hloop++) {
+ row_ptrs[hloop] = buffer + (rowstride * hloop);
+ }
+ }
+
+ return row_ptrs;
+}
+
+/** PNG content to bitmap conversion.
+ *
+ * This routine generates a bitmap object from a PNG image content
+ */
+static struct bitmap *
+png_cache_convert(struct content *c)
+{
+ png_structp png_ptr;
+ png_infop info_ptr;
+ png_infop end_info_ptr;
+ volatile struct bitmap * volatile bitmap = NULL;
+ struct png_cache_read_data_s png_cache_read_data;
+ png_uint_32 width, height;
+ volatile png_bytep * volatile row_pointers = NULL;
+
+ png_cache_read_data.data =
+ content__get_source_data(c, &png_cache_read_data.size);
+
+ if ((png_cache_read_data.data == NULL) ||
+ (png_cache_read_data.size <= 8)) {
+ return NULL;
+ }
+
+ png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL,
+ nspng_error, nspng_warning);
+ if (png_ptr == NULL) {
+ return NULL;
+ }
+
+ info_ptr = png_create_info_struct(png_ptr);
+ if (info_ptr == NULL) {
+ png_destroy_read_struct(&png_ptr, NULL, NULL);
+ return NULL;
+ }
+
+ end_info_ptr = png_create_info_struct(png_ptr);
+ if (end_info_ptr == NULL) {
+ png_destroy_read_struct(&png_ptr, &info_ptr, NULL);
+ return NULL;
+ }
+
+ /* setup error exit path */
+ if (setjmp(png_jmpbuf(png_ptr))) {
+ /* cleanup and bail */
+ goto png_cache_convert_error;
+ }
+
+ /* read from a buffer instead of stdio */
+ png_set_read_fn(png_ptr, &png_cache_read_data, png_cache_read_fn);
+
+ /* ensure the png info structure is populated */
+ png_read_info(png_ptr, info_ptr);
+
+ /* setup output transforms */
+ nspng_setup_transforms(png_ptr, info_ptr);
+
+ width = png_get_image_width(png_ptr, info_ptr);
+ height = png_get_image_height(png_ptr, info_ptr);
+
+ /* Claim the required memory for the converted PNG */
+ bitmap = guit->bitmap->create(width, height, BITMAP_NEW);
+ if (bitmap == NULL) {
+ /* cleanup and bail */
+ goto png_cache_convert_error;
+ }
+
+ row_pointers = calc_row_pointers((struct bitmap *) bitmap);
+
+ if (row_pointers != NULL) {
+ png_read_image(png_ptr, (png_bytep *) row_pointers);
+ } else {
+ guit->bitmap->destroy((struct bitmap *)bitmap);
+ bitmap = NULL;
+ }
+
+png_cache_convert_error:
+
+ /* cleanup png read */
+ png_destroy_read_struct(&png_ptr, &info_ptr, &end_info_ptr);
+
+ if (row_pointers != NULL) {
+ free((png_bytep *) row_pointers);
+ }
+
+ if (bitmap != NULL) {
+ guit->bitmap->modified((struct bitmap *)bitmap);
+ }
+
+ return (struct bitmap *)bitmap;
+}
+
+static bool nspng_convert(struct content *c)
+{
+ nspng_content *png_c = (nspng_content *) c;
+ char *title;
+
+ assert(png_c->png != NULL);
+ assert(png_c->info != NULL);
+
+ /* clean up png structures */
+ png_destroy_read_struct(&png_c->png, &png_c->info, 0);
+
+ /* set title text */
+ title = messages_get_buff("PNGTitle",
+ nsurl_access_leaf(llcache_handle_get_url(c->llcache)),
+ c->width, c->height);
+ if (title != NULL) {
+ content__set_title(c, title);
+ free(title);
+ }
+
+ if (png_c->bitmap != NULL) {
+ guit->bitmap->set_opaque(png_c->bitmap, guit->bitmap->test_opaque(png_c->bitmap));
+ guit->bitmap->modified(png_c->bitmap);
+ }
+
+ image_cache_add(c, png_c->bitmap, png_cache_convert);
+
+ content_set_ready(c);
+ content_set_done(c);
+ content_set_status(c, "");
+
+ return true;
+}
+
+
+static nserror nspng_clone(const struct content *old_c, struct content **new_c)
+{
+ nspng_content *clone_png_c;
+ nserror error;
+ const char *data;
+ unsigned long size;
+
+ clone_png_c = calloc(1, sizeof(nspng_content));
+ if (clone_png_c == NULL)
+ return NSERROR_NOMEM;
+
+ error = content__clone(old_c, &clone_png_c->base);
+ if (error != NSERROR_OK) {
+ content_destroy(&clone_png_c->base);
+ return error;
+ }
+
+ /* Simply replay create/process/convert */
+ error = nspng_create_png_data(clone_png_c);
+ if (error != NSERROR_OK) {
+ content_destroy(&clone_png_c->base);
+ return error;
+ }
+
+ data = content__get_source_data(&clone_png_c->base, &size);
+ if (size > 0) {
+ if (nspng_process_data(&clone_png_c->base, data, size) == false) {
+ content_destroy(&clone_png_c->base);
+ return NSERROR_NOMEM;
+ }
+ }
+
+ if ((old_c->status == CONTENT_STATUS_READY) ||
+ (old_c->status == CONTENT_STATUS_DONE)) {
+ if (nspng_convert(&clone_png_c->base) == false) {
+ content_destroy(&clone_png_c->base);
+ return NSERROR_CLONE_FAILED;
+ }
+ }
+
+ *new_c = (struct content *)clone_png_c;
+
+ return NSERROR_OK;
+}
+
+static const content_handler nspng_content_handler = {
+ .create = nspng_create,
+ .process_data = nspng_process_data,
+ .data_complete = nspng_convert,
+ .clone = nspng_clone,
+ .destroy = image_cache_destroy,
+ .redraw = image_cache_redraw,
+ .get_internal = image_cache_get_internal,
+ .type = image_cache_content_type,
+ .no_share = false,
+};
+
+static const char *nspng_types[] = {
+ "image/png",
+ "image/x-png"
+};
+
+CONTENT_FACTORY_REGISTER_TYPES(nspng, nspng_types, nspng_content_handler);
diff --git a/content/handlers/image/png.h b/content/handlers/image/png.h
new file mode 100644
index 000000000..3a2e3741f
--- /dev/null
+++ b/content/handlers/image/png.h
@@ -0,0 +1,25 @@
+/*
+ * Copyright 2003 James Bursa <bursa@users.sourceforge.net>
+ * Copyright 2008 Daniel Silverstone <dsilvers@netsurf-browser.org>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * NetSurf is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef _NETSURF_RISCOS_PNG_H_
+#define _NETSURF_RISCOS_PNG_H_
+
+nserror nspng_init(void);
+
+#endif
diff --git a/content/handlers/image/rsvg.c b/content/handlers/image/rsvg.c
new file mode 100644
index 000000000..bd773682f
--- /dev/null
+++ b/content/handlers/image/rsvg.c
@@ -0,0 +1,326 @@
+/*
+ * Copyright 2007 Rob Kendrick <rjek@netsurf-browser.org>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * NetSurf is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+/** \file
+ * Content handler for image/svg using librsvg (implementation).
+ *
+ * SVG files are rendered to a NetSurf bitmap by creating a Cairo rendering
+ * surface (content_rsvg_data.cs) over the bitmap's data, creating a Cairo
+ * drawing context using that surface, and then passing that drawing context
+ * to librsvg which then uses Cairo calls to plot the graphic to the bitmap.
+ * We store this in content->bitmap, and then use the usual bitmap plotter
+ * function to render it for redraw requests.
+ */
+
+#include <stdlib.h>
+#include <stdbool.h>
+#include <assert.h>
+#include <string.h>
+#include <sys/types.h>
+
+#include <librsvg/rsvg.h>
+#ifndef RSVG_CAIRO_H
+#include <librsvg/rsvg-cairo.h>
+#endif
+
+#include "utils/log.h"
+#include "utils/utils.h"
+#include "utils/messages.h"
+#include "content/content_protected.h"
+#include "desktop/plotters.h"
+#include "desktop/gui_internal.h"
+
+#include "bitmap.h"
+#include "rsvg.h"
+
+typedef struct rsvg_content {
+ struct content base;
+
+ RsvgHandle *rsvgh; /**< Context handle for RSVG renderer */
+ cairo_surface_t *cs; /**< The surface built inside a nsbitmap */
+ cairo_t *ct; /**< Cairo drawing context */
+ struct bitmap *bitmap; /**< Created NetSurf bitmap */
+} rsvg_content;
+
+static nserror rsvg_create_svg_data(rsvg_content *c)
+{
+ union content_msg_data msg_data;
+
+ c->rsvgh = NULL;
+ c->cs = NULL;
+ c->ct = NULL;
+ c->bitmap = NULL;
+
+ if ((c->rsvgh = rsvg_handle_new()) == NULL) {
+ LOG("rsvg_handle_new() returned NULL.");
+ msg_data.error = messages_get("NoMemory");
+ content_broadcast(&c->base, CONTENT_MSG_ERROR, msg_data);
+ return NSERROR_NOMEM;
+ }
+
+ return NSERROR_OK;
+}
+
+
+static nserror rsvg_create(const content_handler *handler,
+ lwc_string *imime_type, const struct http_parameter *params,
+ llcache_handle *llcache, const char *fallback_charset,
+ bool quirks, struct content **c)
+{
+ rsvg_content *svg;
+ nserror error;
+
+ svg = calloc(1, sizeof(rsvg_content));
+ if (svg == NULL)
+ return NSERROR_NOMEM;
+
+ error = content__init(&svg->base, handler, imime_type, params,
+ llcache, fallback_charset, quirks);
+ if (error != NSERROR_OK) {
+ free(svg);
+ return error;
+ }
+
+ error = rsvg_create_svg_data(svg);
+ if (error != NSERROR_OK) {
+ free(svg);
+ return error;
+ }
+
+ *c = (struct content *) svg;
+
+ return NSERROR_OK;
+}
+
+
+static bool rsvg_process_data(struct content *c, const char *data,
+ unsigned int size)
+{
+ rsvg_content *d = (rsvg_content *) c;
+ union content_msg_data msg_data;
+ GError *err = NULL;
+
+ if (rsvg_handle_write(d->rsvgh, (const guchar *)data, (gsize)size,
+ &err) == FALSE) {
+ LOG("rsvg_handle_write returned an error: %s", err->message);
+ msg_data.error = err->message;
+ content_broadcast(c, CONTENT_MSG_ERROR, msg_data);
+ return false;
+ }
+
+ return true;
+}
+
+/** Convert Cairo's ARGB output to NetSurf's favoured ABGR format. It converts
+ * the data in-place.
+ *
+ * \param pixels Pixel data, in the form of ARGB. This will
+ * be overwritten with new data in the form of ABGR.
+ * \param width Width of the bitmap
+ * \param height Height of the bitmap
+ * \param rowstride Number of bytes to skip after each row (this
+ * implementation requires this to be a multiple of 4.)
+ */
+static inline void rsvg_argb_to_abgr(uint8_t *pixels,
+ int width, int height, size_t rowstride)
+{
+ uint8_t *p = pixels;
+
+ for (int y = 0; y < height; y++) {
+ for (int x = 0; x < width; x++) {
+ /* Swap R and B */
+ const uint8_t r = p[x+3];
+
+ p[x+3] = p[x];
+
+ p[x] = r;
+ }
+
+ p += rowstride;
+ }
+}
+
+static bool rsvg_convert(struct content *c)
+{
+ rsvg_content *d = (rsvg_content *) c;
+ union content_msg_data msg_data;
+ RsvgDimensionData rsvgsize;
+ GError *err = NULL;
+
+ if (rsvg_handle_close(d->rsvgh, &err) == FALSE) {
+ LOG("rsvg_handle_close returned an error: %s", err->message);
+ msg_data.error = err->message;
+ content_broadcast(c, CONTENT_MSG_ERROR, msg_data);
+ return false;
+ }
+
+ assert(err == NULL);
+
+ /* we should now be able to query librsvg for the natural size of the
+ * graphic, so we can create our bitmap.
+ */
+
+ rsvg_handle_get_dimensions(d->rsvgh, &rsvgsize);
+ c->width = rsvgsize.width;
+ c->height = rsvgsize.height;
+
+ if ((d->bitmap = guit->bitmap->create(c->width, c->height,
+ BITMAP_NEW)) == NULL) {
+ LOG("Failed to create bitmap for rsvg render.");
+ msg_data.error = messages_get("NoMemory");
+ content_broadcast(c, CONTENT_MSG_ERROR, msg_data);
+ return false;
+ }
+
+ if ((d->cs = cairo_image_surface_create_for_data(
+ (unsigned char *)guit->bitmap->get_buffer(d->bitmap),
+ CAIRO_FORMAT_ARGB32,
+ c->width, c->height,
+ guit->bitmap->get_rowstride(d->bitmap))) == NULL) {
+ LOG("Failed to create Cairo image surface for rsvg render.");
+ msg_data.error = messages_get("NoMemory");
+ content_broadcast(c, CONTENT_MSG_ERROR, msg_data);
+ return false;
+ }
+
+ if ((d->ct = cairo_create(d->cs)) == NULL) {
+ LOG("Failed to create Cairo drawing context for rsvg render.");
+ msg_data.error = messages_get("NoMemory");
+ content_broadcast(c, CONTENT_MSG_ERROR, msg_data);
+ return false;
+ }
+
+ rsvg_handle_render_cairo(d->rsvgh, d->ct);
+ rsvg_argb_to_abgr(guit->bitmap->get_buffer(d->bitmap),
+ c->width, c->height,
+ guit->bitmap->get_rowstride(d->bitmap));
+
+ guit->bitmap->modified(d->bitmap);
+ content_set_ready(c);
+ content_set_done(c);
+ /* Done: update status bar */
+ content_set_status(c, "");
+
+ return true;
+}
+
+static bool rsvg_redraw(struct content *c, struct content_redraw_data *data,
+ const struct rect *clip, const struct redraw_context *ctx)
+{
+ rsvg_content *rsvgcontent = (rsvg_content *) c;
+ bitmap_flags_t flags = BITMAPF_NONE;
+
+ assert(rsvgcontent->bitmap != NULL);
+
+ if (data->repeat_x)
+ flags |= BITMAPF_REPEAT_X;
+ if (data->repeat_y)
+ flags |= BITMAPF_REPEAT_Y;
+
+ return ctx->plot->bitmap(data->x, data->y, data->width, data->height,
+ rsvgcontent->bitmap, data->background_colour, flags);
+}
+
+static void rsvg_destroy(struct content *c)
+{
+ rsvg_content *d = (rsvg_content *) c;
+
+ if (d->bitmap != NULL) guit->bitmap->destroy(d->bitmap);
+ if (d->rsvgh != NULL) g_object_unref(d->rsvgh);
+ if (d->ct != NULL) cairo_destroy(d->ct);
+ if (d->cs != NULL) cairo_surface_destroy(d->cs);
+
+ return;
+}
+
+static nserror rsvg_clone(const struct content *old, struct content **newc)
+{
+ rsvg_content *svg;
+ nserror error;
+ const char *data;
+ unsigned long size;
+
+ svg = calloc(1, sizeof(rsvg_content));
+ if (svg == NULL)
+ return NSERROR_NOMEM;
+
+ error = content__clone(old, &svg->base);
+ if (error != NSERROR_OK) {
+ content_destroy(&svg->base);
+ return error;
+ }
+
+ /* Simply replay create/process/convert */
+ error = rsvg_create_svg_data(svg);
+ if (error != NSERROR_OK) {
+ content_destroy(&svg->base);
+ return error;
+ }
+
+ data = content__get_source_data(&svg->base, &size);
+ if (size > 0) {
+ if (rsvg_process_data(&svg->base, data, size) == false) {
+ content_destroy(&svg->base);
+ return NSERROR_NOMEM;
+ }
+ }
+
+ if (old->status == CONTENT_STATUS_READY ||
+ old->status == CONTENT_STATUS_DONE) {
+ if (rsvg_convert(&svg->base) == false) {
+ content_destroy(&svg->base);
+ return NSERROR_CLONE_FAILED;
+ }
+ }
+
+ *newc = (struct content *) svg;
+
+ return NSERROR_OK;
+}
+
+static void *rsvg_get_internal(const struct content *c, void *context)
+{
+ rsvg_content *d = (rsvg_content *) c;
+
+ return d->bitmap;
+}
+
+static content_type rsvg_content_type(void)
+{
+ return CONTENT_IMAGE;
+}
+
+static const content_handler rsvg_content_handler = {
+ .create = rsvg_create,
+ .process_data = rsvg_process_data,
+ .data_complete = rsvg_convert,
+ .destroy = rsvg_destroy,
+ .redraw = rsvg_redraw,
+ .clone = rsvg_clone,
+ .get_internal = rsvg_get_internal,
+ .type = rsvg_content_type,
+ .no_share = false,
+};
+
+static const char *rsvg_types[] = {
+ "image/svg",
+ "image/svg+xml"
+};
+
+CONTENT_FACTORY_REGISTER_TYPES(nsrsvg, rsvg_types, rsvg_content_handler);
+
diff --git a/content/handlers/image/rsvg.h b/content/handlers/image/rsvg.h
new file mode 100644
index 000000000..f38f8b0e8
--- /dev/null
+++ b/content/handlers/image/rsvg.h
@@ -0,0 +1,28 @@
+/*
+ * Copyright 2007 Rob Kendrick <rjek@netsurf-browser.org>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * NetSurf is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+/** \file
+ * Content handler for image/svg using librsvg (interface).
+ */
+
+#ifndef _NETSURF_IMAGE_RSVG_H_
+#define _NETSURF_IMAGE_RSVG_H_
+
+nserror nsrsvg_init(void);
+
+#endif
diff --git a/content/handlers/image/svg.c b/content/handlers/image/svg.c
new file mode 100644
index 000000000..c91b00650
--- /dev/null
+++ b/content/handlers/image/svg.c
@@ -0,0 +1,353 @@
+/*
+ * Copyright 2007-2008 James Bursa <bursa@users.sourceforge.net>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * NetSurf is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+/** \file
+ * Content for image/svg (implementation).
+ */
+
+#include <assert.h>
+#include <limits.h>
+#include <string.h>
+
+#include <svgtiny.h>
+
+#include "utils/messages.h"
+#include "utils/utils.h"
+#include "content/content_protected.h"
+#include "css/css.h"
+#include "desktop/plotters.h"
+
+#include "svg.h"
+
+typedef struct svg_content {
+ struct content base;
+
+ struct svgtiny_diagram *diagram;
+
+ int current_width;
+ int current_height;
+} svg_content;
+
+
+
+static nserror svg_create_svg_data(svg_content *c)
+{
+ union content_msg_data msg_data;
+
+ c->diagram = svgtiny_create();
+ if (c->diagram == NULL)
+ goto no_memory;
+
+ c->current_width = INT_MAX;
+ c->current_height = INT_MAX;
+
+ return NSERROR_OK;
+
+no_memory:
+ msg_data.error = messages_get("NoMemory");
+ content_broadcast(&c->base, CONTENT_MSG_ERROR, msg_data);
+ return NSERROR_NOMEM;
+}
+
+
+/**
+ * Create a CONTENT_SVG.
+ */
+
+static nserror svg_create(const content_handler *handler,
+ lwc_string *imime_type, const struct http_parameter *params,
+ llcache_handle *llcache, const char *fallback_charset,
+ bool quirks, struct content **c)
+{
+ svg_content *svg;
+ nserror error;
+
+ svg = calloc(1, sizeof(svg_content));
+ if (svg == NULL)
+ return NSERROR_NOMEM;
+
+ error = content__init(&svg->base, handler, imime_type, params,
+ llcache, fallback_charset, quirks);
+ if (error != NSERROR_OK) {
+ free(svg);
+ return error;
+ }
+
+ error = svg_create_svg_data(svg);
+ if (error != NSERROR_OK) {
+ free(svg);
+ return error;
+ }
+
+ *c = (struct content *) svg;
+
+ return NSERROR_OK;
+}
+
+
+
+/**
+ * Convert a CONTENT_SVG for display.
+ */
+
+static bool svg_convert(struct content *c)
+{
+ /*c->title = malloc(100);
+ if (c->title)
+ snprintf(c->title, 100, messages_get("svgTitle"),
+ width, height, c->source_size);*/
+ //c->size += ?;
+ content_set_ready(c);
+ content_set_done(c);
+ /* Done: update status bar */
+ content_set_status(c, "");
+
+ return true;
+}
+
+/**
+ * Reformat a CONTENT_SVG.
+ */
+
+static void svg_reformat(struct content *c, int width, int height)
+{
+ svg_content *svg = (svg_content *) c;
+ const char *source_data;
+ unsigned long source_size;
+
+ assert(svg->diagram);
+
+ /* Avoid reformats to same width/height as we already reformatted to */
+ if (width != svg->current_width || height != svg->current_height) {
+ source_data = content__get_source_data(c, &source_size);
+
+ svgtiny_parse(svg->diagram, source_data, source_size,
+ nsurl_access(content_get_url(c)),
+ width, height);
+
+ svg->current_width = width;
+ svg->current_height = height;
+ }
+
+ c->width = svg->diagram->width;
+ c->height = svg->diagram->height;
+}
+
+
+/**
+ * Redraw a CONTENT_SVG.
+ */
+
+static bool svg_redraw_internal(struct content *c, int x, int y,
+ int width, int height, const struct rect *clip,
+ const struct redraw_context *ctx, float scale,
+ colour background_colour)
+{
+ svg_content *svg = (svg_content *) c;
+ float transform[6];
+ struct svgtiny_diagram *diagram = svg->diagram;
+ bool ok;
+ int px, py;
+ unsigned int i;
+ plot_font_style_t fstyle = *plot_style_font;
+
+ assert(diagram);
+
+ transform[0] = (float) width / (float) c->width;
+ transform[1] = 0;
+ transform[2] = 0;
+ transform[3] = (float) height / (float) c->height;
+ transform[4] = x;
+ transform[5] = y;
+
+#define BGR(c) ((c) == svgtiny_TRANSPARENT ? NS_TRANSPARENT : \
+ ((svgtiny_RED((c))) | \
+ (svgtiny_GREEN((c)) << 8) | \
+ (svgtiny_BLUE((c)) << 16)))
+
+ for (i = 0; i != diagram->shape_count; i++) {
+ if (diagram->shape[i].path) {
+ ok = ctx->plot->path(diagram->shape[i].path,
+ diagram->shape[i].path_length,
+ BGR(diagram->shape[i].fill),
+ diagram->shape[i].stroke_width,
+ BGR(diagram->shape[i].stroke),
+ transform);
+ if (!ok)
+ return false;
+
+ } else if (diagram->shape[i].text) {
+ px = transform[0] * diagram->shape[i].text_x +
+ transform[2] * diagram->shape[i].text_y +
+ transform[4];
+ py = transform[1] * diagram->shape[i].text_x +
+ transform[3] * diagram->shape[i].text_y +
+ transform[5];
+
+ fstyle.background = 0xffffff;
+ fstyle.foreground = 0x000000;
+ fstyle.size = (8 * FONT_SIZE_SCALE) * scale;
+
+ ok = ctx->plot->text(px, py,
+ diagram->shape[i].text,
+ strlen(diagram->shape[i].text),
+ &fstyle);
+ if (!ok)
+ return false;
+ }
+ }
+
+#undef BGR
+
+ return true;
+}
+
+
+/**
+ * Redraw a CONTENT_SVG.
+ */
+
+static bool svg_redraw(struct content *c, struct content_redraw_data *data,
+ const struct rect *clip, const struct redraw_context *ctx)
+{
+ int x = data->x;
+ int y = data->y;
+
+ if ((data->width <= 0) && (data->height <= 0)) {
+ /* No point trying to plot SVG if it does not occupy a valid
+ * area */
+ return true;
+ }
+
+ if ((data->repeat_x == false) && (data->repeat_y == false)) {
+ /* Simple case: SVG is not tiled */
+ return svg_redraw_internal(c, x, y,
+ data->width, data->height,
+ clip, ctx, data->scale,
+ data->background_colour);
+ } else {
+ /* Tiled redraw required. SVG repeats to extents of clip
+ * rectangle, in x, y or both directions */
+ int x0, y0, x1, y1;
+
+ /* Find the redraw boundaries to loop within */
+ x0 = x;
+ if (data->repeat_x) {
+ for (; x0 > clip->x0; x0 -= data->width);
+ x1 = clip->x1;
+ } else {
+ x1 = x + 1;
+ }
+ y0 = y;
+ if (data->repeat_y) {
+ for (; y0 > clip->y0; y0 -= data->height);
+ y1 = clip->y1;
+ } else {
+ y1 = y + 1;
+ }
+
+ /* Repeatedly plot the SVG across the area */
+ for (y = y0; y < y1; y += data->height) {
+ for (x = x0; x < x1; x += data->width) {
+ if (!svg_redraw_internal(c, x, y,
+ data->width, data->height,
+ clip, ctx, data->scale,
+ data->background_colour)) {
+ return false;
+ }
+ }
+ }
+ }
+
+ return true;
+}
+
+
+/**
+ * Destroy a CONTENT_SVG and free all resources it owns.
+ */
+
+static void svg_destroy(struct content *c)
+{
+ svg_content *svg = (svg_content *) c;
+
+ if (svg->diagram != NULL)
+ svgtiny_free(svg->diagram);
+}
+
+
+static nserror svg_clone(const struct content *old, struct content **newc)
+{
+ svg_content *svg;
+ nserror error;
+
+ svg = calloc(1, sizeof(svg_content));
+ if (svg == NULL)
+ return NSERROR_NOMEM;
+
+ error = content__clone(old, &svg->base);
+ if (error != NSERROR_OK) {
+ content_destroy(&svg->base);
+ return error;
+ }
+
+ /* Simply replay create/convert */
+ error = svg_create_svg_data(svg);
+ if (error != NSERROR_OK) {
+ content_destroy(&svg->base);
+ return error;
+ }
+
+ if (old->status == CONTENT_STATUS_READY ||
+ old->status == CONTENT_STATUS_DONE) {
+ if (svg_convert(&svg->base) == false) {
+ content_destroy(&svg->base);
+ return NSERROR_CLONE_FAILED;
+ }
+ }
+
+ *newc = (struct content *) svg;
+
+ return NSERROR_OK;
+}
+
+static content_type svg_content_type(void)
+{
+ return CONTENT_IMAGE;
+}
+
+static const content_handler svg_content_handler = {
+ .create = svg_create,
+ .data_complete = svg_convert,
+ .reformat = svg_reformat,
+ .destroy = svg_destroy,
+ .redraw = svg_redraw,
+ .clone = svg_clone,
+ .type = svg_content_type,
+ .no_share = true
+};
+
+static const char *svg_types[] = {
+ "image/svg",
+ "image/svg+xml"
+};
+
+
+CONTENT_FACTORY_REGISTER_TYPES(svg, svg_types, svg_content_handler);
+
+
diff --git a/content/handlers/image/svg.h b/content/handlers/image/svg.h
new file mode 100644
index 000000000..96b9c3879
--- /dev/null
+++ b/content/handlers/image/svg.h
@@ -0,0 +1,28 @@
+/*
+ * Copyright 2007-2008 James Bursa <bursa@users.sourceforge.net>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * NetSurf is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+/** \file
+ * Content for image/svg (interface).
+ */
+
+#ifndef _NETSURF_IMAGE_SVG_H_
+#define _NETSURF_IMAGE_SVG_H_
+
+nserror svg_init(void);
+
+#endif
diff --git a/content/handlers/image/video.c b/content/handlers/image/video.c
new file mode 100644
index 000000000..53b654337
--- /dev/null
+++ b/content/handlers/image/video.c
@@ -0,0 +1,202 @@
+/*
+ * Copyright 2011 John-Mark Bell <jmb@netsurf-browser.org>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * NetSurf is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <gst/gst.h>
+
+#include "content/content_factory.h"
+#include "content/content_protected.h"
+
+#include "video.h"
+
+typedef struct nsvideo_content {
+ struct content base;
+
+ GstElement *playbin;
+ GstElement *appsrc;
+} nsvideo_content;
+
+static gboolean nsvideo_bus_call(GstBus *bus, GstMessage *msg,
+ nsvideo_content *video)
+{
+ switch (GST_MESSAGE_TYPE(msg)) {
+ case GST_MESSAGE_ERROR:
+ break;
+ case GST_MESSAGE_EOS:
+ break;
+ default:
+ break;
+ }
+
+ return TRUE;
+}
+
+static void nsvideo_need_data_event(GstElement *playbin, guint size,
+ nsvideo_content *video)
+{
+}
+
+static void nsvideo_enough_data_event(GstElement *playbin,
+ nsvideo_content *video)
+{
+}
+
+static void nsvideo_source_event(GObject *object, GObject *orig,
+ GParamSpec *pspec, nsvideo_content *video)
+{
+ g_object_get(orig, pspec->name, &video->appsrc, NULL);
+
+ g_signal_connect(video->appsrc, "need-data",
+ G_CALLBACK(nsvideo_need_data_event), video);
+ g_signal_connect(video->appsrc, "enough-data",
+ G_CALLBACK(nsvideo_enough_data_event), video);
+}
+
+static nserror nsvideo_create(const content_handler *handler,
+ lwc_string *imime_type, const http_parameter *params,
+ llcache_handle *llcache,
+ const char *fallback_charset, bool quirks,
+ struct content **c)
+{
+ nsvideo_content *video;
+ nserror error;
+ GstBus *bus;
+
+ video = calloc(1, sizeof(nsvideo_content));
+ if (video == NULL)
+ return NSERROR_NOMEM;
+
+ error = content__init(&video->base, handler, imime_type, params,
+ llcache, fallback_charset, quirks);
+ if (error != NSERROR_OK) {
+ free(video);
+ return error;
+ }
+
+ error = llcache_handle_force_stream(llcache);
+ if (error != NSERROR_OK) {
+ free(video);
+ return error;
+ }
+
+ video->playbin = gst_element_factory_make("playbin2", NULL);
+ if (video->playbin == NULL) {
+ free(video);
+ return NSERROR_NOMEM;
+ }
+
+ bus = gst_pipeline_get_bus(GST_PIPELINE(video->playbin));
+ gst_bus_add_watch(bus, (GstBusFunc) nsvideo_bus_call, video);
+ gst_object_unref(bus);
+
+ g_object_set(video->playbin, "uri", "appsrc://", NULL);
+ g_signal_connect(video->playbin, "deep-notify::source",
+ G_CALLBACK(nsvideo_source_event), video);
+
+ /** \todo Create appsink & register with playbin */
+
+ gst_element_set_state(video->playbin, GST_STATE_PLAYING);
+
+ *c = (struct content *) video;
+
+ return NSERROR_OK;
+}
+
+static bool nsvideo_process_data(struct content *c, const char *data,
+ unsigned int size)
+{
+ nsvideo_content *video = (nsvideo_content *) c;
+ GstBuffer *buffer;
+ GstFlowReturn ret;
+
+ buffer = gst_buffer_new();
+ GST_BUFFER_DATA(buffer) = (guint8 *) data;
+ GST_BUFFER_SIZE(buffer) = (gsize) size;
+
+ /* Send data to appsrc */
+ g_signal_emit_by_name(video->appsrc, "push-buffer", buffer, &ret);
+
+ return ret == GST_FLOW_OK;
+}
+
+static bool nsvideo_convert(struct content *c)
+{
+ nsvideo_content *video = (nsvideo_content *) c;
+ GstFlowReturn ret;
+
+ /* Tell appsrc we're done */
+ g_signal_emit_by_name(video->appsrc, "end-of-stream", &ret);
+
+ /* Appsink will flag DONE on receipt of first frame */
+
+ return ret == GST_FLOW_OK;
+}
+
+static void nsvideo_destroy(struct content *c)
+{
+ nsvideo_content *video = (nsvideo_content *) c;
+
+ gst_element_set_state(video->playbin, GST_STATE_NULL);
+ gst_object_unref(video->playbin);
+}
+
+static bool nsvideo_redraw(struct content *c, struct content_redraw_data *data,
+ const struct rect *clip, const struct redraw_context *ctx)
+{
+ /** \todo Implement */
+ return true;
+}
+
+static nserror nsvideo_clone(const struct content *old, struct content **newc)
+{
+ /** \todo Implement */
+ return NSERROR_CLONE_FAILED;
+}
+
+static content_type nsvideo_type(void)
+{
+ /** \todo Lies */
+ return CONTENT_IMAGE;
+}
+
+static void *nsvideo_get_internal(const struct content *c, void *context)
+{
+ /** \todo Return pointer to bitmap containing current frame, if any? */
+ return NULL;
+}
+
+static const content_handler nsvideo_content_handler = {
+ .create = nsvideo_create,
+ .process_data = nsvideo_process_data,
+ .data_complete = nsvideo_convert,
+ .destroy = nsvideo_destroy,
+ .redraw = nsvideo_redraw,
+ .clone = nsvideo_clone,
+ .type = nsvideo_type,
+ .get_internal = nsvideo_get_internal,
+ /* Can't share videos because we stream them */
+ .no_share = true
+};
+
+static const char *nsvideo_types[] = {
+ "video/mp4",
+ "video/webm"
+};
+
+CONTENT_FACTORY_REGISTER_TYPES(nsvideo, nsvideo_types,
+ nsvideo_content_handler);
+
diff --git a/content/handlers/image/video.h b/content/handlers/image/video.h
new file mode 100644
index 000000000..376c837ba
--- /dev/null
+++ b/content/handlers/image/video.h
@@ -0,0 +1,26 @@
+/*
+ * Copyright 2011 John-Mark Bell <jmb@netsurf-browser.org>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * NetSurf is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef NETSURF_IMAGE_VIDEO_H_
+#define NETSURF_IMAGE_VIDEO_H_
+
+#include "utils/errors.h"
+
+nserror nsvideo_init(void);
+
+#endif
diff --git a/content/urldb.c b/content/urldb.c
index 0b59e1f4d..b11ec987e 100644
--- a/content/urldb.c
+++ b/content/urldb.c
@@ -104,7 +104,7 @@
#include "utils/utils.h"
#include "utils/bloom.h"
#include "utils/time.h"
-#include "image/bitmap.h"
+#include "content/handlers/image/bitmap.h"
#include "desktop/cookie_manager.h"
#include "desktop/gui_internal.h"