diff options
author | Matthias Clasen <mclasen@redhat.com> | 2022-05-08 00:23:35 -0400 |
---|---|---|
committer | Matthias Clasen <mclasen@redhat.com> | 2022-05-11 15:01:09 -0400 |
commit | c58606d11607c6c2807fffa47f67fce6263a9b1e (patch) | |
tree | 08427f712bd91d3a2b574ae202d8acfbab263457 | |
parent | 76d0edf64ab775c27f23fc8fdacc94e93511cbf7 (diff) | |
download | gtk+-c58606d11607c6c2807fffa47f67fce6263a9b1e.tar.gz |
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.
-rw-r--r-- | tests/meson.build | 9 | ||||
-rw-r--r-- | tests/testheif.c | 290 |
2 files changed, 299 insertions, 0 deletions
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..2bba3b105f --- /dev/null +++ b/tests/testheif.c @@ -0,0 +1,290 @@ +#include <gtk/gtk.h> +#include <libheif/heif.h> +#include <lcms2.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); +} + +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; + char *profile_type = NULL; + + 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; + } + + 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 = "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); + + g_print ("%s\n", details->str); + + 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; +} + +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); +} |