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 c2d6894a6..26bb98e91 100644 Binary files a/frontends/gnustep/res/Browser.gorm/data.info and b/frontends/gnustep/res/Browser.gorm/data.info differ diff --git a/frontends/gnustep/res/Browser.gorm/objects.gorm b/frontends/gnustep/res/Browser.gorm/objects.gorm index 0ca18c6a4..41417b9eb 100644 Binary files a/frontends/gnustep/res/Browser.gorm/objects.gorm and b/frontends/gnustep/res/Browser.gorm/objects.gorm differ diff --git a/frontends/gnustep/res/Menu.gorm/data.info b/frontends/gnustep/res/Menu.gorm/data.info index c2d6894a6..26bb98e91 100644 Binary files a/frontends/gnustep/res/Menu.gorm/data.info and b/frontends/gnustep/res/Menu.gorm/data.info differ diff --git a/frontends/gnustep/res/Menu.gorm/objects.gorm b/frontends/gnustep/res/Menu.gorm/objects.gorm index 25add3765..ff267b062 100644 Binary files a/frontends/gnustep/res/Menu.gorm/objects.gorm and b/frontends/gnustep/res/Menu.gorm/objects.gorm differ 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. *