netsurf/riscos/bitmap.c
Vincent Sanders c105738fa3 Change LOG() macro to be varadic
This changes the LOG macro to be varadic removing the need for all
callsites to have double bracketing and allows for future improvement
on how we use the logging macros.

The callsites were changed with coccinelle and the changes checked by
hand. Compile tested for several frontends but not all.

A formatting annotation has also been added which allows the compiler
to check the parameters and types passed to the logging.
2015-05-28 16:08:46 +01:00

872 lines
22 KiB
C

/*
* 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/utils.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->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);
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) {
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);
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);
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);
warn_user("SaveError", error->errmess);
return false;
}
}
error = xosfind_closew(fw);
if (error) {
LOG("xosfind_closew: 0x%x: %s", error->errnum, error->errmess);
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);
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;