Fix against latest Netsurf - eukara

gnustep
Marco Cawthorne 1 year ago
parent fdcff90d09
commit cbd3105023
Signed by: eukara
GPG Key ID: CE2032F0A2882A22

@ -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);
}
/**
* Convert client bitmap format to a LibNSGIF format specifier.
*/
static nsgif_bitmap_fmt_t nsgif__get_bitmap_format(void)
{
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 nsgif_create_gif_data(nsgif_content *c)
static nserror gif_create_gif_data(gif_content *c)
{
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
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;
/* 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;
}
}
nsgif_error gif_res;
nsgif_rect_t rect;
uint32_t delay;
uint32_t f;
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 */
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;
}
/* 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);
}
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;