diff options
Diffstat (limited to 'content/handlers/image')
-rw-r--r-- | content/handlers/image/Makefile | 3 | ||||
-rw-r--r-- | content/handlers/image/image.c | 7 | ||||
-rw-r--r-- | content/handlers/image/jpeg.c | 2 | ||||
-rw-r--r-- | content/handlers/image/jpegxl.c | 340 | ||||
-rw-r--r-- | content/handlers/image/jpegxl.h | 28 | ||||
-rw-r--r-- | content/handlers/image/nssprite.c | 2 | ||||
-rw-r--r-- | content/handlers/image/rsvg246.c | 280 | ||||
-rw-r--r-- | content/handlers/image/webp.c | 2 |
8 files changed, 661 insertions, 3 deletions
diff --git a/content/handlers/image/Makefile b/content/handlers/image/Makefile index 1c27f74d7..ac052b37a 100644 --- a/content/handlers/image/Makefile +++ b/content/handlers/image/Makefile @@ -7,10 +7,11 @@ S_IMAGE_$(NETSURF_USE_BMP) += bmp.c S_IMAGE_$(NETSURF_USE_GIF) += gif.c S_IMAGE_$(NETSURF_USE_BMP) += ico.c S_IMAGE_$(NETSURF_USE_JPEG) += jpeg.c +S_IMAGE_$(NETSURF_USE_JPEGXL) += jpegxl.c S_IMAGE_$(NETSURF_USE_ROSPRITE) += nssprite.c S_IMAGE_$(NETSURF_USE_PNG) += png.c S_IMAGE_$(NETSURF_USE_NSSVG) += svg.c -S_IMAGE_$(NETSURF_USE_RSVG) += rsvg.c +S_IMAGE_$(NETSURF_USE_RSVG) += rsvg$(RSVG_API).c S_IMAGE_$(NETSURF_USE_VIDEO) += video.c S_IMAGE_$(NETSURF_USE_WEBP) += webp.c diff --git a/content/handlers/image/image.c b/content/handlers/image/image.c index 3107ee495..2bd5f5f8d 100644 --- a/content/handlers/image/image.c +++ b/content/handlers/image/image.c @@ -32,6 +32,7 @@ #include "image/gif.h" #include "image/ico.h" #include "image/jpeg.h" +#include "image/jpegxl.h" #include "image/nssprite.h" #include "image/png.h" #include "image/rsvg.h" @@ -72,6 +73,12 @@ nserror image_init(void) return error; #endif +#ifdef WITH_JPEGXL + error = nsjpegxl_init(); + if (error != NSERROR_OK) + return error; +#endif + #ifdef WITH_PNG error = nspng_init(); if (error != NSERROR_OK) diff --git a/content/handlers/image/jpeg.c b/content/handlers/image/jpeg.c index e07fb47bb..93372f15a 100644 --- a/content/handlers/image/jpeg.c +++ b/content/handlers/image/jpeg.c @@ -206,7 +206,9 @@ static inline void nsjpeg__decode_rgb( uint8_t * volatile pixels, size_t rowstride) { +#if RGB_RED != 0 || RGB_GREEN != 1 || RGB_BLUE != 2 || RGB_PIXELSIZE != 4 int width = cinfo->output_width; +#endif do { JSAMPROW scanlines[1] = { diff --git a/content/handlers/image/jpegxl.c b/content/handlers/image/jpegxl.c new file mode 100644 index 000000000..01c704577 --- /dev/null +++ b/content/handlers/image/jpegxl.c @@ -0,0 +1,340 @@ +/* + * Copyright 2023 Vincent Sanders <vince@netsurf-browser.org> + * + * This file is part of NetSurf, http://www.netsurf-browser.org/ + * + * NetSurf is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * NetSurf is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +/** + * \file + * implementation of content handling for image/jpegxl + * + * This implementation uses the JXL library. + */ + +#include <stdbool.h> +#include <stdlib.h> +#include <setjmp.h> +#include <string.h> + +#include <jxl/decode.h> + +#include "utils/utils.h" +#include "utils/log.h" +#include "utils/messages.h" +#include "netsurf/bitmap.h" +#include "content/llcache.h" +#include "content/content.h" +#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/jpegxl.h" + + +/** + * output image format + */ +static const JxlPixelFormat jxl_output_format = { + .num_channels = 4, + .data_type = JXL_TYPE_UINT8, + .endianness = JXL_LITTLE_ENDIAN, + .align = 0, +}; + +/** + * Content create entry point. + */ +static nserror +nsjpegxl_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) +{ + struct content *jpeg; + nserror error; + + jpeg = calloc(1, sizeof(struct content)); + if (jpeg == NULL) + return NSERROR_NOMEM; + + error = content__init(jpeg, handler, imime_type, params, + llcache, fallback_charset, quirks); + if (error != NSERROR_OK) { + free(jpeg); + return error; + } + + *c = jpeg; + + return NSERROR_OK; +} + +/** + * create a bitmap from jpeg xl content. + */ +static struct bitmap * +jpegxl_cache_convert(struct content *c) +{ + struct bitmap * bitmap = NULL; + JxlDecoder *jxldec; + JxlDecoderStatus decstatus; + JxlBasicInfo binfo; + const uint8_t *src_data; + size_t src_size; + uint8_t * output; + bitmap_fmt_t jxl_fmt = { + /** TODO: At the moment we have to set the layout to the only + * pixel layout that libjxl supports. It looks like they + * plan to add support for decoding to other layouts + * in the future, as shown by the TODO in the docs: + * + * https://libjxl.readthedocs.io/en/latest/api_common.html#_CPPv414JxlPixelFormat + */ + .layout = BITMAP_LAYOUT_R8G8B8A8, + .pma = bitmap_fmt.pma, + }; + + jxldec = JxlDecoderCreate(NULL); + if (jxldec == NULL) { + NSLOG(netsurf, ERROR, "Unable to allocate decoder"); + return NULL; + } + + decstatus = JxlDecoderSetUnpremultiplyAlpha(jxldec, !bitmap_fmt.pma); + if (decstatus != JXL_DEC_SUCCESS) { + NSLOG(netsurf, ERROR, "unable to set premultiplied alpha status: %d", + decstatus); + JxlDecoderDestroy(jxldec); + return NULL; + } + + decstatus= JxlDecoderSubscribeEvents(jxldec, JXL_DEC_FULL_IMAGE); + if (decstatus != JXL_DEC_SUCCESS) { + NSLOG(netsurf, ERROR, "Unable to subscribe"); + return NULL; + } + src_data = content__get_source_data(c, &src_size); + + decstatus = JxlDecoderSetInput(jxldec, src_data, src_size); + if (decstatus != JXL_DEC_SUCCESS) { + NSLOG(netsurf, ERROR, "unable to set input"); + return NULL; + } + + decstatus = JxlDecoderProcessInput(jxldec); + if (decstatus != JXL_DEC_NEED_IMAGE_OUT_BUFFER) { + NSLOG(netsurf, ERROR, + "expected status JXL_DEC_NEED_IMAGE_OUT_BUFFER(%d) got %d", + JXL_DEC_NEED_IMAGE_OUT_BUFFER, + decstatus); + JxlDecoderDestroy(jxldec); + return NULL; + } + + decstatus = JxlDecoderGetBasicInfo(jxldec, &binfo); + if (decstatus != JXL_DEC_SUCCESS) { + NSLOG(netsurf, ERROR, "unable to get basic info status:%d",decstatus); + JxlDecoderDestroy(jxldec); + return NULL; + } + + /* create bitmap with appropriate opacity */ + if (binfo.alpha_bits > 0) { + bitmap = guit->bitmap->create(c->width, c->height, BITMAP_OPAQUE); + } else { + bitmap = guit->bitmap->create(c->width, c->height, BITMAP_NONE); + } + if (bitmap == NULL) { + /* empty bitmap could not be created */ + JxlDecoderDestroy(jxldec); + return NULL; + } + + /* ensure buffer was allocated */ + output = guit->bitmap->get_buffer(bitmap); + if (output == NULL) { + /* bitmap with no buffer available */ + guit->bitmap->destroy(bitmap); + JxlDecoderDestroy(jxldec); + return NULL; + } + decstatus = JxlDecoderSetImageOutBuffer(jxldec, &jxl_output_format, output, c->size); + if (decstatus != JXL_DEC_SUCCESS) { + NSLOG(netsurf, ERROR, "unable to set output buffer callback status:%d",decstatus); + guit->bitmap->destroy(bitmap); + JxlDecoderDestroy(jxldec); + return NULL; + } + + decstatus = JxlDecoderProcessInput(jxldec); + if (decstatus != JXL_DEC_FULL_IMAGE) { + NSLOG(netsurf, ERROR, "did not get decode event"); + guit->bitmap->destroy(bitmap); + JxlDecoderDestroy(jxldec); + return NULL; + } + + JxlDecoderDestroy(jxldec); + + bitmap_format_to_client(bitmap, &jxl_fmt); + guit->bitmap->modified(bitmap); + + return bitmap; +} + +/** + * report failiure + */ +static bool jxl_report_fail(struct content *c, JxlDecoderStatus decstatus, const char *msg) +{ + union content_msg_data msg_data; + NSLOG(netsurf, ERROR, "%s decoder status:%d", msg, decstatus); + msg_data.errordata.errorcode = NSERROR_UNKNOWN; + msg_data.errordata.errormsg = msg; + content_broadcast(c, CONTENT_MSG_ERROR, &msg_data); + return false; +} + +/** + * Convert a CONTENT_JPEGXL for display. + */ +static bool nsjpegxl_convert(struct content *c) +{ + JxlDecoder *jxldec; + JxlSignature decsig; + JxlDecoderStatus decstatus = JXL_DEC_ERROR; + JxlBasicInfo binfo; + union content_msg_data msg_data; + const uint8_t *data; + size_t size; + char *title; + size_t image_size; + + /* check image header is valid and get width/height */ + data = content__get_source_data(c, &size); + + decsig = JxlSignatureCheck(data,size); + if ((decsig != JXL_SIG_CODESTREAM) && (decsig != JXL_SIG_CONTAINER)) { + NSLOG(netsurf, ERROR, "signature failed"); + msg_data.errordata.errorcode = NSERROR_UNKNOWN; + msg_data.errordata.errormsg = "Signature failed"; + content_broadcast(c, CONTENT_MSG_ERROR, &msg_data); + return false; + } + + jxldec = JxlDecoderCreate(NULL); + if (jxldec == NULL) { + return jxl_report_fail(c, decstatus, "Unable to allocate decoder"); + } + decstatus= JxlDecoderSubscribeEvents(jxldec, JXL_DEC_BASIC_INFO); + if (decstatus != JXL_DEC_SUCCESS) { + return jxl_report_fail(c, decstatus, "Unable to subscribe"); + } + decstatus = JxlDecoderSetInput(jxldec, data,size); + if (decstatus != JXL_DEC_SUCCESS) { + return jxl_report_fail(c, decstatus, "unable to set input"); + } + decstatus = JxlDecoderProcessInput(jxldec); + if (decstatus != JXL_DEC_BASIC_INFO) { + return jxl_report_fail(c, decstatus, "did not get basic info event"); + } + decstatus = JxlDecoderGetBasicInfo(jxldec, &binfo); + if (decstatus != JXL_DEC_SUCCESS) { + return jxl_report_fail(c, decstatus, "unable to get basic info"); + } + decstatus = JxlDecoderImageOutBufferSize(jxldec, &jxl_output_format, &image_size); + if (decstatus != JXL_DEC_SUCCESS) { + return jxl_report_fail(c, decstatus, "unable get image size"); + } + + JxlDecoderDestroy(jxldec); + + NSLOG(netsurf, INFO, "got basic info size:%ld x:%d y:%d", image_size, binfo.xsize, binfo.ysize); + + c->width = binfo.xsize; + c->height = binfo.ysize; + c->size = image_size; + + image_cache_add(c, NULL, jpegxl_cache_convert); + + /* set title text */ + title = messages_get_buff("JPEGXLTitle", + nsurl_access_leaf(llcache_handle_get_url(c->llcache)), + c->width, c->height); + if (title != NULL) { + content__set_title(c, title); + free(title); + } + + content_set_ready(c); + content_set_done(c); + content_set_status(c, ""); /* Done: update status bar */ + + return true; +} + + +/** + * Clone content. + */ +static nserror nsjpegxl_clone(const struct content *old, struct content **newc) +{ + struct content *jpegxl_c; + nserror error; + + jpegxl_c = calloc(1, sizeof(struct content)); + if (jpegxl_c == NULL) + return NSERROR_NOMEM; + + error = content__clone(old, jpegxl_c); + if (error != NSERROR_OK) { + content_destroy(jpegxl_c); + return error; + } + + /* re-convert if the content is ready */ + if ((old->status == CONTENT_STATUS_READY) || + (old->status == CONTENT_STATUS_DONE)) { + if (nsjpegxl_convert(jpegxl_c) == false) { + content_destroy(jpegxl_c); + return NSERROR_CLONE_FAILED; + } + } + + *newc = jpegxl_c; + + return NSERROR_OK; +} + +static const content_handler nsjpegxl_content_handler = { + .create = nsjpegxl_create, + .data_complete = nsjpegxl_convert, + .destroy = image_cache_destroy, + .redraw = image_cache_redraw, + .clone = nsjpegxl_clone, + .get_internal = image_cache_get_internal, + .type = image_cache_content_type, + .is_opaque = image_cache_is_opaque, + .no_share = false, +}; + +static const char *nsjpegxl_types[] = { + "image/jxl", +}; + +CONTENT_FACTORY_REGISTER_TYPES(nsjpegxl, nsjpegxl_types, nsjpegxl_content_handler); diff --git a/content/handlers/image/jpegxl.h b/content/handlers/image/jpegxl.h new file mode 100644 index 000000000..e37d9344e --- /dev/null +++ b/content/handlers/image/jpegxl.h @@ -0,0 +1,28 @@ +/* + * Copyright 2023 Vincent Sanders <vince@netsurf-browser.org> + * + * This file is part of NetSurf, http://www.netsurf-browser.org/ + * + * NetSurf is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * NetSurf is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +/** \file + * Content for image/jpegxl (interface). + */ + +#ifndef _NETSURF_IMAGE_JPEGXL_H_ +#define _NETSURF_IMAGE_JPEGXL_H_ + +nserror nsjpegxl_init(void); + +#endif diff --git a/content/handlers/image/nssprite.c b/content/handlers/image/nssprite.c index a98c48aa2..c18f49063 100644 --- a/content/handlers/image/nssprite.c +++ b/content/handlers/image/nssprite.c @@ -124,7 +124,7 @@ static bool nssprite_convert(struct content *c) content_broadcast_error(c, NSERROR_NOMEM, NULL); return false; } - uint32_t* imagebuf = (uint32_t *)guit->bitmap->get_buffer(nssprite->bitmap); + uint32_t* imagebuf = (uint32_t *)(void *)guit->bitmap->get_buffer(nssprite->bitmap); if (!imagebuf) { content_broadcast_error(c, NSERROR_NOMEM, NULL); return false; diff --git a/content/handlers/image/rsvg246.c b/content/handlers/image/rsvg246.c new file mode 100644 index 000000000..0e337132f --- /dev/null +++ b/content/handlers/image/rsvg246.c @@ -0,0 +1,280 @@ +/* + * Copyright 2022 Vincent Sanders <vince@netsurf-browser.org> + * + * This file is part of NetSurf, http://www.netsurf-browser.org/ + * + * NetSurf is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * NetSurf is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +/** + * \file + * implementation of content handler for image/svg using librsvg 2.46 API. + * + * SVG files are rendered to a NetSurf bitmap by creating a Cairo rendering + * surface (content_rsvg_data.cs) over the bitmap's data, creating a Cairo + * drawing context using that surface, and then passing that drawing context + * to librsvg which then uses Cairo calls to plot the graphic to the bitmap. + * We store this in content->bitmap, and then use the usual bitmap plotter + * function to render it for redraw requests. + */ + +#include <stdlib.h> +#include <stdbool.h> +#include <assert.h> +#include <string.h> +#include <sys/types.h> + +#include <librsvg/rsvg.h> + +#include <nsutils/endian.h> + +#include "utils/log.h" +#include "utils/utils.h" +#include "utils/messages.h" +#include "netsurf/plotters.h" +#include "netsurf/bitmap.h" +#include "netsurf/content.h" +#include "content/llcache.h" +#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/rsvg.h" + + +typedef struct rsvg_content { + struct content base; + + RsvgHandle *rsvgh; /**< Context handle for RSVG renderer */ +} rsvg_content; + + +static nserror +rsvg_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) +{ + rsvg_content *svg; + nserror error; + + svg = calloc(1, sizeof(rsvg_content)); + if (svg == NULL) + return NSERROR_NOMEM; + + error = content__init(&svg->base, handler, imime_type, params, + llcache, fallback_charset, quirks); + if (error != NSERROR_OK) { + free(svg); + return error; + } + + *c = (struct content *)svg; + + return NSERROR_OK; +} + + +/** + * create a bitmap from jpeg content for the image cache. + */ +static struct bitmap * +rsvg_cache_convert(struct content *c) +{ + rsvg_content *svgc = (rsvg_content *)c; + struct bitmap *bitmap; + cairo_surface_t *cs; + cairo_t *cr; + RsvgRectangle viewport; + gboolean renderres; + + if ((bitmap = guit->bitmap->create(c->width, c->height, BITMAP_NONE)) == NULL) { + NSLOG(netsurf, INFO, "Failed to create bitmap for rsvg render."); + return NULL; + } + + if ((cs = cairo_image_surface_create_for_data( + (unsigned char *)guit->bitmap->get_buffer(bitmap), + CAIRO_FORMAT_ARGB32, + c->width, c->height, + guit->bitmap->get_rowstride(bitmap))) == NULL) { + NSLOG(netsurf, INFO, "Failed to create Cairo image surface for rsvg render."); + guit->bitmap->destroy(bitmap); + return NULL; + } + if ((cr = cairo_create(cs)) == NULL) { + NSLOG(netsurf, INFO, + "Failed to create Cairo drawing context for rsvg render."); + cairo_surface_destroy(cs); + guit->bitmap->destroy(bitmap); + return NULL; + } + + viewport.x = 0; + viewport.y = 0; + viewport.width = c->width; + viewport.height = c->height; + renderres = rsvg_handle_render_document(svgc->rsvgh, cr, &viewport, NULL); + NSLOG(netsurf, DEBUG, "rsvg render:%d, width:%d, height %d", renderres, c->width, c->height); + + bitmap_format_to_client(bitmap, &(bitmap_fmt_t) { + .layout = BITMAP_LAYOUT_ARGB8888, + }); + guit->bitmap->modified(bitmap); + + cairo_destroy(cr); + cairo_surface_destroy(cs); + + return bitmap; +} + +static void rsvg__get_demensions(const rsvg_content *svgc, + int *width, int *height) +{ +#if LIBRSVG_MAJOR_VERSION >= 2 && LIBRSVG_MINOR_VERSION >= 52 + gdouble rwidth; + gdouble rheight; + gboolean gotsize; + + gotsize = rsvg_handle_get_intrinsic_size_in_pixels(svgc->rsvgh, + &rwidth, + &rheight); + if (gotsize == TRUE) { + *width = rwidth; + *height = rheight; + } else { + RsvgRectangle ink_rect; + RsvgRectangle logical_rect; + rsvg_handle_get_geometry_for_element(svgc->rsvgh, + NULL, + &ink_rect, + &logical_rect, + NULL); + *width = ink_rect.width; + *height = ink_rect.height; + } +#else + RsvgDimensionData rsvgsize; + + rsvg_handle_get_dimensions(svgc->rsvgh, &rsvgsize); + *width = rsvgsize.width; + *height = rsvgsize.height; +#endif + NSLOG(netsurf, DEBUG, "rsvg width:%d height:%d.", *width, *height); +} + +static bool rsvg_convert(struct content *c) +{ + rsvg_content *svgc = (rsvg_content *)c; + const uint8_t *data; /* content data */ + size_t size; /* content data size */ + GInputStream * istream; + GError *gerror = NULL; + + /* check image header is valid and get width/height */ + + data = content__get_source_data(c, &size); + + istream = g_memory_input_stream_new_from_data(data, size, NULL); + svgc->rsvgh = rsvg_handle_new_from_stream_sync(istream, + NULL, + RSVG_HANDLE_FLAGS_NONE, + NULL, + &gerror); + g_object_unref(istream); + if (svgc->rsvgh == NULL) { + NSLOG(netsurf, INFO, "Failed to create rsvg handle for content."); + return false; + } + + rsvg__get_demensions(svgc, &c->width, &c->height); + + c->size = c->width * c->height * 4; + + image_cache_add(c, NULL, rsvg_cache_convert); + + content_set_ready(c); + content_set_done(c); + content_set_status(c, ""); /* Done: update status bar */ + + return true; +} + + +static nserror rsvg_clone(const struct content *old, struct content **newc) +{ + rsvg_content *svg; + nserror error; + + svg = calloc(1, sizeof(rsvg_content)); + if (svg == NULL) + return NSERROR_NOMEM; + + error = content__clone(old, &svg->base); + if (error != NSERROR_OK) { + content_destroy(&svg->base); + return error; + } + + /* re-convert if the content is ready */ + if ((old->status == CONTENT_STATUS_READY) || + (old->status == CONTENT_STATUS_DONE)) { + if (rsvg_convert(&svg->base) == false) { + content_destroy(&svg->base); + return NSERROR_CLONE_FAILED; + } + } + + *newc = (struct content *)svg; + + return NSERROR_OK; +} + + +static void rsvg_destroy(struct content *c) +{ + rsvg_content *d = (rsvg_content *) c; + + if (d->rsvgh != NULL) { + g_object_unref(d->rsvgh); + d->rsvgh = NULL; + } + + return image_cache_destroy(c); +} + +static const content_handler rsvg_content_handler = { + .create = rsvg_create, + .data_complete = rsvg_convert, + .destroy = rsvg_destroy, + .redraw = image_cache_redraw, + .clone = rsvg_clone, + .get_internal = image_cache_get_internal, + .type = image_cache_content_type, + .is_opaque = image_cache_is_opaque, + .no_share = false, +}; + +static const char *rsvg_types[] = { + "image/svg", + "image/svg+xml" +}; + +CONTENT_FACTORY_REGISTER_TYPES(nsrsvg, rsvg_types, rsvg_content_handler); + diff --git a/content/handlers/image/webp.c b/content/handlers/image/webp.c index da13316bc..c04c0efd2 100644 --- a/content/handlers/image/webp.c +++ b/content/handlers/image/webp.c @@ -142,7 +142,7 @@ webp_cache_convert(struct content *c) default: /* WebP has no ABGR function, fall back to default. */ webp_fmt.layout = BITMAP_LAYOUT_R8G8B8A8; - /* Fall through. */ + fallthrough; case BITMAP_LAYOUT_R8G8B8A8: decoded = WebPDecodeRGBAInto(source_data, source_size, pixels, rowstride * webpfeatures.height, rowstride); |