[gthumb: 1/5] Add JPEG XL (*.jxl) read support
- From: Paolo Bacchilega <paobac src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gthumb: 1/5] Add JPEG XL (*.jxl) read support
- Date: Sun, 28 Feb 2021 10:03:17 +0000 (UTC)
commit a1500ec17c21bfb360c34a9099968dc24333dc32
Author: Ian Tester <imroykun gmail com>
Date: Tue Feb 23 19:56:28 2021 +1100
Add JPEG XL (*.jxl) read support
README.md | 1 +
data/org.gnome.gThumb.desktop.in.in | 2 +-
extensions/cairo_io/cairo-image-surface-jxl.c | 293 ++++++++++++++++++++++++++
extensions/cairo_io/cairo-image-surface-jxl.h | 42 ++++
extensions/cairo_io/main.c | 8 +
extensions/cairo_io/meson.build | 7 +-
meson.build | 13 ++
meson_options.txt | 6 +
8 files changed, 370 insertions(+), 2 deletions(-)
---
diff --git a/README.md b/README.md
index 1f320729..72e40816 100644
--- a/README.md
+++ b/README.md
@@ -91,6 +91,7 @@ More information can be found at <https://wiki.gnome.org/Apps/Gthumb>.
* libraw - some support for RAW photos
* librsvg - display SVG images
* libwebp - display and save WebP images
+ * libjxl - display JPEG XL images
* lcms2, colord - color profile support
* champlain, champlain-gtk - view the place a photo was taken on a map
* clutter, clutter-gtk - enhanced slideshow effects
diff --git a/data/org.gnome.gThumb.desktop.in.in b/data/org.gnome.gThumb.desktop.in.in
index 1e795106..28de3806 100644
--- a/data/org.gnome.gThumb.desktop.in.in
+++ b/data/org.gnome.gThumb.desktop.in.in
@@ -9,7 +9,7 @@ Categories=GNOME;GTK;Graphics;Viewer;RasterGraphics;2DGraphics;Photography;
Exec=gthumb %U
# Translators: Do NOT translate or transliterate this text (this is an icon file name)!
Icon=org.gnome.gThumb
-MimeType=image/bmp;image/jpeg;image/gif;image/png;image/tiff;image/x-bmp;image/x-ico;image/x-png;image/x-pcx;image/x-tga;image/xpm;image/svg+xml;image/webp;
+MimeType=image/bmp;image/jpeg;image/gif;image/png;image/tiff;image/x-bmp;image/x-ico;image/x-png;image/x-pcx;image/x-tga;image/xpm;image/svg+xml;image/webp;image/jxl;
StartupNotify=true
Terminal=false
Type=Application
diff --git a/extensions/cairo_io/cairo-image-surface-jxl.c b/extensions/cairo_io/cairo-image-surface-jxl.c
new file mode 100644
index 00000000..264492be
--- /dev/null
+++ b/extensions/cairo_io/cairo-image-surface-jxl.c
@@ -0,0 +1,293 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+
+/*
+ * GThumb
+ *
+ * Copyright (C) 2021 Free Software Foundation, Inc.
+ *
+ * This program 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; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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/>.
+ */
+
+#include <config.h>
+#include <glib.h>
+#include <jxl/decode.h>
+#include <jxl/thread_parallel_runner.h>
+#if HAVE_LCMS2
+#include <lcms2.h>
+#endif
+#include <gthumb.h>
+#include "cairo-image-surface-jxl.h"
+
+void convert_pixels(int width, int height, guchar* buffer) {
+ int x, y;
+ guchar *p = buffer, r, g, b, a;
+
+ for (y = 0; y < height; y++)
+ for (x = width; x; x--, p += 4) {
+ a = p[3];
+ if (a == 0) {
+ *(guint32*)p = 0;
+ continue;
+ }
+
+ r = p[0];
+ g = p[1];
+ b = p[2];
+ if (a < 0xff) {
+ r = _cairo_multiply_alpha(r, a);
+ g = _cairo_multiply_alpha(g, a);
+ b = _cairo_multiply_alpha(b, a);
+ }
+ *(guint32*)p = CAIRO_RGBA_TO_UINT32(r, g, b, a);
+ }
+}
+
+#define BUFFER_SIZE (1024*1024)
+
+GthImage *
+_cairo_image_surface_create_from_jxl(GInputStream *istream,
+ GthFileData *file_data,
+ int requested_size,
+ int *original_width,
+ int *original_height,
+ gboolean *loaded_original,
+ gpointer user_data,
+ GCancellable *cancellable,
+ GError **error)
+{
+ GthImage *image;
+ guchar *filebuffer;
+ gsize filebuffer_size, read_size, buffer_read, unprocessed_len, processed_len;
+ int width = 0, height = 0;
+ cairo_surface_t *surface = NULL;
+ cairo_surface_metadata_t *metadata;
+ JxlDecoder *dec;
+ void *runner;
+ JxlDecoderStatus status;
+ JxlBasicInfo info;
+ JxlPixelFormat pixel_format;
+ guchar *surface_data = NULL;
+
+ image = gth_image_new();
+
+ dec = JxlDecoderCreate(NULL);
+ if (dec == NULL) {
+ g_error("Could not create JXL decoder.\n");
+ return image;
+ }
+
+ filebuffer_size = read_size = JxlDecoderSizeHintBasicInfo(dec);
+ filebuffer = g_new(guchar, filebuffer_size);
+ if (! g_input_stream_read_all(istream,
+ filebuffer,
+ filebuffer_size,
+ &buffer_read,
+ cancellable,
+ error))
+ {
+ g_error("Could not read start of JXL file.\n");
+ JxlDecoderDestroy(dec);
+ g_free(filebuffer);
+ return image;
+ }
+
+ if (JxlSignatureCheck(filebuffer, buffer_read) < JXL_SIG_CODESTREAM) {
+ g_error("Signature does not match for JPEG XL codestream or container.\n");
+ JxlDecoderDestroy(dec);
+ g_free(filebuffer);
+ return image;
+ }
+
+ read_size = 1048576;
+
+ runner = JxlThreadParallelRunnerCreate(NULL, JxlThreadParallelRunnerDefaultNumWorkerThreads());
+ if (runner == NULL) {
+ g_error("Could not create threaded parallel runner.\n");
+ JxlDecoderDestroy(dec);
+ g_free(filebuffer);
+ return image;
+ }
+
+ if (JxlDecoderSetParallelRunner(dec,
+ JxlThreadParallelRunner,
+ runner) > 0) {
+ g_error("Could not set parallel runner.\n");
+ JxlThreadParallelRunnerDestroy(runner);
+ JxlDecoderDestroy(dec);
+ g_free(filebuffer);
+ return image;
+ }
+
+ if (JxlDecoderSubscribeEvents(dec,
+ JXL_DEC_BASIC_INFO
+ | JXL_DEC_COLOR_ENCODING
+ | JXL_DEC_FULL_IMAGE) > 0) {
+ g_error("Could not subscribe to decoder events.\n");
+ JxlThreadParallelRunnerDestroy(runner);
+ JxlDecoderDestroy(dec);
+ g_free(filebuffer);
+ return image;
+ }
+
+ if (JxlDecoderSetInput(dec, filebuffer, buffer_read) > 0) {
+ g_error("Could not set decoder input.\n");
+ JxlThreadParallelRunnerDestroy(runner);
+ JxlDecoderDestroy(dec);
+ g_free(filebuffer);
+ return image;
+ }
+
+ status = JXL_DEC_NEED_MORE_INPUT;
+ while (status != JXL_DEC_SUCCESS) {
+ status = JxlDecoderProcessInput(dec);
+
+ switch (status) {
+ case JXL_DEC_ERROR:
+ g_error("jxl: decoder error.\n");
+ JxlThreadParallelRunnerDestroy(runner);
+ JxlDecoderDestroy(dec);
+ g_free(filebuffer);
+ return image;
+
+ case JXL_DEC_NEED_MORE_INPUT:
+ if (buffer_read == 0) {
+ g_message("Reached end of file but decoder still wants more.\n");
+ status = JXL_DEC_SUCCESS;
+ break;
+ }
+
+ {
+ unprocessed_len = JxlDecoderReleaseInput(dec);
+ processed_len = filebuffer_size - unprocessed_len;
+
+ guchar *old_filebuffer = filebuffer;
+ filebuffer_size = unprocessed_len + read_size;
+ filebuffer = g_new(guchar, filebuffer_size);
+ if (unprocessed_len > 0)
+ memcpy(filebuffer, old_filebuffer + processed_len, unprocessed_len);
+ g_free(old_filebuffer);
+
+ gssize signed_buffer_read = g_input_stream_read(istream,
+ filebuffer + unprocessed_len,
+ read_size,
+ cancellable,
+ error);
+ if (signed_buffer_read <= 0) {
+ buffer_read = 0;
+ break;
+ }
+ buffer_read = signed_buffer_read;
+
+ if (JxlDecoderSetInput(dec, filebuffer, unprocessed_len + buffer_read) > 0) {
+ g_error("Could not set decoder input.\n");
+ JxlThreadParallelRunnerDestroy(runner);
+ JxlDecoderDestroy(dec);
+ g_free(filebuffer);
+ return image;
+ }
+ }
+ break;
+
+ case JXL_DEC_BASIC_INFO:
+ if (JxlDecoderGetBasicInfo(dec, &info) > 0) {
+ g_error("Could not get basic info from decoder.\n");
+ JxlThreadParallelRunnerDestroy(runner);
+ JxlDecoderDestroy(dec);
+ g_free(filebuffer);
+ return image;
+ }
+
+ pixel_format.num_channels = 4;
+ pixel_format.data_type = JXL_TYPE_UINT8;
+ pixel_format.endianness = JXL_NATIVE_ENDIAN;
+ pixel_format.align = 0;
+
+ width = info.xsize;
+ height = info.ysize;
+
+ if (original_width != NULL)
+ *original_width = width;
+ if (original_height != NULL)
+ *original_height = height;
+
+ surface = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, width, height);
+ surface_data = _cairo_image_surface_flush_and_get_data(surface);
+ metadata = _cairo_image_surface_get_metadata(surface);
+ _cairo_metadata_set_has_alpha(metadata, info.num_extra_channels);
+
+ break;
+
+ case JXL_DEC_COLOR_ENCODING:
+#if HAVE_LCMS2
+ if (JxlDecoderGetColorAsEncodedProfile(dec, &pixel_format,
JXL_COLOR_PROFILE_TARGET_DATA, NULL) == JXL_DEC_SUCCESS)
+ break;
+
+ {
+ gsize profile_size;
+ if (JxlDecoderGetICCProfileSize(dec, &pixel_format,
JXL_COLOR_PROFILE_TARGET_DATA, &profile_size) > 0) {
+ g_message("Could not get ICC profile size.\n");
+ break;
+ }
+
+ guchar *profile_data = g_new(guchar, profile_size);
+ if (JxlDecoderGetColorAsICCProfile(dec, &pixel_format,
JXL_COLOR_PROFILE_TARGET_DATA, profile_data, profile_size) > 0) {
+ g_message("Could not get ICC profile.\n");
+ g_free(profile_data);
+ break;
+ }
+
+ GthICCProfile *profile = NULL;
+ profile = gth_icc_profile_new(GTH_ICC_PROFILE_ID_UNKNOWN,
cmsOpenProfileFromMem(profile_data, profile_size));
+ if (profile != NULL) {
+ gth_image_set_icc_profile(image, profile);
+ g_object_unref(profile);
+ }
+ }
+#endif
+ break;
+
+ case JXL_DEC_NEED_IMAGE_OUT_BUFFER:
+ if (JxlDecoderSetImageOutBuffer(dec, &pixel_format, surface_data, width * height * 4)
0) {
+ g_error("Could not set image-out buffer.\n");
+ JxlThreadParallelRunnerDestroy(runner);
+ JxlDecoderDestroy(dec);
+ g_free(filebuffer);
+ return image;
+ }
+
+ break;
+
+ case JXL_DEC_SUCCESS:
+ continue;
+
+ case JXL_DEC_FULL_IMAGE:
+ continue;
+
+ default:
+ break;
+ }
+ }
+
+ JxlThreadParallelRunnerDestroy(runner);
+ JxlDecoderDestroy(dec);
+ g_free(filebuffer);
+
+ convert_pixels(width, height, surface_data);
+
+ cairo_surface_mark_dirty(surface);
+ if (cairo_surface_status(surface) == CAIRO_STATUS_SUCCESS)
+ gth_image_set_cairo_surface(image, surface);
+
+ return image;
+}
diff --git a/extensions/cairo_io/cairo-image-surface-jxl.h b/extensions/cairo_io/cairo-image-surface-jxl.h
new file mode 100644
index 00000000..2920fd50
--- /dev/null
+++ b/extensions/cairo_io/cairo-image-surface-jxl.h
@@ -0,0 +1,42 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+
+/*
+ * GThumb
+ *
+ * Copyright (C) 2021 Free Software Foundation, Inc.
+ *
+ * This program 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; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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/>.
+ */
+
+#ifndef CAIRO_IMAGE_SURFACE_JXL_H
+#define CAIRO_IMAGE_SURFACE_JXL_H
+
+#include <gtk/gtk.h>
+#include <gthumb.h>
+
+G_BEGIN_DECLS
+
+GthImage * _cairo_image_surface_create_from_jxl (GInputStream *istream,
+ GthFileData *file_data,
+ int requested_size,
+ int *original_width,
+ int *original_height,
+ gboolean *loaded_original,
+ gpointer user_data,
+ GCancellable *cancellable,
+ GError **error);
+
+G_END_DECLS
+
+#endif /* CAIRO_IMAGE_SURFACE_JXL_H */
diff --git a/extensions/cairo_io/main.c b/extensions/cairo_io/main.c
index 1661b2ac..a2f89829 100644
--- a/extensions/cairo_io/main.c
+++ b/extensions/cairo_io/main.c
@@ -27,6 +27,7 @@
#include "cairo-image-surface-svg.h"
#include "cairo-image-surface-tiff.h"
#include "cairo-image-surface-webp.h"
+#include "cairo-image-surface-jxl.h"
#include "cairo-image-surface-xcf.h"
#include "gth-image-saver-jpeg.h"
#include "gth-image-saver-png.h"
@@ -78,6 +79,13 @@ gthumb_extension_activate (void)
gth_main_register_type ("image-saver", GTH_TYPE_IMAGE_SAVER_WEBP);
#endif
+#ifdef HAVE_LIBJXL
+ gth_main_register_image_loader_func (_cairo_image_surface_create_from_jxl,
+ GTH_IMAGE_FORMAT_CAIRO_SURFACE,
+ "image/jxl",
+ NULL);
+#endif
+
gth_main_register_image_loader_func (_cairo_image_surface_create_from_xcf,
GTH_IMAGE_FORMAT_CAIRO_SURFACE,
"image/x-xcf",
diff --git a/extensions/cairo_io/meson.build b/extensions/cairo_io/meson.build
index 90c75e5d..870842a4 100644
--- a/extensions/cairo_io/meson.build
+++ b/extensions/cairo_io/meson.build
@@ -22,6 +22,10 @@ if use_libtiff
source_files += files('cairo-image-surface-tiff.c')
endif
+if use_libjxl
+ source_files += files('cairo-image-surface-jxl.c')
+endif
+
enum_files = gnome.mkenums_simple('cairo-io-enum-types', sources: [ 'preferences.h' ])
shared_module('cairo_io',
@@ -31,7 +35,8 @@ shared_module('cairo_io',
jpeg_deps,
use_libtiff ? tiff_deps : [],
use_librsvg ? librsvg_dep : [],
- use_libwebp ? libwebp_dep : []
+ use_libwebp ? libwebp_dep : [],
+ use_libjxl ? libjxl_deps : []
],
include_directories : [ config_inc, gthumb_inc ],
c_args : c_args,
diff --git a/meson.build b/meson.build
index 1108735d..21295ed6 100644
--- a/meson.build
+++ b/meson.build
@@ -17,6 +17,8 @@ libbrasero_version = '>=3.2.0'
libchamplain_version = '>=0.12.0'
librsvg_version = '>=2.34.0'
libwebp_version = '>=0.2.0'
+libjxl_version = '>=0.3.0'
+libjxl_threads_version = '>=0.3.0'
libjson_glib_version = '>=0.15.0'
webkit2_version = '>=1.10.0'
lcms2_version = '>=2.6'
@@ -160,6 +162,13 @@ else
use_libwebp = false
endif
+if get_option('libjxl')
+ libjxl_deps = [ dependency('libjxl', version : libjxl_version, required : false),
dependency('libjxl_threads', version : libjxl_version, required : false) ]
+ use_libjxl = libjxl_deps[0].found()
+else
+ use_libjxl = false
+endif
+
if get_option('libraw')
libraw_dep = dependency('libraw', version : libraw_version, required : false)
use_libraw = libraw_dep.found()
@@ -282,6 +291,9 @@ if use_libwebp
config_data.set('HAVE_LIBWEBP', 1)
config_data.set('WEBP_IS_UNKNOWN_TO_GLIB', 1) # Define to 1 if webp images area not recognized by the glib
functions
endif
+if use_libjxl
+ config_data.set('HAVE_LIBJXL', 1)
+endif
if use_libraw
config_data.set('HAVE_LIBRAW', 1)
endif
@@ -371,6 +383,7 @@ summary = [
' progressive jpeg: @0@'.format(have_progressive_jpeg),
' tiff: @0@'.format(use_libtiff),
' webp: @0@'.format(use_libwebp),
+ ' jxl: @0@'.format(use_libjxl),
' raw: @0@'.format(use_libraw),
' svg: @0@'.format(use_librsvg),
' web services: @0@'.format(with_webservices),
diff --git a/meson_options.txt b/meson_options.txt
index e34b80c6..d7655fb3 100644
--- a/meson_options.txt
+++ b/meson_options.txt
@@ -58,6 +58,12 @@ option('libwebp',
description : 'Use libwebp to load WebP images'
)
+option('libjxl',
+ type : 'boolean',
+ value : true,
+ description : 'Use libjxl to load JPEG XL images'
+)
+
option('libraw',
type : 'boolean',
value : true,
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]