Implement simple jpeg xl image handler
This commit is contained in:
parent
bbeb93d512
commit
dbe5d1ef87
1
Makefile
1
Makefile
|
@ -171,6 +171,7 @@ endif
|
|||
$(eval $(call pkg_config_find_and_add_enabled,OPENSSL,openssl,OpenSSL))
|
||||
|
||||
$(eval $(call pkg_config_find_and_add_enabled,UTF8PROC,libutf8proc,utf8))
|
||||
$(eval $(call pkg_config_find_and_add_enabled,JPEGXL,libjxl,JPEGXL))
|
||||
$(eval $(call pkg_config_find_and_add_enabled,WEBP,libwebp,WEBP))
|
||||
$(eval $(call pkg_config_find_and_add_enabled,PNG,libpng,PNG))
|
||||
$(eval $(call pkg_config_find_and_add_enabled,BMP,libnsbmp,BMP))
|
||||
|
|
|
@ -7,6 +7,7 @@ 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
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -0,0 +1,327 @@
|
|||
/*
|
||||
* 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 = {4, JXL_TYPE_UINT8, JXL_LITTLE_ENDIAN, 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;
|
||||
}
|
||||
|
||||
static void image_out_callback(void *opaque, size_t x, size_t y, size_t num_pixels, const void *pixels)
|
||||
{
|
||||
struct bitmap * bitmap = opaque;
|
||||
uint8_t * output;
|
||||
|
||||
output = guit->bitmap->get_buffer(bitmap);
|
||||
if (output != NULL) {
|
||||
/* bitmap buffer available */
|
||||
memcpy(output + (x*jxl_output_format.num_channels) + (y * guit->bitmap->get_rowstride(bitmap)),
|
||||
pixels,
|
||||
num_pixels*jxl_output_format.num_channels);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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;
|
||||
|
||||
jxldec = JxlDecoderCreate(NULL);
|
||||
if (jxldec == NULL) {
|
||||
NSLOG(netsurf, ERROR, "Unable to allocate decoder");
|
||||
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 */
|
||||
if (guit->bitmap->get_buffer(bitmap) == NULL) {
|
||||
/* bitmap with no buffer available */
|
||||
guit->bitmap->destroy(bitmap);
|
||||
JxlDecoderDestroy(jxldec);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
decstatus = JxlDecoderSetImageOutCallback(jxldec, &jxl_output_format, image_out_callback, bitmap);
|
||||
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);
|
||||
|
||||
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);
|
|
@ -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
|
|
@ -260,6 +260,13 @@ static nserror mimesniff__match_unknown_exact(const uint8_t *data, size_t len,
|
|||
SIG(&corestring_lwc_application_x_gzip, "\x1f\x8b\x08", true),
|
||||
SIG(&corestring_lwc_application_postscript, "%!PS-Adobe-",true),
|
||||
SIG(&corestring_lwc_application_pdf, "%PDF-", false),
|
||||
SIG(&corestring_lwc_image_jxl, "\xFF\x0A", true), /* containerless jpeg xl*/
|
||||
{
|
||||
(const uint8_t *)"\x00\x00\x00\x0CJXL \x0D\x0A\x87\x0A",
|
||||
12,
|
||||
true,
|
||||
&corestring_lwc_image_jxl
|
||||
}, /* containered jpeg xl*/
|
||||
{ NULL, 0, false, NULL }
|
||||
};
|
||||
#undef SIG
|
||||
|
@ -376,6 +383,12 @@ static nserror mimesniff__compute_image(lwc_string *official_type,
|
|||
SIG(&corestring_lwc_image_jpeg, "\xff\xd8\xff"),
|
||||
SIG(&corestring_lwc_image_bmp, "BM"),
|
||||
SIG(&corestring_lwc_image_vnd_microsoft_icon, "\x00\x00\x01\x00"),
|
||||
SIG(&corestring_lwc_image_jxl, "\xFF\x0A"), /* containerless jpeg xl*/
|
||||
{
|
||||
(const uint8_t *)"\x00\x00\x00\x0CJXL \x0D\x0A\x87\x0A",
|
||||
12,
|
||||
&corestring_lwc_image_jxl
|
||||
}, /* containered jpeg xl*/
|
||||
{ NULL, 0, NULL }
|
||||
};
|
||||
#undef SIG
|
||||
|
|
|
@ -75,6 +75,7 @@ void gtk_fetch_filetype_init(const char *mimefile)
|
|||
hash_add(mime_hash, "html", "text/html");
|
||||
hash_add(mime_hash, "jpg", "image/jpeg");
|
||||
hash_add(mime_hash, "jpeg", "image/jpeg");
|
||||
hash_add(mime_hash, "jxl", "image/jxl");
|
||||
hash_add(mime_hash, "gif", "image/gif");
|
||||
hash_add(mime_hash, "png", "image/png");
|
||||
hash_add(mime_hash, "jng", "image/jng");
|
||||
|
|
|
@ -167,6 +167,7 @@ CORESTRING_LWC_VALUE(application_octet_stream, "application/octet-stream");
|
|||
CORESTRING_LWC_VALUE(image_gif, "image/gif");
|
||||
CORESTRING_LWC_VALUE(image_png, "image/png");
|
||||
CORESTRING_LWC_VALUE(image_jpeg, "image/jpeg");
|
||||
CORESTRING_LWC_VALUE(image_jxl, "image/jxl");
|
||||
CORESTRING_LWC_VALUE(image_bmp, "image/bmp");
|
||||
CORESTRING_LWC_VALUE(image_vnd_microsoft_icon, "image/vnd.microsoft.icon");
|
||||
CORESTRING_LWC_VALUE(image_webp, "image/webp");
|
||||
|
|
Loading…
Reference in New Issue