[gtk/heif-demo: 45/45] Add a quick-and-dirty heif loading test




commit 6a995d7acb45264f62d85f24b76aa1158003084c
Author: Matthias Clasen <mclasen redhat com>
Date:   Sun May 8 00:23:35 2022 -0400

    Add a quick-and-dirty heif loading test
    
    This uses libheif to load an image and produce
    a texture. If the image has more than 8 bits of
    chroma or luma, the texture will be 16bit.

 tests/meson.build |   9 +++
 tests/testheif.c  | 187 ++++++++++++++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 196 insertions(+)
---
diff --git a/tests/meson.build b/tests/meson.build
index d837b5af6d..8f1ff34381 100644
--- a/tests/meson.build
+++ b/tests/meson.build
@@ -148,3 +148,12 @@ if libsysprof_dep.found()
     dependencies: [libsysprof_dep, platform_gio_dep, libm],
   )
 endif
+
+libheif_dep = dependency('libheif')
+
+if libheif_dep.found()
+  executable('testheif',
+    sources: 'testheif.c',
+    dependencies: [libgtk_dep, libheif_dep],
+  )
+endif
diff --git a/tests/testheif.c b/tests/testheif.c
new file mode 100644
index 0000000000..14b6a395d7
--- /dev/null
+++ b/tests/testheif.c
@@ -0,0 +1,187 @@
+#include <gtk/gtk.h>
+#include <libheif/heif.h>
+
+static GdkTexture *
+load_heif_image (const char  *filename,
+                 GString     *details,
+                 GError     **error)
+{
+  struct heif_context *ctx;
+  struct heif_error err;
+  struct heif_image_handle *handle = NULL;
+  struct heif_image *image = NULL;
+  enum heif_chroma chroma;
+  GdkMemoryFormat format;
+  const char *format_name;
+  uint8_t *data = NULL;
+  int width, height, bpp, stride, bits;
+  GBytes *bytes;
+  GdkTexture *texture = NULL;
+  GdkColorProfile *profile = NULL;
+  gsize icc_size;
+  guchar *icc_data;
+
+  ctx = heif_context_alloc ();
+
+  err = heif_context_read_from_file (ctx, filename, NULL);
+
+  if (err.code != heif_error_Ok)
+    {
+      g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_FAILED, err.message);
+      goto out;
+    }
+
+  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 out;
+    }
+
+  icc_size = heif_image_handle_get_raw_color_profile_size (handle);
+  icc_data = g_new (guchar, icc_size);
+  err = heif_image_handle_get_raw_color_profile (handle, icc_data);
+  if (err.code == heif_error_Ok)
+    {
+      bytes = g_bytes_new (icc_data, icc_size);
+      profile = gdk_color_profile_new_from_icc_bytes (bytes, NULL);
+      g_bytes_unref (bytes);
+    }
+  g_free (icc_data);
+
+  g_string_append_printf (details, "%dā€†Ć—ā€†%d pixels\n%d bits of luma, %d bits of chroma%s%s\n",
+                          heif_image_handle_get_width (handle),
+                          heif_image_handle_get_height (handle),
+                          heif_image_handle_get_luma_bits_per_pixel (handle),
+                          heif_image_handle_get_chroma_bits_per_pixel (handle),
+                          heif_image_handle_has_alpha_channel (handle) ? ", with alpha" : "",
+                          profile ? ", has ICC profile" : "");
+
+  if (profile == NULL)
+    profile = gdk_color_profile_get_srgb ();
+
+  if (heif_image_handle_has_alpha_channel (handle))
+    {
+      if (heif_image_handle_get_luma_bits_per_pixel (handle) > 8 ||
+          heif_image_handle_get_chroma_bits_per_pixel (handle) > 8)
+        {
+          chroma = heif_chroma_interleaved_RRGGBBAA_BE;
+          format = GDK_MEMORY_R16G16B16A16;
+          format_name = "R<sub>16</sub>G<sub>16</sub>B<sub>16</sub>A<sub>16</sub>";
+        }
+      else
+        {
+          chroma = heif_chroma_interleaved_RGBA;
+          format = GDK_MEMORY_R8G8B8A8;
+          format_name = "R<sub>8</sub>G<sub>8</sub>B<sub>8</sub>A<sub>8</sub>";
+        }
+    }
+  else
+    {
+      if (heif_image_handle_get_luma_bits_per_pixel (handle) > 8 ||
+          heif_image_handle_get_chroma_bits_per_pixel (handle) > 8)
+        {
+          chroma = heif_chroma_interleaved_RRGGBB_BE;
+          format = GDK_MEMORY_R16G16B16;
+          format_name = "R<sub>16</sub>G<sub>16</sub>B<sub>16</sub>";
+        }
+      else
+        {
+          chroma = heif_chroma_interleaved_RGB;
+          format = GDK_MEMORY_R8G8B8;
+          format_name = "R<sub>8</sub>G<sub>8</sub>B<sub>8</sub>";
+        }
+    }
+
+  err = heif_decode_image (handle, &image, heif_colorspace_RGB, chroma, NULL);
+  if (err.code != heif_error_Ok)
+    {
+      g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_FAILED, err.message);
+      goto out;
+    }
+
+  width = heif_image_get_width (image, heif_channel_interleaved);
+  height = heif_image_get_height (image, heif_channel_interleaved);
+  bpp = heif_image_get_bits_per_pixel (image, heif_channel_interleaved);
+  data = (uint8_t*)heif_image_get_plane_readonly (image, heif_channel_interleaved, &stride);
+  bits = heif_image_get_bits_per_pixel_range (image, heif_channel_interleaved);
+
+  g_string_append_printf (details, "texture format %s", format_name);
+
+  if (format == GDK_MEMORY_R16G16B16A16 || format == GDK_MEMORY_R16G16B16)
+    {
+      /* Shift image data to full 16bit range */
+      int shift = 16 - bits;
+      if (shift > 0)
+        {
+          for (int y = 0; y < height; ++y)
+            {
+              uint8_t *row = data + y * stride;
+
+              for (int x = 0; x < stride; x += 2)
+                {
+                  uint8_t* p = &row[x];
+                  int v = (p[0] << 8) | p[1];
+                  v = (v << shift) | (v >> (16 - shift));
+                  p[1] = (uint8_t) (v >> 8);
+                  p[0] = (uint8_t) (v & 0xFF);
+                }
+            }
+        }
+    }
+
+  bytes = g_bytes_new_with_free_func (data, height * stride * bpp, (GDestroyNotify)heif_image_release, 
image);
+
+  image = NULL;
+
+  texture = gdk_memory_texture_new_with_color_profile (width, height,
+                                                       format, profile,
+                                                       bytes, stride);
+  g_bytes_unref (bytes);
+
+out:
+  heif_context_free (ctx);
+  if (handle)
+    heif_image_handle_release (handle);
+  if (image)
+    heif_image_release (image);
+
+  return texture;
+}
+
+int main (int argc, char *argv[])
+{
+  GdkTexture *texture;
+  GError *error = NULL;
+  GtkWidget *window, *picture, *box, *label;
+  GString *details;
+
+  gtk_init ();
+
+  details = g_string_new ("");
+
+  texture = load_heif_image (argv[1], details, &error);
+  if (!texture)
+    g_error ("%s", error->message);
+
+  window = gtk_window_new ();
+
+  gtk_window_set_title (GTK_WINDOW (window), argv[1]);
+  gtk_window_set_default_size (GTK_WINDOW (window), 1024, 768);
+
+  box = gtk_box_new (GTK_ORIENTATION_VERTICAL, 10);
+  picture = gtk_picture_new_for_paintable (GDK_PAINTABLE (texture));
+  gtk_box_append (GTK_BOX (box), picture);
+  label = gtk_label_new (details->str);
+  gtk_label_set_use_markup (GTK_LABEL (label), TRUE);
+  gtk_widget_set_margin_bottom (label, 10);
+  gtk_box_append (GTK_BOX (box), label);
+  gtk_window_set_child (GTK_WINDOW (window), box);
+
+  g_string_free (details, TRUE);
+
+  gtk_window_present (GTK_WINDOW (window));
+
+  while (1)
+    g_main_context_iteration (NULL, TRUE);
+}


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]