summaryrefslogtreecommitdiff
path: root/image/jpeg.c
diff options
context:
space:
mode:
authorVincent Sanders <vince@netsurf-browser.org>2011-09-04 23:50:14 +0000
committerVincent Sanders <vince@netsurf-browser.org>2011-09-04 23:50:14 +0000
commitb051cf466f7c9f008f2ab8c3f6ccac34882a1b9e (patch)
tree081f1eb48b34fed18e0def6f897dd1f9928ec9b7 /image/jpeg.c
parent330227d87a7c099775d5db000416413f0271701d (diff)
downloadnetsurf-b051cf466f7c9f008f2ab8c3f6ccac34882a1b9e.tar.gz
netsurf-b051cf466f7c9f008f2ab8c3f6ccac34882a1b9e.tar.bz2
Add Image cache and inegrate png and jpeg content handlers
Current periodic cache clean algorithm is poor and requires replacing with something suitable (probably a segregated LRU) The speculative load algorithm is likewise poor and only uses the image size to make a decision. svn path=/trunk/netsurf/; revision=12720
Diffstat (limited to 'image/jpeg.c')
-rw-r--r--image/jpeg.c224
1 files changed, 123 insertions, 101 deletions
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,
};