From 3d9a1198db571973e2760d6f27c771cbe31c844b Mon Sep 17 00:00:00 2001 From: Richard Wilson Date: Wed, 22 Feb 2006 01:58:19 +0000 Subject: [project @ 2006-02-22 01:58:19 by rjw] Reduce constant bitmap overhead per reference by moving to a flag word. Allow bitmaps to be reduced back to their raw data to free extra memory in a highly efficient manner. svn path=/import/netsurf/; revision=2089 --- debug/debug_bitmap.c | 16 ++++++- gtk/gtk_bitmap.c | 17 ++++++- image/bitmap.h | 16 ++++--- image/gif.c | 8 ++++ image/gifread.c | 4 +- image/jpeg.c | 3 +- image/mng.c | 2 +- riscos/bitmap.c | 124 +++++++++++++++++++++++++++++++-------------------- riscos/bitmap.h | 9 ++-- riscos/history.c | 4 +- riscos/plotters.c | 4 +- riscos/save.c | 9 +--- riscos/thumbnail.c | 2 - 13 files changed, 138 insertions(+), 80 deletions(-) diff --git a/debug/debug_bitmap.c b/debug/debug_bitmap.c index 6d7cd3f44..9c8942f7d 100644 --- a/debug/debug_bitmap.c +++ b/debug/debug_bitmap.c @@ -29,10 +29,11 @@ struct bitmap { * * \param width width of image in pixels * \param height width of image in pixels + * \param state a flag word indicating the initial state * \return an opaque struct bitmap, or NULL on memory exhaustion */ -struct bitmap *bitmap_create(int width, int height, bitmap_state state) +struct bitmap *bitmap_create(int width, int height, unsigned int state) { struct bitmap *bitmap; bitmap = calloc(sizeof *bitmap + width * height * 4, 1); @@ -120,3 +121,16 @@ bool bitmap_save(struct bitmap *bitmap, const char *path) */ void bitmap_modified(struct bitmap *bitmap) { } + + +/** + * The bitmap image can be suspended. + * + * \param bitmap a bitmap, as returned by bitmap_create() + * \param private_word a private word to be returned later + * \param suspend the function to be called upon suspension + * \param resume the function to be called when resuming + */ +void bitmap_set_suspendable(struct bitmap *bitmap, void *private_word, + void (*invalidate)(struct bitmap *bitmap, void *private_word)) { +} diff --git a/gtk/gtk_bitmap.c b/gtk/gtk_bitmap.c index 26b6b5a37..58fff8280 100644 --- a/gtk/gtk_bitmap.c +++ b/gtk/gtk_bitmap.c @@ -29,11 +29,11 @@ struct bitmap; * * \param width width of image in pixels * \param height width of image in pixels - * \param clear whether to clear the image ready for use + * \param state a flag word indicating the initial state * \return an opaque struct bitmap, or NULL on memory exhaustion */ -struct bitmap *bitmap_create(int width, int height, bitmap_state state) +struct bitmap *bitmap_create(int width, int height, unsigned int state) { GdkPixbuf *pixbuf = gdk_pixbuf_new(GDK_COLORSPACE_RGB, true, 8, width, height); @@ -146,3 +146,16 @@ bool bitmap_save(struct bitmap *bitmap, const char *path) */ void bitmap_modified(struct bitmap *bitmap) { } + + +/** + * The bitmap image can be suspended. + * + * \param bitmap a bitmap, as returned by bitmap_create() + * \param private_word a private word to be returned later + * \param suspend the function to be called upon suspension + * \param resume the function to be called when resuming + */ +void bitmap_set_suspendable(struct bitmap *bitmap, void *private_word, + void (*invalidate)(struct bitmap *bitmap, void *private_word)) { +} diff --git a/image/bitmap.h b/image/bitmap.h index c187b3ecb..bae1e1a15 100644 --- a/image/bitmap.h +++ b/image/bitmap.h @@ -20,18 +20,20 @@ #include #include -typedef enum { - BITMAP_READY, /** Bitmap buffer is ready */ - BITMAP_ALLOCATE_MEMORY, /** Allocate memory */ - BITMAP_CLEAR_MEMORY, /** Clear the memory */ -} bitmap_state; +#define BITMAP_NEW 0 +#define BITMAP_OPAQUE (1 << 0) /** image is opaque */ +#define BITMAP_MODIFIED (1 << 1) /** buffer has been modified */ +#define BITMAP_PERSISTENT (1 << 2) /** retain between sessions */ +#define BITMAP_CLEAR_MEMORY (1 << 3) /** memory should be wiped */ +#define BITMAP_SUSPENDED (1 << 4) /** currently suspended */ +#define BITMAP_READY (1 << 5) /** fully initialised */ struct content; /** An opaque image. */ struct bitmap; -struct bitmap *bitmap_create(int width, int height, bitmap_state state); +struct bitmap *bitmap_create(int width, int height, unsigned int state); void bitmap_set_opaque(struct bitmap *bitmap, bool opaque); bool bitmap_test_opaque(struct bitmap *bitmap); bool bitmap_get_opaque(struct bitmap *bitmap); @@ -40,5 +42,7 @@ size_t bitmap_get_rowstride(struct bitmap *bitmap); void bitmap_destroy(struct bitmap *bitmap); bool bitmap_save(struct bitmap *bitmap, const char *path); void bitmap_modified(struct bitmap *bitmap); +void bitmap_set_suspendable(struct bitmap *bitmap, void *private_word, + void (*invalidate)(struct bitmap *bitmap, void *private_word)); #endif diff --git a/image/gif.c b/image/gif.c index 312139173..5bd2c25aa 100644 --- a/image/gif.c +++ b/image/gif.c @@ -35,6 +35,7 @@ #ifdef WITH_GIF +static void nsgif_invalidate(struct bitmap *bitmap, void *private_word); static void nsgif_animate(void *p); static void nsgif_get_frame(struct content *c); @@ -105,6 +106,8 @@ bool nsgif_convert(struct content *c, int iwidth, int iheight) { c->data.gif.current_frame = 0; if (gif->frame_count_partial > 1) schedule(gif->frames[0].frame_delay, nsgif_animate, c); + else + bitmap_set_suspendable(gif->frame_image, gif, nsgif_invalidate); /* Exit as a success */ @@ -113,6 +116,11 @@ bool nsgif_convert(struct content *c, int iwidth, int iheight) { return true; } +void nsgif_invalidate(struct bitmap *bitmap, void *private_word) { + struct gif_animation *gif = (struct gif_animation *)private_word; + + gif->decoded_frame = -1; +} bool nsgif_redraw(struct content *c, int x, int y, int width, int height, diff --git a/image/gifread.c b/image/gifread.c index ba26452d8..9d81bb6a7 100644 --- a/image/gifread.c +++ b/image/gifread.c @@ -204,7 +204,7 @@ int gif_initialise(struct gif_animation *gif) { /* Initialise the sprite header */ - if ((gif->frame_image = bitmap_create(gif->width, gif->height, BITMAP_ALLOCATE_MEMORY)) == NULL) { + if ((gif->frame_image = bitmap_create(gif->width, gif->height, BITMAP_NEW)) == NULL) { gif_finalise(gif); return GIF_INSUFFICIENT_MEMORY; } @@ -283,7 +283,7 @@ static int gif_initialise_sprite(struct gif_animation *gif, unsigned int width, /* Allocate some more memory */ - if ((buffer = bitmap_create(max_width, max_height, BITMAP_ALLOCATE_MEMORY)) == NULL) + if ((buffer = bitmap_create(max_width, max_height, BITMAP_NEW)) == NULL) return GIF_INSUFFICIENT_MEMORY; bitmap_destroy(gif->frame_image); gif->frame_image = buffer; diff --git a/image/jpeg.c b/image/jpeg.c index 3efe698df..197a977b4 100644 --- a/image/jpeg.c +++ b/image/jpeg.c @@ -94,7 +94,7 @@ bool nsjpeg_convert(struct content *c, int w, int h) width = cinfo.output_width; height = cinfo.output_height; - bitmap = bitmap_create(width, height, BITMAP_ALLOCATE_MEMORY); + bitmap = bitmap_create(width, height, BITMAP_NEW | BITMAP_OPAQUE); if (bitmap) pixels = bitmap_get_buffer(bitmap); if ((!bitmap) || (!pixels)) { @@ -107,7 +107,6 @@ bool nsjpeg_convert(struct content *c, int w, int h) warn_user("NoMemory", 0); return false; } - bitmap_set_opaque(bitmap, true); rowstride = bitmap_get_rowstride(bitmap); do { diff --git a/image/mng.c b/image/mng.c index 838595960..cb4a7eef4 100644 --- a/image/mng.c +++ b/image/mng.c @@ -190,7 +190,7 @@ mng_bool nsmng_processheader(mng_handle mng, mng_uint32 width, mng_uint32 height LOG(("processing header (%p) %d, %d", c, width, height)); - c->bitmap = bitmap_create(width, height, BITMAP_ALLOCATE_MEMORY); + c->bitmap = bitmap_create(width, height, BITMAP_NEW); if (!c->bitmap) { msg_data.error = messages_get("NoMemory"); content_broadcast(c, CONTENT_MSG_ERROR, msg_data); diff --git a/riscos/bitmap.c b/riscos/bitmap.c index 505b27ee5..f9c66bb34 100644 --- a/riscos/bitmap.c +++ b/riscos/bitmap.c @@ -13,8 +13,6 @@ * sprites. */ -#define NDEBUG - #include #include #include @@ -61,6 +59,14 @@ unsigned int bitmap_compressed_size; */ unsigned int bitmap_compressed_used = 0; +/** Total number of suspendable bitmaps +*/ +unsigned int bitmap_suspendable = 0; + +/** Total number of suspended bitmaps +*/ +unsigned int bitmap_suspended = 0; + /** Compressed data header */ struct bitmap_compressed_header { @@ -145,7 +151,8 @@ void bitmap_quit(void) struct bitmap *bitmap; for (bitmap = bitmap_head; bitmap; bitmap = bitmap->next) - if ((bitmap->persistent) && ((bitmap->modified) || + if ((bitmap->state & BITMAP_PERSISTENT) && + ((bitmap->state & BITMAP_MODIFIED) || (bitmap->filename[0] == '\0'))) bitmap_save_file(bitmap); } @@ -160,7 +167,7 @@ void bitmap_quit(void) * \return an opaque struct bitmap, or NULL on memory exhaustion */ -struct bitmap *bitmap_create(int width, int height, bitmap_state state) +struct bitmap *bitmap_create(int width, int height, unsigned int state) { struct bitmap *bitmap; @@ -172,17 +179,7 @@ struct bitmap *bitmap_create(int width, int height, bitmap_state state) return NULL; bitmap->width = width; bitmap->height = height; - bitmap->opaque = false; - switch (state) { - case BITMAP_CLEAR_MEMORY: - case BITMAP_ALLOCATE_MEMORY: - bitmap->state = state; - break; - default: - LOG(("Invalid bitmap state")); - assert(false); - return false; - } + bitmap->state = state; /* link into our list of bitmaps at the head */ if (bitmap_head) { @@ -212,9 +209,7 @@ struct bitmap *bitmap_create_file(char *file) bitmap = calloc(1, sizeof(struct bitmap)); if (!bitmap) return NULL; - bitmap->opaque = true; - bitmap->persistent = true; - bitmap->state = BITMAP_READY; + bitmap->state = BITMAP_OPAQUE | BITMAP_PERSISTENT | BITMAP_READY; strcpy(bitmap->filename, file); /* link in at the head */ @@ -243,21 +238,14 @@ bool bitmap_initialise(struct bitmap *bitmap) assert(!bitmap->sprite_area); area_size = 16 + 44 + bitmap->width * bitmap->height * 4; - switch (bitmap->state) { - case BITMAP_CLEAR_MEMORY: - bitmap->sprite_area = calloc(1, area_size); - break; - case BITMAP_ALLOCATE_MEMORY: - bitmap->sprite_area = malloc(area_size); - break; - default: - LOG(("Invalid bitmap state")); - assert(false); - return false; - } + if (bitmap->state & BITMAP_CLEAR_MEMORY) + bitmap->sprite_area = calloc(1, area_size); + else + bitmap->sprite_area = malloc(area_size); + if (!bitmap->sprite_area) return false; - bitmap->state = BITMAP_READY; + bitmap->state |= BITMAP_READY; bitmap_direct_used += area_size; /* area control block */ @@ -295,7 +283,11 @@ bool bitmap_initialise(struct bitmap *bitmap) void bitmap_set_opaque(struct bitmap *bitmap, bool opaque) { assert(bitmap); - bitmap->opaque = opaque; + + if (opaque) + bitmap->state |= BITMAP_OPAQUE; + else + bitmap->state &= ~BITMAP_OPAQUE; } @@ -345,7 +337,7 @@ bool bitmap_test_opaque(struct bitmap *bitmap) bool bitmap_get_opaque(struct bitmap *bitmap) { assert(bitmap); - return (bitmap->opaque); + return (bitmap->state & BITMAP_OPAQUE); } @@ -374,18 +366,17 @@ char *bitmap_get_buffer(struct bitmap *bitmap) bitmap->previous = NULL; bitmap_head = bitmap; } - + /* dynamically create the buffer */ - switch (bitmap->state) { - case BITMAP_ALLOCATE_MEMORY: - case BITMAP_CLEAR_MEMORY: - if (!bitmap_initialise(bitmap)) - return NULL; - break; - default: - break; + if (!(bitmap->state & BITMAP_READY)) { + if (!bitmap_initialise(bitmap)) + return NULL; } + /* reset our suspended flag */ + if (bitmap->state & BITMAP_SUSPENDED) + bitmap->state &= ~BITMAP_SUSPENDED; + /* image is already decompressed, no change to image states */ if (bitmap->sprite_area) return ((char *) (bitmap->sprite_area)) + 16 + 44; @@ -493,10 +484,25 @@ bool bitmap_save(struct bitmap *bitmap, const char *path) * \param bitmap a bitmap, as returned by bitmap_create() */ void bitmap_modified(struct bitmap *bitmap) { - bitmap->modified = true; + bitmap->state |= BITMAP_MODIFIED; } +/** + * The bitmap image can be suspended. + * + * \param bitmap a bitmap, as returned by bitmap_create() + * \param private_word a private word to be returned later + * \param invalidate the function to be called upon suspension + */ +void bitmap_set_suspendable(struct bitmap *bitmap, void *private_word, + void (*invalidate)(struct bitmap *bitmap, void *private_word)) { + bitmap->private_word = private_word; + bitmap->invalidate = invalidate; + bitmap_suspendable++; +} + + /** * Performs routine maintenance. */ @@ -504,6 +510,7 @@ void bitmap_maintain(void) { unsigned int memory = 0; unsigned int compressed_memory = 0; + unsigned int suspended = 0; struct bitmap *bitmap = bitmap_head; struct bitmap_compressed_header *header; unsigned int maintain_direct_size; @@ -535,7 +542,8 @@ void bitmap_maintain(void) bitmap->compressed; compressed_memory += header->input_size + sizeof(struct bitmap_compressed_header); - } + } else if (bitmap->state & BITMAP_SUSPENDED) + suspended++; } if (!bitmap) { @@ -543,6 +551,25 @@ void bitmap_maintain(void) bitmap_maintenance_priority = false; return; } + + /* the fastest and easiest way to release memory is by suspending + * images. as such, we try to do this first for as many images as + * possible, potentially freeing up large amounts of memory */ + if (suspended <= (bitmap_suspendable - bitmap_suspended)) { + for (; bitmap; bitmap = bitmap->next) { + if (bitmap->invalidate) { + bitmap->invalidate(bitmap, bitmap->private_word); + free(bitmap->sprite_area); + bitmap->sprite_area = NULL; + bitmap->state |= BITMAP_SUSPENDED; + bitmap->state &= ~BITMAP_READY; + bitmap_direct_used -= 16 + 44 + + bitmap->width * bitmap->height * 4; + bitmap_suspended++; + } + } + return; + } /* under heavy loads, we ignore compression */ if (!bitmap_maintenance_priority) { @@ -608,7 +635,6 @@ void bitmap_decompress(struct bitmap *bitmap) } /* create the image memory/header to decompress to */ - bitmap->state = BITMAP_ALLOCATE_MEMORY; if (!bitmap_initialise(bitmap)) return; @@ -654,7 +680,7 @@ void bitmap_compress(struct bitmap *bitmap) return; /* compress the data */ - if (bitmap->opaque) + if (bitmap->state & BITMAP_OPAQUE) flags |= tinct_OPAQUE_IMAGE; error = _swix(Tinct_Compress, _IN(0) | _IN(2) | _IN(7) | _OUT(0), (char *)(bitmap->sprite_area + 1), @@ -754,7 +780,7 @@ void bitmap_load_file(struct bitmap *bitmap) bitmap->compressed = NULL; return; } - if (bitmap->modified) + if (bitmap->state & BITMAP_MODIFIED) bitmap_delete_file(bitmap); } @@ -769,7 +795,7 @@ void bitmap_save_file(struct bitmap *bitmap) assert(bitmap->compressed || bitmap->sprite_area); /* unmodified bitmaps will still have their file available */ - if (!bitmap->modified && bitmap->filename[0]) { + if ((!(bitmap->state & BITMAP_MODIFIED)) && bitmap->filename[0]) { if (bitmap->sprite_area) free(bitmap->sprite_area); bitmap->sprite_area = NULL; @@ -814,7 +840,7 @@ void bitmap_save_file(struct bitmap *bitmap) free(bitmap->compressed); } bitmap->compressed = NULL; - bitmap->modified = false; + bitmap->state &= ~BITMAP_MODIFIED; LOG(("Saved file to disk")); } } diff --git a/riscos/bitmap.h b/riscos/bitmap.h index 910c201bb..a113d9b1b 100644 --- a/riscos/bitmap.h +++ b/riscos/bitmap.h @@ -16,10 +16,11 @@ struct osspriteop_area; struct bitmap { int width; int height; - bool opaque; - bool modified; - bool persistent; - bitmap_state state; + + unsigned int state; + + void *private_word; + void (*invalidate)(struct bitmap *bitmap, void *private_word); osspriteop_area *sprite_area; /** Uncompressed data, or NULL */ char *compressed; /** Compressed data, or NULL */ diff --git a/riscos/history.c b/riscos/history.c index 46ef458bf..8b9beaa12 100644 --- a/riscos/history.c +++ b/riscos/history.c @@ -179,12 +179,12 @@ void history_add(struct history *history, struct content *content, char *frag_id bitmap = url_store_get_thumbnail(url); if (!bitmap) { bitmap = bitmap_create(WIDTH / 2, HEIGHT / 2, - BITMAP_ALLOCATE_MEMORY); + BITMAP_NEW | BITMAP_CLEAR_MEMORY | + BITMAP_OPAQUE | BITMAP_PERSISTENT); if (!bitmap) { LOG(("Thumbnail initialisation failed.")); return; } - bitmap_set_opaque(bitmap, true); thumbnail_create(content, bitmap, url); } entry->bitmap = bitmap; diff --git a/riscos/plotters.c b/riscos/plotters.c index d59883075..dcce28c38 100644 --- a/riscos/plotters.c +++ b/riscos/plotters.c @@ -326,7 +326,7 @@ bool ro_plot_bitmap(int x, int y, int width, int height, bitmap->height, bg, false, false, false, - bitmap->opaque ? IMAGE_PLOT_TINCT_OPAQUE : + bitmap_get_opaque(bitmap) ? IMAGE_PLOT_TINCT_OPAQUE : IMAGE_PLOT_TINCT_ALPHA); } @@ -344,7 +344,7 @@ bool ro_plot_bitmap_tile(int x, int y, int width, int height, bitmap->height, bg, repeat_x, repeat_y, true, - bitmap->opaque ? IMAGE_PLOT_TINCT_OPAQUE : + bitmap_get_opaque(bitmap) ? IMAGE_PLOT_TINCT_OPAQUE : IMAGE_PLOT_TINCT_ALPHA); } diff --git a/riscos/save.c b/riscos/save.c index 7792e6ccc..e288150a6 100644 --- a/riscos/save.c +++ b/riscos/save.c @@ -801,12 +801,8 @@ void ro_gui_save_object_native(struct content *c, char *path) bitmap_save(c->bitmap, path); break; #endif -#ifdef WITH_PNG - case CONTENT_PNG: -/* error = xosspriteop_save_sprite_file(osspriteop_USER_AREA, c->data.png.sprite_area, path); - break;*/ -#endif #ifdef WITH_MNG + case CONTENT_PNG: case CONTENT_JNG: case CONTENT_MNG: bitmap_save(c->bitmap, path); @@ -1007,12 +1003,11 @@ bool ro_gui_save_create_thumbnail(struct content *c, const char *name) struct bitmap *bitmap; osspriteop_area *area; - bitmap = bitmap_create(34, 34, BITMAP_CLEAR_MEMORY); + bitmap = bitmap_create(34, 34, BITMAP_NEW | BITMAP_OPAQUE | BITMAP_CLEAR_MEMORY); if (!bitmap) { LOG(("Thumbnail initialisation failed.")); return false; } - bitmap_set_opaque(bitmap, true); thumbnail_create(c, bitmap, NULL); area = thumbnail_convert_8bpp(bitmap); bitmap_destroy(bitmap); diff --git a/riscos/thumbnail.c b/riscos/thumbnail.c index e1231cd2d..8c5351cac 100644 --- a/riscos/thumbnail.c +++ b/riscos/thumbnail.c @@ -139,9 +139,7 @@ bool thumbnail_create(struct content *content, struct bitmap *bitmap, /* register the thumbnail with the URL */ if (url) url_store_add_thumbnail(url, bitmap); - bitmap_modified(bitmap); - bitmap->persistent = true; return true; } -- cgit v1.2.3