Gitweb links:
...log
http://git.netsurf-browser.org/netsurf.git/shortlog/f68aca93b7e9f5dd05f1e49ba8ae70ada3ef3d1f
...commit
http://git.netsurf-browser.org/netsurf.git/commit/f68aca93b7e9f5dd05f1e49ba8ae70ada3ef3d1f
...tree
http://git.netsurf-browser.org/netsurf.git/tree/f68aca93b7e9f5dd05f1e49ba8ae70ada3ef3d1f
The branch, master has been updated
via f68aca93b7e9f5dd05f1e49ba8ae70ada3ef3d1f (commit)
via d59f30c683b9c65a3380a8276247141989359acb (commit)
via 6fa4c04deaf516e938ab0f458cc931318b42ea32 (commit)
via 249e8472a2cdf8b822e46d1e28c91a41efded19d (commit)
via b19fcdd26fc33f0d03597e8cf411c073a99d5d3d (commit)
via dbe5d1ef87ff5a348ae758bdb9635f767822d7d4 (commit)
from bbeb93d512973f7c04714f7a43ea185c15ec65ba (commit)
Those revisions listed above that are new to this repository have
not appeared on any other notification email; so we list those
revisions in full, below.
- Log -----------------------------------------------------------------
commitdiff
http://git.netsurf-browser.org/netsurf.git/commit/?id=f68aca93b7e9f5dd05f1e49ba8ae70ada3ef3d1f
commit f68aca93b7e9f5dd05f1e49ba8ae70ada3ef3d1f
Author: Michael Drake <[email protected]>
Commit: Michael Drake <[email protected]>
jpegxl: Ensure decoded bitmap format is converted to front end format
diff --git a/content/handlers/image/jpegxl.c b/content/handlers/image/jpegxl.c
index 0451e2a..01c7045 100644
--- a/content/handlers/image/jpegxl.c
+++ b/content/handlers/image/jpegxl.c
@@ -97,6 +97,17 @@ jpegxl_cache_convert(struct content *c)
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) {
@@ -180,8 +191,9 @@ jpegxl_cache_convert(struct content *c)
JxlDecoderDestroy(jxldec);
+ bitmap_format_to_client(bitmap, &jxl_fmt);
guit->bitmap->modified(bitmap);
-
+
return bitmap;
}
commitdiff
http://git.netsurf-browser.org/netsurf.git/commit/?id=d59f30c683b9c65a3380a8276247141989359acb
commit d59f30c683b9c65a3380a8276247141989359acb
Author: Michael Drake <[email protected]>
Commit: Michael Drake <[email protected]>
jpegxl: Tell libjxl to decode unpremultiply alpha if needed
On some platforms our bitmap format does not use premultiplied alpha.
diff --git a/content/handlers/image/jpegxl.c b/content/handlers/image/jpegxl.c
index d5152fb..0451e2a 100644
--- a/content/handlers/image/jpegxl.c
+++ b/content/handlers/image/jpegxl.c
@@ -103,6 +103,15 @@ jpegxl_cache_convert(struct content *c)
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");
commitdiff
http://git.netsurf-browser.org/netsurf.git/commit/?id=6fa4c04deaf516e938ab0f458cc931318b42ea32
commit 6fa4c04deaf516e938ab0f458cc931318b42ea32
Author: Michael Drake <[email protected]>
Commit: Michael Drake <[email protected]>
jpegxl: Designated initialiser for output format struct
So I could see the meanings of how we'd set up the decode.
diff --git a/content/handlers/image/jpegxl.c b/content/handlers/image/jpegxl.c
index ba385f3..d5152fb 100644
--- a/content/handlers/image/jpegxl.c
+++ b/content/handlers/image/jpegxl.c
@@ -49,7 +49,12 @@
/**
* output image format
*/
-static const JxlPixelFormat jxl_output_format = {4, JXL_TYPE_UINT8,
JXL_LITTLE_ENDIAN, 0};
+static const JxlPixelFormat jxl_output_format = {
+ .num_channels = 4,
+ .data_type = JXL_TYPE_UINT8,
+ .endianness = JXL_LITTLE_ENDIAN,
+ .align = 0,
+};
/**
* Content create entry point.
commitdiff
http://git.netsurf-browser.org/netsurf.git/commit/?id=249e8472a2cdf8b822e46d1e28c91a41efded19d
commit 249e8472a2cdf8b822e46d1e28c91a41efded19d
Author: Michael Drake <[email protected]>
Commit: Michael Drake <[email protected]>
Makefile: Set libjxl to AUTO
diff --git a/Makefile.defaults b/Makefile.defaults
index 85be748..bbff688 100644
--- a/Makefile.defaults
+++ b/Makefile.defaults
@@ -51,6 +51,10 @@ NETSURF_USE_GIF := AUTO
# Valid options: YES, NO (highly recommended)
NETSURF_USE_JPEG := YES
+# Enable NetSurf's use of libjxl for displaying JPEGXLs
+# Valid options: YES, NO, AUTO (highly recommended)
+NETSURF_USE_JPEGXL := AUTO
+
# Enable NetSurf's use of libpng for displaying PNGs.
# Valid options: YES, NO, AUTO (highly recommended)
NETSURF_USE_PNG := AUTO
commitdiff
http://git.netsurf-browser.org/netsurf.git/commit/?id=b19fcdd26fc33f0d03597e8cf411c073a99d5d3d
commit b19fcdd26fc33f0d03597e8cf411c073a99d5d3d
Author: Vincent Sanders <[email protected]>
Commit: Michael Drake <[email protected]>
avoid the unecessary callback API
diff --git a/content/handlers/image/jpegxl.c b/content/handlers/image/jpegxl.c
index 4bba02d..ba385f3 100644
--- a/content/handlers/image/jpegxl.c
+++ b/content/handlers/image/jpegxl.c
@@ -79,20 +79,6 @@ nsjpegxl_create(const content_handler *handler,
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.
*/
@@ -105,6 +91,7 @@ jpegxl_cache_convert(struct content *c)
JxlBasicInfo binfo;
const uint8_t *src_data;
size_t src_size;
+ uint8_t * output;
jxldec = JxlDecoderCreate(NULL);
if (jxldec == NULL) {
@@ -154,14 +141,14 @@ jpegxl_cache_convert(struct content *c)
}
/* ensure buffer was allocated */
- if (guit->bitmap->get_buffer(bitmap) == NULL) {
+ output = guit->bitmap->get_buffer(bitmap);
+ if (output == NULL) {
/* bitmap with no buffer available */
guit->bitmap->destroy(bitmap);
JxlDecoderDestroy(jxldec);
return NULL;
}
-
- decstatus = JxlDecoderSetImageOutCallback(jxldec, &jxl_output_format,
image_out_callback, bitmap);
+ 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);
commitdiff
http://git.netsurf-browser.org/netsurf.git/commit/?id=dbe5d1ef87ff5a348ae758bdb9635f767822d7d4
commit dbe5d1ef87ff5a348ae758bdb9635f767822d7d4
Author: Vincent Sanders <[email protected]>
Commit: Michael Drake <[email protected]>
Implement simple jpeg xl image handler
diff --git a/Makefile b/Makefile
index 828f83b..05cb03e 100644
--- a/Makefile
+++ b/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))
diff --git a/content/handlers/image/Makefile b/content/handlers/image/Makefile
index afc90f4..ac052b3 100644
--- a/content/handlers/image/Makefile
+++ b/content/handlers/image/Makefile
@@ -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
diff --git a/content/handlers/image/image.c b/content/handlers/image/image.c
index 3107ee4..2bd5f5f 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/jpegxl.c b/content/handlers/image/jpegxl.c
new file mode 100644
index 0000000..4bba02d
--- /dev/null
+++ b/content/handlers/image/jpegxl.c
@@ -0,0 +1,327 @@
+/*
+ * Copyright 2023 Vincent Sanders <[email protected]>
+ *
+ * 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);
diff --git a/content/handlers/image/jpegxl.h b/content/handlers/image/jpegxl.h
new file mode 100644
index 0000000..e37d934
--- /dev/null
+++ b/content/handlers/image/jpegxl.h
@@ -0,0 +1,28 @@
+/*
+ * Copyright 2023 Vincent Sanders <[email protected]>
+ *
+ * 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/mimesniff.c b/content/mimesniff.c
index 9a11834..adc000d 100644
--- a/content/mimesniff.c
+++ b/content/mimesniff.c
@@ -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
diff --git a/frontends/gtk/fetch.c b/frontends/gtk/fetch.c
index 58bd0b8..d77073a 100644
--- a/frontends/gtk/fetch.c
+++ b/frontends/gtk/fetch.c
@@ -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");
diff --git a/utils/corestringlist.h b/utils/corestringlist.h
index b253b3b..5cdbb3a 100644
--- a/utils/corestringlist.h
+++ b/utils/corestringlist.h
@@ -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");
-----------------------------------------------------------------------
Summary of changes:
Makefile | 1 +
Makefile.defaults | 4 +
content/handlers/image/Makefile | 1 +
content/handlers/image/image.c | 7 +
content/handlers/image/jpegxl.c | 340 ++++++++++++++++++++++++++++
content/handlers/image/{svg.h => jpegxl.h} | 10 +-
content/mimesniff.c | 13 ++
frontends/gtk/fetch.c | 1 +
utils/corestringlist.h | 1 +
9 files changed, 373 insertions(+), 5 deletions(-)
create mode 100644 content/handlers/image/jpegxl.c
copy content/handlers/image/{svg.h => jpegxl.h} (77%)
diff --git a/Makefile b/Makefile
index 828f83b..05cb03e 100644
--- a/Makefile
+++ b/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))
diff --git a/Makefile.defaults b/Makefile.defaults
index 85be748..bbff688 100644
--- a/Makefile.defaults
+++ b/Makefile.defaults
@@ -51,6 +51,10 @@ NETSURF_USE_GIF := AUTO
# Valid options: YES, NO (highly recommended)
NETSURF_USE_JPEG := YES
+# Enable NetSurf's use of libjxl for displaying JPEGXLs
+# Valid options: YES, NO, AUTO (highly recommended)
+NETSURF_USE_JPEGXL := AUTO
+
# Enable NetSurf's use of libpng for displaying PNGs.
# Valid options: YES, NO, AUTO (highly recommended)
NETSURF_USE_PNG := AUTO
diff --git a/content/handlers/image/Makefile b/content/handlers/image/Makefile
index afc90f4..ac052b3 100644
--- a/content/handlers/image/Makefile
+++ b/content/handlers/image/Makefile
@@ -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
diff --git a/content/handlers/image/image.c b/content/handlers/image/image.c
index 3107ee4..2bd5f5f 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/jpegxl.c b/content/handlers/image/jpegxl.c
new file mode 100644
index 0000000..01c7045
--- /dev/null
+++ b/content/handlers/image/jpegxl.c
@@ -0,0 +1,340 @@
+/*
+ * Copyright 2023 Vincent Sanders <[email protected]>
+ *
+ * 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/svg.h b/content/handlers/image/jpegxl.h
similarity index 77%
copy from content/handlers/image/svg.h
copy to content/handlers/image/jpegxl.h
index 96b9c38..e37d934 100644
--- a/content/handlers/image/svg.h
+++ b/content/handlers/image/jpegxl.h
@@ -1,5 +1,5 @@
/*
- * Copyright 2007-2008 James Bursa <[email protected]>
+ * Copyright 2023 Vincent Sanders <[email protected]>
*
* This file is part of NetSurf, http://www.netsurf-browser.org/
*
@@ -17,12 +17,12 @@
*/
/** \file
- * Content for image/svg (interface).
+ * Content for image/jpegxl (interface).
*/
-#ifndef _NETSURF_IMAGE_SVG_H_
-#define _NETSURF_IMAGE_SVG_H_
+#ifndef _NETSURF_IMAGE_JPEGXL_H_
+#define _NETSURF_IMAGE_JPEGXL_H_
-nserror svg_init(void);
+nserror nsjpegxl_init(void);
#endif
diff --git a/content/mimesniff.c b/content/mimesniff.c
index 9a11834..adc000d 100644
--- a/content/mimesniff.c
+++ b/content/mimesniff.c
@@ -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
diff --git a/frontends/gtk/fetch.c b/frontends/gtk/fetch.c
index 58bd0b8..d77073a 100644
--- a/frontends/gtk/fetch.c
+++ b/frontends/gtk/fetch.c
@@ -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");
diff --git a/utils/corestringlist.h b/utils/corestringlist.h
index b253b3b..5cdbb3a 100644
--- a/utils/corestringlist.h
+++ b/utils/corestringlist.h
@@ -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");
--
NetSurf Browser
_______________________________________________
netsurf-commits mailing list -- [email protected]
To unsubscribe send an email to [email protected]