From cbd310502340444b80e06a86dc148e92476b62a9 Mon Sep 17 00:00:00 2001 From: Marco Cawthorne Date: Sat, 17 Sep 2022 00:05:32 -0700 Subject: [PATCH] Fix against latest Netsurf - eukara --- content/handlers/image/bmp.c | 12 +- content/handlers/image/gif.c | 393 +++++++++--------- content/handlers/image/ico.c | 49 ++- content/handlers/image/jpeg.c | 182 +++++--- content/handlers/image/png.c | 53 ++- content/handlers/image/rsvg.c | 44 +- content/handlers/image/webp.c | 38 +- desktop/Makefile | 2 +- desktop/bitmap.c | 338 +++++++++++++++ desktop/bitmap.h | 147 +++++++ desktop/browser_history.c | 4 +- desktop/gui_factory.c | 12 - desktop/treeview.c | 97 +++-- frontends/gnustep/res/Browser.gorm/data.info | Bin 184 -> 184 bytes .../gnustep/res/Browser.gorm/objects.gorm | Bin 5109 -> 6081 bytes frontends/gnustep/res/Menu.gorm/data.info | Bin 184 -> 184 bytes frontends/gnustep/res/Menu.gorm/objects.gorm | Bin 18998 -> 19104 bytes frontends/gnustep/tables/bitmap.m | 8 - frontends/gnustep/tables/font.m | 3 +- include/netsurf/bitmap.h | 154 ++++--- 20 files changed, 1067 insertions(+), 469 deletions(-) create mode 100644 desktop/bitmap.c create mode 100644 desktop/bitmap.h diff --git a/content/handlers/image/bmp.c b/content/handlers/image/bmp.c index a723022fe..3fec2cc75 100644 --- a/content/handlers/image/bmp.c +++ b/content/handlers/image/bmp.c @@ -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); } diff --git a/content/handlers/image/gif.c b/content/handlers/image/gif.c index e2a0ca5db..deabd0adc 100644 --- a/content/handlers/image/gif.c +++ b/content/handlers/image/gif.c @@ -34,8 +34,12 @@ #include #include #include -#include +#include + +#include + +#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); diff --git a/content/handlers/image/ico.c b/content/handlers/image/ico.c index 2d839b1d1..871da41a9 100644 --- a/content/handlers/image/ico.c +++ b/content/handlers/image/ico.c @@ -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); diff --git a/content/handlers/image/jpeg.c b/content/handlers/image/jpeg.c index 549c2b674..e07fb47bb 100644 --- a/content/handlers/image/jpeg.c +++ b/content/handlers/image/jpeg.c @@ -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); diff --git a/content/handlers/image/png.c b/content/handlers/image/png.c index 06a38ca0f..97a5795b3 100644 --- a/content/handlers/image/png.c +++ b/content/handlers/image/png.c @@ -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); } diff --git a/content/handlers/image/rsvg.c b/content/handlers/image/rsvg.c index 0051df38f..24fc1a4e0 100644 --- a/content/handlers/image/rsvg.c +++ b/content/handlers/image/rsvg.c @@ -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); diff --git a/content/handlers/image/webp.c b/content/handlers/image/webp.c index 721e92438..da13316bc 100644 --- a/content/handlers/image/webp.c +++ b/content/handlers/image/webp.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; diff --git a/desktop/Makefile b/desktop/Makefile index 63749a6f2..5e190275d 100644 --- a/desktop/Makefile +++ b/desktop/Makefile @@ -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 diff --git a/desktop/bitmap.c b/desktop/bitmap.c new file mode 100644 index 000000000..0602773ca --- /dev/null +++ b/desktop/bitmap.c @@ -0,0 +1,338 @@ +/* + * Copyright 2022 Michael Drake + * + * 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 . + */ + +/** \file + * Internal core bitmap interface. + */ + +#include +#include +#include + +#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; +} diff --git a/desktop/bitmap.h b/desktop/bitmap.h new file mode 100644 index 000000000..51ce2c908 --- /dev/null +++ b/desktop/bitmap.h @@ -0,0 +1,147 @@ +/* + * Copyright 2022 Michael Drake + * + * 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 . + */ + +/** \file + * Internal core bitmap interface. + */ + +#ifndef _NETSURF_DESKTOP_BITMAP_H_ +#define _NETSURF_DESKTOP_BITMAP_H_ + +#include + +#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 diff --git a/desktop/browser_history.c b/desktop/browser_history.c index 2fbc80f89..ce9821af8 100644 --- a/desktop/browser_history.c +++ b/desktop/browser_history.c @@ -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) { diff --git a/desktop/gui_factory.c b/desktop/gui_factory.c index a141d7d23..89f2e373f 100644 --- a/desktop/gui_factory.c +++ b/desktop/gui_factory.c @@ -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; } diff --git a/desktop/treeview.c b/desktop/treeview.c index feb1a7c6e..a65a37e72 100644 --- a/desktop/treeview.c +++ b/desktop/treeview.c @@ -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; diff --git a/frontends/gnustep/res/Browser.gorm/data.info b/frontends/gnustep/res/Browser.gorm/data.info index c2d6894a68792c008546fbacbbbe83769b7db8bd..26bb98e9164142d86386ac44f3a15b22024dd0ac 100644 GIT binary patch delta 11 ScmdnNxPx&*8l%j_^gsX@7z57$ delta 11 ScmdnNxPx&*8l%+2^gsX@5ChEs diff --git a/frontends/gnustep/res/Browser.gorm/objects.gorm b/frontends/gnustep/res/Browser.gorm/objects.gorm index 0ca18c6a49fdfb696d22bfdf5355fa843e73d8f1..41417b9eb1c357be181ff5a5e80bde9c74d08075 100644 GIT binary patch literal 6081 zcmeHLTUQ*%5$+w9U0|2{Rn`q(Bn#WJs1-}~)xdJ9w$B(dxWT&2 z^4jKG_Evz;jDf*BSkWxs*$b>q*Dl+n=2_NhmZJxD5pfyl;GJw>x%|MXR=mcBy<2mw zz?LICCs$f@t$MxOuxd4031`moyj-jt_)c|40%@YI3n=!C;-9W8Gg+BEU=FLY3iDWl zcgYvj3JB=n-K=1i@7eX5SFO;r875z6;n-Ml?%z^ktXO(R31h|5CrVc=z4ry_ZmSX2 ze=8CU1l7NxhhphdrG!r2!*XW%b;oX?<}sH*OVl)INh};;iNWwUV`O5BhQbhXyukW2 z_4B@O?aLW>k>$;D{1~xDgh8J7vO(k;L+X2OA_z!#S2HgIH6#h@l<*Y-UNzEX_>c@*^ps^#Q2uvo|GN#>+p^q; zUOfn*$cH6-37NwQJIKe*!hDG_Q6Sr7bYzU;U-_goI%X^mURv`VhKkuR4C5Iz{;`CN zW-9zFgkC-(6J^nJJ-N3#81yL$1|sDe75ghY?3xYaT1w|9!r+c9zVS3FR0I#RR&bY#U(O zq+Dz*3&Voso&IR07L@mJsyQkdqNBKU_e&`r3f=laq&hit?XaUl_f#r$r<-GNM!A#M zampJ@p5L(iN+`yEpfSS8Jdxm>O$NvMGK$3iWM7?21?M~lXEGg}d-k?(*B|~rI8QhG zT07^Xi8wf)lYocb8>L8f^3G|8weOs6JoKJP1?So30A?6Gr_QqO!u$c=fD_S)_5-^C zMXwl)?v)FCToyLBcC8&ds^g4A9rIzD<%ryGyIR^|N3nb{#q#rfLYouUFf4>i{FJ=0 z$x4iI2Jej{w!`5lAVwd(vGDyamM3|i=t4vbn7A)f-r_9~33TI6CA0m55ii9A0b=z)R_)sBFttY9< zb4q=*6iceh{3%($Z&;gA;;Z78E@nmPF`daljfRgQT(?pYtetS#z`U#o&&X~g16P_o z`dn)@XK`4uIs2N{E0*3BCobFuOD!y}h${e5tbrf0VVa+2y_$?$Sj5s&@QO-;WR2_K z;3S8u=@I$bB*#Twm4z+*D1o4=I+w-jd7sr;fHn0l{8)`sIYi^5S=MMZwQPav>_=<| zt8S0&QM0S-aAFKSx-7;?l-{gfPOl^4d070mMG(~k zHx#N@o6>8vI+IJ&Q{p__RFEUwZ0iMBP9s``;2clG&lG}OrCiRd4ss1M zx{BHE!!3oNY$a01jRoIp)NNm-W%O;rwSBmqn2ex#gKWW-uE2^KJi;0D^(?Ha;iF@R zh$*zB4KWM9Rz@UQ+r$P~ zWh4Bid0uRYoABGT+uN%Bihyqta1Ja5Odh>^zPn)gu*oOcXiS9spw(u~tk{4p^>(M; z7M+GPW(F$FNpx)kYz3gtiWK86BeG)#w$X$+#O$Oo72CCU@B=11L}pebMGgu##7YQn zk>M-wNWGOzFkkgBk%umtu!7U?Dq!-css~sXCQClKFQCv;_MK zRM6}e3@(p*HDU7P#n0685P^>nV&a;AIbqF@5wnh%Pfo!6m6lqC yzbQNfmc!RWH5bQ?9CsuMVtyX5%wK!2--e>Bg~cXKf(y8Z*i7<}gd literal 5109 zcmdT|>2}-J5x#^-Q4+K-ly@^tElW-6&b8gtRa;A3LdmrRMWPjF=@LkSWX2G|f>0QB zi`u5yn}d@l0sr@h9-vRr(+B7a^qkJz0YOltocK?BA|Byf-1}jcZ|2S&V8z(3dsZ!L zy1P#*?^yt_fA{uXSi}MT-EIx;S>X)eOhP8a=t|kB>=c};XI83~D}>ObCI*c1#?HIe zu7~$RfF}FIuu)z=^2{CEDqCt=CPm69hc{vlaS7;?DKWlMeq5>UIgLlwVa+x@i&kVm zX)4%ey9v6Nlqc5EZq36bh$mqqD6QLJJPQ^T~jorkHmeC9UqRBg_ z0#b5F^c&^ZD^>%SzD5K}MvzdKm}E`hFQFw!!=&#+ztqKOL|e{v&0~5@4vVx=Zrz3+ zD`z1sN5lm3gyC=Fow+0^WrY8g4by3Dq0y!f?QT5!7?U6{+*sl z5KSwR!vr73B$)0-h%bocyV_W#5avh&;oU5yb`7;O{uocVRFgP`k3P_~<( zEoQBC11ijE4jWVb>f7+_T`G5A#pw@Ieg9dQ}ey4+=7n|c4Dflk$t3j@k7sB23 zKdzF&NH7uBo*QzO4y6Z&=7ANSuUh*IDrO_^>Gtx;d$Pgqhj5@Wb|B(T*Zs3&I|EuxP&WwCg4dGFiqkl#K-3rVP>jD$TvJpqav)* zgh{w1FHxiN$lM8&gb#aFWHE4^-B#bpnMQ@%JDiS_LQU8Yn3M!{ObLMIP_*y3IV5`Z zV(C=qh5vL3ZB2Iw4IluJA=hxbL;f9kS&T$ZJix;o)nfB@mje+$>npgJmGHyv6?!Zy zQOPTGz{8JX2-RKmC$Wn-7IooaZ*d6scwHSyYJDat8tsNz87v?rLax3YK|!m5RGdUgeG-v^LbnoC!_?!5xCzCtQ-Y`=c$HBjktriuyF;75y%1c7 z5<_~pD;I)`uyT$iEn9EEYB!=y2+DW?9xy_=s&X{V9r6ZdY8A8Cg`YD*WuYQ-n1uz()>V%R$)JhNv%q`g9 z^>YYC$PB#3kn_Wjie7|GM#U{%KhCnZnCM&2)PufWhBEJkIjU`Q@JL>x*G+z%uj0$Q zdOg9~z6skb>wNlsc)fWo$OoG~JjTItkoVyY92A3PAKt{l63x7YgNIeW-ytnz%}~dN_D3 zSoPru2P-u59u5k@iVqDXWfk7tyK(4syY|4}ZdeK{J2G!TrGS;V(FN7~J>aLuH=< ze`SI3%8Q@2sEEKv2r*FS$IsCD31T)8^XW4%pD9)A@Hu0;saP|2EE~R1q8`HE7^V(! fm2NXyrCRYSrd_JIbuaq91z$F=P;Z*RxDWgnGk#!4 diff --git a/frontends/gnustep/res/Menu.gorm/data.info b/frontends/gnustep/res/Menu.gorm/data.info index c2d6894a68792c008546fbacbbbe83769b7db8bd..26bb98e9164142d86386ac44f3a15b22024dd0ac 100644 GIT binary patch delta 11 ScmdnNxPx&*8l%j_^gsX@7z57$ delta 11 ScmdnNxPx&*8l%+2^gsX@5ChEs diff --git a/frontends/gnustep/res/Menu.gorm/objects.gorm b/frontends/gnustep/res/Menu.gorm/objects.gorm index 25add3765afd9050bc4a61373ee93bae3b16ab71..ff267b0623ae38e68b40fb956f397dc4ad63402c 100644 GIT binary patch literal 19104 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 RW!Iq;1aV*gME{R9{SRUy)HeVC literal 18998 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 F{tt~~x*Pxi diff --git a/frontends/gnustep/tables/bitmap.m b/frontends/gnustep/tables/bitmap.m index 5695d7a66..3bb5063f6 100644 --- a/frontends/gnustep/tables/bitmap.m +++ b/frontends/gnustep/tables/bitmap.m @@ -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 }; diff --git a/frontends/gnustep/tables/font.m b/frontends/gnustep/tables/font.m index a11d55d92..701d11800 100644 --- a/frontends/gnustep/tables/font.m +++ b/frontends/gnustep/tables/font.m @@ -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; diff --git a/include/netsurf/bitmap.h b/include/netsurf/bitmap.h index f5de51423..10e9a07fb 100644 --- a/include/netsurf/bitmap.h +++ b/include/netsurf/bitmap.h @@ -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. *