[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
This commit is contained in:
Richard Wilson 2006-02-22 01:58:19 +00:00
parent 01cc7987c7
commit 3d9a1198db
13 changed files with 138 additions and 80 deletions

View File

@ -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)) {
}

View File

@ -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)) {
}

View File

@ -20,18 +20,20 @@
#include <stdbool.h>
#include <stdlib.h>
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

View File

@ -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,

View File

@ -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;

View File

@ -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 {

View File

@ -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);

View File

@ -13,8 +13,6 @@
* sprites.
*/
#define NDEBUG
#include <assert.h>
#include <stdbool.h>
#include <string.h>
@ -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"));
}
}

View File

@ -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 */

View File

@ -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;

View File

@ -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);
}

View File

@ -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);

View File

@ -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;
}