summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Makefile.sources2
-rw-r--r--image/gif.c1
-rw-r--r--image/image.c9
-rw-r--r--image/image_cache.c471
-rw-r--r--image/image_cache.h101
-rw-r--r--image/jpeg.c224
-rw-r--r--image/png.c373
7 files changed, 970 insertions, 211 deletions
diff --git a/Makefile.sources b/Makefile.sources
index 8689e8961..6dcf106b6 100644
--- a/Makefile.sources
+++ b/Makefile.sources
@@ -37,7 +37,7 @@ S_COMMON := $(addprefix content/,$(S_CONTENT)) \
$(addprefix desktop/,$(S_DESKTOP))
# S_IMAGE are sources related to image management
-S_IMAGE_YES := image.c
+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
diff --git a/image/gif.c b/image/gif.c
index aa05a8c81..dec9653b6 100644
--- a/image/gif.c
+++ b/image/gif.c
@@ -33,6 +33,7 @@
#include <stdbool.h>
#include <stdlib.h>
#include <libnsgif.h>
+
#include "utils/config.h"
#include "content/content_protected.h"
#include "content/hlcache.h"
diff --git a/image/image.c b/image/image.c
index 779826a17..fb8197b28 100644
--- a/image/image.c
+++ b/image/image.c
@@ -16,6 +16,7 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
+#include <assert.h>
#include <stdint.h>
#include <stdbool.h>
#include <string.h>
@@ -23,6 +24,7 @@
#include "utils/errors.h"
#include "image/image.h"
+#include "image/image_cache.h"
#include "image/bmp.h"
#include "image/gif.h"
#include "image/ico.h"
@@ -45,6 +47,10 @@ nserror image_init(void)
{
nserror error;
+ error = image_cache_init();
+ if (error != NSERROR_OK)
+ return error;
+
#ifdef WITH_BMP
error = nsbmp_init();
if (error != NSERROR_OK)
@@ -158,5 +164,8 @@ void image_fini(void)
#ifdef WITH_WEBP
webp_fini();
#endif
+
+ /* dump any remaining cache entries */
+ image_cache_fini();
}
diff --git a/image/image_cache.c b/image/image_cache.c
new file mode 100644
index 000000000..f70d8ddb2
--- /dev/null
+++ b/image/image_cache.c
@@ -0,0 +1,471 @@
+/*
+ * 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 <stdint.h>
+#include <stdbool.h>
+#include <string.h>
+
+#include "utils/errors.h"
+#include "utils/log.h"
+#include "utils/config.h"
+#include "utils/schedule.h"
+#include "content/content_protected.h"
+#include "image/image_cache.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*/
+};
+
+/** 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_age current_age; /** the "age" of the current operation */
+ struct image_cache_entry_s *entries; /* cache objects */
+
+ /* Statistics for replacement algorithm */
+
+ /** total size of bitmaps currently allocated */
+ size_t total_bitmap_size;
+
+ /** Max size of bitmaps allocated at any one time */
+ size_t max_bitmap_size;
+ int max_bitmap_size_count;
+
+ int bitmap_count;
+ int max_bitmap_count;
+ size_t max_bitmap_count_size;
+
+ int miss_count; /* bitmap was not available at plot time required conversion */
+ int specultive_miss_count; /* bitmap was available but never actually required conversion */
+ int hit_count; /* bitmap was available at plot time required no conversion */
+ int fail_count; /* bitmap was not available at plot time, required conversion which failed */
+};
+
+static struct image_cache_s *image_cache = NULL;
+
+/** low water mark for speculative pre-conversion */
+
+/* Experimenting by visiting every page from default page in order and
+ * then netsurf homepage
+ *
+ * 0 : Cache hit/miss/speculative miss/fail 604/147/ 0/0 (80%/19%/ 0%/ 0%)
+ * 2048 : Cache hit/miss/speculative miss/fail 622/119/ 17/0 (82%/15%/ 2%/ 0%)
+ * 4096 : Cache hit/miss/speculative miss/fail 656/109/ 25/0 (83%/13%/ 3%/ 0%)
+ * 8192 : Cache hit/miss/speculative miss/fail 648/104/ 40/0 (81%/13%/ 5%/ 0%)
+ * ALL : Cache hit/miss/speculative miss/fail 775/ 0/161/0 (82%/ 0%/17%/ 0%)
+*/
+#define SPECULATE_SMALL 4096
+
+/* the time between cache clean runs in ms */
+#define CACHE_CLEAN_TIME (10 * 1000)
+
+
+/** 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;
+
+ 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;
+ }
+}
+
+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) {
+ 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));
+
+ 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)
+{
+ LOG(("freeing %p ", centry));
+
+ image_cache__free_bitmap(centry);
+
+ image_cache__unlink(centry);
+
+ free(centry);
+}
+
+/* exported interface documented in image_cache.h */
+struct bitmap *image_cache_get_bitmap(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++;
+ } else {
+ image_cache->fail_count++;
+ }
+ } else {
+ image_cache->hit_count++;
+ }
+
+ return centry->bitmap;
+}
+
+/* exported interface documented in image_cache.h */
+bool image_cache_speculate(struct content *c)
+{
+ bool decision = false;
+
+ if (c->size <= SPECULATE_SMALL) {
+ LOG(("content size (%d) is smaller than minimum (%d)", c->size, SPECULATE_SMALL));
+ decision = true;
+ }
+
+ LOG(("returning %d", decision));
+ 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;
+}
+
+static void image_cache__clean(void *p)
+{
+ struct image_cache_s *icache = p;
+ struct image_cache_entry_s *centry = icache->entries;
+
+ /* increment current cache age */
+ icache->current_age += CACHE_CLEAN_TIME;
+
+ LOG(("Running cache clean at cache age %ds", icache->current_age / 1000));
+
+ LOG(("Brain dead cache cleaner removing all bitmaps not redraw in last %ds", CACHE_CLEAN_TIME / 1000));
+ while (centry != NULL) {
+ if ((icache->current_age - centry->redraw_age) > CACHE_CLEAN_TIME) {
+ image_cache__free_bitmap(centry);
+ }
+ centry=centry->next;
+ }
+
+ schedule((CACHE_CLEAN_TIME / 10), image_cache__clean, icache);
+}
+
+/* exported interface documented in image_cache.h */
+nserror image_cache_init(void)
+{
+ image_cache = calloc(1, sizeof(struct image_cache_s));
+
+ schedule((CACHE_CLEAN_TIME / 10), image_cache__clean, image_cache);
+
+ return NSERROR_OK;
+}
+
+/* exported interface documented in image_cache.h */
+nserror image_cache_fini(void)
+{
+ int op_count;
+
+ schedule_remove(image_cache__clean, image_cache);
+
+ op_count = image_cache->hit_count +
+ image_cache->miss_count +
+ image_cache->specultive_miss_count +
+ image_cache->fail_count;
+
+ LOG(("Destroying Remaining Image cache entries"));
+
+ while (image_cache->entries != NULL) {
+ image_cache__free_entry(image_cache->entries);
+ }
+
+ LOG(("Image cache size at finish %d (in %d)", image_cache->total_bitmap_size, image_cache->bitmap_count));
+ LOG(("Peak size %d (in %d)", image_cache->max_bitmap_size, image_cache->max_bitmap_size_count ));
+ LOG(("Peak image count %d (size %d)", image_cache->max_bitmap_count, image_cache->max_bitmap_count_size));
+ LOG(("Cache hit/miss/speculative miss/fail %d/%d/%d/%d (%d%%/%d%%/%d%%/%d%%)",
+ image_cache->hit_count,
+ image_cache->miss_count,
+ image_cache->specultive_miss_count,
+ image_cache->fail_count,
+ (image_cache->hit_count * 100) / op_count,
+ (image_cache->miss_count * 100) / op_count,
+ (image_cache->specultive_miss_count * 100) / op_count,
+ (image_cache->fail_count * 100) / op_count));
+ 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) {
+ 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 */
+bool image_cache_redraw(struct content *c,
+ struct content_redraw_data *data,
+ const struct rect *clip,
+ const struct redraw_context *ctx)
+{
+ bitmap_flags_t flags = BITMAPF_NONE;
+ 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 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++;
+ } else {
+ image_cache->fail_count++;
+ return false;
+ }
+ } else {
+ image_cache->hit_count++;
+ }
+
+
+ /* update statistics */
+ centry->redraw_count++;
+ centry->redraw_age = image_cache->current_age;
+
+ /* 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,
+ centry->bitmap, data->background_colour, flags);
+}
+
+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)
+{
+ struct image_cache_entry_s *centry;
+
+ centry = image_cache__find(c);
+ if (centry == NULL) {
+ return NULL;
+ }
+
+ return centry->bitmap;
+}
+
+content_type image_cache_content_type(void)
+{
+ return CONTENT_IMAGE;
+}
+
diff --git a/image/image_cache.h b/image/image_cache.h
new file mode 100644
index 000000000..c0ccfac90
--- /dev/null
+++ b/image/image_cache.h
@@ -0,0 +1,101 @@
+/*
+ * 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"
+#include "desktop/plotters.h"
+#include "image/bitmap.h"
+
+typedef struct bitmap * (image_cache_convert_fn) (struct content *content);
+
+/** Initialise the image cache */
+nserror image_cache_init(void);
+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(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 otehrwise.
+ */
+bool image_cache_speculate(struct content *c);
+
+/* 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/image/jpeg.c b/image/jpeg.c
index ec1be8eab..2c4610091 100644
--- a/image/jpeg.c
+++ b/image/jpeg.c
@@ -31,7 +31,7 @@
#include "content/content_protected.h"
#include "desktop/plotters.h"
-#include "image/bitmap.h"
+#include "image/image_cache.h"
#include "utils/log.h"
#include "utils/messages.h"
@@ -43,6 +43,11 @@
#include "jpeglib.h"
#include "image/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. */
@@ -54,12 +59,6 @@
static char nsjpeg_error_buffer[JMSG_LENGTH_MAX];
-typedef struct nsjpeg_content {
- struct content base; /**< base content */
-
- struct bitmap *bitmap; /**< Created NetSurf bitmap */
-} nsjpeg_content;
-
struct nsjpeg_error_mgr {
struct jpeg_error_mgr pub;
jmp_buf setjmp_buffer;
@@ -75,21 +74,21 @@ static nserror nsjpeg_create(const content_handler *handler,
llcache_handle *llcache, const char *fallback_charset,
bool quirks, struct content **c)
{
- nsjpeg_content *jpeg;
+ struct content *jpeg;
nserror error;
- jpeg = talloc_zero(0, nsjpeg_content);
+ jpeg = talloc_zero(0, struct content);
if (jpeg == NULL)
return NSERROR_NOMEM;
- error = content__init(&jpeg->base, handler, imime_type, params,
+ error = content__init(jpeg, handler, imime_type, params,
llcache, fallback_charset, quirks);
if (error != NSERROR_OK) {
talloc_free(jpeg);
return error;
}
- *c = (struct content *) jpeg;
+ *c = jpeg;
return NSERROR_OK;
}
@@ -164,83 +163,98 @@ static void nsjpeg_error_exit(j_common_ptr cinfo)
{
struct nsjpeg_error_mgr *err = (struct nsjpeg_error_mgr *) cinfo->err;
err->pub.format_message(cinfo, nsjpeg_error_buffer);
+ LOG(("%s", nsjpeg_error_buffer));
+
longjmp(err->setjmp_buffer, 1);
}
-
-/**
- * Convert a CONTENT_JPEG for display.
- */
-static bool nsjpeg_convert(struct content *c)
+static struct bitmap *
+jpeg_cache_convert(struct content *c)
{
- struct nsjpeg_content *jpeg_content = (nsjpeg_content *)c;
+ uint8_t *source_data; /* Jpeg source data */
+ unsigned long source_size; /* length of Jpeg source data */
struct jpeg_decompress_struct cinfo;
struct nsjpeg_error_mgr jerr;
- 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 };
unsigned int height;
unsigned int width;
struct bitmap * volatile bitmap = NULL;
uint8_t * volatile pixels = NULL;
size_t rowstride;
- union content_msg_data msg_data;
- const char *data;
- unsigned long size;
- char title[100];
+ 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 };
- data = content__get_source_data(c, &size);
+ /* 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.pub);
jerr.pub.error_exit = nsjpeg_error_exit;
jerr.pub.output_message = nsjpeg_error_log;
+
+ /* handler for fatal errors during decompression */
if (setjmp(jerr.setjmp_buffer)) {
jpeg_destroy_decompress(&cinfo);
- if (bitmap)
- bitmap_destroy(bitmap);
-
- msg_data.error = nsjpeg_error_buffer;
- content_broadcast(c, CONTENT_MSG_ERROR, msg_data);
- return false;
+ return bitmap;
}
+
jpeg_create_decompress(&cinfo);
- source_mgr.next_input_byte = (unsigned char *) data;
- source_mgr.bytes_in_buffer = size;
+
+ /* 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 = bitmap_create(width, height, BITMAP_NEW | BITMAP_OPAQUE);
- if (bitmap)
- pixels = bitmap_get_buffer(bitmap);
- if ((!bitmap) || (!pixels)) {
+ if (bitmap == NULL) {
+ /* empty bitmap could not be created */
jpeg_destroy_decompress(&cinfo);
- if (bitmap)
- bitmap_destroy(bitmap);
+ return NULL;
+ }
- msg_data.error = messages_get("NoMemory");
- content_broadcast(c, CONTENT_MSG_ERROR, msg_data);
- return false;
+ pixels = bitmap_get_buffer(bitmap);
+ if (pixels == NULL) {
+ /* bitmap with no buffer available */
+ bitmap_destroy(bitmap);
+ jpeg_destroy_decompress(&cinfo);
+ return NULL;
}
+ /* Convert scanlines from jpeg into bitmap */
rowstride = bitmap_get_rowstride(bitmap);
do {
+ JSAMPROW scanlines[1];
#if RGB_RED != 0 || RGB_GREEN != 1 || RGB_BLUE != 2 || RGB_PIXELSIZE != 4
int i;
-#endif
- 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
/* expand to RGBA */
for (i = width - 1; 0 <= i; i--) {
int r = scanlines[0][i * RGB_PIXELSIZE + RGB_RED];
@@ -251,6 +265,11 @@ static bool nsjpeg_convert(struct content *c)
scanlines[0][i * 4 + 2] = b;
scanlines[0][i * 4 + 3] = 0xff;
}
+#else
+ scanlines[0] = (JSAMPROW) (pixels +
+ rowstride * cinfo.output_scanline);
+ jpeg_read_scanlines(&cinfo, scanlines, 1);
+
#endif
} while (cinfo.output_scanline != cinfo.output_height);
bitmap_modified(bitmap);
@@ -258,50 +277,65 @@ static bool nsjpeg_convert(struct content *c)
jpeg_finish_decompress(&cinfo);
jpeg_destroy_decompress(&cinfo);
- c->width = width;
- c->height = height;
- jpeg_content->bitmap = bitmap;
- snprintf(title, sizeof(title), messages_get("JPEGTitle"),
- width, height, size);
- content__set_title(c, title);
- c->size += height * rowstride;
- content_set_ready(c);
- content_set_done(c);
- /* Done: update status bar */
- content_set_status(c, "");
- return true;
+ return bitmap;
}
-
/**
- * Destroy a CONTENT_JPEG and free all resources it owns.
+ * Convert a CONTENT_JPEG for display.
*/
-static void nsjpeg_destroy(struct content *c)
+static bool nsjpeg_convert(struct content *c)
{
- struct nsjpeg_content *jpeg_content = (nsjpeg_content *)c;
+ struct jpeg_decompress_struct cinfo;
+ struct nsjpeg_error_mgr jerr;
+ 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[100];
- if (jpeg_content->bitmap) {
- bitmap_destroy(jpeg_content->bitmap);
+ /* check image header is valid and get width/height */
+ data = content__get_source_data(c, &size);
+
+ cinfo.err = jpeg_std_error(&jerr.pub);
+ jerr.pub.error_exit = nsjpeg_error_exit;
+ jerr.pub.output_message = nsjpeg_error_log;
+ if (setjmp(jerr.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);
+ 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);
-/**
- * Redraw a CONTENT_JPEG with appropriate tiling.
- */
-static bool nsjpeg_redraw(struct content *c, struct content_redraw_data *data,
- const struct rect *clip, const struct redraw_context *ctx)
-{
- struct nsjpeg_content *jpeg_content = (nsjpeg_content *)c;
- bitmap_flags_t flags = BITMAPF_NONE;
+ c->width = cinfo.output_width;
+ c->height = cinfo.output_height;
+ c->size = c->width * c->height * 4;
+
+ jpeg_destroy_decompress(&cinfo);
- if (data->repeat_x)
- flags |= BITMAPF_REPEAT_X;
- if (data->repeat_y)
- flags |= BITMAPF_REPEAT_Y;
+ image_cache_add(c, NULL, jpeg_cache_convert);
- return ctx->plot->bitmap(data->x, data->y, data->width, data->height,
- jpeg_content->bitmap, data->background_colour, flags);
+ snprintf(title, sizeof(title), messages_get("JPEGTitle"),
+ c->width, c->height, size);
+ content__set_title(c, title);
+
+ content_set_ready(c);
+ content_set_done(c);
+ content_set_status(c, ""); /* Done: update status bar */
+
+ return true;
}
@@ -311,53 +345,41 @@ static bool nsjpeg_redraw(struct content *c, struct content_redraw_data *data,
*/
static nserror nsjpeg_clone(const struct content *old, struct content **newc)
{
- nsjpeg_content *jpeg_c;
+ struct content *jpeg_c;
nserror error;
- jpeg_c = talloc_zero(0, nsjpeg_content);
+ jpeg_c = talloc_zero(0, struct content);
if (jpeg_c == NULL)
return NSERROR_NOMEM;
- error = content__clone(old, &jpeg_c->base);
+ error = content__clone(old, jpeg_c);
if (error != NSERROR_OK) {
- content_destroy(&jpeg_c->base);
+ 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->base) == false) {
- content_destroy(&jpeg_c->base);
+ if (nsjpeg_convert(jpeg_c) == false) {
+ content_destroy(jpeg_c);
return NSERROR_CLONE_FAILED;
}
}
- *newc = (struct content *)jpeg_c;
+ *newc = jpeg_c;
return NSERROR_OK;
}
-static void *nsjpeg_get_internal(const struct content *c, void *context)
-{
- nsjpeg_content *jpeg_c = (nsjpeg_content *)c;
-
- return jpeg_c->bitmap;
-}
-
-static content_type nsjpeg_content_type(void)
-{
- return CONTENT_IMAGE;
-}
-
static const content_handler nsjpeg_content_handler = {
.create = nsjpeg_create,
.data_complete = nsjpeg_convert,
- .destroy = nsjpeg_destroy,
- .redraw = nsjpeg_redraw,
+ .destroy = image_cache_destroy,
+ .redraw = image_cache_redraw,
.clone = nsjpeg_clone,
- .get_internal = nsjpeg_get_internal,
- .type = nsjpeg_content_type,
+ .get_internal = image_cache_get_internal,
+ .type = image_cache_content_type,
.no_share = false,
};
diff --git a/image/png.c b/image/png.c
index b2dc0e145..a94e1a68b 100644
--- a/image/png.c
+++ b/image/png.c
@@ -30,6 +30,7 @@
#include "content/content_protected.h"
#include "image/bitmap.h"
+#include "image/image_cache.h"
#include "image/png.h"
#include "utils/log.h"
@@ -51,6 +52,7 @@
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;
@@ -64,6 +66,13 @@ 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
*/
@@ -78,7 +87,58 @@ static void nspng_warning(png_structp png_ptr, png_const_charp warning_message)
static void nspng_error(png_structp png_ptr, png_const_charp error_message)
{
LOG(("%s", error_message));
- longjmp(png_jmpbuf(png_ptr), 1);
+ 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);
}
/**
@@ -87,62 +147,40 @@ static void nspng_error(png_structp png_ptr, png_const_charp error_message)
*/
static void info_callback(png_structp png_s, png_infop info)
{
- int bit_depth, color_type, interlace, intent;
- double gamma;
+ int interlace;
png_uint_32 width, height;
nspng_content *png_c = png_get_progressive_ptr(png_s);
- /* Read the PNG details */
- png_get_IHDR(png_s, info, &width, &height, &bit_depth,
- &color_type, &interlace, 0, 0);
+ 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 = bitmap_create(width, height, BITMAP_NEW);
if (png_c->bitmap == NULL) {
- /* Failed -- bail out */
- longjmp(png_jmpbuf(png_s), 1);
+ /* Failed to create bitmap skip pre-conversion */
+ longjmp(png_jmpbuf(png_s), CBERR_NOPRE);
}
png_c->rowstride = bitmap_get_rowstride(png_c->bitmap);
png_c->bpp = bitmap_get_bpp(png_c->bitmap);
- /* Set up our transformations */
- if (color_type == PNG_COLOR_TYPE_PALETTE)
- png_set_palette_to_rgb(png_s);
- if ((color_type == PNG_COLOR_TYPE_GRAY) && (bit_depth < 8))
- png_set_expand_gray_1_2_4_to_8(png_s);
- if (png_get_valid(png_s, info, PNG_INFO_tRNS))
- png_set_tRNS_to_alpha(png_s);
- if (bit_depth == 16)
- png_set_strip_16(png_s);
- if (color_type == PNG_COLOR_TYPE_GRAY ||
- color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
- png_set_gray_to_rgb(png_s);
- if (!(color_type & PNG_COLOR_MASK_ALPHA))
- png_set_filler(png_s, 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_s, info, &intent)) {
- png_set_gamma(png_s, 2.2, 0.45455);
- } else {
- if (png_get_gAMA(png_s, info, &gamma)) {
- png_set_gamma(png_s, 2.2, gamma);
- } else {
- png_set_gamma(png_s, 2.2, 0.45455);
- }
- }
-
- png_read_update_info(png_s, info);
+ nspng_setup_transforms(png_s, info);
png_c->rowbytes = png_get_rowbytes(png_s, info);
png_c->interlace = (interlace == PNG_INTERLACE_ADAM7);
- png_c->base.width = width;
- png_c->base.height = height;
- LOG(("size %li * %li, bpp %i, rowbytes %zu", (unsigned long)width,
- (unsigned long)height, bit_depth, png_c->rowbytes));
+ LOG(("size %li * %li, rowbytes %zu", (unsigned long)width,
+ (unsigned long)height, png_c->rowbytes));
}
static void row_callback(png_structp png_s, png_bytep new_row,
@@ -276,30 +314,191 @@ static nserror nspng_create(const content_handler *handler,
}
-static bool nspng_process_data(struct content *c, const char *data,
+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;
+ bool ret = true;
- if (setjmp(png_jmpbuf(png_c->png))) {
- png_destroy_read_struct(&png_c->png, &png_c->info, 0);
- LOG(("Failed to process data"));
- png_c->png = NULL;
- png_c->info = NULL;
+ 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) {
- bitmap_destroy(png_c->bitmap);
- 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;
+ }
- msg_data.error = messages_get("PNGError");
- content_broadcast(c, CONTENT_MSG_ERROR, msg_data);
- return false;
+ 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;
}
- png_process_data(png_c->png, png_c->info, (uint8_t *)data, size);
+ if (length == 0) {
+ png_error(png_ptr, "Read Error");
+ }
- return true;
+ 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 = bitmap_get_height(bitmap);
+ unsigned char *buffer= bitmap_get_buffer(bitmap);
+ size_t rowstride = bitmap_get_rowstride(bitmap);
+ png_bytep *row_ptrs;
+ int hloop;
+
+ 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;
+ struct bitmap *bitmap = NULL;
+ struct png_cache_read_data_s png_cache_read_data;
+ png_uint_32 width, height;
+ png_bytep *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 (png_ptr == NULL) {
+ png_destroy_read_struct(&png_ptr, NULL, NULL);
+ return NULL;
+ }
+
+ end_info = png_create_info_struct(png_ptr);
+ if (png_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 = bitmap_create(width, height, BITMAP_NEW);
+ if (bitmap == NULL) {
+ /* cleanup and bail */
+ goto png_cache_convert_error;
+ }
+
+ row_pointers = calc_row_pointers(bitmap);
+
+ if (row_pointers != NULL) {
+ png_read_image(png_ptr, row_pointers);
+ }
+
+png_cache_convert_error:
+
+ /* cleanup png read */
+ png_destroy_read_struct(&png_ptr, &info_ptr, &end_info);
+
+ free(row_pointers);
+
+ return bitmap;
}
static bool nspng_convert(struct content *c)
@@ -310,26 +509,21 @@ static bool nspng_convert(struct content *c)
assert(png_c->png != NULL);
assert(png_c->info != NULL);
- if (png_c->bitmap == NULL) {
- union content_msg_data msg_data;
-
- msg_data.error = messages_get("PNGError");
- content_broadcast(c, CONTENT_MSG_ERROR, msg_data);
- return false;
- }
-
/* clean up png structures */
png_destroy_read_struct(&png_c->png, &png_c->info, 0);
- c->size += (c->width * c->height * 4);
-
/* set title text */
snprintf(title, sizeof(title), messages_get("PNGTitle"),
c->width, c->height, c->size);
+
content__set_title(c, title);
- bitmap_set_opaque(png_c->bitmap, bitmap_test_opaque(png_c->bitmap));
- bitmap_modified(png_c->bitmap);
+ if (png_c->bitmap != NULL) {
+ bitmap_set_opaque(png_c->bitmap, bitmap_test_opaque(png_c->bitmap));
+ bitmap_modified(png_c->bitmap);
+ }
+
+ image_cache_add(c, png_c->bitmap, png_cache_convert);
content_set_ready(c);
content_set_done(c);
@@ -339,33 +533,6 @@ static bool nspng_convert(struct content *c)
}
-static void nspng_destroy(struct content *c)
-{
- nspng_content *png_c = (nspng_content *) c;
-
- if (png_c->bitmap != NULL) {
- bitmap_destroy(png_c->bitmap);
- }
-}
-
-
-static bool nspng_redraw(struct content *c, struct content_redraw_data *data,
- const struct rect *clip, const struct redraw_context *ctx)
-{
- nspng_content *png_c = (nspng_content *) c;
- bitmap_flags_t flags = BITMAPF_NONE;
-
- assert(png_c->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,
- png_c->bitmap, data->background_colour, flags);
-}
-
static nserror nspng_clone(const struct content *old_c, struct content **new_c)
{
nspng_content *clone_png_c;
@@ -411,27 +578,15 @@ static nserror nspng_clone(const struct content *old_c, struct content **new_c)
return NSERROR_OK;
}
-static void *nspng_get_internal(const struct content *c, void *context)
-{
- nspng_content *png_c = (nspng_content *) c;
-
- return png_c->bitmap;
-}
-
-static content_type nspng_content_type(void)
-{
- return CONTENT_IMAGE;
-}
-
static const content_handler nspng_content_handler = {
.create = nspng_create,
.process_data = nspng_process_data,
.data_complete = nspng_convert,
- .destroy = nspng_destroy,
- .redraw = nspng_redraw,
.clone = nspng_clone,
- .get_internal = nspng_get_internal,
- .type = nspng_content_type,
+ .destroy = image_cache_destroy,
+ .redraw = image_cache_redraw,
+ .get_internal = image_cache_get_internal,
+ .type = image_cache_content_type,
.no_share = false,
};