[gthumb] added ability to load AVIF/HEIF images using libheif



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]