summaryrefslogtreecommitdiff
path: root/frontends/riscos/bitmap.c
diff options
context:
space:
mode:
Diffstat (limited to 'frontends/riscos/bitmap.c')
-rw-r--r--frontends/riscos/bitmap.c876
1 files changed, 876 insertions, 0 deletions
diff --git a/frontends/riscos/bitmap.c b/frontends/riscos/bitmap.c
new file mode 100644
index 000000000..cc4be590c
--- /dev/null
+++ b/frontends/riscos/bitmap.c
@@ -0,0 +1,876 @@
+/*
+ * Copyright 2004 James Bursa <bursa@users.sourceforge.net>
+ * Copyright 2005 Richard Wilson <info@tinct.net>
+ * Copyright 2008 Adrian Lees <adrianl@users.sourceforge.net>
+ *
+ * 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
+ * RISC OS implementation of bitmap operations.
+ *
+ * This implements the interface given by image/bitmap.h using RISC OS
+ * sprites.
+ */
+
+#include <assert.h>
+#include <stdbool.h>
+#include <string.h>
+#include <swis.h>
+#include <rufl.h>
+#include <unixlib/local.h>
+#include <oslib/colourtrans.h>
+#include <oslib/osfile.h>
+#include <oslib/osfind.h>
+#include <oslib/osgbpb.h>
+#include <oslib/osspriteop.h>
+#include <oslib/wimp.h>
+
+#include "utils/nsoption.h"
+#include "utils/filename.h"
+#include "utils/log.h"
+#include "utils/messages.h"
+#include "desktop/plotters.h"
+#include "content/content.h"
+#include "image/bitmap.h"
+
+#include "riscos/gui.h"
+#include "riscos/image.h"
+#include "riscos/palettes.h"
+#include "riscos/content-handlers/sprite.h"
+#include "riscos/tinct.h"
+#include "riscos/bitmap.h"
+
+/** Colour in the overlay sprite that allows the bitmap to show through */
+#define OVERLAY_INDEX 0xfe
+
+/** Size of buffer used when constructing mask data to be saved */
+#define SAVE_CHUNK_SIZE 4096
+
+/**
+ * Whether we can use 32bpp sprites
+ */
+static int thumbnail_32bpp_available = -1;
+
+/**
+ * Sprite output context saving
+ */
+struct thumbnail_save_area {
+ osspriteop_save_area *save_area;
+ int context1;
+ int context2;
+ int context3;
+};
+
+/**
+ * Initialise a bitmaps sprite area.
+ *
+ * \param bitmap the bitmap to initialise
+ * \return true if bitmap initialised else false.
+ */
+
+static bool bitmap_initialise(struct bitmap *bitmap)
+{
+ unsigned int area_size;
+ osspriteop_area *sprite_area;
+ osspriteop_header *sprite;
+
+ assert(!bitmap->sprite_area);
+
+ area_size = 16 + 44 + bitmap->width * bitmap->height * 4;
+ 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;
+
+ /* area control block */
+ sprite_area = bitmap->sprite_area;
+ sprite_area->size = area_size;
+ sprite_area->sprite_count = 1;
+ sprite_area->first = 16;
+ sprite_area->used = area_size;
+
+ /* sprite control block */
+ sprite = (osspriteop_header *) (sprite_area + 1);
+ sprite->size = area_size - 16;
+ memset(sprite->name, 0x00, 12);
+ strncpy(sprite->name, "bitmap", 12);
+ 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 = tinct_SPRITE_MODE;
+
+ return true;
+}
+
+
+/* exported interface documented in riscos/bitmap.h */
+void *riscos_bitmap_create(int width, int height, unsigned int state)
+{
+ struct bitmap *bitmap;
+
+ if (width == 0 || height == 0)
+ return NULL;
+
+ bitmap = calloc(1, sizeof(struct bitmap));
+ if (!bitmap)
+ return NULL;
+ bitmap->width = width;
+ bitmap->height = height;
+ bitmap->state = state;
+
+ return bitmap;
+}
+
+
+/* exported interface documented in riscos/bitmap.h */
+unsigned char *riscos_bitmap_get_buffer(void *vbitmap)
+{
+ struct bitmap *bitmap = (struct bitmap *) vbitmap;
+ assert(bitmap);
+
+ /* dynamically create the buffer */
+ if (bitmap->sprite_area == NULL) {
+ if (!bitmap_initialise(bitmap))
+ return NULL;
+ }
+
+ /* image data area should exist */
+ if (bitmap->sprite_area)
+ return ((unsigned char *) (bitmap->sprite_area)) + 16 + 44;
+
+ return NULL;
+}
+
+
+/**
+ * Sets whether a bitmap should be plotted opaque
+ *
+ * \param vbitmap a bitmap, as returned by bitmap_create()
+ * \param opaque whether the bitmap should be plotted opaque
+ */
+static void bitmap_set_opaque(void *vbitmap, bool opaque)
+{
+ struct bitmap *bitmap = (struct bitmap *) vbitmap;
+ assert(bitmap);
+
+ if (opaque)
+ bitmap->state |= BITMAP_OPAQUE;
+ else
+ bitmap->state &= ~BITMAP_OPAQUE;
+}
+
+
+/**
+ * Find the width of a pixel row in bytes.
+ *
+ * \param vbitmap A bitmap, as returned by riscos_bitmap_create()
+ * \return width of a pixel row in the bitmap
+ */
+static size_t bitmap_get_rowstride(void *vbitmap)
+{
+ struct bitmap *bitmap = (struct bitmap *) vbitmap;
+ return bitmap->width * 4;
+}
+
+
+/**
+ * Tests whether a bitmap has an opaque alpha channel
+ *
+ * \param vbitmap a bitmap, as returned by bitmap_create()
+ * \return whether the bitmap is opaque
+ */
+static bool bitmap_test_opaque(void *vbitmap)
+{
+ struct bitmap *bitmap = (struct bitmap *) vbitmap;
+ unsigned char *sprite;
+ unsigned int width, height, size;
+ osspriteop_header *sprite_header;
+ unsigned *p, *ep;
+
+ assert(bitmap);
+
+ sprite = riscos_bitmap_get_buffer(bitmap);
+ if (!sprite)
+ return false;
+
+ width = bitmap_get_rowstride(bitmap);
+
+ sprite_header = (osspriteop_header *) (bitmap->sprite_area + 1);
+
+ height = (sprite_header->height + 1);
+
+ size = width * height;
+
+ p = (void *) sprite;
+
+ ep = (void *) (sprite + (size & ~31));
+ while (p < ep) {
+ /* \todo prefetch(p, 128)? */
+ if (((p[0] & p[1] & p[2] & p[3] & p[4] & p[5] & p[6] & p[7])
+ & 0xff000000U) != 0xff000000U)
+ return false;
+ p += 8;
+ }
+
+ ep = (void *) (sprite + size);
+ while (p < ep) {
+ if ((*p & 0xff000000U) != 0xff000000U) return false;
+ p++;
+ }
+
+ return true;
+}
+
+
+/* exported interface documented in riscos/bitmap.h */
+bool riscos_bitmap_get_opaque(void *vbitmap)
+{
+ struct bitmap *bitmap = (struct bitmap *) vbitmap;
+ assert(bitmap);
+ return (bitmap->state & BITMAP_OPAQUE);
+}
+
+
+/* exported interface documented in riscos/bitmap.h */
+void riscos_bitmap_destroy(void *vbitmap)
+{
+ struct bitmap *bitmap = (struct bitmap *) vbitmap;
+
+ assert(bitmap);
+
+ /* destroy bitmap */
+ if (bitmap->sprite_area) {
+ free(bitmap->sprite_area);
+ }
+
+ free(bitmap);
+}
+
+
+/* exported interface documented in riscos/bitmap.h */
+bool riscos_bitmap_save(void *vbitmap, const char *path, unsigned flags)
+{
+ struct bitmap *bitmap = (struct bitmap *) vbitmap;
+ os_error *error;
+
+ if (bitmap == NULL) {
+ ro_warn_user("SaveError", messages_get("SprIsNull"));
+ return false;
+ }
+
+ if (!bitmap->sprite_area) {
+ riscos_bitmap_get_buffer(bitmap);
+ }
+ if (!bitmap->sprite_area)
+ return false;
+
+ if (riscos_bitmap_get_opaque(bitmap)) {
+ error = xosspriteop_save_sprite_file(osspriteop_USER_AREA,
+ (bitmap->sprite_area), path);
+ if (error) {
+ LOG("xosspriteop_save_sprite_file: 0x%x: %s", error->errnum, error->errmess);
+ ro_warn_user("SaveError", error->errmess);
+ return false;
+ }
+ return true;
+ } else {
+ /* to make the saved sprite useful we must convert from 'Tinct'
+ * format to either a bi-level mask or a Select-style full
+ * alpha channel */
+ osspriteop_area *area = bitmap->sprite_area;
+ osspriteop_header *hdr = (void *) ((char *) area + area->first);
+ unsigned width = hdr->width + 1, height = hdr->height + 1;
+ unsigned image_size = height * width * 4;
+ unsigned char *chunk_buf;
+ unsigned *p, *elp, *eip;
+ unsigned mask_size;
+ size_t chunk_pix;
+ struct {
+ osspriteop_area area;
+ osspriteop_header hdr;
+ } file_hdr;
+ os_fw fw;
+
+ /* we only support 32bpp sprites */
+ if ((((unsigned)hdr->mode >> 27)&15) != 6) {
+ assert(!"Unsupported sprite format in bitmap_save");
+ return false;
+ }
+
+ chunk_buf = malloc(SAVE_CHUNK_SIZE);
+ if (!chunk_buf) {
+ ro_warn_user("NoMemory", NULL);
+ return false;
+ }
+
+ file_hdr.area = *area;
+ file_hdr.hdr = *hdr;
+
+ if (flags & BITMAP_SAVE_FULL_ALPHA) {
+ mask_size = ((width + 3) & ~3) * height;
+ chunk_pix = SAVE_CHUNK_SIZE;
+ file_hdr.hdr.mode = (os_mode)((unsigned)file_hdr.hdr.mode
+ | (1U<<31));
+ } else {
+ mask_size = (((width + 31) & ~31)/8) * height;
+ chunk_pix = SAVE_CHUNK_SIZE<<3;
+ file_hdr.hdr.mode = (os_mode)((unsigned)file_hdr.hdr.mode
+ & ~(1U<<31));
+ }
+
+ file_hdr.area.sprite_count = 1;
+ file_hdr.area.first = sizeof(file_hdr.area);
+ file_hdr.area.used = sizeof(file_hdr) + image_size + mask_size;
+
+ file_hdr.hdr.image = sizeof(file_hdr.hdr);
+ file_hdr.hdr.mask = file_hdr.hdr.image + image_size;
+ file_hdr.hdr.size = file_hdr.hdr.mask + mask_size;
+
+ error = xosfind_openoutw(0, path, NULL, &fw);
+ if (error) {
+ LOG("xosfind_openoutw: 0x%x: %s", error->errnum, error->errmess);
+ free(chunk_buf);
+ ro_warn_user("SaveError", error->errmess);
+ return false;
+ }
+
+ p = (void *) ((char *) hdr + hdr->image);
+
+ /* write out the area header, sprite header and image data */
+ error = xosgbpb_writew(fw, (byte*)&file_hdr + 4,
+ sizeof(file_hdr)-4, NULL);
+ if (!error)
+ error = xosgbpb_writew(fw, (byte*)p, image_size, NULL);
+ if (error) {
+ LOG("xosgbpb_writew: 0x%x: %s", error->errnum, error->errmess);
+ free(chunk_buf);
+ xosfind_closew(fw);
+ ro_warn_user("SaveError", error->errmess);
+ return false;
+ }
+
+ /* then write out the mask data in chunks */
+ eip = p + (width * height); /* end of image */
+ elp = p + width; /* end of line */
+
+ while (p < eip) {
+ unsigned char *dp = chunk_buf;
+ unsigned *ep = p + chunk_pix;
+ if (ep > elp) ep = elp;
+
+ if (flags & BITMAP_SAVE_FULL_ALPHA) {
+ while (p < ep) {
+ *dp++ = ((unsigned char*)p)[3];
+ p++;
+ }
+ }
+ else {
+ unsigned char mb = 0;
+ int msh = 0;
+ while (p < ep) {
+ if (((unsigned char*)p)[3]) mb |= (1 << msh);
+ if (++msh >= 8) {
+ *dp++ = mb;
+ msh = 0;
+ mb = 0;
+ }
+ p++;
+ }
+ if (msh > 0) *dp++ = mb;
+ }
+
+ if (p >= elp) { /* end of line yet? */
+ /* align to word boundary */
+ while ((int)dp & 3) *dp++ = 0;
+ /* advance end of line pointer */
+ elp += width;
+ }
+ error = xosgbpb_writew(fw, (byte*)chunk_buf, dp-chunk_buf, NULL);
+ if (error) {
+ LOG("xosgbpb_writew: 0x%x: %s", error->errnum, error->errmess);
+ free(chunk_buf);
+ xosfind_closew(fw);
+ ro_warn_user("SaveError", error->errmess);
+ return false;
+ }
+ }
+
+ error = xosfind_closew(fw);
+ if (error) {
+ LOG("xosfind_closew: 0x%x: %s", error->errnum, error->errmess);
+ ro_warn_user("SaveError", error->errmess);
+ }
+
+ error = xosfile_set_type(path, osfile_TYPE_SPRITE);
+ if (error) {
+ LOG("xosfile_set_type: 0x%x: %s", error->errnum, error->errmess);
+ ro_warn_user("SaveError", error->errmess);
+ }
+
+ free(chunk_buf);
+ return true;
+ }
+}
+
+
+/**
+ * The bitmap image has changed, so flush any persistent cache.
+ *
+ * \param vbitmap a bitmap, as returned by bitmap_create()
+ */
+static void bitmap_modified(void *vbitmap)
+{
+ struct bitmap *bitmap = (struct bitmap *) vbitmap;
+ bitmap->state |= BITMAP_MODIFIED;
+}
+
+
+/**
+ * Get the width of a bitmap.
+ *
+ * \param vbitmap A bitmap, as returned by bitmap_create()
+ * \return The bitmaps width in pixels.
+ */
+static int bitmap_get_width(void *vbitmap)
+{
+ struct bitmap *bitmap = (struct bitmap *) vbitmap;
+ return bitmap->width;
+}
+
+
+/**
+ * Get the height of a bitmap.
+ *
+ * \param vbitmap A bitmap, as returned by bitmap_create()
+ * \return The bitmaps height in pixels.
+ */
+static int bitmap_get_height(void *vbitmap)
+{
+ struct bitmap *bitmap = (struct bitmap *) vbitmap;
+ return bitmap->height;
+}
+
+
+/**
+ * Find the bytes per pixel of a bitmap
+ *
+ * \param vbitmap a bitmap, as returned by bitmap_create()
+ * \return bytes per pixel
+ */
+static size_t bitmap_get_bpp(void *vbitmap)
+{
+ struct bitmap *bitmap = (struct bitmap *)vbitmap;
+ assert(bitmap);
+ return 4;
+}
+
+
+/* exported interface documented in riscos/bitmap.h */
+void riscos_bitmap_overlay_sprite(struct bitmap *bitmap,
+ const osspriteop_header *s)
+{
+ const os_colour *palette;
+ const byte *sp, *mp;
+ bool masked = false;
+ bool alpha = false;
+ os_error *error;
+ int dp_offset;
+ int sp_offset;
+ unsigned *dp;
+ int x, y;
+ int w, h;
+
+ assert(sprite_bpp(s) == 8);
+
+ if ((unsigned)s->mode & 0x80000000U)
+ alpha = true;
+
+ error = xosspriteop_read_sprite_info(osspriteop_PTR,
+ (osspriteop_area *)0x100,
+ (osspriteop_id)s,
+ &w, &h, NULL, NULL);
+ if (error) {
+ LOG("xosspriteop_read_sprite_info: 0x%x:%s", error->errnum, error->errmess);
+ return;
+ }
+ sp_offset = ((s->width + 1) * 4) - w;
+
+ if (w > bitmap->width)
+ w = bitmap->width;
+ if (h > bitmap->height)
+ h = bitmap->height;
+
+ dp_offset = bitmap_get_rowstride(bitmap) / 4;
+
+ dp = (void*)riscos_bitmap_get_buffer(bitmap);
+ if (!dp)
+ return;
+ sp = (byte*)s + s->image;
+ mp = (byte*)s + s->mask;
+
+ sp += s->left_bit / 8;
+ mp += s->left_bit / 8;
+
+ if (s->image > (int)sizeof(*s))
+ palette = (os_colour*)(s + 1);
+ else
+ palette = default_palette8;
+
+ if (s->mask != s->image) {
+ masked = true;
+ bitmap_set_opaque(bitmap, false);
+ }
+
+ /* (partially-)transparent pixels in the overlayed sprite retain
+ * their transparency in the output bitmap; opaque sprite pixels
+ * are also propagated to the bitmap, except those which are the
+ * OVERLAY_INDEX colour which allow the original bitmap contents to
+ * show through */
+ for (y = 0; y < h; y++) {
+ unsigned *sdp = dp;
+ for(x = 0; x < w; x++) {
+ os_colour d = ((unsigned)palette[(*sp) << 1]) >> 8;
+ if (*sp++ == OVERLAY_INDEX)
+ d = *dp;
+ if (masked) {
+ if (alpha)
+ d |= ((*mp << 24) ^ 0xff000000U);
+ else if (*mp)
+ d |= 0xff000000U;
+ }
+ *dp++ = d;
+ mp++;
+ }
+ dp = sdp + dp_offset;
+ sp += sp_offset;
+ mp += sp_offset;
+ }
+}
+
+
+/**
+ * Creates an 8bpp canvas.
+ *
+ * \param bitmap the bitmap to clone the size of
+ * \return a sprite area containing an 8bpp sprite
+ */
+static osspriteop_area *thumbnail_create_8bpp(struct bitmap *bitmap)
+{
+ unsigned image_size = ((bitmap->width + 3) & ~3) * bitmap->height;
+ bool opaque = riscos_bitmap_get_opaque(bitmap);
+ osspriteop_header *sprite_header = NULL;
+ osspriteop_area *sprite_area = NULL;
+ unsigned area_size;
+
+ /* clone the sprite */
+ area_size = sizeof(osspriteop_area) +
+ sizeof(osspriteop_header) +
+ image_size +
+ 2048;
+
+ if (!opaque) area_size += image_size;
+
+ sprite_area = (osspriteop_area *)malloc(area_size);
+ if (!sprite_area) {
+ LOG("no memory for malloc()");
+ return NULL;
+ }
+ sprite_area->size = area_size;
+ sprite_area->sprite_count = 1;
+ sprite_area->first = 16;
+ sprite_area->used = area_size;
+ sprite_header = (osspriteop_header *)(sprite_area + 1);
+ sprite_header->size = area_size - sizeof(osspriteop_area);
+ memset(sprite_header->name, 0x00, 12);
+ strcpy(sprite_header->name, "bitmap");
+ sprite_header->left_bit = 0;
+ sprite_header->height = bitmap->height - 1;
+ sprite_header->mode = os_MODE8BPP90X90;
+ sprite_header->right_bit = ((bitmap->width << 3) - 1) & 31;
+ sprite_header->width = ((bitmap->width + 3) >> 2) - 1;
+ sprite_header->image = sizeof(osspriteop_header) + 2048;
+ sprite_header->mask = sizeof(osspriteop_header) + 2048;
+ if (!opaque) sprite_header->mask += image_size;
+
+ /* create the palette. we don't read the necessary size like
+ * we really should as we know it's going to have 256 entries
+ * of 8 bytes = 2048. */
+ xcolourtrans_read_palette((osspriteop_area *)os_MODE8BPP90X90,
+ (osspriteop_id)0,
+ (os_palette *)(sprite_header + 1), 2048,
+ (colourtrans_palette_flags)(1 << 1), 0);
+ return sprite_area;
+}
+
+
+/**
+ * Switches output to the specified sprite and returns the previous context.
+ */
+static struct thumbnail_save_area*
+thumbnail_switch_output(osspriteop_area *sprite_area,
+ osspriteop_header *sprite_header)
+{
+ struct thumbnail_save_area *save_area;
+ int size;
+
+ /* create a save area */
+ save_area = calloc(sizeof(struct thumbnail_save_area), 1);
+ if (save_area == NULL) return NULL;
+
+ /* allocate OS_SpriteOp save area */
+ if (xosspriteop_read_save_area_size(osspriteop_PTR, sprite_area,
+ (osspriteop_id)sprite_header, &size)) {
+ free(save_area);
+ return NULL;
+ }
+
+ /* create the save area */
+ save_area->save_area = malloc((unsigned)size);
+ if (save_area->save_area == NULL) {
+ free(save_area);
+ return NULL;
+ }
+ save_area->save_area->a[0] = 0;
+
+ /* switch output to sprite */
+ if (xosspriteop_switch_output_to_sprite(osspriteop_PTR, sprite_area,
+ (osspriteop_id)sprite_header, save_area->save_area,
+ 0, &save_area->context1, &save_area->context2,
+ &save_area->context3)) {
+ free(save_area->save_area);
+ free(save_area);
+ return NULL;
+ }
+ return save_area;
+}
+
+
+/**
+ * Restores output to the specified context, and destroys it.
+ */
+static void thumbnail_restore_output(struct thumbnail_save_area *save_area)
+{
+ /* we don't care if we err, as there's nothing we can do about it */
+ xosspriteop_switch_output_to_sprite(osspriteop_PTR,
+ (osspriteop_area *)save_area->context1,
+ (osspriteop_id)save_area->context2,
+ (osspriteop_save_area *)save_area->context3,
+ 0, 0, 0, 0);
+ free(save_area->save_area);
+ free(save_area);
+}
+
+
+/**
+ * Convert a bitmap to 8bpp.
+ *
+ * \param bitmap the bitmap to convert
+ * \return a sprite area containing an 8bpp sprite
+ */
+osspriteop_area *riscos_bitmap_convert_8bpp(struct bitmap *bitmap)
+{
+ struct thumbnail_save_area *save_area;
+ osspriteop_area *sprite_area = NULL;
+ osspriteop_header *sprite_header = NULL;
+
+ sprite_area = thumbnail_create_8bpp(bitmap);
+ if (!sprite_area)
+ return NULL;
+ sprite_header = (osspriteop_header *)(sprite_area + 1);
+
+
+ /* switch output and redraw */
+ save_area = thumbnail_switch_output(sprite_area, sprite_header);
+ if (save_area == NULL) {
+ if (thumbnail_32bpp_available != 1)
+ free(sprite_area);
+ return false;
+ }
+ _swix(Tinct_Plot, _IN(2) | _IN(3) | _IN(4) | _IN(7),
+ (osspriteop_header *)(bitmap->sprite_area + 1),
+ 0, 0,
+ tinct_ERROR_DIFFUSE);
+ thumbnail_restore_output(save_area);
+
+ if (sprite_header->image != sprite_header->mask) {
+ /* build the sprite mask from the alpha channel */
+ void *buf = riscos_bitmap_get_buffer(bitmap);
+ unsigned *dp = (unsigned *) buf;
+ if (!dp)
+ return sprite_area;
+ int w = bitmap_get_width(bitmap);
+ int h = bitmap_get_height(bitmap);
+ int dp_offset = bitmap_get_rowstride(bitmap) / 4 - w;
+ int mp_offset = ((sprite_header->width + 1) * 4) - w;
+ byte *mp = (byte*)sprite_header + sprite_header->mask;
+ bool alpha = ((unsigned)sprite_header->mode & 0x80000000U) != 0;
+
+ while (h-- > 0) {
+ int x = 0;
+ for(x = 0; x < w; x++) {
+ unsigned d = *dp++;
+ if (alpha)
+ *mp++ = (d >> 24) ^ 0xff;
+ else
+ *mp++ = (d < 0xff000000U) ? 0 : 0xff;
+ }
+ dp += dp_offset;
+ mp += mp_offset;
+ }
+ }
+
+ return sprite_area;
+}
+
+
+
+
+/**
+ * Check to see whether 32bpp sprites are available.
+ *
+ * Rather than using Wimp_ReadSysInfo we test if 32bpp sprites are available
+ * in case the user has a 3rd party patch to enable them.
+ */
+static void thumbnail_test(void)
+{
+ unsigned int area_size;
+ osspriteop_area *sprite_area;
+
+ /* try to create a 1x1 32bpp sprite */
+ area_size = sizeof(osspriteop_area) +
+ sizeof(osspriteop_header) + sizeof(int);
+ if ((sprite_area = (osspriteop_area *)malloc(area_size)) == NULL) {
+ LOG("Insufficient memory to perform sprite test.");
+ return;
+ }
+ sprite_area->size = area_size + 1;
+ sprite_area->sprite_count = 0;
+ sprite_area->first = 16;
+ sprite_area->used = 16;
+ if (xosspriteop_create_sprite(osspriteop_NAME, sprite_area,
+ "test", false, 1, 1, (os_mode)tinct_SPRITE_MODE))
+ thumbnail_32bpp_available = 0;
+ else
+ thumbnail_32bpp_available = 1;
+ free(sprite_area);
+}
+
+
+/* exported interface documented in riscos/bitmap.h */
+nserror riscos_bitmap_render(struct bitmap *bitmap,
+ struct hlcache_handle *content)
+{
+ struct thumbnail_save_area *save_area;
+ osspriteop_area *sprite_area = NULL;
+ osspriteop_header *sprite_header = NULL;
+ struct redraw_context ctx = {
+ .interactive = false,
+ .background_images = true,
+ .plot = &ro_plotters
+ };
+
+ assert(content);
+ assert(bitmap);
+
+ LOG("content %p in bitmap %p", content, bitmap);
+
+ /* check if we have access to 32bpp sprites natively */
+ if (thumbnail_32bpp_available == -1) {
+ thumbnail_test();
+ }
+
+ /* if we don't support 32bpp sprites then we redirect to an 8bpp
+ * image and then convert back.
+ */
+ if (thumbnail_32bpp_available != 1) {
+ sprite_area = thumbnail_create_8bpp(bitmap);
+ if (!sprite_area)
+ return false;
+ sprite_header = (osspriteop_header *)(sprite_area + 1);
+ } else {
+ const uint8_t *pixbufp = riscos_bitmap_get_buffer(bitmap);
+ if (!pixbufp || !bitmap->sprite_area)
+ return false;
+ sprite_area = bitmap->sprite_area;
+ sprite_header = (osspriteop_header *)(sprite_area + 1);
+ }
+
+ /* set up the plotters */
+ ro_plot_origin_x = 0;
+ ro_plot_origin_y = bitmap->height * 2;
+
+ /* switch output and redraw */
+ save_area = thumbnail_switch_output(sprite_area, sprite_header);
+ if (!save_area) {
+ if (thumbnail_32bpp_available != 1)
+ free(sprite_area);
+ return false;
+ }
+ rufl_invalidate_cache();
+ colourtrans_set_gcol(os_COLOUR_WHITE, colourtrans_SET_BG_GCOL,
+ os_ACTION_OVERWRITE, 0);
+
+ /* render the content */
+ content_scaled_redraw(content, bitmap->width, bitmap->height, &ctx);
+
+ thumbnail_restore_output(save_area);
+ rufl_invalidate_cache();
+
+ /* if we changed to 8bpp then go back to 32bpp */
+ if (thumbnail_32bpp_available != 1) {
+ const uint8_t *pixbufp = riscos_bitmap_get_buffer(bitmap);
+ _kernel_oserror *error;
+
+ if (!pixbufp || !bitmap->sprite_area) {
+ free(sprite_area);
+ return false;
+ }
+ error = _swix(Tinct_ConvertSprite, _INR(2,3),
+ sprite_header,
+ (osspriteop_header *)(bitmap->sprite_area + 1));
+ free(sprite_area);
+ if (error)
+ return false;
+ }
+
+ bitmap_modified(bitmap);
+
+ return NSERROR_OK;
+}
+
+static struct gui_bitmap_table bitmap_table = {
+ .create = riscos_bitmap_create,
+ .destroy = riscos_bitmap_destroy,
+ .set_opaque = bitmap_set_opaque,
+ .get_opaque = riscos_bitmap_get_opaque,
+ .test_opaque = bitmap_test_opaque,
+ .get_buffer = riscos_bitmap_get_buffer,
+ .get_rowstride = bitmap_get_rowstride,
+ .get_width = bitmap_get_width,
+ .get_height = bitmap_get_height,
+ .get_bpp = bitmap_get_bpp,
+ .save = riscos_bitmap_save,
+ .modified = bitmap_modified,
+ .render = riscos_bitmap_render,
+};
+
+struct gui_bitmap_table *riscos_bitmap_table = &bitmap_table;