summaryrefslogtreecommitdiff
path: root/riscos/bitmap.c
diff options
context:
space:
mode:
Diffstat (limited to 'riscos/bitmap.c')
-rw-r--r--riscos/bitmap.c579
1 files changed, 555 insertions, 24 deletions
diff --git a/riscos/bitmap.c b/riscos/bitmap.c
index 2f2416550..a29cc1229 100644
--- a/riscos/bitmap.c
+++ b/riscos/bitmap.c
@@ -1,8 +1,9 @@
/*
* This file is part of NetSurf, http://netsurf.sourceforge.net/
* Licensed under the GNU General Public License,
- * http://www.opensource.org/licenses/gpl-license
+ * http://www.opensource.org/licenses/gpl-license
* Copyright 2004 James Bursa <bursa@users.sourceforge.net>
+ * Copyright 2005 Richard Wilson <info@tinct.net>
*/
/** \file
@@ -15,14 +16,136 @@
#include <assert.h>
#include <stdbool.h>
#include <string.h>
+#include <swis.h>
+#include "oslib/osfile.h"
#include "oslib/osspriteop.h"
#include "netsurf/content/content.h"
#include "netsurf/image/bitmap.h"
#include "netsurf/riscos/bitmap.h"
+#include "netsurf/riscos/filename.h"
#include "netsurf/riscos/image.h"
+#include "netsurf/riscos/options.h"
+#include "netsurf/riscos/tinct.h"
#include "netsurf/utils/log.h"
#include "netsurf/utils/utils.h"
+#define MAINTENANCE_THRESHOLD 32
+
+/** The head of the bitmap list
+*/
+struct bitmap *bitmap_head = NULL;
+
+/** Whether maintenance of the pool states is needed
+*/
+bool bitmap_maintenance = false;
+
+/** Whether maintenance of the pool is high priority
+*/
+bool bitmap_maintenance_priority = false;
+
+/** Maximum amount of memory for direct images
+*/
+unsigned int bitmap_direct_size;
+
+/** Current amount of memory for direct images
+*/
+unsigned int bitmap_direct_used = 0;
+
+/** Total size of compressed area
+*/
+unsigned int bitmap_compressed_size;
+
+/** Total size of compressed area
+*/
+unsigned int bitmap_compressed_used = 0;
+
+
+/** Compressed data header
+*/
+struct bitmap_compressed_header {
+ int width;
+ int height;
+ char name[12];
+ unsigned int flags;
+ unsigned int input_size;
+};
+
+char bitmap_filename[256];
+
+
+bool bitmap_initialise(struct bitmap *bitmap, bool clear);
+void bitmap_decompress(struct bitmap *bitmap);
+void bitmap_compress(struct bitmap *bitmap);
+void bitmap_load_file(struct bitmap *bitmap);
+void bitmap_save_file(struct bitmap *bitmap);
+void bitmap_delete_file(struct bitmap *bitmap);
+
+
+/**
+ * Initialise the bitmap memory pool.
+ */
+
+void bitmap_initialise_memory(void)
+{
+ int available_memory, direct_size, compressed_size;
+ int next_slot, free_slot;
+ os_error *error;
+
+ /* calculate how much memory is currently free */
+ error = xwimp_slot_size(-1, -1, 0, &next_slot, &free_slot);
+ if (error) {
+ LOG(("xwimp_slot_size: 0x%x: %s",
+ error->errnum, error->errmess));
+ warn_user("WimpError", error->errmess);
+ return;
+ }
+ available_memory = next_slot + free_slot;
+
+ /* calculate our memory block sizes */
+ if (option_image_memory_direct == -1) {
+ /* claim 20% of free memory - min 256KB, max 32768KB */
+ direct_size = available_memory * 0.20;
+ if (direct_size < (256 << 10))
+ direct_size = (256 << 10);
+ if (direct_size > (32768 << 10))
+ direct_size = (32768 << 10);
+ } else {
+ direct_size = (option_image_memory_direct << 10);
+ }
+ if (option_image_memory_compressed == -1) {
+ /* claim 5% of free memory - min 256KB, max 4192KB */
+ compressed_size = available_memory * 0.05;
+ if (compressed_size < (256 << 10))
+ compressed_size = 0;
+ if (compressed_size > (4192 << 10))
+ compressed_size = (4192 << 10);
+ } else {
+ compressed_size = (option_image_memory_compressed << 10);
+ }
+
+ /* set our values. No fixed buffers here, ho hum. */
+ bitmap_direct_size = direct_size;
+ bitmap_compressed_size = compressed_size;
+ LOG(("Allowing %iKB of memory for compressed images.",
+ (bitmap_compressed_size >> 10)));
+ LOG(("Allowing %iKB of memory for uncompressed images.",
+ (bitmap_direct_size >> 10)));
+}
+
+
+/**
+ * Prepare for the end of a session.
+ */
+
+void bitmap_quit(void)
+{
+ struct bitmap *bitmap;
+
+ for (bitmap = bitmap_head; bitmap; bitmap = bitmap->next)
+ if ((bitmap->persistent) && (bitmap->filename[0] == '\0'))
+ bitmap_save_file(bitmap);
+}
+
/**
* Create a bitmap.
@@ -35,28 +158,100 @@
struct bitmap *bitmap_create(int width, int height, bool clear)
{
- unsigned int area_size;
struct bitmap *bitmap;
- osspriteop_area *sprite_area;
- osspriteop_header *sprite;
if (width == 0 || height == 0)
return NULL;
- area_size = 16 + 44 + width * height * 4;
- if (clear)
- bitmap = calloc(sizeof(struct bitmap) + area_size, 1);
- else
- bitmap = malloc(sizeof(struct bitmap) + area_size);
+ bitmap = calloc(sizeof(struct bitmap), 1);
if (!bitmap)
return NULL;
-
bitmap->width = width;
bitmap->height = height;
bitmap->opaque = false;
+ if (!bitmap_initialise(bitmap, clear)) {
+ free(bitmap);
+ return NULL;
+ }
+
+ /* link into our list of bitmaps at the head */
+ bitmap_maintenance = true;
+ bitmap_maintenance_priority |=
+ (bitmap_direct_used > bitmap_direct_size * 1.1);
+ if (bitmap_head) {
+ bitmap->next = bitmap_head;
+ bitmap_head->previous = bitmap;
+ }
+ bitmap_head = bitmap;
+ return bitmap;
+}
+
+
+/**
+ * Create a persistent, opaque bitmap from a file reference.
+ *
+ * \param width width of image in pixels
+ * \param height width of image in pixels
+ * \param file the file containing the image data
+ * \return an opaque struct bitmap, or NULL on memory exhaustion
+ */
+
+struct bitmap *bitmap_create_file(int width, int height, char *file)
+{
+ struct bitmap *bitmap;
+ struct bitmap *link;
+
+ if (width == 0 || height == 0)
+ return NULL;
+
+ if (!ro_filename_claim(file))
+ return NULL;
+ bitmap = calloc(sizeof(struct bitmap), 1);
+ if (!bitmap)
+ return NULL;
+ bitmap->width = width;
+ bitmap->height = height;
+ bitmap->opaque = true;
+ bitmap->persistent = true;
+ sprintf(bitmap->filename, file);
+
+ /* link into our list of bitmaps at the tail */
+ if (bitmap_head) {
+ for (link = bitmap_head; link->next; link = link->next);
+ link->next = bitmap;
+ bitmap->previous = link;
+ } else {
+ bitmap_head = bitmap;
+ }
+ return bitmap;
+}
+
+
+/**
+ * Initialise a bitmaps sprite area.
+ *
+ * \param bitmap the bitmap to initialise
+ * \param clear whether to clear the image ready for use
+ */
+
+bool bitmap_initialise(struct bitmap *bitmap, bool clear)
+{
+ unsigned int area_size;
+ osspriteop_area *sprite_area;
+ osspriteop_header *sprite;
+
+ area_size = 16 + 44 + bitmap->width * bitmap->height * 4;
+ if (clear)
+ bitmap->sprite_area = calloc(area_size, 1);
+ else
+ bitmap->sprite_area = malloc(area_size);
+ if (!bitmap->sprite_area) {
+ return false;
+ }
+ bitmap_direct_used += area_size;
/* area control block */
- sprite_area = &bitmap->sprite_area;
+ sprite_area = bitmap->sprite_area;
sprite_area->size = area_size;
sprite_area->sprite_count = 1;
sprite_area->first = 16;
@@ -68,14 +263,13 @@ struct bitmap *bitmap_create(int width, int height, bool clear)
if (!clear)
memset(sprite->name, 0x00, 12);
strncpy(sprite->name, "bitmap", 12);
- sprite->width = width - 1;
- sprite->height = height - 1;
+ sprite->width = bitmap->width - 1;
+ sprite->height = bitmap->height - 1;
sprite->left_bit = 0;
sprite->right_bit = 31;
sprite->image = sprite->mask = 44;
- sprite->mode = (os_mode) 0x301680b5;
-
- return bitmap;
+ sprite->mode = tinct_SPRITE_MODE;
+ return true;
}
@@ -102,9 +296,11 @@ bool bitmap_test_opaque(struct bitmap *bitmap)
{
assert(bitmap);
char *sprite = bitmap_get_buffer(bitmap);
+ if (!sprite)
+ return false;
unsigned int width = bitmap_get_rowstride(bitmap);
osspriteop_header *sprite_header =
- (osspriteop_header *) (&(bitmap->sprite_area) + 1);
+ (osspriteop_header *) (bitmap->sprite_area + 1);
unsigned int height = (sprite_header->height + 1);
unsigned int size = width * height;
unsigned *p = (unsigned*)sprite;
@@ -153,7 +349,36 @@ bool bitmap_get_opaque(struct bitmap *bitmap)
char *bitmap_get_buffer(struct bitmap *bitmap)
{
assert(bitmap);
- return ((char *) (&(bitmap->sprite_area))) + 16 + 44;
+
+ /* move to the head of the list */
+ if (bitmap_head != bitmap) {
+ if (bitmap->previous)
+ bitmap->previous->next = bitmap->next;
+ if (bitmap->next)
+ bitmap->next->previous = bitmap->previous;
+ bitmap->next = bitmap_head;
+ bitmap_head->previous = bitmap;
+ bitmap->previous = NULL;
+ bitmap_head = bitmap;
+ }
+
+ /* image is already decompressed, no change to image states */
+ if (bitmap->sprite_area)
+ return ((char *) (bitmap->sprite_area)) + 16 + 44;
+
+ /* load and/or decompress the image */
+ if (bitmap->filename[0])
+ bitmap_load_file(bitmap);
+ if (bitmap->compressed)
+ bitmap_decompress(bitmap);
+
+ bitmap_maintenance = true;
+ bitmap_maintenance_priority |=
+ (bitmap_direct_used > bitmap_direct_size * 1.1);
+
+ if (bitmap->sprite_area)
+ return ((char *) (bitmap->sprite_area)) + 16 + 44;
+ return NULL;
}
@@ -166,10 +391,7 @@ char *bitmap_get_buffer(struct bitmap *bitmap)
size_t bitmap_get_rowstride(struct bitmap *bitmap)
{
- osspriteop_header *sprite;
- assert(bitmap);
- sprite = (osspriteop_header *) (&(bitmap->sprite_area) + 1);
- return (sprite->width + 1) * 4;
+ return bitmap->width * 4;
}
@@ -181,7 +403,29 @@ size_t bitmap_get_rowstride(struct bitmap *bitmap)
void bitmap_destroy(struct bitmap *bitmap)
{
+ unsigned int area_size;
+
assert(bitmap);
+
+ /* delink from list */
+ bitmap_maintenance = true;
+ if (bitmap_head == bitmap)
+ bitmap_head = bitmap->next;
+ if (bitmap->previous)
+ bitmap->previous->next = bitmap->next;
+ if (bitmap->next)
+ bitmap->next->previous = bitmap->previous;
+
+ /* destroy bitmap */
+ if (bitmap->sprite_area) {
+ area_size = 16 + 44 + bitmap->width * bitmap->height * 4;
+ bitmap_direct_used -= area_size;
+ free(bitmap->sprite_area);
+ }
+ if (bitmap->compressed)
+ free(bitmap->compressed);
+ if (bitmap->filename[0])
+ bitmap_delete_file(bitmap);
free(bitmap);
}
@@ -190,15 +434,19 @@ void bitmap_destroy(struct bitmap *bitmap)
* Save a bitmap in the platform's native format.
*
* \param bitmap a bitmap, as returned by bitmap_create()
- * \param path pathname for file
+ * \param path pathname for file
* \return true on success, false on error and error reported
*/
bool bitmap_save(struct bitmap *bitmap, const char *path)
{
os_error *error;
+ if (!bitmap->sprite_area)
+ bitmap_get_buffer(bitmap);
+ if (!bitmap->sprite_area)
+ return false;
error = xosspriteop_save_sprite_file(osspriteop_USER_AREA,
- &(bitmap->sprite_area), path);
+ (bitmap->sprite_area), path);
if (error) {
LOG(("xosspriteop_save_sprite_file: 0x%x: %s",
error->errnum, error->errmess));
@@ -207,3 +455,286 @@ bool bitmap_save(struct bitmap *bitmap, const char *path)
}
return true;
}
+
+
+/**
+ * The bitmap image has changed, so flush any persistant cache.
+ *
+ * \param bitmap a bitmap, as returned by bitmap_create()
+ */
+void bitmap_modified(struct bitmap *bitmap) {
+ bitmap->modified = true;
+}
+
+
+/**
+ * Performs routine maintenance.
+ */
+void bitmap_maintain(void)
+{
+ unsigned int memory;
+ struct bitmap *bitmap = bitmap_head;
+ struct bitmap_compressed_header *header;
+ unsigned int maintain_direct_size;
+
+// LOG(("Performing maintenance."));
+
+ if (!bitmap) {
+ bitmap_maintenance = false;
+ bitmap_maintenance_priority = false;
+ return;
+ }
+ maintain_direct_size = bitmap_direct_size;
+ if (!bitmap_maintenance_priority)
+ maintain_direct_size = maintain_direct_size * 0.7;
+
+ /* we don't change the first bitmap_MEMORY entries as they
+ * will automatically be loaded/decompressed from whatever state
+ * they are in when neeeded. */
+ for (memory = 0; bitmap && (memory < maintain_direct_size);
+ bitmap = bitmap->next)
+ if (bitmap->sprite_area)
+ memory += bitmap->width * bitmap->height * 4;
+ if (!bitmap) {
+ bitmap_maintenance = bitmap_maintenance_priority;
+ bitmap_maintenance_priority = false;
+ return;
+ }
+
+ /* under heavy loads, we ignore compression */
+ if (!bitmap_maintenance_priority) {
+ /* for the next section, up until bitmap_COMPRESSED we
+ * forcibly compress the data if it's currently held directly
+ * in memory */
+ for (memory = 0; bitmap && (memory < bitmap_compressed_size);
+ bitmap = bitmap->next) {
+ if (bitmap->sprite_area) {
+ bitmap_compress(bitmap);
+ return;
+ }
+ if (bitmap->compressed) {
+ header = (struct bitmap_compressed_header *)
+ bitmap->compressed;
+ memory += header->input_size +
+ sizeof(struct bitmap_compressed_header);
+ }
+ }
+ if (!bitmap) {
+ bitmap_maintenance = false;
+ return;
+ }
+ }
+
+ /* for the remaining entries we dump to disk */
+ for (; bitmap; bitmap = bitmap->next) {
+ if ((bitmap->sprite_area) || (bitmap->compressed)) {
+ if (bitmap_maintenance_priority) {
+ if (bitmap->sprite_area)
+ bitmap_save_file(bitmap);
+
+ } else {
+ bitmap_save_file(bitmap);
+ return;
+ }
+ }
+ }
+ bitmap_maintenance = bitmap_maintenance_priority;
+ bitmap_maintenance_priority = false;
+}
+
+
+void bitmap_decompress(struct bitmap *bitmap)
+{
+ unsigned int area_size;
+ _kernel_oserror *error;
+ int output_size;
+ struct bitmap_compressed_header *header;
+
+ assert(bitmap->compressed);
+
+ /* create the image memory/header to decompress to */
+ if (!bitmap_initialise(bitmap, false))
+ return;
+
+ /* decompress the data */
+ output_size = bitmap->width * bitmap->height * 4 +
+ sizeof(struct osspriteop_header);
+ error = _swix(Tinct_Decompress, _IN(0) | _IN(2) | _IN(3) | _IN(7),
+ bitmap->compressed,
+ (char *)(bitmap->sprite_area + 1),
+ output_size,
+ 0);
+ if (error) {
+ LOG(("Decompression error"));
+ free(bitmap->sprite_area);
+ bitmap->sprite_area = NULL;
+ } else {
+// LOG(("Decompressed"));
+ header = (struct bitmap_compressed_header *)bitmap->compressed;
+ area_size = header->input_size +
+ sizeof(struct bitmap_compressed_header);
+ bitmap_compressed_used -= area_size;
+ free(bitmap->compressed);
+ bitmap->compressed = NULL;
+ area_size = 16 + 44 + bitmap->width * bitmap->height * 4;
+ bitmap_direct_used += area_size;
+ }
+}
+
+
+void bitmap_compress(struct bitmap *bitmap)
+{
+ unsigned int area_size;
+ _kernel_oserror *error;
+ char *output;
+ unsigned int output_size, new_size;
+ unsigned int flags = 0;
+ float calc;
+
+ /* get the maximum output size (33/32 * size) */
+ output_size = ((bitmap->width * bitmap->height * 4 * 33) >> 5) +
+ sizeof(struct bitmap_compressed_header);
+ output = malloc(output_size);
+ if (!output)
+ return;
+
+ /* compress the data */
+ if (bitmap->opaque)
+ flags |= tinct_OPAQUE_IMAGE;
+ error = _swix(Tinct_Compress, _IN(0) | _IN(2) | _IN(7) | _OUT(0),
+ (char *)(bitmap->sprite_area + 1),
+ output,
+ flags,
+ &new_size);
+ if (error) {
+ LOG(("Compression error"));
+ free(output);
+ } else {
+ bitmap->compressed = realloc(output, new_size);
+ if (!bitmap->compressed) {
+ free(output);
+ } else {
+ bitmap_compressed_used += new_size;
+ if (bitmap->sprite_area) {
+ area_size = 16 + 44 + bitmap->width *
+ bitmap->height * 4;
+ bitmap_direct_used -= area_size;
+ free(bitmap->sprite_area);
+ }
+ bitmap->sprite_area = NULL;
+ calc = (100 / (float)output_size) * new_size;
+// LOG(("Compression: %i->%i, %.3f%%",
+// output_size, new_size, calc));
+ }
+ }
+}
+
+void bitmap_load_file(struct bitmap *bitmap)
+{
+ unsigned int area_size;
+ int len;
+ fileswitch_object_type obj_type;
+ os_error *error;
+
+ assert(bitmap->filename);
+
+ sprintf(bitmap_filename, "%s.%s", CACHE_FILENAME_PREFIX,
+ bitmap->filename);
+ error = xosfile_read_stamped_no_path(bitmap_filename,
+ &obj_type, 0, 0, &len, 0, 0);
+ if ((error) || (obj_type != fileswitch_IS_FILE))
+ return;
+ bitmap->compressed = malloc(len);
+ if (!bitmap->compressed)
+ return;
+ error = xosfile_load_stamped_no_path(bitmap_filename,
+ bitmap->compressed, 0, 0, 0, 0, 0);
+ if (error) {
+ free(bitmap->compressed);
+ bitmap->compressed = NULL;
+ return;
+ } else {
+// LOG(("Loaded file from disk"));
+ if (strncmp(bitmap->compressed + 8, "bitmap", 6)) {
+ bitmap->sprite_area =
+ (osspriteop_area *)bitmap->compressed;
+ bitmap->compressed = NULL;
+ area_size = 16 + 44 +
+ bitmap->width * bitmap->height * 4;
+ bitmap_direct_used += area_size;
+ } else {
+ bitmap_compressed_used -= len;
+ }
+ if (bitmap->modified)
+ bitmap_delete_file(bitmap);
+ }
+}
+
+
+void bitmap_save_file(struct bitmap *bitmap)
+{
+ unsigned int area_size;
+ char *filename;
+ os_error *error;
+ struct bitmap_compressed_header *header;
+
+ assert(bitmap->compressed || bitmap->sprite_area);
+
+ /* unmodified bitmaps will still have their file available */
+ if (!bitmap->modified && bitmap->filename[0]) {
+ if (bitmap->sprite_area)
+ free(bitmap->sprite_area);
+ bitmap->sprite_area = NULL;
+ if (bitmap->compressed)
+ free(bitmap->compressed);
+ bitmap->compressed = NULL;
+ return;
+ }
+
+ /* dump the data (compressed or otherwise) to disk */
+ filename = ro_filename_request();
+ strcpy(bitmap->filename, filename);
+ sprintf(bitmap_filename, "%s.%s", CACHE_FILENAME_PREFIX,
+ bitmap->filename);
+ if (bitmap->compressed) {
+ header = (struct bitmap_compressed_header *)bitmap->compressed;
+ area_size = header->input_size +
+ sizeof(struct bitmap_compressed_header);
+ error = xosfile_save_stamped(bitmap_filename, 0xffd,
+ bitmap->compressed,
+ bitmap->compressed + area_size);
+ } else {
+ area_size = bitmap->width * bitmap->height * 4 +
+ sizeof(osspriteop_header) +
+ sizeof(osspriteop_area);
+ error = xosfile_save_stamped(bitmap_filename, 0xffd,
+ (char *)bitmap->sprite_area,
+ ((char *)bitmap->sprite_area) + area_size);
+ }
+
+ if (error) {
+ LOG(("File save error"));
+ bitmap->filename[0] = 0;
+ } else {
+ if (bitmap->sprite_area) {
+ bitmap_direct_used -= area_size;
+ free(bitmap->sprite_area);
+ }
+ bitmap->sprite_area = NULL;
+ if (bitmap->compressed) {
+ bitmap_compressed_used -= area_size;
+ free(bitmap->compressed);
+ }
+ bitmap->compressed = NULL;
+ bitmap->modified = false;
+// LOG(("Saved file to disk"));
+ }
+}
+
+
+void bitmap_delete_file(struct bitmap *bitmap)
+{
+ assert(bitmap->filename[0]);
+ ro_filename_release(bitmap->filename);
+ bitmap->filename[0] = 0;
+}