Fix against latest Netsurf - eukara

This commit is contained in:
Marco Cawthorne 2022-09-17 00:05:32 -07:00
parent fdcff90d09
commit cbd3105023
Signed by: eukara
GPG Key ID: CE2032F0A2882A22
20 changed files with 1067 additions and 469 deletions

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

338
desktop/bitmap.c Normal file
View File

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

147
desktop/bitmap.h Normal file
View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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