WebSurf/content/handlers/image/webp.c

261 lines
6.0 KiB
C

/*
* Copyright 2019 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/webp
*
* This implementation uses the google webp library.
* Image cache handling is performed by the generic NetSurf handler.
*/
#include <stdbool.h>
#include <stdlib.h>
#include <setjmp.h>
#include <webp/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_protected.h"
#include "content/content_factory.h"
#include "desktop/gui_internal.h"
#include "desktop/bitmap.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 */
size_t 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;
bitmap_fmt_t webp_fmt = {
.layout = bitmap_fmt.layout,
};
source_data = 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_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_NONE;
}
/* 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);
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;
}
/**
* 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;
const uint8_t* data;
size_t data_size;
int width;
int height;
data = 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,
.is_opaque = image_cache_is_opaque,
.no_share = false,
};
static const char *webp_types[] = {
"image/webp"
};
CONTENT_FACTORY_REGISTER_TYPES(nswebp, webp_types, webp_content_handler);