From 0806fafbab43b7a08677aa2e4f7ded0dc013e3be Mon Sep 17 00:00:00 2001 From: Matthias Clasen Date: Mon, 9 May 2022 11:37:48 -0400 Subject: 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 | 257 ++++++++++++++++++++++ demos/widget-factory/heif-loader.h | 7 + demos/widget-factory/meson.build | 6 +- demos/widget-factory/widget-factory.c | 11 + demos/widget-factory/widget-factory.gresource.xml | 1 + demos/widget-factory/widget-factory.ui | 19 +- 7 files changed, 297 insertions(+), 4 deletions(-) create mode 100755 demos/widget-factory/hdr-example.heif create mode 100644 demos/widget-factory/heif-loader.c create mode 100644 demos/widget-factory/heif-loader.h 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..f17c3bc177 --- /dev/null +++ b/demos/widget-factory/heif-loader.c @@ -0,0 +1,257 @@ +#include "heif-loader.h" +#include +#include + +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; + GdkColorProfile *profile = 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); + profile = gdk_color_profile_new_from_icc_bytes (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); + profile = gdk_color_profile_new_from_cicp (nclx->color_primaries, + nclx->transfer_characteristics, + 0, + TRUE, + NULL); + 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 (profile == NULL) + profile = g_object_ref (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 = "R16G16B16A16"; + } + else + { + chroma = heif_chroma_interleaved_RGBA; + format = GDK_MEMORY_R8G8B8A8; + format_name = "R8G8B8A8"; + } + } + 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 = "R16G16B16"; + } + else + { + chroma = heif_chroma_interleaved_RGB; + format = GDK_MEMORY_R8G8B8; + format_name = "R8G8B8"; + } + } + + 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); + g_object_unref (profile); + +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 + +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..fc3b03baad 100644 --- a/demos/widget-factory/meson.build +++ b/demos/widget-factory/meson.build @@ -73,10 +73,12 @@ else ) endif +libheif_dep = dependency('libheif') + executable('gtk4-widget-factory', - sources: ['widget-factory.c', widgetfactory_resources], + sources: ['widget-factory.c', 'heif-loader.c', widgetfactory_resources], c_args: common_cflags, - dependencies: [ libgtk_dep, demo_conf_h ], + 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 5e0353b80b..ba818e3476 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, @@ -2360,6 +2362,15 @@ 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"); + 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); + 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 0db9306704..9062698153 100644 --- a/demos/widget-factory/widget-factory.gresource.xml +++ b/demos/widget-factory/widget-factory.gresource.xml @@ -110,6 +110,7 @@ icons/scalable/status/weather-severe-alert-symbolic.svg icons/scalable/status/weather-showers-symbolic.svg icons/scalable/status/weather-snow-symbolic.svg + hdr-example.heif diff --git a/demos/widget-factory/widget-factory.ui b/demos/widget-factory/widget-factory.ui index 63756441d3..e7bd6dccad 100644 --- a/demos/widget-factory/widget-factory.ui +++ b/demos/widget-factory/widget-factory.ui @@ -1586,6 +1586,7 @@ Suspendisse feugiat quam quis dolor accumsan cursus. resource:///org/gtk/WidgetFactory4/color-profile-check.tiff + 0 3 @@ -1615,12 +1616,26 @@ Suspendisse feugiat quam quis dolor accumsan cursus. 2 - 1 + vertical + + + 1 + + + + + 10 + 10 + 10 + 10 + 1 + + - page 3 + HDR -- cgit v1.2.1