[gtk/matthiasc/color-profile-rebased: 52/52] widget-factory: Add an hdr image example




commit bfc85c8354c3d28e4c11ab7997d285dcb5a8acb2
Author: Matthias Clasen <mclasen redhat com>
Date:   Mon May 9 11:37:48 2022 -0400

    widget-factory: Add an hdr image example
    
    This uses the heif loader to show an image in
    the 'color profile' notebook. The current image
    isn't a very good HDR example, and should be
    replaced by a more suitable image.

 demos/widget-factory/hdr-example.heif             | Bin 0 -> 5251955 bytes
 demos/widget-factory/heif-loader.c                | 256 ++++++++++++++++++++++
 demos/widget-factory/heif-loader.h                |   7 +
 demos/widget-factory/meson.build                  |  14 +-
 demos/widget-factory/widget-factory.c             |  16 ++
 demos/widget-factory/widget-factory.gresource.xml |   1 +
 demos/widget-factory/widget-factory.ui            |  19 +-
 7 files changed, 308 insertions(+), 5 deletions(-)
---
diff --git a/demos/widget-factory/hdr-example.heif b/demos/widget-factory/hdr-example.heif
new file mode 100755
index 0000000000..95152c8259
Binary files /dev/null and b/demos/widget-factory/hdr-example.heif differ
diff --git a/demos/widget-factory/heif-loader.c b/demos/widget-factory/heif-loader.c
new file mode 100644
index 0000000000..3d49a2f8c3
--- /dev/null
+++ b/demos/widget-factory/heif-loader.c
@@ -0,0 +1,256 @@
+#include "heif-loader.h"
+#include <gtk/gtk.h>
+#include <libheif/heif.h>
+
+static void
+describe_nclx_color_profile (const struct heif_color_profile_nclx *nclx,
+                             GString                              *s)
+{
+  if (nclx->color_primaries == heif_color_primaries_unspecified)
+    return;
+
+  if (nclx->color_primaries == heif_color_primaries_ITU_R_BT_709_5)
+    {
+      if (nclx->transfer_characteristics == heif_transfer_characteristic_IEC_61966_2_1 &&
+          (nclx->matrix_coefficients == heif_matrix_coefficients_ITU_R_BT_470_6_System_B_G ||
+           nclx->matrix_coefficients == heif_matrix_coefficients_ITU_R_BT_601_6))
+        {
+          g_string_append (s, "sRGB");
+          return;
+        }
+
+      if (nclx->transfer_characteristics == heif_transfer_characteristic_linear &&
+          (nclx->matrix_coefficients == heif_matrix_coefficients_ITU_R_BT_470_6_System_B_G ||
+           nclx->matrix_coefficients == heif_matrix_coefficients_ITU_R_BT_601_6))
+        {
+          g_string_append (s, "sRGB linear");
+          return;
+        }
+    }
+
+ if (nclx->color_primaries == heif_color_primaries_ITU_R_BT_2020_2_and_2100_0)
+    {
+      if (nclx->transfer_characteristics == heif_transfer_characteristic_ITU_R_BT_2100_0_PQ &&
+          nclx->matrix_coefficients == heif_matrix_coefficients_ITU_R_BT_2020_2_non_constant_luminance)
+        {
+          g_string_append (s, "BT.2020 PQ");
+          return;
+        }
+
+      if (nclx->transfer_characteristics == heif_transfer_characteristic_ITU_R_BT_2100_0_HLG &&
+          nclx->matrix_coefficients == heif_matrix_coefficients_ITU_R_BT_2020_2_non_constant_luminance)
+        {
+          g_string_append (s, "BT.2020 HLG");
+          return;
+        }
+    }
+
+  if (nclx->color_primaries == heif_color_primaries_SMPTE_EG_432_1)
+    {
+      if (nclx->transfer_characteristics == heif_transfer_characteristic_ITU_R_BT_2100_0_PQ)
+        {
+          g_string_append (s, "P3 PQ");
+          return;
+        }
+    }
+
+  g_string_append_printf (s, "%d/%d/%d",
+                          nclx->color_primaries,
+                          nclx->matrix_coefficients,
+                          nclx->transfer_characteristics);
+}
+
+GdkTexture *
+load_heif_image (const char  *resource_path,
+                 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;
+  gsize size;
+  int width, height, bpp, stride, bits;
+  GBytes *bytes;
+  GdkTexture *texture = NULL;
+  GdkColorSpace *color_space = NULL;
+  char *profile_type = NULL;
+
+  ctx = heif_context_alloc ();
+
+  bytes = g_resources_lookup_data (resource_path, 0, NULL);
+  data = (uint8_t *) g_bytes_get_data (bytes, &size);
+
+  err = heif_context_read_from_memory (ctx, data, size, NULL);
+
+  g_bytes_unref (bytes);
+
+  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;
+    }
+
+  switch (heif_image_handle_get_color_profile_type (handle))
+    {
+    case heif_color_profile_type_not_present:
+      break;
+    case heif_color_profile_type_rICC:
+    case heif_color_profile_type_prof:
+      {
+        guchar *icc_data;
+        gsize icc_size;
+
+        profile_type = g_strdup ("icc");
+        icc_size = heif_image_handle_get_raw_color_profile_size (handle);
+        icc_data = g_new0 (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);
+            color_space = gdk_color_space_new_from_icc_profile (bytes, NULL);
+            g_bytes_unref (bytes);
+          }
+        g_free (icc_data);
+      }
+      break;
+    case heif_color_profile_type_nclx:
+      {
+        struct heif_color_profile_nclx *nclx = NULL;
+
+        err = heif_image_handle_get_nclx_color_profile (handle, &nclx);
+        if (err.code == heif_error_Ok)
+          {
+            GString *s;
+
+            s = g_string_new ("");
+            describe_nclx_color_profile (nclx, s);
+            profile_type = g_string_free (s, FALSE);
+            color_space = gdk_color_space_new_from_cicp (nclx->color_primaries,
+                                                         nclx->transfer_characteristics,
+                                                         0,
+                                                         TRUE);
+            heif_nclx_color_profile_free (nclx);
+          }
+      }
+      break;
+    default:
+      g_assert_not_reached ();
+    }
+
+  g_string_append_printf (details, "%dā€†Ć—ā€†%d pixels\n%d bits of luma, %d bits of chroma%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" : "");
+
+  if (profile_type)
+    {
+      g_string_append_printf (details, "color profile: %s\n", profile_type);
+      g_free (profile_type);
+    }
+
+  if (color_space == NULL)
+    color_space = g_object_ref (gdk_color_space_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_space (width, height,
+                                                     format, color_space,
+                                                     bytes, stride);
+  g_bytes_unref (bytes);
+  g_object_unref (color_space);
+
+out:
+  heif_context_free (ctx);
+  if (handle)
+    heif_image_handle_release (handle);
+  if (image)
+    heif_image_release (image);
+
+  return texture;
+}
diff --git a/demos/widget-factory/heif-loader.h b/demos/widget-factory/heif-loader.h
new file mode 100644
index 0000000000..933254f9b5
--- /dev/null
+++ b/demos/widget-factory/heif-loader.h
@@ -0,0 +1,7 @@
+#pragma once
+
+#include <gtk/gtk.h>
+
+GdkTexture *load_heif_image (const char  *resource_path,
+                             GString     *details,
+                             GError     **error);
diff --git a/demos/widget-factory/meson.build b/demos/widget-factory/meson.build
index 550eb09bd9..e6f5644512 100644
--- a/demos/widget-factory/meson.build
+++ b/demos/widget-factory/meson.build
@@ -73,10 +73,18 @@ else
   )
 endif
 
