/* * Copyright 2019 Vincent Sanders * * 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 * implementation of content handling for image/webp * * This implementation uses the google webp library. * Image cache handling is performed by the generic NetSurf handler. */ #include #include #include #include #include "utils/utils.h" #include "utils/log.h" #include "utils/messages.h" #include "netsurf/bitmap.h" #include "content/llcache.h" #include "content/content_protected.h" #include "desktop/gui_internal.h" #include "image/image_cache.h" #include "webp.h" /** * Content create entry point. * * create a content object for the webp */ static nserror webp_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 *webp_c; /* webp content object */ nserror res; webp_c = calloc(1, sizeof(struct content)); if (webp_c == NULL) { return NSERROR_NOMEM; } res = content__init(webp_c, handler, imime_type, params, llcache, fallback_charset, quirks); if (res != NSERROR_OK) { free(webp_c); return res; } *c = webp_c; return NSERROR_OK; } /** * create a bitmap from webp content. */ static struct bitmap * webp_cache_convert(struct content *c) { const uint8_t *source_data; /* webp source data */ unsigned long source_size; /* length of webp source data */ VP8StatusCode webpres; WebPBitstreamFeatures webpfeatures; unsigned int bmap_flags; uint8_t *pixels = NULL; uint8_t *decoded; size_t rowstride; struct bitmap *bitmap = NULL; source_data = (uint8_t *)content__get_source_data(c, &source_size); webpres = WebPGetFeatures(source_data, source_size, &webpfeatures); if (webpres != VP8_STATUS_OK) { return NULL; } if (webpfeatures.has_alpha == 0) { bmap_flags = BITMAP_NEW | BITMAP_OPAQUE; } else { bmap_flags = BITMAP_NEW; } /* create bitmap */ bitmap = guit->bitmap->create(webpfeatures.width, webpfeatures.height, bmap_flags); if (bitmap == NULL) { /* empty bitmap could not be created */ return NULL; } pixels = guit->bitmap->get_buffer(bitmap); if (pixels == NULL) { /* bitmap with no buffer available */ guit->bitmap->destroy(bitmap); return NULL; } rowstride = guit->bitmap->get_rowstride(bitmap); decoded = WebPDecodeBGRAInto(source_data, source_size, pixels, webpfeatures.width * webpfeatures.height * 4, rowstride); if (decoded == NULL) { /* decode failed */ guit->bitmap->destroy(bitmap); return NULL; } return bitmap; } /** * Convert the webp source data content. * * This ensures there is valid webp source data in the content object * and then adds it to the image cache ready to be converted on * demand. * * \param c The webp content object * \return true on successful processing of teh webp content else false */ static bool webp_convert(struct content *c) { int res; unsigned long data_size; const uint8_t* data; int width; int height; data = (uint8_t *)content__get_source_data(c, &data_size); res = WebPGetInfo(data, data_size, &width, &height); if (res == 0) { NSLOG(netsurf, INFO, "WebPGetInfo failed:%p", c); return false; } c->width = width; c->height = height; c->size = c->width * c->height * 4; image_cache_add(c, NULL, webp_cache_convert); content_set_ready(c); content_set_done(c); return true; } /** * Clone content. */ static nserror webp_clone(const struct content *old, struct content **new_c) { struct content *webp_c; /* cloned webp content */ nserror res; webp_c = calloc(1, sizeof(struct content)); if (webp_c == NULL) { return NSERROR_NOMEM; } res = content__clone(old, webp_c); if (res != NSERROR_OK) { content_destroy(webp_c); return res; } /* re-convert if the content is ready */ if ((old->status == CONTENT_STATUS_READY) || (old->status == CONTENT_STATUS_DONE)) { if (webp_convert(webp_c) == false) { content_destroy(webp_c); return NSERROR_CLONE_FAILED; } } *new_c = webp_c; return NSERROR_OK; } static const content_handler webp_content_handler = { .create = webp_create, .data_complete = webp_convert, .destroy = image_cache_destroy, .redraw = image_cache_redraw, .clone = webp_clone, .get_internal = image_cache_get_internal, .type = image_cache_content_type, .no_share = false, }; static const char *webp_types[] = { "image/webp" }; CONTENT_FACTORY_REGISTER_TYPES(nswebp, webp_types, webp_content_handler);