[gthumb] added ability to load AVIF/HEIF images using libheif
- From: Paolo Bacchilega <paobac src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gthumb] added ability to load AVIF/HEIF images using libheif
- Date: Sat, 24 Apr 2021 16:03:42 +0000 (UTC)
commit b2d5dffc3c22a5fa1253efbceb71000e48c03b9d
Author: Paolo Bacchilega <paobac src gnome org>
Date: Fri Apr 23 20:47:36 2021 +0200
added ability to load AVIF/HEIF images using libheif
extensions/cairo_io/cairo-image-surface-avif.c | 167 +++++++++++++++++++++++++
extensions/cairo_io/cairo-image-surface-avif.h | 42 +++++++
extensions/cairo_io/main.c | 10 ++
extensions/cairo_io/meson.build | 7 +-
gthumb/cairo-utils.c | 78 +++++++-----
gthumb/cairo-utils.h | 5 +
gthumb/glib-utils.c | 5 +-
meson.build | 12 ++
meson_options.txt | 6 +
9 files changed, 299 insertions(+), 33 deletions(-)
---
diff --git a/extensions/cairo_io/cairo-image-surface-avif.c b/extensions/cairo_io/cairo-image-surface-avif.c
new file mode 100644
index 00000000..9dd01f59
--- /dev/null
+++ b/extensions/cairo_io/cairo-image-surface-avif.c
@@ -0,0 +1,167 @@
+/* -*- 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 <libheif/heif.h>
+#if HAVE_LCMS2
+#include <lcms2.h>
+#endif
+#include <gthumb.h>
+#include "cairo-image-surface-avif.h"
+
+
+GthImage *
+_cairo_image_surface_create_from_avif (GInputStream *istream,
+ GthFileData *file_data,
+ int requested_size,
+ int *p_original_width,
+ int *p_original_height,
+ gboolean *loaded_original,
+ gpointer user_data,
+ GCancellable *cancellable,
+ GError **error)
+{
+ GthImage *image;
+ void *buffer = NULL;
+ gsize buffer_size;
+ struct heif_context *ctx = NULL;
+ struct heif_error err;
+ struct heif_image_handle *handle = NULL;
+ gboolean has_alpha;
+ struct heif_image *img = NULL;
+ int original_width;
+ int original_height;
+ int image_width;
+ int image_height;
+ int out_stride;
+ const uint8_t *data;
+ cairo_surface_t *surface;
+ cairo_surface_metadata_t *metadata;
+
+ image = gth_image_new ();
+
+ if (! _g_input_stream_read_all (istream,
+ &buffer,
+ &buffer_size,
+ cancellable,
+ error))
+ {
+ goto stop_loading;
+ }
+
+ ctx = heif_context_alloc ();
+ err = heif_context_read_from_memory_without_copy (ctx, buffer, buffer_size, NULL);
+ if (err.code != heif_error_Ok) {
+ g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_FAILED, err.message);
+ goto stop_loading;
+ }
+
+ err = heif_context_get_primary_image_handle (ctx, &handle);
+ if (err.code != heif_error_Ok) {
+ g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_FAILED, err.message);
+ goto stop_loading;
+ }
+
+#if HAVE_LCMS2
+ if (heif_image_handle_get_color_profile_type (handle) != heif_color_profile_type_not_present) {
+ size_t icc_data_size;
+ gpointer icc_data;
+
+ icc_data_size = heif_image_handle_get_raw_color_profile_size (handle);
+ icc_data = g_malloc (icc_data_size);
+ err = heif_image_handle_get_raw_color_profile (handle, icc_data);
+ if (err.code == heif_error_Ok) {
+ GthICCProfile *profile;
+
+ profile = gth_icc_profile_new (GTH_ICC_PROFILE_ID_UNKNOWN, cmsOpenProfileFromMem
(icc_data, icc_data_size));
+ if (profile != NULL) {
+ gth_image_set_icc_profile (image, profile);
+ g_object_unref (profile);
+ }
+ }
+
+ g_free (icc_data);
+ }
+#endif
+
+ has_alpha = heif_image_handle_has_alpha_channel (handle);
+ err = heif_decode_image (handle, &img, heif_colorspace_RGB, has_alpha ? heif_chroma_interleaved_RGBA
: heif_chroma_interleaved_RGB, NULL);
+ if (err.code != heif_error_Ok) {
+ g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_FAILED, err.message);
+ goto stop_loading;
+ }
+
+ original_width = heif_image_get_primary_width (img);
+ original_height = heif_image_get_primary_height (img);
+
+ image_width = original_width;
+ image_height = original_height;
+
+ /* Scaling is disabled because quality is too low. */
+
+#if 0
+ if ((requested_size > 0) && (requested_size < image_width) && (requested_size < image_height)) {
+ struct heif_image *scaled_img;
+
+ scale_keeping_ratio (&image_width, &image_height, requested_size, requested_size, TRUE);
+
+ err = heif_image_scale_image (img, &scaled_img, image_width, image_height, NULL);
+ heif_image_release (img);
+
+ if (err.code != heif_error_Ok) {
+ g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_FAILED, err.message);
+ goto stop_loading;
+ }
+
+ img = scaled_img;
+ }
+#endif
+
+ data = heif_image_get_plane_readonly (img, heif_channel_interleaved, &out_stride);
+
+ surface = _cairo_image_surface_create_from_rgba (data, image_width, image_height, out_stride,
has_alpha);
+ if (surface == NULL)
+ goto stop_loading;
+
+ metadata = _cairo_image_surface_get_metadata (surface);
+ _cairo_metadata_set_original_size (metadata, original_width, original_height);
+
+ if (p_original_width != NULL)
+ *p_original_width = original_width;
+ if (p_original_height != NULL)
+ *p_original_height = original_height;
+
+ gth_image_set_cairo_surface (image, surface);
+
+stop_loading:
+
+ if (img != NULL)
+ heif_image_release (img);
+ if (handle != NULL)
+ heif_image_handle_release (handle);
+ if (ctx != NULL)
+ heif_context_free (ctx);
+ if (buffer != NULL)
+ g_free (buffer);
+
+ return image;
+}
diff --git a/extensions/cairo_io/cairo-image-surface-avif.h b/extensions/cairo_io/cairo-image-surface-avif.h
new file mode 100644
index 00000000..b3625894
--- /dev/null
+++ b/extensions/cairo_io/cairo-image-surface-avif.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_AVIF_H
+#define CAIRO_IMAGE_SURFACE_AVIF_H
+
+#include <gtk/gtk.h>
+#include <gthumb.h>
+
+G_BEGIN_DECLS
+
+GthImage * _cairo_image_surface_create_from_avif (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_AVIF_H */
diff --git a/extensions/cairo_io/main.c b/extensions/cairo_io/main.c
index a2f89829..faa3f229 100644
--- a/extensions/cairo_io/main.c
+++ b/extensions/cairo_io/main.c
@@ -22,6 +22,7 @@
#include <config.h>
#include <gthumb.h>
+#include "cairo-image-surface-avif.h"
#include "cairo-image-surface-jpeg.h"
#include "cairo-image-surface-png.h"
#include "cairo-image-surface-svg.h"
@@ -86,6 +87,15 @@ gthumb_extension_activate (void)
NULL);
#endif
+#ifdef HAVE_LIBHEIF
+ gth_main_register_image_loader_func (_cairo_image_surface_create_from_avif,
+ GTH_IMAGE_FORMAT_CAIRO_SURFACE,
+ "image/avif",
+ "image/heic",
+ "image/heif",
+ 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 870842a4..2fe10648 100644
--- a/extensions/cairo_io/meson.build
+++ b/extensions/cairo_io/meson.build
@@ -26,6 +26,10 @@ if use_libjxl
source_files += files('cairo-image-surface-jxl.c')
endif
+if use_libheif
+ source_files += files('cairo-image-surface-avif.c')
+endif
+
enum_files = gnome.mkenums_simple('cairo-io-enum-types', sources: [ 'preferences.h' ])
shared_module('cairo_io',
@@ -36,7 +40,8 @@ shared_module('cairo_io',
use_libtiff ? tiff_deps : [],
use_librsvg ? librsvg_dep : [],
use_libwebp ? libwebp_dep : [],
- use_libjxl ? libjxl_deps : []
+ use_libjxl ? libjxl_deps : [],
+ use_libheif ? libheif_dep : []
],
include_directories : [ config_inc, gthumb_inc ],
c_args : c_args,
diff --git a/gthumb/cairo-utils.c b/gthumb/cairo-utils.c
index dce1ac54..3ae05a22 100644
--- a/gthumb/cairo-utils.c
+++ b/gthumb/cairo-utils.c
@@ -390,45 +390,37 @@ _cairo_image_surface_copy_subsurface (cairo_surface_t *source,
cairo_surface_t *
-_cairo_image_surface_create_from_pixbuf (GdkPixbuf *pixbuf)
+_cairo_image_surface_create_from_rgba (const guchar *pixels,
+ int width,
+ int height,
+ int p_stride,
+ gboolean has_alpha)
{
cairo_surface_t *surface;
+ int s_stride;
+ unsigned char *s_pixels;
cairo_surface_metadata_t *metadata;
- int width;
- int height;
- int p_stride;
- int p_n_channels;
- guchar *p_pixels;
- int s_stride;
- unsigned char *s_pixels;
- int h, w;
- guint32 pixel;
- guchar r, g, b, a;
+ int h, w;
+ guint32 pixel;
+ guchar r, g, b, a;
- if (pixbuf == NULL)
+ if (pixels == NULL)
return NULL;
- g_object_get (G_OBJECT (pixbuf),
- "width", &width,
- "height", &height,
- "rowstride", &p_stride,
- "n-channels", &p_n_channels,
- "pixels", &p_pixels,
- NULL );
surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, width, height);
s_stride = cairo_image_surface_get_stride (surface);
s_pixels = _cairo_image_surface_flush_and_get_data (surface);
metadata = _cairo_image_surface_get_metadata (surface);
- _cairo_metadata_set_has_alpha (metadata, (p_n_channels == 4));
+ _cairo_metadata_set_has_alpha (metadata, has_alpha);
- if (p_n_channels == 4) {
- guchar *s_iter;
- guchar *p_iter;
+ if (has_alpha) {
+ guchar *s_iter;
+ const guchar *p_iter;
for (h = 0; h < height; h++) {
s_iter = s_pixels;
- p_iter = p_pixels;
+ p_iter = pixels;
for (w = 0; w < width; w++) {
a = p_iter[3];
@@ -447,31 +439,31 @@ _cairo_image_surface_create_from_pixbuf (GdkPixbuf *pixbuf)
memcpy (s_iter, &pixel, sizeof (guint32));
s_iter += 4;
- p_iter += p_n_channels;
+ p_iter += 4;
}
s_pixels += s_stride;
- p_pixels += p_stride;
+ pixels += p_stride;
}
}
else {
- guchar *s_iter;
- guchar *p_iter;
+ guchar *s_iter;
+ const guchar *p_iter;
for (h = 0; h < height; h++) {
s_iter = s_pixels;
- p_iter = p_pixels;
+ p_iter = pixels;
for (w = 0; w < width; w++) {
pixel = CAIRO_RGBA_TO_UINT32 (p_iter[0], p_iter[1], p_iter[2], 0xff);
memcpy (s_iter, &pixel, sizeof (guint32));
s_iter += 4;
- p_iter += p_n_channels;
+ p_iter += 3;
}
s_pixels += s_stride;
- p_pixels += p_stride;
+ pixels += p_stride;
}
}
@@ -481,6 +473,30 @@ _cairo_image_surface_create_from_pixbuf (GdkPixbuf *pixbuf)
}
+cairo_surface_t *
+_cairo_image_surface_create_from_pixbuf (GdkPixbuf *pixbuf)
+{
+ guchar *pixels;
+ int width;
+ int height;
+ int row_stride;
+ int n_channels;
+
+ if (pixbuf == NULL)
+ return NULL;
+
+ g_object_get (G_OBJECT (pixbuf),
+ "pixels", &pixels,
+ "width", &width,
+ "height", &height,
+ "rowstride", &row_stride,
+ "n-channels", &n_channels,
+ NULL );
+
+ return _cairo_image_surface_create_from_rgba (pixels, width, height, row_stride, n_channels == 4);
+}
+
+
cairo_surface_t *
_cairo_image_surface_create_compatible (cairo_surface_t *surface)
{
diff --git a/gthumb/cairo-utils.h b/gthumb/cairo-utils.h
index 52e927cf..8d3baf67 100644
--- a/gthumb/cairo-utils.h
+++ b/gthumb/cairo-utils.h
@@ -210,6 +210,11 @@ cairo_surface_t * _cairo_image_surface_copy_subsurface (cairo_surface_t
int src_y,
int width,
int height);
+cairo_surface_t * _cairo_image_surface_create_from_rgba (const guchar *pixels,
+ int width,
+ int height,
+ int row_stride,
+ gboolean has_alpha);
cairo_surface_t * _cairo_image_surface_create_from_pixbuf (GdkPixbuf *pixbuf);
cairo_surface_t * _cairo_image_surface_create_compatible (cairo_surface_t *surface);
void _cairo_image_surface_transform_get_steps (cairo_format_t format,
diff --git a/gthumb/glib-utils.c b/gthumb/glib-utils.c
index 39698375..386c831c 100644
--- a/gthumb/glib-utils.c
+++ b/gthumb/glib-utils.c
@@ -1918,7 +1918,10 @@ _g_mime_type_has_transparency (const char *mime_type)
|| (strcmp (mime_type, "image/gif") == 0)
|| (strcmp (mime_type, "image/svg+xml") == 0)
|| (strcmp (mime_type, "image/webp") == 0)
- || (strcmp (mime_type, "image/jxl") == 0);
+ || (strcmp (mime_type, "image/jxl") == 0)
+ || (strcmp (mime_type, "image/avif") == 0)
+ || (strcmp (mime_type, "image/heif") == 0)
+ || (strcmp (mime_type, "image/heic") == 0);
}
diff --git a/meson.build b/meson.build
index a2770a69..9e5deb94 100644
--- a/meson.build
+++ b/meson.build
@@ -19,6 +19,7 @@ librsvg_version = '>=2.34.0'
libwebp_version = '>=0.2.0'
libjxl_version = '>=0.3.0'
libjxl_threads_version = '>=0.3.0'
+libheif_version = '>= 1.11'
libjson_glib_version = '>=0.15.0'
webkit2_version = '>=1.10.0'
lcms2_version = '>=2.6'
@@ -169,6 +170,13 @@ else
use_libjxl = false
endif
+if get_option('libheif')
+ libheif_dep = dependency('libheif', version : libheif_version, required : false)
+ use_libheif = libheif_dep.found()
+else
+ use_libheif = false
+endif
+
if get_option('libraw')
libraw_dep = dependency('libraw', version : libraw_version, required : false)
use_libraw = libraw_dep.found()
@@ -295,6 +303,9 @@ if use_libjxl
config_data.set('HAVE_LIBJXL', 1)
config_data.set('JXL_IS_UNKNOWN_TO_GLIB', 1) # Define to 1 if jxl images are not recognized by the glib
functions
endif
+if use_libheif
+ config_data.set('HAVE_LIBHEIF', 1)
+endif
if use_libraw
config_data.set('HAVE_LIBRAW', 1)
endif
@@ -387,6 +398,7 @@ summary = [
' jxl: @0@'.format(use_libjxl),
' raw: @0@'.format(use_libraw),
' svg: @0@'.format(use_librsvg),
+ ' heif/avif: @0@'.format(use_libheif),
' web services: @0@'.format(with_webservices),
' libsecret: @0@'.format(use_libsecret),
' libbrasero: @0@'.format(use_libbrasero),
diff --git a/meson_options.txt b/meson_options.txt
index d7655fb3..a6863433 100644
--- a/meson_options.txt
+++ b/meson_options.txt
@@ -64,6 +64,12 @@ option('libjxl',
description : 'Use libjxl to load JPEG XL images'
)
+option('libheif',
+ type : 'boolean',
+ value : true,
+ description : 'Use libheif to load HEIF and AVIF images'
+)
+
option('libraw',
type : 'boolean',
value : true,
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]