+widgetfactory_cflags = []
+widgetfactory_sources = [ 'widget-factory.c' ]
+
+if libheif_dep.found()
+  widgetfactory_cflags += '-DHAVE_LIBHEIF'
+  widgetfactory_sources += 'heif-loader.c'
+endif
+
 executable('gtk4-widget-factory',
-  sources: ['widget-factory.c', widgetfactory_resources],
-  c_args: common_cflags,
-  dependencies: [ libgtk_dep, demo_conf_h ],
+  sources: [ widgetfactory_sources, widgetfactory_resources],
+  c_args: [ common_cflags, widgetfactory_cflags ],
+  dependencies: [ libgtk_dep, libheif_dep, demo_conf_h ],
   include_directories: confinc,
   win_subsystem: 'windows',
   link_args: extra_demo_ldflags,
diff --git a/demos/widget-factory/widget-factory.c b/demos/widget-factory/widget-factory.c
index 4ec907d531..535e1d5e37 100644
--- a/demos/widget-factory/widget-factory.c
+++ b/demos/widget-factory/widget-factory.c
@@ -27,6 +27,8 @@
 
 #include "demo_conf.h"
 
+#include "heif-loader.h"
+
 static void
 change_dark_state (GSimpleAction *action,
                     GVariant      *state,
@@ -2383,6 +2385,20 @@ activate (GApplication *app)
   model = (GMenuModel *)gtk_builder_get_object (builder, "new_style_context_menu_model");
   set_up_context_popover (widget, model);
 
+  widget = (GtkWidget *)gtk_builder_get_object (builder, "hdr_picture");
+#ifdef HAVE_LIBHEIF
+  widget2 = (GtkWidget *)gtk_builder_get_object (builder, "hdr_label");
+  GString *details = g_string_new ("");
+  GdkTexture *texture = load_heif_image ("/org/gtk/WidgetFactory4/hdr-example.heif", details, NULL);
+  gtk_picture_set_paintable (GTK_PICTURE (widget), GDK_PAINTABLE (texture));
+  gtk_label_set_label (GTK_LABEL (widget2), details->str);
+  g_string_free (details, TRUE);
+  g_object_unref (texture);
+#else
+  widget2 = gtk_widget_get_ancestor (widget, GTK_TYPE_NOTEBOOK);
+  gtk_notebook_remove_page (GTK_NOTEBOOK (widget2), 2);
+#endif
+
   gtk_window_present (window);
 
   g_object_unref (builder);
diff --git a/demos/widget-factory/widget-factory.gresource.xml 
b/demos/widget-factory/widget-factory.gresource.xml
index 3706f8de98..82d1bc7475 100644
--- a/demos/widget-factory/widget-factory.gresource.xml
+++ b/demos/widget-factory/widget-factory.gresource.xml
@@ -110,6 +110,7 @@
     <file>icons/scalable/status/weather-severe-alert-symbolic.svg</file>
     <file>icons/scalable/status/weather-showers-symbolic.svg</file>
     <file>icons/scalable/status/weather-snow-symbolic.svg</file>
+    <file>hdr-example.heif</file>
 
     <file 
alias='icons/scalable/apps/org.gtk.WidgetFactory4.svg'>data/scalable/apps/org.gtk.WidgetFactory4.svg</file>
 
diff --git a/demos/widget-factory/widget-factory.ui b/demos/widget-factory/widget-factory.ui
index a49c56bde2..6afc0f7059 100644
--- a/demos/widget-factory/widget-factory.ui
+++ b/demos/widget-factory/widget-factory.ui
@@ -1587,6 +1587,7 @@ Suspendisse feugiat quam quis dolor accumsan cursus.</property>
                                       <object class="GtkImage">
                                         <property 
name="paintable">resource:///org/gtk/WidgetFactory4/color-profile-check.tiff</property>
                                         <layout>
+
                                           <property name="column">0</property>
                                           <property name="row">3</property>
                                         </layout>
@@ -1616,12 +1617,26 @@ Suspendisse feugiat quam quis dolor accumsan cursus.</property>
                                 <property name="position">2</property>
                                 <property name="child">
                                   <object class="GtkBox" id="box11">
-                                    <property name="orientation">1</property>
+                                    <property name="orientation">vertical</property>
+                                    <child>
+                                      <object class="GtkPicture" id="hdr_picture">
+                                        <property name="can-shrink">1</property>
+                                      </object>
+                                    </child>
+                                    <child>
+                                      <object class="GtkLabel" id="hdr_label">
+                                        <property name="margin-start">10</property>
+                                        <property name="margin-end">10</property>
+                                        <property name="margin-top">10</property>
+                                        <property name="margin-bottom">10</property>
+                                        <property name="use-markup">1</property>
+                                      </object>
+                                    </child>
                                   </object>
                                 </property>
                                 <property name="tab">
                                   <object class="GtkLabel" id="label10">
-                                    <property name="label" translatable="1">page 3</property>
+                                    <property name="label" translatable="1">HDR</property>
                                   </object>
                                 </property>
                               </object>


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