Fix against latest Netsurf - eukara
This commit is contained in:
parent
fdcff90d09
commit
cbd3105023
|
@ -35,6 +35,7 @@
|
|||
#include "content/content_protected.h"
|
||||
#include "content/content_factory.h"
|
||||
#include "desktop/gui_internal.h"
|
||||
#include "desktop/bitmap.h"
|
||||
|
||||
#include "image/bmp.h"
|
||||
|
||||
|
@ -57,12 +58,12 @@ typedef struct nsbmp_content {
|
|||
*/
|
||||
static void *nsbmp_bitmap_create(int width, int height, unsigned int bmp_state)
|
||||
{
|
||||
unsigned int bitmap_state = BITMAP_NEW;
|
||||
unsigned int bitmap_state = BITMAP_NONE;
|
||||
|
||||
/* set bitmap state based on bmp state */
|
||||
bitmap_state |= (bmp_state & BMP_OPAQUE) ? BITMAP_OPAQUE : 0;
|
||||
bitmap_state |= (bmp_state & BMP_CLEAR_MEMORY) ?
|
||||
BITMAP_CLEAR_MEMORY : 0;
|
||||
BITMAP_CLEAR : 0;
|
||||
|
||||
/* return the created bitmap */
|
||||
return guit->bitmap->create(width, height, bitmap_state);
|
||||
|
@ -74,7 +75,6 @@ static nserror nsbmp_create_bmp_data(nsbmp_content *bmp)
|
|||
.bitmap_create = nsbmp_bitmap_create,
|
||||
.bitmap_destroy = guit->bitmap->destroy,
|
||||
.bitmap_get_buffer = guit->bitmap->get_buffer,
|
||||
.bitmap_get_bpp = guit->bitmap->get_bpp
|
||||
};
|
||||
|
||||
bmp->bmp = calloc(sizeof(struct bmp_image), 1);
|
||||
|
@ -151,8 +151,7 @@ static bool nsbmp_convert(struct content *c)
|
|||
/* Store our content width and description */
|
||||
c->width = bmp->bmp->width;
|
||||
c->height = bmp->bmp->height;
|
||||
swidth = bmp->bmp->bitmap_callbacks.bitmap_get_bpp(bmp->bmp->bitmap) *
|
||||
bmp->bmp->width;
|
||||
swidth = sizeof(uint32_t) * bmp->bmp->width;
|
||||
c->size += (swidth * bmp->bmp->height) + 16 + 44;
|
||||
|
||||
/* set title text */
|
||||
|
@ -191,6 +190,9 @@ static bool nsbmp_redraw(struct content *c, struct content_redraw_data *data,
|
|||
return false;
|
||||
}
|
||||
|
||||
bitmap_format_to_client(bmp->bitmap, &(bitmap_fmt_t) {
|
||||
.layout = BITMAP_LAYOUT_R8G8B8A8,
|
||||
});
|
||||
guit->bitmap->modified(bmp->bitmap);
|
||||
}
|
||||
|
||||
|
|
|
@ -34,8 +34,12 @@
|
|||
#include <string.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdlib.h>
|
||||
#include <libnsgif.h>
|
||||
|
||||
#include <nsutils/assert.h>
|
||||
|
||||
#include <nsgif.h>
|
||||
|
||||
#include "utils/log.h"
|
||||
#include "utils/utils.h"
|
||||
#include "utils/messages.h"
|
||||
#include "utils/nsoption.h"
|
||||
|
@ -47,17 +51,36 @@
|
|||
#include "content/content_protected.h"
|
||||
#include "content/content_factory.h"
|
||||
#include "desktop/gui_internal.h"
|
||||
#include "desktop/bitmap.h"
|
||||
|
||||
#include "image/image.h"
|
||||
#include "image/gif.h"
|
||||
|
||||
typedef struct nsgif_content {
|
||||
typedef struct gif_content {
|
||||
struct content base;
|
||||
|
||||
struct gif_animation *gif; /**< GIF animation data */
|
||||
int current_frame; /**< current frame to display [0...(max-1)] */
|
||||
} nsgif_content;
|
||||
nsgif_t *gif; /**< GIF animation data */
|
||||
uint32_t current_frame; /**< current frame to display [0...(max-1)] */
|
||||
} gif_content;
|
||||
|
||||
static inline nserror gif__nsgif_error_to_ns(nsgif_error gif_res)
|
||||
{
|
||||
nserror err;
|
||||
|
||||
switch (gif_res) {
|
||||
case NSGIF_OK:
|
||||
err = NSERROR_OK;
|
||||
break;
|
||||
case NSGIF_ERR_OOM:
|
||||
err = NSERROR_NOMEM;
|
||||
break;
|
||||
default:
|
||||
err = NSERROR_GIF_ERROR;
|
||||
break;
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
/**
|
||||
* Callback for libnsgif; forwards the call to bitmap_create()
|
||||
|
@ -66,44 +89,60 @@ typedef struct nsgif_content {
|
|||
* \param height width of image in pixels
|
||||
* \return an opaque struct bitmap, or NULL on memory exhaustion
|
||||
*/
|
||||
static void *nsgif_bitmap_create(int width, int height)
|
||||
static void *gif_bitmap_create(int width, int height)
|
||||
{
|
||||
return guit->bitmap->create(width, height, BITMAP_NEW);
|
||||
return guit->bitmap->create(width, height, BITMAP_NONE);
|
||||
}
|
||||
|
||||
|
||||
static nserror nsgif_create_gif_data(nsgif_content *c)
|
||||
/**
|
||||
* Convert client bitmap format to a LibNSGIF format specifier.
|
||||
*/
|
||||
static nsgif_bitmap_fmt_t nsgif__get_bitmap_format(void)
|
||||
{
|
||||
gif_bitmap_callback_vt gif_bitmap_callbacks = {
|
||||
.bitmap_create = nsgif_bitmap_create,
|
||||
.bitmap_destroy = guit->bitmap->destroy,
|
||||
.bitmap_get_buffer = guit->bitmap->get_buffer,
|
||||
.bitmap_set_opaque = guit->bitmap->set_opaque,
|
||||
.bitmap_test_opaque = guit->bitmap->test_opaque,
|
||||
.bitmap_modified = guit->bitmap->modified
|
||||
ns_static_assert((int)BITMAP_LAYOUT_R8G8B8A8 == (int)NSGIF_BITMAP_FMT_R8G8B8A8);
|
||||
ns_static_assert((int)BITMAP_LAYOUT_B8G8R8A8 == (int)NSGIF_BITMAP_FMT_B8G8R8A8);
|
||||
ns_static_assert((int)BITMAP_LAYOUT_A8R8G8B8 == (int)NSGIF_BITMAP_FMT_A8R8G8B8);
|
||||
ns_static_assert((int)BITMAP_LAYOUT_A8B8G8R8 == (int)NSGIF_BITMAP_FMT_A8B8G8R8);
|
||||
ns_static_assert((int)BITMAP_LAYOUT_RGBA8888 == (int)NSGIF_BITMAP_FMT_RGBA8888);
|
||||
ns_static_assert((int)BITMAP_LAYOUT_BGRA8888 == (int)NSGIF_BITMAP_FMT_BGRA8888);
|
||||
ns_static_assert((int)BITMAP_LAYOUT_ARGB8888 == (int)NSGIF_BITMAP_FMT_ARGB8888);
|
||||
ns_static_assert((int)BITMAP_LAYOUT_ABGR8888 == (int)NSGIF_BITMAP_FMT_ABGR8888);
|
||||
|
||||
return (nsgif_bitmap_fmt_t)bitmap_fmt.layout;
|
||||
}
|
||||
|
||||
static nserror gif_create_gif_data(gif_content *c)
|
||||
{
|
||||
nsgif_error gif_res;
|
||||
const nsgif_bitmap_cb_vt gif_bitmap_callbacks = {
|
||||
.create = gif_bitmap_create,
|
||||
.destroy = guit->bitmap->destroy,
|
||||
.get_buffer = guit->bitmap->get_buffer,
|
||||
.set_opaque = guit->bitmap->set_opaque,
|
||||
.test_opaque = bitmap_test_opaque,
|
||||
.modified = guit->bitmap->modified,
|
||||
};
|
||||
|
||||
/* Initialise our data structure */
|
||||
c->gif = calloc(sizeof(gif_animation), 1);
|
||||
if (c->gif == NULL) {
|
||||
content_broadcast_error(&c->base, NSERROR_NOMEM, NULL);
|
||||
return NSERROR_NOMEM;
|
||||
gif_res = nsgif_create(&gif_bitmap_callbacks,
|
||||
nsgif__get_bitmap_format(), &c->gif);
|
||||
if (gif_res != NSGIF_OK) {
|
||||
nserror err = gif__nsgif_error_to_ns(gif_res);
|
||||
content_broadcast_error(&c->base, err, NULL);
|
||||
return err;
|
||||
}
|
||||
gif_create(c->gif, &gif_bitmap_callbacks);
|
||||
|
||||
return NSERROR_OK;
|
||||
}
|
||||
|
||||
|
||||
|
||||
static nserror nsgif_create(const content_handler *handler,
|
||||
lwc_string *imime_type, const struct http_parameter *params,
|
||||
static nserror gif_create(const content_handler *handler,
|
||||
lwc_string *imime_type, const struct http_parameter *params,
|
||||
llcache_handle *llcache, const char *fallback_charset,
|
||||
bool quirks, struct content **c)
|
||||
{
|
||||
nsgif_content *result;
|
||||
gif_content *result;
|
||||
nserror error;
|
||||
|
||||
result = calloc(1, sizeof(nsgif_content));
|
||||
result = calloc(1, sizeof(gif_content));
|
||||
if (result == NULL)
|
||||
return NSERROR_NOMEM;
|
||||
|
||||
|
@ -114,7 +153,7 @@ static nserror nsgif_create(const content_handler *handler,
|
|||
return error;
|
||||
}
|
||||
|
||||
error = nsgif_create_gif_data(result);
|
||||
error = gif_create_gif_data(result);
|
||||
if (error != NSERROR_OK) {
|
||||
free(result);
|
||||
return error;
|
||||
|
@ -125,100 +164,66 @@ static nserror nsgif_create(const content_handler *handler,
|
|||
return NSERROR_OK;
|
||||
}
|
||||
|
||||
/**
|
||||
* Scheduler callback. Performs any necessary animation.
|
||||
*
|
||||
* \param p The content to animate
|
||||
*/
|
||||
static void gif_animate_cb(void *p);
|
||||
|
||||
/**
|
||||
* Performs any necessary animation.
|
||||
*
|
||||
* \param p The content to animate
|
||||
*/
|
||||
static void nsgif_animate(void *p)
|
||||
static nserror gif__animate(gif_content *gif, bool redraw)
|
||||
{
|
||||
nsgif_content *gif = p;
|
||||
union content_msg_data data;
|
||||
int delay;
|
||||
int f;
|
||||
nsgif_error gif_res;
|
||||
nsgif_rect_t rect;
|
||||
uint32_t delay;
|
||||
uint32_t f;
|
||||
|
||||
/* Advance by a frame, updating the loop count accordingly */
|
||||
gif->current_frame++;
|
||||
if (gif->current_frame == (int)gif->gif->frame_count_partial) {
|
||||
gif->current_frame = 0;
|
||||
|
||||
/* A loop count of 0 has a special meaning of infinite */
|
||||
if (gif->gif->loop_count != 0) {
|
||||
gif->gif->loop_count--;
|
||||
if (gif->gif->loop_count == 0) {
|
||||
gif->current_frame =
|
||||
gif->gif->frame_count_partial - 1;
|
||||
gif->gif->loop_count = -1;
|
||||
}
|
||||
}
|
||||
gif_res = nsgif_frame_prepare(gif->gif, &rect, &delay, &f);
|
||||
if (gif_res != NSGIF_OK) {
|
||||
return gif__nsgif_error_to_ns(gif_res);
|
||||
}
|
||||
|
||||
gif->current_frame = f;
|
||||
|
||||
/* Continue animating if we should */
|
||||
if (gif->gif->loop_count >= 0) {
|
||||
delay = gif->gif->frames[gif->current_frame].frame_delay;
|
||||
if (delay <= 1) {
|
||||
/* Assuming too fast to be intended, set default. */
|
||||
delay = 10;
|
||||
}
|
||||
guit->misc->schedule(delay * 10, nsgif_animate, gif);
|
||||
if (nsoption_bool(animate_images) && delay != NSGIF_INFINITE) {
|
||||
guit->misc->schedule(delay * 10, gif_animate_cb, gif);
|
||||
}
|
||||
|
||||
if ((!nsoption_bool(animate_images)) ||
|
||||
(!gif->gif->frames[gif->current_frame].display)) {
|
||||
return;
|
||||
if (redraw) {
|
||||
union content_msg_data data;
|
||||
|
||||
/* area within gif to redraw */
|
||||
data.redraw.x = rect.x0;
|
||||
data.redraw.y = rect.y0;
|
||||
data.redraw.width = rect.x1 - rect.x0;
|
||||
data.redraw.height = rect.y1 - rect.y0;
|
||||
|
||||
content_broadcast(&gif->base, CONTENT_MSG_REDRAW, &data);
|
||||
}
|
||||
|
||||
/* area within gif to redraw */
|
||||
f = gif->current_frame;
|
||||
data.redraw.x = gif->gif->frames[f].redraw_x;
|
||||
data.redraw.y = gif->gif->frames[f].redraw_y;
|
||||
data.redraw.width = gif->gif->frames[f].redraw_width;
|
||||
data.redraw.height = gif->gif->frames[f].redraw_height;
|
||||
|
||||
/* redraw background (true) or plot on top (false) */
|
||||
if (gif->current_frame > 0) {
|
||||
/* previous frame needed clearing: expand the redraw area to
|
||||
* cover it */
|
||||
if (gif->gif->frames[f - 1].redraw_required) {
|
||||
if (data.redraw.x >
|
||||
(int)(gif->gif->frames[f - 1].redraw_x)) {
|
||||
data.redraw.width += data.redraw.x -
|
||||
gif->gif->frames[f - 1].redraw_x;
|
||||
data.redraw.x =
|
||||
gif->gif->frames[f - 1].redraw_x;
|
||||
}
|
||||
if (data.redraw.y >
|
||||
(int)(gif->gif->frames[f - 1].redraw_y)) {
|
||||
data.redraw.height += (data.redraw.y -
|
||||
gif->gif->frames[f - 1].redraw_y);
|
||||
data.redraw.y =
|
||||
gif->gif->frames[f - 1].redraw_y;
|
||||
}
|
||||
if ((int)(gif->gif->frames[f - 1].redraw_x +
|
||||
gif->gif->frames[f - 1].redraw_width) >
|
||||
(data.redraw.x + data.redraw.width))
|
||||
data.redraw.width =
|
||||
gif->gif->frames[f - 1].redraw_x -
|
||||
data.redraw.x +
|
||||
gif->gif->frames[f - 1].redraw_width;
|
||||
if ((int)(gif->gif->frames[f - 1].redraw_y +
|
||||
gif->gif->frames[f - 1].redraw_height) >
|
||||
(data.redraw.y + data.redraw.height))
|
||||
data.redraw.height =
|
||||
gif->gif->frames[f - 1].redraw_y -
|
||||
data.redraw.y +
|
||||
gif->gif->frames[f - 1].redraw_height;
|
||||
}
|
||||
}
|
||||
|
||||
content_broadcast(&gif->base, CONTENT_MSG_REDRAW, &data);
|
||||
return NSERROR_OK;
|
||||
}
|
||||
|
||||
static bool nsgif_convert(struct content *c)
|
||||
static void gif_animate_cb(void *p)
|
||||
{
|
||||
nsgif_content *gif = (nsgif_content *) c;
|
||||
int res;
|
||||
gif_content *gif = p;
|
||||
|
||||
gif__animate(gif, true);
|
||||
}
|
||||
|
||||
static bool gif_convert(struct content *c)
|
||||
{
|
||||
gif_content *gif = (gif_content *) c;
|
||||
const nsgif_info_t *gif_info;
|
||||
const uint8_t *data;
|
||||
nsgif_error gif_err;
|
||||
nserror err;
|
||||
size_t size;
|
||||
char *title;
|
||||
|
||||
|
@ -226,37 +231,30 @@ static bool nsgif_convert(struct content *c)
|
|||
data = content__get_source_data(c, &size);
|
||||
|
||||
/* Initialise the GIF */
|
||||
do {
|
||||
res = gif_initialise(gif->gif, size, (unsigned char *) data);
|
||||
if (res != GIF_OK && res != GIF_WORKING &&
|
||||
res != GIF_INSUFFICIENT_FRAME_DATA) {
|
||||
nserror error = NSERROR_UNKNOWN;
|
||||
switch (res) {
|
||||
case GIF_FRAME_DATA_ERROR:
|
||||
case GIF_INSUFFICIENT_DATA:
|
||||
case GIF_DATA_ERROR:
|
||||
error = NSERROR_GIF_ERROR;
|
||||
break;
|
||||
case GIF_INSUFFICIENT_MEMORY:
|
||||
error = NSERROR_NOMEM;
|
||||
break;
|
||||
}
|
||||
content_broadcast_error(c, error, NULL);
|
||||
return false;
|
||||
}
|
||||
} while (res != GIF_OK && res != GIF_INSUFFICIENT_FRAME_DATA);
|
||||
gif_err = nsgif_data_scan(gif->gif, size, data);
|
||||
if (gif_err != NSGIF_OK) {
|
||||
NSLOG(netsurf, DEBUG, "%s", nsgif_strerror(gif_err));
|
||||
/* Not fatal unless er have no frames. */
|
||||
}
|
||||
|
||||
gif_info = nsgif_get_info(gif->gif);
|
||||
assert(gif_info != NULL);
|
||||
|
||||
/* Abort on bad GIFs */
|
||||
if ((gif->gif->frame_count_partial == 0) || (gif->gif->width == 0) ||
|
||||
(gif->gif->height == 0)) {
|
||||
content_broadcast_error(c, NSERROR_GIF_ERROR, NULL);
|
||||
if (gif_info->frame_count == 0) {
|
||||
err = gif__nsgif_error_to_ns(gif_err);
|
||||
content_broadcast_error(c, err, "GIF with no frames.");
|
||||
return false;
|
||||
} else if (gif_info->width == 0 || gif_info->height == 0) {
|
||||
err = gif__nsgif_error_to_ns(gif_err);
|
||||
content_broadcast_error(c, err, "Zero size image.");
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Store our content width, height and calculate size */
|
||||
c->width = gif->gif->width;
|
||||
c->height = gif->gif->height;
|
||||
c->size += (gif->gif->width * gif->gif->height * 4) + 16 + 44;
|
||||
c->width = gif_info->width;
|
||||
c->height = gif_info->height;
|
||||
c->size += (gif_info->width * gif_info->height * 4) + 16 + 44;
|
||||
|
||||
/* set title text */
|
||||
title = messages_get_buff("GIFTitle",
|
||||
|
@ -267,12 +265,11 @@ static bool nsgif_convert(struct content *c)
|
|||
free(title);
|
||||
}
|
||||
|
||||
/* Schedule the animation if we have one */
|
||||
gif->current_frame = 0;
|
||||
if (gif->gif->frame_count_partial > 1)
|
||||
guit->misc->schedule(gif->gif->frames[0].frame_delay * 10,
|
||||
nsgif_animate,
|
||||
c);
|
||||
err = gif__animate(gif, false);
|
||||
if (err != NSERROR_OK) {
|
||||
content_broadcast_error(c, NSERROR_GIF_ERROR, NULL);
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Exit as a success */
|
||||
content_set_ready(c);
|
||||
|
@ -283,68 +280,51 @@ static bool nsgif_convert(struct content *c)
|
|||
return true;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Updates the GIF bitmap to display the current frame
|
||||
*
|
||||
* \param gif The gif context to update.
|
||||
* \return GIF_OK on success else apropriate error code.
|
||||
* \return NSGIF_OK on success else apropriate error code.
|
||||
*/
|
||||
static gif_result nsgif_get_frame(nsgif_content *gif)
|
||||
static nsgif_error gif_get_frame(gif_content *gif,
|
||||
nsgif_bitmap_t **bitmap)
|
||||
{
|
||||
int previous_frame, current_frame, frame;
|
||||
gif_result res = GIF_OK;
|
||||
|
||||
current_frame = gif->current_frame;
|
||||
uint32_t current_frame = gif->current_frame;
|
||||
if (!nsoption_bool(animate_images)) {
|
||||
current_frame = 0;
|
||||
}
|
||||
|
||||
if (current_frame < gif->gif->decoded_frame) {
|
||||
previous_frame = 0;
|
||||
} else {
|
||||
previous_frame = gif->gif->decoded_frame + 1;
|
||||
}
|
||||
|
||||
for (frame = previous_frame; frame <= current_frame; frame++) {
|
||||
res = gif_decode_frame(gif->gif, frame);
|
||||
}
|
||||
|
||||
return res;
|
||||
return nsgif_frame_decode(gif->gif, current_frame, bitmap);
|
||||
}
|
||||
|
||||
static bool nsgif_redraw(struct content *c, struct content_redraw_data *data,
|
||||
static bool gif_redraw(struct content *c, struct content_redraw_data *data,
|
||||
const struct rect *clip, const struct redraw_context *ctx)
|
||||
{
|
||||
nsgif_content *gif = (nsgif_content *) c;
|
||||
gif_content *gif = (gif_content *) c;
|
||||
nsgif_bitmap_t *bitmap;
|
||||
|
||||
if (gif->current_frame != gif->gif->decoded_frame) {
|
||||
if (nsgif_get_frame(gif) != GIF_OK) {
|
||||
return false;
|
||||
}
|
||||
if (gif_get_frame(gif, &bitmap) != NSGIF_OK) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return image_bitmap_plot(gif->gif->frame_image, data, clip, ctx);
|
||||
return image_bitmap_plot(bitmap, data, clip, ctx);
|
||||
}
|
||||
|
||||
|
||||
static void nsgif_destroy(struct content *c)
|
||||
static void gif_destroy(struct content *c)
|
||||
{
|
||||
nsgif_content *gif = (nsgif_content *) c;
|
||||
gif_content *gif = (gif_content *) c;
|
||||
|
||||
/* Free all the associated memory buffers */
|
||||
guit->misc->schedule(-1, nsgif_animate, c);
|
||||
gif_finalise(gif->gif);
|
||||
free(gif->gif);
|
||||
guit->misc->schedule(-1, gif_animate_cb, c);
|
||||
nsgif_destroy(gif->gif);
|
||||
}
|
||||
|
||||
|
||||
static nserror nsgif_clone(const struct content *old, struct content **newc)
|
||||
static nserror gif_clone(const struct content *old, struct content **newc)
|
||||
{
|
||||
nsgif_content *gif;
|
||||
gif_content *gif;
|
||||
nserror error;
|
||||
|
||||
gif = calloc(1, sizeof(nsgif_content));
|
||||
gif = calloc(1, sizeof(gif_content));
|
||||
if (gif == NULL)
|
||||
return NSERROR_NOMEM;
|
||||
|
||||
|
@ -355,7 +335,7 @@ static nserror nsgif_clone(const struct content *old, struct content **newc)
|
|||
}
|
||||
|
||||
/* Simply replay creation and conversion of content */
|
||||
error = nsgif_create_gif_data(gif);
|
||||
error = gif_create_gif_data(gif);
|
||||
if (error != NSERROR_OK) {
|
||||
content_destroy(&gif->base);
|
||||
return error;
|
||||
|
@ -363,7 +343,7 @@ static nserror nsgif_clone(const struct content *old, struct content **newc)
|
|||
|
||||
if (old->status == CONTENT_STATUS_READY ||
|
||||
old->status == CONTENT_STATUS_DONE) {
|
||||
if (nsgif_convert(&gif->base) == false) {
|
||||
if (gif_convert(&gif->base) == false) {
|
||||
content_destroy(&gif->base);
|
||||
return NSERROR_CLONE_FAILED;
|
||||
}
|
||||
|
@ -374,9 +354,9 @@ static nserror nsgif_clone(const struct content *old, struct content **newc)
|
|||
return NSERROR_OK;
|
||||
}
|
||||
|
||||
static void nsgif_add_user(struct content *c)
|
||||
static void gif_add_user(struct content *c)
|
||||
{
|
||||
nsgif_content *gif = (nsgif_content *) c;
|
||||
gif_content *gif = (gif_content *) c;
|
||||
|
||||
/* Ensure this content has already been converted.
|
||||
* If it hasn't, the animation will start at the conversion phase instead. */
|
||||
|
@ -384,67 +364,66 @@ static void nsgif_add_user(struct content *c)
|
|||
|
||||
if (content_count_users(c) == 1) {
|
||||
/* First user, and content already converted, so start the animation. */
|
||||
if (gif->gif->frame_count_partial > 1) {
|
||||
guit->misc->schedule(gif->gif->frames[0].frame_delay * 10,
|
||||
nsgif_animate, c);
|
||||
if (nsgif_reset(gif->gif) == NSGIF_OK) {
|
||||
gif__animate(gif, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void nsgif_remove_user(struct content *c)
|
||||
static void gif_remove_user(struct content *c)
|
||||
{
|
||||
if (content_count_users(c) == 1) {
|
||||
/* Last user is about to be removed from this content, so stop the animation. */
|
||||
guit->misc->schedule(-1, nsgif_animate, c);
|
||||
guit->misc->schedule(-1, gif_animate_cb, c);
|
||||
}
|
||||
}
|
||||
|
||||
static void *nsgif_get_internal(const struct content *c, void *context)
|
||||
static nsgif_bitmap_t *gif_get_bitmap(
|
||||
const struct content *c, void *context)
|
||||
{
|
||||
nsgif_content *gif = (nsgif_content *) c;
|
||||
gif_content *gif = (gif_content *) c;
|
||||
nsgif_bitmap_t *bitmap;
|
||||
|
||||
if (gif->current_frame != gif->gif->decoded_frame) {
|
||||
if (nsgif_get_frame(gif) != GIF_OK)
|
||||
return NULL;
|
||||
if (gif_get_frame(gif, &bitmap) != NSGIF_OK) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return gif->gif->frame_image;
|
||||
return bitmap;
|
||||
}
|
||||
|
||||
static content_type nsgif_content_type(void)
|
||||
static content_type gif_content_type(void)
|
||||
{
|
||||
return CONTENT_IMAGE;
|
||||
}
|
||||
|
||||
static bool nsgif_content_is_opaque(struct content *c)
|
||||
static bool gif_content_is_opaque(struct content *c)
|
||||
{
|
||||
nsgif_content *gif = (nsgif_content *) c;
|
||||
gif_content *gif = (gif_content *) c;
|
||||
nsgif_bitmap_t *bitmap;
|
||||
|
||||
if (gif->current_frame != gif->gif->decoded_frame) {
|
||||
if (nsgif_get_frame(gif) != GIF_OK) {
|
||||
return false;
|
||||
}
|
||||
if (gif_get_frame(gif, &bitmap) != NSGIF_OK) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return guit->bitmap->get_opaque(gif->gif->frame_image);
|
||||
return guit->bitmap->get_opaque(bitmap);
|
||||
}
|
||||
|
||||
static const content_handler nsgif_content_handler = {
|
||||
.create = nsgif_create,
|
||||
.data_complete = nsgif_convert,
|
||||
.destroy = nsgif_destroy,
|
||||
.redraw = nsgif_redraw,
|
||||
.clone = nsgif_clone,
|
||||
.add_user = nsgif_add_user,
|
||||
.remove_user = nsgif_remove_user,
|
||||
.get_internal = nsgif_get_internal,
|
||||
.type = nsgif_content_type,
|
||||
.is_opaque = nsgif_content_is_opaque,
|
||||
static const content_handler gif_content_handler = {
|
||||
.create = gif_create,
|
||||
.data_complete = gif_convert,
|
||||
.destroy = gif_destroy,
|
||||
.redraw = gif_redraw,
|
||||
.clone = gif_clone,
|
||||
.add_user = gif_add_user,
|
||||
.remove_user = gif_remove_user,
|
||||
.get_internal = gif_get_bitmap,
|
||||
.type = gif_content_type,
|
||||
.is_opaque = gif_content_is_opaque,
|
||||
.no_share = false,
|
||||
};
|
||||
|
||||
static const char *nsgif_types[] = {
|
||||
static const char *gif_types[] = {
|
||||
"image/gif"
|
||||
};
|
||||
|
||||
CONTENT_FACTORY_REGISTER_TYPES(nsgif, nsgif_types, nsgif_content_handler);
|
||||
CONTENT_FACTORY_REGISTER_TYPES(nsgif, gif_types, gif_content_handler);
|
||||
|
|
|
@ -34,6 +34,7 @@
|
|||
#include "content/content_protected.h"
|
||||
#include "content/content_factory.h"
|
||||
#include "desktop/gui_internal.h"
|
||||
#include "desktop/bitmap.h"
|
||||
|
||||
#include "image/image.h"
|
||||
#include "image/ico.h"
|
||||
|
@ -54,12 +55,12 @@ typedef struct nsico_content {
|
|||
*/
|
||||
static void *nsico_bitmap_create(int width, int height, unsigned int bmp_state)
|
||||
{
|
||||
unsigned int bitmap_state = BITMAP_NEW;
|
||||
unsigned int bitmap_state = BITMAP_NONE;
|
||||
|
||||
/* set bitmap state based on bmp state */
|
||||
bitmap_state |= (bmp_state & BMP_OPAQUE) ? BITMAP_OPAQUE : 0;
|
||||
bitmap_state |= (bmp_state & BMP_CLEAR_MEMORY) ?
|
||||
BITMAP_CLEAR_MEMORY : 0;
|
||||
BITMAP_CLEAR : 0;
|
||||
|
||||
/* return the created bitmap */
|
||||
return guit->bitmap->create(width, height, bitmap_state);
|
||||
|
@ -71,7 +72,6 @@ static nserror nsico_create_ico_data(nsico_content *c)
|
|||
.bitmap_create = nsico_bitmap_create,
|
||||
.bitmap_destroy = guit->bitmap->destroy,
|
||||
.bitmap_get_buffer = guit->bitmap->get_buffer,
|
||||
.bitmap_get_bpp = guit->bitmap->get_bpp
|
||||
};
|
||||
|
||||
c->ico = calloc(sizeof(ico_collection), 1);
|
||||
|
@ -173,6 +173,23 @@ static bool nsico_convert(struct content *c)
|
|||
return true;
|
||||
}
|
||||
|
||||
static bool nsico__decode(struct bmp_image *ico)
|
||||
{
|
||||
if (ico->decoded == false) {
|
||||
NSLOG(netsurf, DEBUG, "Decoding ICO %p", ico);
|
||||
if (bmp_decode(ico) != BMP_OK) {
|
||||
return false;
|
||||
}
|
||||
|
||||
bitmap_format_to_client(ico->bitmap, &(bitmap_fmt_t) {
|
||||
.layout = BITMAP_LAYOUT_R8G8B8A8,
|
||||
});
|
||||
guit->bitmap->modified(ico->bitmap);
|
||||
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool nsico_redraw(struct content *c, struct content_redraw_data *data,
|
||||
const struct rect *clip, const struct redraw_context *ctx)
|
||||
|
@ -189,14 +206,8 @@ static bool nsico_redraw(struct content *c, struct content_redraw_data *data,
|
|||
}
|
||||
|
||||
/* ensure its decided */
|
||||
if (bmp->decoded == false) {
|
||||
if (bmp_decode(bmp) != BMP_OK) {
|
||||
return false;
|
||||
} else {
|
||||
NSLOG(netsurf, INFO, "Decoding bitmap");
|
||||
guit->bitmap->modified(bmp->bitmap);
|
||||
}
|
||||
|
||||
if (!nsico__decode(bmp)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return image_bitmap_plot(bmp->bitmap, data, clip, ctx);
|
||||
|
@ -260,12 +271,8 @@ static void *nsico_get_internal(const struct content *c, void *context)
|
|||
return NULL;
|
||||
}
|
||||
|
||||
if (bmp->decoded == false) {
|
||||
if (bmp_decode(bmp) != BMP_OK) {
|
||||
return NULL;
|
||||
} else {
|
||||
guit->bitmap->modified(bmp->bitmap);
|
||||
}
|
||||
if (!nsico__decode(bmp)) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return bmp->bitmap;
|
||||
|
@ -292,12 +299,8 @@ static bool nsico_is_opaque(struct content *c)
|
|||
return false;
|
||||
}
|
||||
|
||||
if (bmp->decoded == false) {
|
||||
if (bmp_decode(bmp) != BMP_OK) {
|
||||
return false;
|
||||
}
|
||||
|
||||
guit->bitmap->modified(bmp->bitmap);
|
||||
if (!nsico__decode(bmp)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return guit->bitmap->get_opaque(bmp->bitmap);
|
||||
|
|
|
@ -37,6 +37,7 @@
|
|||
#include "content/content_protected.h"
|
||||
#include "content/content_factory.h"
|
||||
#include "desktop/gui_internal.h"
|
||||
#include "desktop/bitmap.h"
|
||||
|
||||
#include "image/image_cache.h"
|
||||
|
||||
|
@ -49,13 +50,8 @@
|
|||
*/
|
||||
#define MIN_JPEG_SIZE 20
|
||||
|
||||
#ifdef riscos
|
||||
/* We prefer the library to be configured with these options to save
|
||||
* copying data during decoding. */
|
||||
#if RGB_RED != 0 || RGB_GREEN != 1 || RGB_BLUE != 2 || RGB_PIXELSIZE != 4
|
||||
#warning JPEG library not optimally configured. Decoding will be slower.
|
||||
#endif
|
||||
/* but we don't care if we're not on RISC OS */
|
||||
#ifndef LIBJPEG_TURBO_VERSION
|
||||
#warning Using libjpeg (libjpeg-turbo is recommended)
|
||||
#endif
|
||||
|
||||
static char nsjpeg_error_buffer[JMSG_LENGTH_MAX];
|
||||
|
@ -164,6 +160,94 @@ static void nsjpeg_error_exit(j_common_ptr cinfo)
|
|||
longjmp(*setjmp_buffer, 1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert scan lines from CMYK to core client bitmap layout.
|
||||
*/
|
||||
static inline void nsjpeg__decode_cmyk(
|
||||
struct jpeg_decompress_struct *cinfo,
|
||||
uint8_t * volatile pixels,
|
||||
size_t rowstride)
|
||||
{
|
||||
int width = cinfo->output_width * 4;
|
||||
|
||||
do {
|
||||
JSAMPROW scanlines[1] = {
|
||||
[0] = (JSAMPROW)
|
||||
(pixels + rowstride * cinfo->output_scanline),
|
||||
};
|
||||
jpeg_read_scanlines(cinfo, scanlines, 1);
|
||||
|
||||
for (int i = width - 4; 0 <= i; i -= 4) {
|
||||
/* Trivial inverse CMYK -> RGBA */
|
||||
const int c = scanlines[0][i + 0];
|
||||
const int m = scanlines[0][i + 1];
|
||||
const int y = scanlines[0][i + 2];
|
||||
const int k = scanlines[0][i + 3];
|
||||
|
||||
const int ck = c * k;
|
||||
const int mk = m * k;
|
||||
const int yk = y * k;
|
||||
|
||||
#define DIV255(x) ((x) + 1 + ((x) >> 8)) >> 8
|
||||
scanlines[0][i + bitmap_layout.r] = DIV255(ck);
|
||||
scanlines[0][i + bitmap_layout.g] = DIV255(mk);
|
||||
scanlines[0][i + bitmap_layout.b] = DIV255(yk);
|
||||
scanlines[0][i + bitmap_layout.a] = 0xff;
|
||||
#undef DIV255
|
||||
}
|
||||
} while (cinfo->output_scanline != cinfo->output_height);
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert scan lines from CMYK to core client bitmap layout.
|
||||
*/
|
||||
static inline void nsjpeg__decode_rgb(
|
||||
struct jpeg_decompress_struct *cinfo,
|
||||
uint8_t * volatile pixels,
|
||||
size_t rowstride)
|
||||
{
|
||||
int width = cinfo->output_width;
|
||||
|
||||
do {
|
||||
JSAMPROW scanlines[1] = {
|
||||
[0] = (JSAMPROW)
|
||||
(pixels + rowstride * cinfo->output_scanline),
|
||||
};
|
||||
jpeg_read_scanlines(cinfo, scanlines, 1);
|
||||
|
||||
#if RGB_RED != 0 || RGB_GREEN != 1 || RGB_BLUE != 2 || RGB_PIXELSIZE != 4
|
||||
/* Missmatch between configured libjpeg pixel format and
|
||||
* NetSurf pixel format. Convert to RGBA */
|
||||
for (int i = width - 1; 0 <= i; i--) {
|
||||
int r = scanlines[0][i * RGB_PIXELSIZE + RGB_RED];
|
||||
int g = scanlines[0][i * RGB_PIXELSIZE + RGB_GREEN];
|
||||
int b = scanlines[0][i * RGB_PIXELSIZE + RGB_BLUE];
|
||||
scanlines[0][i * 4 + bitmap_layout.r] = r;
|
||||
scanlines[0][i * 4 + bitmap_layout.g] = g;
|
||||
scanlines[0][i * 4 + bitmap_layout.b] = b;
|
||||
scanlines[0][i * 4 + bitmap_layout.a] = 0xff;
|
||||
}
|
||||
#endif
|
||||
} while (cinfo->output_scanline != cinfo->output_height);
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert scan lines from CMYK to core client bitmap layout.
|
||||
*/
|
||||
static inline void nsjpeg__decode_client_fmt(
|
||||
struct jpeg_decompress_struct *cinfo,
|
||||
uint8_t * volatile pixels,
|
||||
size_t rowstride)
|
||||
{
|
||||
do {
|
||||
JSAMPROW scanlines[1] = {
|
||||
[0] = (JSAMPROW)
|
||||
(pixels + rowstride * cinfo->output_scanline),
|
||||
};
|
||||
jpeg_read_scanlines(cinfo, scanlines, 1);
|
||||
} while (cinfo->output_scanline != cinfo->output_height);
|
||||
}
|
||||
|
||||
/**
|
||||
* create a bitmap from jpeg content.
|
||||
*/
|
||||
|
@ -175,8 +259,6 @@ jpeg_cache_convert(struct content *c)
|
|||
struct jpeg_decompress_struct cinfo;
|
||||
struct jpeg_error_mgr jerr;
|
||||
jmp_buf setjmp_buffer;
|
||||
unsigned int height;
|
||||
unsigned int width;
|
||||
struct bitmap * volatile bitmap = NULL;
|
||||
uint8_t * volatile pixels = NULL;
|
||||
size_t rowstride;
|
||||
|
@ -221,21 +303,42 @@ jpeg_cache_convert(struct content *c)
|
|||
|
||||
/* set output processing parameters */
|
||||
if (cinfo.jpeg_color_space == JCS_CMYK ||
|
||||
cinfo.jpeg_color_space == JCS_YCCK) {
|
||||
cinfo.jpeg_color_space == JCS_YCCK) {
|
||||
cinfo.out_color_space = JCS_CMYK;
|
||||
} else {
|
||||
#ifdef JCS_ALPHA_EXTENSIONS
|
||||
switch (bitmap_fmt.layout) {
|
||||
case BITMAP_LAYOUT_R8G8B8A8:
|
||||
cinfo.out_color_space = JCS_EXT_RGBA;
|
||||
break;
|
||||
case BITMAP_LAYOUT_B8G8R8A8:
|
||||
cinfo.out_color_space = JCS_EXT_BGRA;
|
||||
break;
|
||||
case BITMAP_LAYOUT_A8R8G8B8:
|
||||
cinfo.out_color_space = JCS_EXT_ARGB;
|
||||
break;
|
||||
case BITMAP_LAYOUT_A8B8G8R8:
|
||||
cinfo.out_color_space = JCS_EXT_ABGR;
|
||||
break;
|
||||
default:
|
||||
NSLOG(netsurf, ERROR, "Unexpected bitmap format: %u",
|
||||
bitmap_fmt.layout);
|
||||
jpeg_destroy_decompress(&cinfo);
|
||||
return NULL;
|
||||
}
|
||||
#else
|
||||
cinfo.out_color_space = JCS_RGB;
|
||||
#endif
|
||||
}
|
||||
cinfo.dct_method = JDCT_ISLOW;
|
||||
|
||||
/* commence the decompression, output parameters now valid */
|
||||
jpeg_start_decompress(&cinfo);
|
||||
|
||||
width = cinfo.output_width;
|
||||
height = cinfo.output_height;
|
||||
|
||||
/* create opaque bitmap (jpegs cannot be transparent) */
|
||||
bitmap = guit->bitmap->create(width, height, BITMAP_NEW | BITMAP_OPAQUE);
|
||||
bitmap = guit->bitmap->create(
|
||||
cinfo.output_width,
|
||||
cinfo.output_height, BITMAP_OPAQUE);
|
||||
if (bitmap == NULL) {
|
||||
/* empty bitmap could not be created */
|
||||
jpeg_destroy_decompress(&cinfo);
|
||||
|
@ -252,50 +355,21 @@ jpeg_cache_convert(struct content *c)
|
|||
|
||||
/* Convert scanlines from jpeg into bitmap */
|
||||
rowstride = guit->bitmap->get_rowstride(bitmap);
|
||||
do {
|
||||
JSAMPROW scanlines[1];
|
||||
|
||||
scanlines[0] = (JSAMPROW) (pixels +
|
||||
rowstride * cinfo.output_scanline);
|
||||
jpeg_read_scanlines(&cinfo, scanlines, 1);
|
||||
switch (cinfo.out_color_space) {
|
||||
case JCS_CMYK:
|
||||
nsjpeg__decode_cmyk(&cinfo, pixels, rowstride);
|
||||
break;
|
||||
|
||||
if (cinfo.out_color_space == JCS_CMYK) {
|
||||
int i;
|
||||
for (i = width - 1; 0 <= i; i--) {
|
||||
/* Trivial inverse CMYK -> RGBA */
|
||||
const int c = scanlines[0][i * 4 + 0];
|
||||
const int m = scanlines[0][i * 4 + 1];
|
||||
const int y = scanlines[0][i * 4 + 2];
|
||||
const int k = scanlines[0][i * 4 + 3];
|
||||
case JCS_RGB:
|
||||
nsjpeg__decode_rgb(&cinfo, pixels, rowstride);
|
||||
break;
|
||||
|
||||
const int ck = c * k;
|
||||
const int mk = m * k;
|
||||
const int yk = y * k;
|
||||
default:
|
||||
nsjpeg__decode_client_fmt(&cinfo, pixels, rowstride);
|
||||
break;
|
||||
}
|
||||
|
||||
#define DIV255(x) ((x) + 1 + ((x) >> 8)) >> 8
|
||||
scanlines[0][i * 4 + 0] = DIV255(ck);
|
||||
scanlines[0][i * 4 + 1] = DIV255(mk);
|
||||
scanlines[0][i * 4 + 2] = DIV255(yk);
|
||||
scanlines[0][i * 4 + 3] = 0xff;
|
||||
#undef DIV255
|
||||
}
|
||||
} else {
|
||||
#if RGB_RED != 0 || RGB_GREEN != 1 || RGB_BLUE != 2 || RGB_PIXELSIZE != 4
|
||||
/* Missmatch between configured libjpeg pixel format and
|
||||
* NetSurf pixel format. Convert to RGBA */
|
||||
int i;
|
||||
for (i = width - 1; 0 <= i; i--) {
|
||||
int r = scanlines[0][i * RGB_PIXELSIZE + RGB_RED];
|
||||
int g = scanlines[0][i * RGB_PIXELSIZE + RGB_GREEN];
|
||||
int b = scanlines[0][i * RGB_PIXELSIZE + RGB_BLUE];
|
||||
scanlines[0][i * 4 + 0] = r;
|
||||
scanlines[0][i * 4 + 1] = g;
|
||||
scanlines[0][i * 4 + 2] = b;
|
||||
scanlines[0][i * 4 + 3] = 0xff;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
} while (cinfo.output_scanline != cinfo.output_height);
|
||||
guit->bitmap->modified(bitmap);
|
||||
|
||||
jpeg_finish_decompress(&cinfo);
|
||||
|
|
|
@ -32,6 +32,7 @@
|
|||
#include "content/content_protected.h"
|
||||
#include "content/content_factory.h"
|
||||
#include "desktop/gui_internal.h"
|
||||
#include "desktop/bitmap.h"
|
||||
|
||||
#include "image/image_cache.h"
|
||||
#include "image/png.h"
|
||||
|
@ -118,8 +119,37 @@ static void nspng_setup_transforms(png_structp png_ptr, png_infop info_ptr)
|
|||
png_set_gray_to_rgb(png_ptr);
|
||||
}
|
||||
|
||||
switch (bitmap_fmt.layout) {
|
||||
case BITMAP_LAYOUT_B8G8R8A8: /* Fall through. */
|
||||
case BITMAP_LAYOUT_A8B8G8R8:
|
||||
png_set_bgr(png_ptr);
|
||||
break;
|
||||
default:
|
||||
/* RGB is the default. */
|
||||
break;
|
||||
}
|
||||
|
||||
if (!(color_type & PNG_COLOR_MASK_ALPHA)) {
|
||||
png_set_filler(png_ptr, 0xff, PNG_FILLER_AFTER);
|
||||
switch (bitmap_fmt.layout) {
|
||||
case BITMAP_LAYOUT_A8R8G8B8: /* Fall through. */
|
||||
case BITMAP_LAYOUT_A8B8G8R8:
|
||||
png_set_filler(png_ptr, 0xff, PNG_FILLER_BEFORE);
|
||||
break;
|
||||
|
||||
default:
|
||||
png_set_filler(png_ptr, 0xff, PNG_FILLER_AFTER);
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
switch (bitmap_fmt.layout) {
|
||||
case BITMAP_LAYOUT_A8R8G8B8: /* Fall through. */
|
||||
case BITMAP_LAYOUT_A8B8G8R8:
|
||||
png_set_swap_alpha(png_ptr);
|
||||
break;
|
||||
default:
|
||||
/* Alpha as final component is the default. */
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* gamma correction - we use 2.2 as our screen gamma
|
||||
|
@ -163,14 +193,14 @@ static void info_callback(png_structp png_s, png_infop info)
|
|||
}
|
||||
|
||||
/* Claim the required memory for the converted PNG */
|
||||
png_c->bitmap = guit->bitmap->create(width, height, BITMAP_NEW);
|
||||
png_c->bitmap = guit->bitmap->create(width, height, BITMAP_NONE);
|
||||
if (png_c->bitmap == NULL) {
|
||||
/* Failed to create bitmap skip pre-conversion */
|
||||
longjmp(png_jmpbuf(png_s), CBERR_NOPRE);
|
||||
}
|
||||
|
||||
png_c->rowstride = guit->bitmap->get_rowstride(png_c->bitmap);
|
||||
png_c->bpp = guit->bitmap->get_bpp(png_c->bitmap);
|
||||
png_c->bpp = sizeof(uint32_t);
|
||||
|
||||
nspng_setup_transforms(png_s, info);
|
||||
|
||||
|
@ -483,7 +513,7 @@ png_cache_convert(struct content *c)
|
|||
height = png_get_image_height(png_ptr, info_ptr);
|
||||
|
||||
/* Claim the required memory for the converted PNG */
|
||||
bitmap = guit->bitmap->create(width, height, BITMAP_NEW);
|
||||
bitmap = guit->bitmap->create(width, height, BITMAP_NONE);
|
||||
if (bitmap == NULL) {
|
||||
/* cleanup and bail */
|
||||
goto png_cache_convert_error;
|
||||
|
@ -508,7 +538,13 @@ png_cache_convert_error:
|
|||
}
|
||||
|
||||
if (bitmap != NULL) {
|
||||
guit->bitmap->modified((struct bitmap *)bitmap);
|
||||
bool opaque = bitmap_test_opaque((void *)bitmap);
|
||||
guit->bitmap->set_opaque((void *)bitmap, opaque);
|
||||
bitmap_format_to_client((void *)bitmap, &(bitmap_fmt_t) {
|
||||
.layout = bitmap_fmt.layout,
|
||||
.pma = opaque ? bitmap_fmt.pma : false,
|
||||
});
|
||||
guit->bitmap->modified((void *)bitmap);
|
||||
}
|
||||
|
||||
return (struct bitmap *)bitmap;
|
||||
|
@ -535,7 +571,12 @@ static bool nspng_convert(struct content *c)
|
|||
}
|
||||
|
||||
if (png_c->bitmap != NULL) {
|
||||
guit->bitmap->set_opaque(png_c->bitmap, guit->bitmap->test_opaque(png_c->bitmap));
|
||||
bool opaque = bitmap_test_opaque(png_c->bitmap);
|
||||
guit->bitmap->set_opaque(png_c->bitmap, opaque);
|
||||
bitmap_format_to_client(png_c->bitmap, &(bitmap_fmt_t) {
|
||||
.layout = bitmap_fmt.layout,
|
||||
.pma = opaque ? bitmap_fmt.pma : false,
|
||||
});
|
||||
guit->bitmap->modified(png_c->bitmap);
|
||||
}
|
||||
|
||||
|
|
|
@ -51,6 +51,7 @@
|
|||
#include "content/content_protected.h"
|
||||
#include "content/content_factory.h"
|
||||
#include "desktop/gui_internal.h"
|
||||
#include "desktop/bitmap.h"
|
||||
|
||||
#include "image/rsvg.h"
|
||||
|
||||
|
@ -128,41 +129,6 @@ static bool rsvg_process_data(struct content *c, const char *data,
|
|||
return true;
|
||||
}
|
||||
|
||||
/** Convert Cairo's ARGB output to NetSurf's favoured ABGR format. It converts
|
||||
* the data in-place.
|
||||
*
|
||||
* \param pixels Pixel data, in the form of ARGB. This will
|
||||
* be overwritten with new data in the form of ABGR.
|
||||
* \param width Width of the bitmap
|
||||
* \param height Height of the bitmap
|
||||
* \param rowstride Number of bytes to skip after each row (this
|
||||
* implementation requires this to be a multiple of 4.)
|
||||
*/
|
||||
static inline void rsvg_argb_to_abgr(uint8_t *pixels,
|
||||
int width, int height, size_t rowstride)
|
||||
{
|
||||
uint8_t *p = pixels;
|
||||
int boff = 0, roff = 2;
|
||||
|
||||
if (endian_host_is_le() == false) {
|
||||
boff = 1;
|
||||
roff = 3;
|
||||
}
|
||||
|
||||
for (int y = 0; y < height; y++) {
|
||||
for (int x = 0; x < width; x++) {
|
||||
/* Swap R and B */
|
||||
const uint8_t r = p[4*x+roff];
|
||||
|
||||
p[4*x+roff] = p[4*x+boff];
|
||||
|
||||
p[4*x+boff] = r;
|
||||
}
|
||||
|
||||
p += rowstride;
|
||||
}
|
||||
}
|
||||
|
||||
static bool rsvg_convert(struct content *c)
|
||||
{
|
||||
rsvg_content *d = (rsvg_content *) c;
|
||||
|
@ -187,7 +153,7 @@ static bool rsvg_convert(struct content *c)
|
|||
c->height = rsvgsize.height;
|
||||
|
||||
if ((d->bitmap = guit->bitmap->create(c->width, c->height,
|
||||
BITMAP_NEW)) == NULL) {
|
||||
BITMAP_NONE)) == NULL) {
|
||||
NSLOG(netsurf, INFO,
|
||||
"Failed to create bitmap for rsvg render.");
|
||||
content_broadcast_error(c, NSERROR_NOMEM, NULL);
|
||||
|
@ -213,10 +179,10 @@ static bool rsvg_convert(struct content *c)
|
|||
}
|
||||
|
||||
rsvg_handle_render_cairo(d->rsvgh, d->ct);
|
||||
rsvg_argb_to_abgr(guit->bitmap->get_buffer(d->bitmap),
|
||||
c->width, c->height,
|
||||
guit->bitmap->get_rowstride(d->bitmap));
|
||||
|
||||
bitmap_format_to_client(d->bitmap, &(bitmap_fmt_t) {
|
||||
.layout = BITMAP_LAYOUT_ARGB8888,
|
||||
});
|
||||
guit->bitmap->modified(d->bitmap);
|
||||
content_set_ready(c);
|
||||
content_set_done(c);
|
||||
|
|
|
@ -38,6 +38,7 @@
|
|||
#include "content/content_protected.h"
|
||||
#include "content/content_factory.h"
|
||||
#include "desktop/gui_internal.h"
|
||||
#include "desktop/bitmap.h"
|
||||
|
||||
#include "image/image_cache.h"
|
||||
|
||||
|
@ -97,6 +98,9 @@ webp_cache_convert(struct content *c)
|
|||
uint8_t *decoded;
|
||||
size_t rowstride;
|
||||
struct bitmap *bitmap = NULL;
|
||||
bitmap_fmt_t webp_fmt = {
|
||||
.layout = bitmap_fmt.layout,
|
||||
};
|
||||
|
||||
source_data = content__get_source_data(c, &source_size);
|
||||
|
||||
|
@ -107,9 +111,13 @@ webp_cache_convert(struct content *c)
|
|||
}
|
||||
|
||||
if (webpfeatures.has_alpha == 0) {
|
||||
bmap_flags = BITMAP_NEW | BITMAP_OPAQUE;
|
||||
bmap_flags = BITMAP_OPAQUE;
|
||||
/* Image has no alpha. Premultiplied alpha makes no difference.
|
||||
* Optimisation: Avoid unnecessary conversion by copying format.
|
||||
*/
|
||||
webp_fmt.pma = bitmap_fmt.pma;
|
||||
} else {
|
||||
bmap_flags = BITMAP_NEW;
|
||||
bmap_flags = BITMAP_NONE;
|
||||
}
|
||||
|
||||
/* create bitmap */
|
||||
|
@ -130,17 +138,33 @@ webp_cache_convert(struct content *c)
|
|||
|
||||
rowstride = guit->bitmap->get_rowstride(bitmap);
|
||||
|
||||
decoded = WebPDecodeRGBAInto(source_data,
|
||||
source_size,
|
||||
pixels,
|
||||
webpfeatures.width * webpfeatures.height * 4,
|
||||
rowstride);
|
||||
switch (webp_fmt.layout) {
|
||||
default:
|
||||
/* WebP has no ABGR function, fall back to default. */
|
||||
webp_fmt.layout = BITMAP_LAYOUT_R8G8B8A8;
|
||||
/* Fall through. */
|
||||
case BITMAP_LAYOUT_R8G8B8A8:
|
||||
decoded = WebPDecodeRGBAInto(source_data, source_size, pixels,
|
||||
rowstride * webpfeatures.height, rowstride);
|
||||
break;
|
||||
|
||||
case BITMAP_LAYOUT_B8G8R8A8:
|
||||
decoded = WebPDecodeBGRAInto(source_data, source_size, pixels,
|
||||
rowstride * webpfeatures.height, rowstride);
|
||||
break;
|
||||
|
||||
case BITMAP_LAYOUT_A8R8G8B8:
|
||||
decoded = WebPDecodeARGBInto(source_data, source_size, pixels,
|
||||
rowstride * webpfeatures.height, rowstride);
|
||||
break;
|
||||
}
|
||||
if (decoded == NULL) {
|
||||
/* decode failed */
|
||||
guit->bitmap->destroy(bitmap);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
bitmap_format_to_client(bitmap, &webp_fmt);
|
||||
guit->bitmap->modified(bitmap);
|
||||
|
||||
return bitmap;
|
||||
|
|
|
@ -12,7 +12,7 @@ desktop/version.c: testament $(OBJROOT)/testament.h
|
|||
|
||||
# S_BROWSER are sources related to full browsers but are common
|
||||
# between RISC OS, GTK, BeOS and AmigaOS builds
|
||||
S_BROWSER := browser.c browser_window.c browser_history.c \
|
||||
S_BROWSER := bitmap.c browser.c browser_window.c browser_history.c \
|
||||
download.c frames.c netsurf.c cw_helper.c \
|
||||
save_complete.c save_text.c selection.c textinput.c gui_factory.c \
|
||||
save_pdf.c font_haru.c
|
||||
|
|
|
@ -0,0 +1,338 @@
|
|||
/*
|
||||
* Copyright 2022 Michael Drake <tlsa@netsurf-browser.org>
|
||||
*
|
||||
* 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
|
||||
* Internal core bitmap interface.
|
||||
*/
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
#include "utils/log.h"
|
||||
#include "utils/errors.h"
|
||||
|
||||
#include "desktop/bitmap.h"
|
||||
#include "desktop/gui_internal.h"
|
||||
|
||||
/** The client bitmap format. */
|
||||
bitmap_fmt_t bitmap_fmt;
|
||||
|
||||
/** The client bitmap colour channel layout. */
|
||||
struct bitmap_colour_layout bitmap_layout = {
|
||||
.r = 0,
|
||||
.g = 1,
|
||||
.b = 2,
|
||||
.a = 3,
|
||||
};
|
||||
|
||||
/**
|
||||
* Get the colour layout for the given bitmap format.
|
||||
*
|
||||
* \param[in] fmt Pixel format to get channel layout for,
|
||||
* \return channel layout structure.
|
||||
*/
|
||||
static struct bitmap_colour_layout bitmap__get_colour_layout(
|
||||
const bitmap_fmt_t *fmt)
|
||||
{
|
||||
switch (fmt->layout) {
|
||||
default:
|
||||
/* Fall through. */
|
||||
case BITMAP_LAYOUT_R8G8B8A8:
|
||||
return (struct bitmap_colour_layout) {
|
||||
.r = 0,
|
||||
.g = 1,
|
||||
.b = 2,
|
||||
.a = 3,
|
||||
};
|
||||
|
||||
case BITMAP_LAYOUT_B8G8R8A8:
|
||||
return (struct bitmap_colour_layout) {
|
||||
.b = 0,
|
||||
.g = 1,
|
||||
.r = 2,
|
||||
.a = 3,
|
||||
};
|
||||
|
||||
case BITMAP_LAYOUT_A8R8G8B8:
|
||||
return (struct bitmap_colour_layout) {
|
||||
.a = 0,
|
||||
.r = 1,
|
||||
.g = 2,
|
||||
.b = 3,
|
||||
};
|
||||
|
||||
case BITMAP_LAYOUT_A8B8G8R8:
|
||||
return (struct bitmap_colour_layout) {
|
||||
.a = 0,
|
||||
.b = 1,
|
||||
.g = 2,
|
||||
.r = 3,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get string for given pixel layout.
|
||||
*
|
||||
* \param[in] layout The pixel layout to get string for,
|
||||
* \return String for given layout.
|
||||
*/
|
||||
static const char *bitmap__layout_to_str(enum bitmap_layout layout)
|
||||
{
|
||||
const char *const str[] = {
|
||||
[BITMAP_LAYOUT_R8G8B8A8] = "Byte-wise RGBA",
|
||||
[BITMAP_LAYOUT_B8G8R8A8] = "Byte-wise BGRA",
|
||||
[BITMAP_LAYOUT_A8R8G8B8] = "Byte-wise ARGB",
|
||||
[BITMAP_LAYOUT_A8B8G8R8] = "Byte-wise ABGR",
|
||||
[BITMAP_LAYOUT_RGBA8888] = "0xRRGGBBAA (native endian)",
|
||||
[BITMAP_LAYOUT_BGRA8888] = "0xBBGGRRAA (native endian)",
|
||||
[BITMAP_LAYOUT_ARGB8888] = "0xAARRGGBB (native endian)",
|
||||
[BITMAP_LAYOUT_ABGR8888] = "0xAABBGGRR (native endian)",
|
||||
};
|
||||
|
||||
if ((size_t)layout >= (sizeof(str)) / sizeof(*str) ||
|
||||
str[layout] == NULL) {
|
||||
return "Unknown";
|
||||
}
|
||||
|
||||
return str[layout];
|
||||
}
|
||||
|
||||
/* Exported function, documented in include/netsurf/bitmap.h */
|
||||
void bitmap_set_format(const bitmap_fmt_t *bitmap_format)
|
||||
{
|
||||
bitmap_fmt = *bitmap_format;
|
||||
|
||||
NSLOG(netsurf, INFO, "Setting core bitmap format to: %s%s",
|
||||
bitmap__layout_to_str(bitmap_format->layout),
|
||||
bitmap_format->pma ? " pre multiplied alpha" : "");
|
||||
|
||||
bitmap_fmt.layout = bitmap_sanitise_bitmap_layout(bitmap_fmt.layout);
|
||||
|
||||
if (bitmap_format->layout != bitmap_fmt.layout) {
|
||||
NSLOG(netsurf, INFO, "Sanitised layout to: %s",
|
||||
bitmap__layout_to_str(bitmap_fmt.layout));
|
||||
}
|
||||
|
||||
bitmap_layout = bitmap__get_colour_layout(&bitmap_fmt);
|
||||
}
|
||||
|
||||
/**
|
||||
* Swap colour component order.
|
||||
*
|
||||
* \param[in] width Bitmap width in pixels.
|
||||
* \param[in] height Bitmap height in pixels.
|
||||
* \param[in] buffer Pixel buffer.
|
||||
* \param[in] rowstride Pixel buffer row stride in bytes.
|
||||
* \param[in] to Pixel layout to convert to.
|
||||
* \param[in] from Pixel layout to convert from.
|
||||
*/
|
||||
static inline void bitmap__format_convert(
|
||||
int width,
|
||||
int height,
|
||||
uint8_t *buffer,
|
||||
size_t rowstride,
|
||||
struct bitmap_colour_layout to,
|
||||
struct bitmap_colour_layout from)
|
||||
{
|
||||
/* Just swapping the components around */
|
||||
for (int y = 0; y < height; y++) {
|
||||
uint8_t *row = buffer;
|
||||
|
||||
for (int x = 0; x < width; x++) {
|
||||
const uint32_t px = *((uint32_t *)(void *) row);
|
||||
|
||||
row[to.r] = ((const uint8_t *) &px)[from.r];
|
||||
row[to.g] = ((const uint8_t *) &px)[from.g];
|
||||
row[to.b] = ((const uint8_t *) &px)[from.b];
|
||||
row[to.a] = ((const uint8_t *) &px)[from.a];
|
||||
|
||||
row += sizeof(uint32_t);
|
||||
}
|
||||
|
||||
buffer += rowstride;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert plain alpha to premultiplied alpha.
|
||||
*
|
||||
* \param[in] width Bitmap width in pixels.
|
||||
* \param[in] height Bitmap height in pixels.
|
||||
* \param[in] buffer Pixel buffer.
|
||||
* \param[in] rowstride Pixel buffer row stride in bytes.
|
||||
* \param[in] to Pixel layout to convert to.
|
||||
* \param[in] from Pixel layout to convert from.
|
||||
*/
|
||||
static inline void bitmap__format_convert_to_pma(
|
||||
int width,
|
||||
int height,
|
||||
uint8_t *buffer,
|
||||
size_t rowstride,
|
||||
struct bitmap_colour_layout to,
|
||||
struct bitmap_colour_layout from)
|
||||
{
|
||||
for (int y = 0; y < height; y++) {
|
||||
uint8_t *row = buffer;
|
||||
|
||||
for (int x = 0; x < width; x++) {
|
||||
const uint32_t px = *((uint32_t *)(void *) row);
|
||||
uint32_t a, r, g, b;
|
||||
|
||||
r = ((const uint8_t *) &px)[from.r];
|
||||
g = ((const uint8_t *) &px)[from.g];
|
||||
b = ((const uint8_t *) &px)[from.b];
|
||||
a = ((const uint8_t *) &px)[from.a];
|
||||
|
||||
if (a != 0) {
|
||||
r = ((r * (a + 1)) >> 8) & 0xff;
|
||||
g = ((g * (a + 1)) >> 8) & 0xff;
|
||||
b = ((b * (a + 1)) >> 8) & 0xff;
|
||||
} else {
|
||||
r = g = b = 0;
|
||||
}
|
||||
|
||||
row[to.r] = r;
|
||||
row[to.g] = g;
|
||||
row[to.b] = b;
|
||||
row[to.a] = a;
|
||||
|
||||
row += sizeof(uint32_t);
|
||||
}
|
||||
|
||||
buffer += rowstride;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert from premultiplied alpha to plain alpha.
|
||||
*
|
||||
* \param[in] width Bitmap width in pixels.
|
||||
* \param[in] height Bitmap height in pixels.
|
||||
* \param[in] buffer Pixel buffer.
|
||||
* \param[in] rowstride Pixel buffer row stride in bytes.
|
||||
* \param[in] to Pixel layout to convert to.
|
||||
* \param[in] from Pixel layout to convert from.
|
||||
*/
|
||||
static inline void bitmap__format_convert_from_pma(
|
||||
int width,
|
||||
int height,
|
||||
uint8_t *buffer,
|
||||
size_t rowstride,
|
||||
struct bitmap_colour_layout to,
|
||||
struct bitmap_colour_layout from)
|
||||
{
|
||||
for (int y = 0; y < height; y++) {
|
||||
uint8_t *row = buffer;
|
||||
|
||||
for (int x = 0; x < width; x++) {
|
||||
const uint32_t px = *((uint32_t *)(void *) row);
|
||||
uint32_t a, r, g, b;
|
||||
|
||||
r = ((const uint8_t *) &px)[from.r];
|
||||
g = ((const uint8_t *) &px)[from.g];
|
||||
b = ((const uint8_t *) &px)[from.b];
|
||||
a = ((const uint8_t *) &px)[from.a];
|
||||
|
||||
if (a != 0) {
|
||||
r = (r << 8) / a;
|
||||
g = (g << 8) / a;
|
||||
b = (b << 8) / a;
|
||||
|
||||
r = (r > 255) ? 255 : r;
|
||||
g = (g > 255) ? 255 : g;
|
||||
b = (b > 255) ? 255 : b;
|
||||
} else {
|
||||
r = g = b = 0;
|
||||
}
|
||||
|
||||
row[to.r] = r;
|
||||
row[to.g] = g;
|
||||
row[to.b] = b;
|
||||
row[to.a] = a;
|
||||
|
||||
row += sizeof(uint32_t);
|
||||
}
|
||||
|
||||
buffer += rowstride;
|
||||
}
|
||||
}
|
||||
|
||||
/* Exported function, documented in desktop/bitmap.h */
|
||||
void bitmap_format_convert(void *bitmap,
|
||||
const bitmap_fmt_t *fmt_from,
|
||||
const bitmap_fmt_t *fmt_to)
|
||||
{
|
||||
int width = guit->bitmap->get_width(bitmap);
|
||||
int height = guit->bitmap->get_height(bitmap);
|
||||
bool opaque = guit->bitmap->get_opaque(bitmap);
|
||||
uint8_t *buffer = guit->bitmap->get_buffer(bitmap);
|
||||
size_t rowstride = guit->bitmap->get_rowstride(bitmap);
|
||||
struct bitmap_colour_layout to = bitmap__get_colour_layout(fmt_to);
|
||||
struct bitmap_colour_layout from = bitmap__get_colour_layout(fmt_from);
|
||||
|
||||
NSLOG(netsurf, DEEPDEBUG, "%p: format conversion (%u%s --> %u%s)",
|
||||
bitmap,
|
||||
fmt_from->layout, fmt_from->pma ? " pma" : "",
|
||||
fmt_to->layout, fmt_to->pma ? " pma" : "");
|
||||
|
||||
if (fmt_from->pma == fmt_to->pma) {
|
||||
/* Just component order to switch. */
|
||||
bitmap__format_convert(
|
||||
width, height, buffer,
|
||||
rowstride, to, from);
|
||||
|
||||
} else if (opaque == false) {
|
||||
/* Need to do conversion to/from premultiplied alpha. */
|
||||
if (fmt_to->pma) {
|
||||
bitmap__format_convert_to_pma(
|
||||
width, height, buffer,
|
||||
rowstride, to, from);
|
||||
} else {
|
||||
bitmap__format_convert_from_pma(
|
||||
width, height, buffer,
|
||||
rowstride, to, from);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Exported function, documented in desktop/bitmap.h */
|
||||
bool bitmap_test_opaque(void *bitmap)
|
||||
{
|
||||
int width = guit->bitmap->get_width(bitmap);
|
||||
int height = guit->bitmap->get_height(bitmap);
|
||||
size_t rowstride = guit->bitmap->get_rowstride(bitmap);
|
||||
const uint8_t *buffer = guit->bitmap->get_buffer(bitmap);
|
||||
|
||||
width *= sizeof(uint32_t);
|
||||
|
||||
for (int y = 0; y < height; y++) {
|
||||
const uint8_t *row = buffer;
|
||||
|
||||
for (int x = bitmap_layout.a; x < width; x += 4) {
|
||||
if (row[x] != 0xff) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
buffer += rowstride;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
|
@ -0,0 +1,147 @@
|
|||
/*
|
||||
* Copyright 2022 Michael Drake <tlsa@nesturf-browser.org>
|
||||
*
|
||||
* 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
|
||||
* Internal core bitmap interface.
|
||||
*/
|
||||
|
||||
#ifndef _NETSURF_DESKTOP_BITMAP_H_
|
||||
#define _NETSURF_DESKTOP_BITMAP_H_
|
||||
|
||||
#include <nsutils/endian.h>
|
||||
|
||||
#include "netsurf/types.h"
|
||||
#include "netsurf/bitmap.h"
|
||||
|
||||
/** Pixel format: colour component order. */
|
||||
struct bitmap_colour_layout {
|
||||
uint8_t r; /**< Byte offset within pixel to red component. */
|
||||
uint8_t g; /**< Byte offset within pixel to green component. */
|
||||
uint8_t b; /**< Byte offset within pixel to blue component. */
|
||||
uint8_t a; /**< Byte offset within pixel to alpha component. */
|
||||
};
|
||||
|
||||
/** The client bitmap format. */
|
||||
extern bitmap_fmt_t bitmap_fmt;
|
||||
|
||||
/** The client bitmap colour channel layout. */
|
||||
extern struct bitmap_colour_layout bitmap_layout;
|
||||
|
||||
/**
|
||||
* Convert a bitmap pixel to a NetSurf colour (0xAARRGGBB).
|
||||
*
|
||||
* The bitmap must be in the client format.
|
||||
*
|
||||
* \param[in] Pointer to a pixel in the bitmap's pixel data.
|
||||
* \return The corresponding NetSurf colour.
|
||||
*/
|
||||
static inline colour bitmap_pixel_to_colour(const uint8_t *pixel)
|
||||
{
|
||||
return (pixel[bitmap_layout.r] << 0) |
|
||||
(pixel[bitmap_layout.g] << 8) |
|
||||
(pixel[bitmap_layout.b] << 16) |
|
||||
(pixel[bitmap_layout.a] << 24);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sanitise bitmap pixel component layout.
|
||||
*
|
||||
* Map endian-dependant layouts to byte-wise layout for the host.
|
||||
*
|
||||
* \param[in] layout Layout to convert.
|
||||
* \return sanitised layout.
|
||||
*/
|
||||
static inline enum bitmap_layout bitmap_sanitise_bitmap_layout(
|
||||
enum bitmap_layout layout)
|
||||
{
|
||||
bool le = endian_host_is_le();
|
||||
|
||||
switch (layout) {
|
||||
case BITMAP_LAYOUT_RGBA8888:
|
||||
layout = (le) ? BITMAP_LAYOUT_A8B8G8R8
|
||||
: BITMAP_LAYOUT_R8G8B8A8;
|
||||
break;
|
||||
case BITMAP_LAYOUT_BGRA8888:
|
||||
layout = (le) ? BITMAP_LAYOUT_A8R8G8B8
|
||||
: BITMAP_LAYOUT_B8G8R8A8;
|
||||
break;
|
||||
case BITMAP_LAYOUT_ARGB8888:
|
||||
layout = (le) ? BITMAP_LAYOUT_B8G8R8A8
|
||||
: BITMAP_LAYOUT_A8R8G8B8;
|
||||
break;
|
||||
case BITMAP_LAYOUT_ABGR8888:
|
||||
layout = (le) ? BITMAP_LAYOUT_R8G8B8A8
|
||||
: BITMAP_LAYOUT_A8B8G8R8;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return layout;
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert bitmap from one format to another.
|
||||
*
|
||||
* Note that both formats should be sanitised.
|
||||
*
|
||||
* \param[in] bitmap The bitmap to convert.
|
||||
* \param[in] from The current bitmap format specifier.
|
||||
* \param[in] to The bitmap format to convert to.
|
||||
*/
|
||||
void bitmap_format_convert(void *bitmap,
|
||||
const bitmap_fmt_t *from,
|
||||
const bitmap_fmt_t *to);
|
||||
|
||||
/**
|
||||
* Convert a bitmap to the client bitmap format.
|
||||
*
|
||||
* \param[in] bitmap The bitmap to convert.
|
||||
* \param[in] current_fmt The current bitmap format specifier.
|
||||
*/
|
||||
static inline void bitmap_format_to_client(
|
||||
void *bitmap,
|
||||
const bitmap_fmt_t *current_fmt)
|
||||
{
|
||||
bitmap_fmt_t from = *current_fmt;
|
||||
|
||||
from.layout = bitmap_sanitise_bitmap_layout(from.layout);
|
||||
if (from.layout != bitmap_fmt.layout || from.pma != bitmap_fmt.pma) {
|
||||
bitmap_format_convert(bitmap, &from, &bitmap_fmt);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert a bitmap to the client bitmap format.
|
||||
*
|
||||
* \param[in] bitmap The bitmap to convert.
|
||||
* \param[in] target_fmt The target bitmap format specifier.
|
||||
*/
|
||||
static inline void bitmap_format_from_client(
|
||||
void *bitmap,
|
||||
const bitmap_fmt_t *target_fmt)
|
||||
{
|
||||
bitmap_fmt_t to = *target_fmt;
|
||||
|
||||
to.layout = bitmap_sanitise_bitmap_layout(to.layout);
|
||||
if (to.layout != bitmap_fmt.layout || to.pma != bitmap_fmt.pma) {
|
||||
bitmap_format_convert(bitmap, &bitmap_fmt, &to);
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
|
@ -106,7 +106,7 @@ browser_window_history__clone_entry(struct history *history,
|
|||
new_entry->page.bitmap = guit->bitmap->create(
|
||||
LOCAL_HISTORY_WIDTH,
|
||||
LOCAL_HISTORY_HEIGHT,
|
||||
BITMAP_NEW | BITMAP_OPAQUE);
|
||||
BITMAP_OPAQUE);
|
||||
|
||||
if (new_entry->page.bitmap != NULL) {
|
||||
bmsrc_data = guit->bitmap->get_buffer(entry->page.bitmap);
|
||||
|
@ -388,7 +388,7 @@ browser_window_history_add(struct browser_window *bw,
|
|||
|
||||
entry->page.bitmap = guit->bitmap->create(
|
||||
LOCAL_HISTORY_WIDTH, LOCAL_HISTORY_HEIGHT,
|
||||
BITMAP_NEW | BITMAP_CLEAR_MEMORY | BITMAP_OPAQUE);
|
||||
BITMAP_CLEAR | BITMAP_OPAQUE);
|
||||
if (entry->page.bitmap != NULL) {
|
||||
ret = guit->bitmap->render(entry->page.bitmap, content);
|
||||
if (ret != NSERROR_OK) {
|
||||
|
|
|
@ -560,10 +560,6 @@ static nserror verify_bitmap_register(struct gui_bitmap_table *gbt)
|
|||
return NSERROR_BAD_PARAMETER;
|
||||
}
|
||||
|
||||
if (gbt->test_opaque == NULL) {
|
||||
return NSERROR_BAD_PARAMETER;
|
||||
}
|
||||
|
||||
if (gbt->get_buffer == NULL) {
|
||||
return NSERROR_BAD_PARAMETER;
|
||||
}
|
||||
|
@ -580,14 +576,6 @@ static nserror verify_bitmap_register(struct gui_bitmap_table *gbt)
|
|||
return NSERROR_BAD_PARAMETER;
|
||||
}
|
||||
|
||||
if (gbt->get_bpp == NULL) {
|
||||
return NSERROR_BAD_PARAMETER;
|
||||
}
|
||||
|
||||
if (gbt->save == NULL) {
|
||||
return NSERROR_BAD_PARAMETER;
|
||||
}
|
||||
|
||||
if (gbt->modified == NULL) {
|
||||
return NSERROR_BAD_PARAMETER;
|
||||
}
|
||||
|
|
|
@ -41,6 +41,7 @@
|
|||
#include "content/hlcache.h"
|
||||
#include "css/utils.h"
|
||||
|
||||
#include "desktop/bitmap.h"
|
||||
#include "desktop/knockout.h"
|
||||
#include "desktop/textarea.h"
|
||||
#include "desktop/treeview.h"
|
||||
|
@ -5077,7 +5078,7 @@ treeview_generate_triangle_bitmap(colour bg, colour fg, int size)
|
|||
colour colour4 = fg;
|
||||
|
||||
/* Create the bitmap */
|
||||
b = guit->bitmap->create(size, size, BITMAP_NEW | BITMAP_OPAQUE);
|
||||
b = guit->bitmap->create(size, size, BITMAP_OPAQUE);
|
||||
if (b == NULL)
|
||||
return NULL;
|
||||
|
||||
|
@ -5091,58 +5092,68 @@ treeview_generate_triangle_bitmap(colour bg, colour fg, int size)
|
|||
if (y < size / 2) {
|
||||
/* Top half */
|
||||
for (x = 0; x < y * 2; x++) {
|
||||
*(pos++) = red_from_colour(colour4);
|
||||
*(pos++) = green_from_colour(colour4);
|
||||
*(pos++) = blue_from_colour(colour4);
|
||||
*(pos++) = 0xff;
|
||||
pos[bitmap_layout.r] = red_from_colour(colour4);
|
||||
pos[bitmap_layout.g] = green_from_colour(colour4);
|
||||
pos[bitmap_layout.b] = blue_from_colour(colour4);
|
||||
pos[bitmap_layout.a] = 0xff;
|
||||
pos += 4;
|
||||
}
|
||||
*(pos++) = red_from_colour(colour3);
|
||||
*(pos++) = green_from_colour(colour3);
|
||||
*(pos++) = blue_from_colour(colour3);
|
||||
*(pos++) = 0xff;
|
||||
*(pos++) = red_from_colour(colour1);
|
||||
*(pos++) = green_from_colour(colour1);
|
||||
*(pos++) = blue_from_colour(colour1);
|
||||
*(pos++) = 0xff;
|
||||
pos[bitmap_layout.r] = red_from_colour(colour3);
|
||||
pos[bitmap_layout.g] = green_from_colour(colour3);
|
||||
pos[bitmap_layout.b] = blue_from_colour(colour3);
|
||||
pos[bitmap_layout.a] = 0xff;
|
||||
pos += 4;
|
||||
pos[bitmap_layout.r] = red_from_colour(colour1);
|
||||
pos[bitmap_layout.g] = green_from_colour(colour1);
|
||||
pos[bitmap_layout.b] = blue_from_colour(colour1);
|
||||
pos[bitmap_layout.a] = 0xff;
|
||||
pos += 4;
|
||||
for (x = y * 2 + 2; x < size ; x++) {
|
||||
*(pos++) = red_from_colour(colour0);
|
||||
*(pos++) = green_from_colour(colour0);
|
||||
*(pos++) = blue_from_colour(colour0);
|
||||
*(pos++) = 0xff;
|
||||
pos[bitmap_layout.r] = red_from_colour(colour0);
|
||||
pos[bitmap_layout.g] = green_from_colour(colour0);
|
||||
pos[bitmap_layout.b] = blue_from_colour(colour0);
|
||||
pos[bitmap_layout.a] = 0xff;
|
||||
pos += 4;
|
||||
}
|
||||
} else if ((y == size / 2) && (size & 0x1)) {
|
||||
/* Middle row */
|
||||
for (x = 0; x < size - 1; x++) {
|
||||
*(pos++) = red_from_colour(colour4);
|
||||
*(pos++) = green_from_colour(colour4);
|
||||
*(pos++) = blue_from_colour(colour4);
|
||||
*(pos++) = 0xff;
|
||||
pos[bitmap_layout.r] = red_from_colour(colour4);
|
||||
pos[bitmap_layout.g] = green_from_colour(colour4);
|
||||
pos[bitmap_layout.b] = blue_from_colour(colour4);
|
||||
pos[bitmap_layout.a] = 0xff;
|
||||
pos += 4;
|
||||
}
|
||||
*(pos++) = red_from_colour(colour2);
|
||||
*(pos++) = green_from_colour(colour2);
|
||||
*(pos++) = blue_from_colour(colour2);
|
||||
*(pos++) = 0xff;
|
||||
pos[bitmap_layout.r] = red_from_colour(colour2);
|
||||
pos[bitmap_layout.g] = green_from_colour(colour2);
|
||||
pos[bitmap_layout.b] = blue_from_colour(colour2);
|
||||
pos[bitmap_layout.a] = 0xff;
|
||||
pos += 4;
|
||||
} else {
|
||||
/* Bottom half */
|
||||
for (x = 0; x < (size - y - 1) * 2; x++) {
|
||||
*(pos++) = red_from_colour(colour4);
|
||||
*(pos++) = green_from_colour(colour4);
|
||||
*(pos++) = blue_from_colour(colour4);
|
||||
*(pos++) = 0xff;
|
||||
pos[bitmap_layout.r] = red_from_colour(colour4);
|
||||
pos[bitmap_layout.g] = green_from_colour(colour4);
|
||||
pos[bitmap_layout.b] = blue_from_colour(colour4);
|
||||
pos[bitmap_layout.a] = 0xff;
|
||||
pos += 4;
|
||||
}
|
||||
*(pos++) = red_from_colour(colour3);
|
||||
*(pos++) = green_from_colour(colour3);
|
||||
*(pos++) = blue_from_colour(colour3);
|
||||
*(pos++) = 0xff;
|
||||
*(pos++) = red_from_colour(colour1);
|
||||
*(pos++) = green_from_colour(colour1);
|
||||
*(pos++) = blue_from_colour(colour1);
|
||||
*(pos++) = 0xff;
|
||||
pos[bitmap_layout.r] = red_from_colour(colour3);
|
||||
pos[bitmap_layout.g] = green_from_colour(colour3);
|
||||
pos[bitmap_layout.b] = blue_from_colour(colour3);
|
||||
pos[bitmap_layout.a] = 0xff;
|
||||
pos += 4;
|
||||
pos[bitmap_layout.r] = red_from_colour(colour1);
|
||||
pos[bitmap_layout.g] = green_from_colour(colour1);
|
||||
pos[bitmap_layout.b] = blue_from_colour(colour1);
|
||||
pos[bitmap_layout.a] = 0xff;
|
||||
pos += 4;
|
||||
for (x = (size - y) * 2; x < size ; x++) {
|
||||
*(pos++) = red_from_colour(colour0);
|
||||
*(pos++) = green_from_colour(colour0);
|
||||
*(pos++) = blue_from_colour(colour0);
|
||||
*(pos++) = 0xff;
|
||||
pos[bitmap_layout.r] = red_from_colour(colour0);
|
||||
pos[bitmap_layout.g] = green_from_colour(colour0);
|
||||
pos[bitmap_layout.b] = blue_from_colour(colour0);
|
||||
pos[bitmap_layout.a] = 0xff;
|
||||
pos += 4;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -5176,7 +5187,7 @@ treeview_generate_copy_bitmap(struct bitmap *orig, int size)
|
|||
assert(size == guit->bitmap->get_height(orig));
|
||||
|
||||
/* Create the bitmap */
|
||||
b = guit->bitmap->create(size, size, BITMAP_NEW | BITMAP_OPAQUE);
|
||||
b = guit->bitmap->create(size, size, BITMAP_OPAQUE);
|
||||
if (b == NULL)
|
||||
return NULL;
|
||||
|
||||
|
@ -5224,7 +5235,7 @@ treeview_generate_rotate_bitmap(struct bitmap *orig, int size)
|
|||
assert(size == guit->bitmap->get_height(orig));
|
||||
|
||||
/* Create the bitmap */
|
||||
b = guit->bitmap->create(size, size, BITMAP_NEW | BITMAP_OPAQUE);
|
||||
b = guit->bitmap->create(size, size, BITMAP_OPAQUE);
|
||||
if (b == NULL)
|
||||
return NULL;
|
||||
|
||||
|
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
@ -112,11 +112,6 @@ static int gnustep_bitmap_get_height(void *bitmap) {
|
|||
return [(id)bitmap pixelsHigh];
|
||||
}
|
||||
|
||||
// Get how many byytes pet pixel
|
||||
static size_t gnustep_bitmap_get_bpp(void *bitmap) {
|
||||
return [(id)bitmap bitsPerPixel] / 8;
|
||||
}
|
||||
|
||||
// Save the bitmap to the specified path
|
||||
static bool gnustep_bitmap_save(void *bitmap, const char *path, unsigned flags) {
|
||||
NSData *tiff = [(id)bitmap TIFFRepresentation];
|
||||
|
@ -137,13 +132,10 @@ struct gui_bitmap_table gnustep_bitmap_table = {
|
|||
.destroy = gnustep_bitmap_destroy,
|
||||
.set_opaque = gnustep_bitmap_set_opaque,
|
||||
.get_opaque = gnustep_bitmap_get_opaque,
|
||||
.test_opaque = gnustep_bitmap_test_opaque,
|
||||
.get_buffer = gnustep_bitmap_get_buffer,
|
||||
.get_rowstride = gnustep_bitmap_get_rowstride,
|
||||
.get_width = gnustep_bitmap_get_width,
|
||||
.get_height = gnustep_bitmap_get_height,
|
||||
.get_bpp = gnustep_bitmap_get_bpp,
|
||||
.save = gnustep_bitmap_save,
|
||||
.modified = gnustep_bitmap_modified,
|
||||
.render = gnustep_bitmap_render
|
||||
};
|
||||
|
|
|
@ -221,8 +221,7 @@ NSLayoutManager *cocoa_prepare_layout_manager( const char *bytes, size_t length,
|
|||
|
||||
static inline NSFont *cocoa_font_get_nsfont( const plot_font_style_t *style )
|
||||
{
|
||||
NSFont *font = [NSFont systemFontOfSize:
|
||||
((CGFloat)style->size * 1.25f) / PLOT_STYLE_SCALE];
|
||||
NSFont *font = [NSFont systemFontOfSize: 12]; /* eukara: font size hack. idc */
|
||||
|
||||
NSFontTraitMask traits = 0;
|
||||
if (style->flags & FONTF_ITALIC || style->flags & FONTF_OBLIQUE) traits |= NSItalicFontMask;
|
||||
|
|
|
@ -20,64 +20,120 @@
|
|||
* \file
|
||||
* Generic bitmap handling interface.
|
||||
*
|
||||
* This interface wraps the native platform-specific image format, so that
|
||||
* portable image convertors can be written.
|
||||
* This interface wraps the native platform-specific image format.
|
||||
*
|
||||
* Bitmaps are required to be 32bpp with components in the order RR GG BB AA.
|
||||
* Bitmaps are required to be 32bpp with 8-bit components. The components are
|
||||
* red, green, blue, and alpha, in client specified order.
|
||||
*
|
||||
* For example, an opaque 1x1 pixel image would yield the following bitmap
|
||||
* data:
|
||||
*
|
||||
* > Red : 0xff 0x00 0x00 0x00
|
||||
* > Green: 0x00 0xff 0x00 0x00
|
||||
* > Blue : 0x00 0x00 0xff 0x00
|
||||
*
|
||||
* Any attempt to read pixels by casting bitmap data to uint32_t or similar
|
||||
* will need to cater for the order of bytes in a word being different on
|
||||
* big and little endian systems. To avoid confusion, it is recommended
|
||||
* that pixel data is loaded as follows:
|
||||
*
|
||||
* uint32_t read_pixel(const uint8_t *bmp)
|
||||
* {
|
||||
* // red green blue alpha
|
||||
* return bmp[0] | (bmp[1] << 8) | (bmp[2] << 16) | (bmp[3] << 24);
|
||||
* }
|
||||
*
|
||||
* and *not* as follows:
|
||||
*
|
||||
* uint32_t read_pixel(const uint8_t *bmp)
|
||||
* {
|
||||
* return *((uint32_t *) bmp);
|
||||
* }
|
||||
* The component order may be set in the front ends by calling
|
||||
* \ref bitmap_set_format().
|
||||
*/
|
||||
|
||||
#ifndef _NETSURF_BITMAP_H_
|
||||
#define _NETSURF_BITMAP_H_
|
||||
|
||||
#define BITMAP_NEW 0
|
||||
#define BITMAP_OPAQUE (1 << 0) /**< image is opaque */
|
||||
#define BITMAP_MODIFIED (1 << 1) /**< buffer has been modified */
|
||||
#define BITMAP_CLEAR_MEMORY (1 << 2) /**< memory should be wiped */
|
||||
/** Bitmap creation flags. */
|
||||
enum gui_bitmap_flags {
|
||||
BITMAP_NONE = 0,
|
||||
BITMAP_OPAQUE = (1 << 0), /**< image is opaque */
|
||||
BITMAP_CLEAR = (1 << 1), /**< memory should be wiped to 0 */
|
||||
};
|
||||
|
||||
/**
|
||||
* NetSurf bitmap pixel layout.
|
||||
*
|
||||
* All pixels are 32 bits per pixel (bpp). The different layouts allow control
|
||||
* over the ordering of colour channels. All colour channels are 8 bits wide.
|
||||
*/
|
||||
enum bitmap_layout {
|
||||
/** Bite-wise RGBA: Byte order: 0xRR, 0xGG, 0xBB, 0xAA. */
|
||||
BITMAP_LAYOUT_R8G8B8A8,
|
||||
|
||||
/** Bite-wise BGRA: Byte order: 0xBB, 0xGG, 0xRR, 0xAA. */
|
||||
BITMAP_LAYOUT_B8G8R8A8,
|
||||
|
||||
/** Bite-wise ARGB: Byte order: 0xAA, 0xRR, 0xGG, 0xBB. */
|
||||
BITMAP_LAYOUT_A8R8G8B8,
|
||||
|
||||
/** Bite-wise ABGR: Byte order: 0xAA, 0xBB, 0xGG, 0xRR. */
|
||||
BITMAP_LAYOUT_A8B8G8R8,
|
||||
|
||||
/**
|
||||
* 32-bit RGBA (0xRRGGBBAA).
|
||||
*
|
||||
* * On little endian host, same as \ref BITMAP_LAYOUT_A8B8G8R8.
|
||||
* * On big endian host, same as \ref BITMAP_LAYOUT_R8G8B8A8.
|
||||
*/
|
||||
BITMAP_LAYOUT_RGBA8888,
|
||||
|
||||
/**
|
||||
* 32-bit BGRA (0xBBGGRRAA).
|
||||
*
|
||||
* * On little endian host, same as \ref BITMAP_LAYOUT_A8R8G8B8.
|
||||
* * On big endian host, same as \ref BITMAP_LAYOUT_B8G8R8A8.
|
||||
*/
|
||||
BITMAP_LAYOUT_BGRA8888,
|
||||
|
||||
/**
|
||||
* 32-bit ARGB (0xAARRGGBB).
|
||||
*
|
||||
* * On little endian host, same as \ref BITMAP_LAYOUT_B8G8R8A8.
|
||||
* * On big endian host, same as \ref BITMAP_LAYOUT_A8R8G8B8.
|
||||
*/
|
||||
BITMAP_LAYOUT_ARGB8888,
|
||||
|
||||
/**
|
||||
* 32-bit BGRA (0xAABBGGRR).
|
||||
*
|
||||
* * On little endian host, same as \ref BITMAP_LAYOUT_R8G8B8A8.
|
||||
* * On big endian host, same as \ref BITMAP_LAYOUT_A8B8G8R8.
|
||||
*/
|
||||
BITMAP_LAYOUT_ABGR8888,
|
||||
};
|
||||
|
||||
/** Bitmap format specifier. */
|
||||
typedef struct bitmap_fmt {
|
||||
enum bitmap_layout layout; /**< Colour component layout. */
|
||||
bool pma; /**< Premultiplied alpha. */
|
||||
} bitmap_fmt_t;
|
||||
|
||||
struct content;
|
||||
struct bitmap;
|
||||
struct hlcache_handle;
|
||||
|
||||
/**
|
||||
* Set client bitmap format.
|
||||
*
|
||||
* Set this to ensure that the bitmaps decoded by the core are in the
|
||||
* correct format for the front end.
|
||||
*
|
||||
* \param[in] bitmap_format The bitmap format specification to set.
|
||||
*/
|
||||
void bitmap_set_format(const bitmap_fmt_t *bitmap_format);
|
||||
|
||||
/**
|
||||
* Test whether a bitmap is completely opaque (no transparency).
|
||||
*
|
||||
* \param[in] bitmap The bitmap to test.
|
||||
* \return Returns true if the bitmap is opaque, false otherwise.
|
||||
*/
|
||||
bool bitmap_test_opaque(void *bitmap);
|
||||
|
||||
/**
|
||||
* Bitmap operations.
|
||||
*/
|
||||
struct gui_bitmap_table {
|
||||
/* Mandantory entries */
|
||||
/* Mandatory entries */
|
||||
|
||||
/**
|
||||
* Create a new bitmap.
|
||||
*
|
||||
* \param width width of image in pixels
|
||||
* \param height width of image in pixels
|
||||
* \param state The state to create the bitmap in.
|
||||
* \param width width of image in pixels
|
||||
* \param height height of image in pixels
|
||||
* \param flags flags for bitmap creation
|
||||
* \return A bitmap structure or NULL on error.
|
||||
*/
|
||||
void *(*create)(int width, int height, unsigned int state);
|
||||
void *(*create)(int width, int height, enum gui_bitmap_flags flags);
|
||||
|
||||
/**
|
||||
* Destroy a bitmap.
|
||||
|
@ -102,17 +158,11 @@ struct gui_bitmap_table {
|
|||
*/
|
||||
bool (*get_opaque)(void *bitmap);
|
||||
|
||||
/**
|
||||
* Test if a bitmap is opaque.
|
||||
*
|
||||
* \param bitmap The bitmap to examine.
|
||||
* \return The bitmap opacity.
|
||||
*/
|
||||
bool (*test_opaque)(void *bitmap);
|
||||
|
||||
/**
|
||||
* Get the image buffer from a bitmap
|
||||
*
|
||||
* Note that all pixels must be 4-byte aligned.
|
||||
*
|
||||
* \param bitmap The bitmap to get the buffer from.
|
||||
* \return The image buffer or NULL if there is none.
|
||||
*/
|
||||
|
@ -142,22 +192,6 @@ struct gui_bitmap_table {
|
|||
*/
|
||||
int (*get_height)(void *bitmap);
|
||||
|
||||
/**
|
||||
* Get the *bytes* per pixel.
|
||||
*
|
||||
* \param bitmap The bitmap
|
||||
*/
|
||||
size_t (*get_bpp)(void *bitmap);
|
||||
|
||||
/**
|
||||
* Save a bitmap to disc.
|
||||
*
|
||||
* \param bitmap The bitmap to save
|
||||
* \param path The path to save the bitmap to.
|
||||
* \param flags Flags affecting the save.
|
||||
*/
|
||||
bool (*save)(void *bitmap, const char *path, unsigned flags);
|
||||
|
||||
/**
|
||||
* Marks a bitmap as modified.
|
||||
*
|
||||
|
|
Loading…
Reference in New Issue