From 64bfa57eec9c38bae417e658a78f73a2d7c41ccc Mon Sep 17 00:00:00 2001 From: Daniel Novomesky Date: Thu, 15 Oct 2020 17:38:24 +0200 Subject: [PATCH] Improved HEIF plug-in User can select color subsampling/pixel format (YUV444, YUV420, RGB) User can select encoder speed (Slow, Balanced, Fast) Lossless option delivers visually lossless output --- plug-ins/common/file-heif.c | 235 +++++++++++++++++++++++++++++++++++- 1 file changed, 233 insertions(+), 2 deletions(-) diff --git a/plug-ins/common/file-heif.c b/plug-ins/common/file-heif.c index 2bab97091f..2b3a2b8440 100644 --- a/plug-ins/common/file-heif.c +++ b/plug-ins/common/file-heif.c @@ -40,6 +40,21 @@ typedef struct gint type; } XmpStructs; +typedef enum _HeifpluginEncoderSpeed +{ + HEIFPLUGIN_ENCODER_SPEED_SLOW = 0, + HEIFPLUGIN_ENCODER_SPEED_BALANCED = 1, + HEIFPLUGIN_ENCODER_SPEED_FASTER = 2 +} HeifpluginEncoderSpeed; + +typedef enum _HeifpluginExportFormat +{ + HEIFPLUGIN_EXPORT_FORMAT_RGB = 0, + HEIFPLUGIN_EXPORT_FORMAT_YUV444 = 1, + HEIFPLUGIN_EXPORT_FORMAT_YUV422 = 2, + HEIFPLUGIN_EXPORT_FORMAT_YUV420 = 3 +} HeifpluginExportFormat; + typedef struct _Heif Heif; typedef struct _HeifClass HeifClass; @@ -246,6 +261,20 @@ heif_create_procedure (GimpPlugIn *plug_in, 8, 12, 8, G_PARAM_READWRITE); + GIMP_PROC_ARG_INT (procedure, "pixel-format", + "Pixel format", + "Format of color sub-sampling", + HEIFPLUGIN_EXPORT_FORMAT_RGB, HEIFPLUGIN_EXPORT_FORMAT_YUV420, + HEIFPLUGIN_EXPORT_FORMAT_YUV420, + G_PARAM_READWRITE); + + GIMP_PROC_ARG_INT (procedure, "encoder-speed", + "Encoder speed", + "Tradeoff between speed and compression", + HEIFPLUGIN_ENCODER_SPEED_SLOW, HEIFPLUGIN_ENCODER_SPEED_FASTER, + HEIFPLUGIN_ENCODER_SPEED_BALANCED, + G_PARAM_READWRITE); + GIMP_PROC_ARG_BOOLEAN (procedure, "save-exif", "Save Exif", "Toggle saving Exif data", @@ -309,6 +338,20 @@ heif_create_procedure (GimpPlugIn *plug_in, 8, 12, 8, G_PARAM_READWRITE); + GIMP_PROC_ARG_INT (procedure, "pixel-format", + "Pixel format", + "Format of color sub-sampling", + HEIFPLUGIN_EXPORT_FORMAT_RGB, HEIFPLUGIN_EXPORT_FORMAT_YUV420, + HEIFPLUGIN_EXPORT_FORMAT_YUV420, + G_PARAM_READWRITE); + + GIMP_PROC_ARG_INT (procedure, "encoder-speed", + "Encoder speed", + "Tradeoff between speed and compression", + HEIFPLUGIN_ENCODER_SPEED_SLOW, HEIFPLUGIN_ENCODER_SPEED_FASTER, + HEIFPLUGIN_ENCODER_SPEED_BALANCED, + G_PARAM_READWRITE); + GIMP_PROC_ARG_BOOLEAN (procedure, "save-exif", "Save Exif", "Toggle saving Exif data", @@ -1349,6 +1392,15 @@ save_image (GFile *file, gint quality; gboolean save_profile; gint save_bit_depth = 8; +#if LIBHEIF_HAVE_VERSION(1,9,0) + HeifpluginExportFormat pixel_format = HEIFPLUGIN_EXPORT_FORMAT_YUV420; +#endif +#if LIBHEIF_HAVE_VERSION(1,8,0) + HeifpluginEncoderSpeed encoder_speed = HEIFPLUGIN_ENCODER_SPEED_BALANCED; + const char *encoder_name; + const char *parameter_value; + struct heif_color_profile_nclx nclx_profile; +#endif #if GEXIV2_CHECK_VERSION(0, 12, 2) gboolean save_exif = FALSE; #endif @@ -1364,8 +1416,12 @@ save_image (GFile *file, g_object_get (config, "lossless", &lossless, "quality", &quality, +#if LIBHEIF_HAVE_VERSION(1,9,0) + "pixel-format", &pixel_format, +#endif #if LIBHEIF_HAVE_VERSION(1,8,0) "save-bit-depth", &save_bit_depth, + "encoder-speed", &encoder_speed, #endif "save-color-profile", &save_profile, #if GEXIV2_CHECK_VERSION(0, 12, 2) @@ -1464,6 +1520,28 @@ save_image (GFile *file, } } +#if LIBHEIF_HAVE_VERSION(1,9,0) + if (pixel_format == HEIFPLUGIN_EXPORT_FORMAT_RGB && save_bit_depth == 8) + { + nclx_profile.version = 1; + nclx_profile.color_primaries = heif_color_primaries_unspecified; + + if (out_linear) + { + nclx_profile.transfer_characteristics = heif_transfer_characteristic_linear; + } + else + { + nclx_profile.transfer_characteristics = heif_transfer_characteristic_unspecified; + } + + nclx_profile.matrix_coefficients = heif_matrix_coefficients_RGB_GBR; + nclx_profile.full_range_flag = 1; + + heif_image_set_nclx_color_profile (h_image, &nclx_profile); + } +#endif + icc_data = gimp_color_profile_get_icc_profile (profile, &icc_length); heif_image_set_raw_color_profile (h_image, "prof", icc_data, icc_length); space = gimp_color_profile_get_space (profile, @@ -1483,7 +1561,6 @@ save_image (GFile *file, { #if LIBHEIF_HAVE_VERSION(1,8,0) /* We save as sRGB */ - struct heif_color_profile_nclx nclx_profile; nclx_profile.version = 1; nclx_profile.color_primaries = heif_color_primaries_ITU_R_BT_709_5; @@ -1491,6 +1568,13 @@ save_image (GFile *file, nclx_profile.matrix_coefficients = heif_matrix_coefficients_ITU_R_BT_601_6; nclx_profile.full_range_flag = 1; +#if LIBHEIF_HAVE_VERSION(1,9,0) + if (pixel_format == HEIFPLUGIN_EXPORT_FORMAT_RGB && save_bit_depth == 8) + { + nclx_profile.matrix_coefficients = heif_matrix_coefficients_RGB_GBR; + } +#endif + heif_image_set_nclx_color_profile (h_image, &nclx_profile); space = babl_space ("sRGB"); @@ -1643,12 +1727,131 @@ save_image (GFile *file, heif_image_release (h_image); heif_context_free (context); return FALSE; - } + } heif_encoder_set_lossy_quality (encoder, quality); heif_encoder_set_lossless (encoder, lossless); /* heif_encoder_set_logging_level (encoder, logging_level); */ +#if LIBHEIF_HAVE_VERSION(1,8,0) + encoder_name = heif_encoder_get_name (encoder); +#if LIBHEIF_HAVE_VERSION(1,9,0) + + if (lossless && pixel_format != HEIFPLUGIN_EXPORT_FORMAT_RGB) + { + /* disable subsampling for lossless */ + pixel_format = HEIFPLUGIN_EXPORT_FORMAT_YUV444; + } + + switch (pixel_format) + { + case HEIFPLUGIN_EXPORT_FORMAT_RGB: + /* same as HEIFPLUGIN_EXPORT_FORMAT_YUV444 */ + case HEIFPLUGIN_EXPORT_FORMAT_YUV444: + parameter_value = "444"; + break; + case HEIFPLUGIN_EXPORT_FORMAT_YUV422: + parameter_value = "422"; + break; + default: /* HEIFPLUGIN_EXPORT_FORMAT_YUV420 */ + parameter_value = "420"; + break; + } + + err = heif_encoder_set_parameter_string (encoder, "chroma", parameter_value); + if (err.code != 0) + { + g_printerr ("Failed to set chroma %s for %s encoder: %s", parameter_value, encoder_name, err.message); + } +#endif + + if (compression == heif_compression_HEVC) + { + switch (encoder_speed) + { + case HEIFPLUGIN_ENCODER_SPEED_SLOW: + parameter_value = "veryslow"; + break; + case HEIFPLUGIN_ENCODER_SPEED_FASTER: + parameter_value = "faster"; + break; + default: /* HEIFPLUGIN_ENCODER_SPEED_BALANCED */ + parameter_value = "medium"; + break; + } + + err = heif_encoder_set_parameter_string (encoder, "preset", parameter_value); + if (err.code != 0) + { + g_printerr ("Failed to set preset %s for %s encoder: %s", parameter_value, encoder_name, err.message); + } + + } + else if (compression == heif_compression_AV1) + { + int parameter_number; + + parameter_number = g_get_num_processors(); + parameter_number = CLAMP(parameter_number, 1, 16); + + err = heif_encoder_set_parameter_integer (encoder, "threads", parameter_number); + if (err.code != 0) + { + g_printerr ("Failed to set threads=%d for %s encoder: %s", parameter_number, encoder_name, err.message); + } + + + if (g_ascii_strncasecmp (encoder_name, "AOM", 3) == 0) /* AOMedia AV1 encoder */ + { + switch (encoder_speed) + { + case HEIFPLUGIN_ENCODER_SPEED_SLOW: + parameter_number = 1; + break; + case HEIFPLUGIN_ENCODER_SPEED_FASTER: + parameter_number = 6; + break; + default: /* HEIFPLUGIN_ENCODER_SPEED_BALANCED */ + parameter_number = 4; + break; + } + + err = heif_encoder_set_parameter_integer (encoder, "speed", parameter_number); + if (err.code != 0) + { + g_printerr ("Failed to set speed=%d for %s encoder: %s", parameter_number, encoder_name, err.message); + } + + } + else if (g_ascii_strncasecmp (encoder_name, "Rav1e", 5) == 0) /* Rav1e encoder */ + { + switch (encoder_speed) + { + case HEIFPLUGIN_ENCODER_SPEED_SLOW: + parameter_number = 6; + break; + case HEIFPLUGIN_ENCODER_SPEED_FASTER: + parameter_number = 10; + break; + default: /* HEIFPLUGIN_ENCODER_SPEED_BALANCED */ + parameter_number = 8; + break; + } + + err = heif_encoder_set_parameter_integer (encoder, "speed", parameter_number); + if (err.code != 0) + { + g_printerr ("Failed to set speed=%d for %s encoder: %s", parameter_number, encoder_name, err.message); + } + + } + else + { + g_printerr ("Parameters not set, unsupported AV1 encoder: %s", encoder_name); + } + } +#endif + err = heif_context_encode_image (context, h_image, encoder, @@ -2272,6 +2475,7 @@ save_dialog (GimpProcedure *procedure, grid = gtk_grid_new (); gtk_grid_set_column_spacing (GTK_GRID (grid), 6); + gtk_grid_set_row_spacing (GTK_GRID (grid), 2); gtk_container_add (GTK_CONTAINER (frame), grid); gtk_widget_show (grid); @@ -2286,6 +2490,20 @@ save_dialog (GimpProcedure *procedure, 1, 10, 0, FALSE, 0, 0); +#if LIBHEIF_HAVE_VERSION(1,9,0) + store = gimp_int_store_new (_("RGB"), HEIFPLUGIN_EXPORT_FORMAT_RGB, + _("YUV444"), HEIFPLUGIN_EXPORT_FORMAT_YUV444, + _("YUV420"), HEIFPLUGIN_EXPORT_FORMAT_YUV420, + NULL); + + combo = gimp_prop_int_combo_box_new (config, "pixel-format", + GIMP_INT_STORE (store)); + g_object_unref (store); + gimp_grid_attach_aligned (GTK_GRID (grid), 0, 2, + _("Pixel format:"), 0.0, 0.5, + combo, 2); +#endif + #if LIBHEIF_HAVE_VERSION(1,8,0) g_object_get (config, "save-bit-depth", &save_bit_depth, @@ -2319,6 +2537,7 @@ save_dialog (GimpProcedure *procedure, grid2 = gtk_grid_new (); gtk_grid_set_column_spacing (GTK_GRID (grid2), 6); + gtk_grid_set_row_spacing (GTK_GRID (grid2), 2); gtk_box_pack_start (GTK_BOX (main_vbox), grid2, FALSE, FALSE, 0); gtk_widget_show (grid2); @@ -2333,6 +2552,18 @@ save_dialog (GimpProcedure *procedure, gimp_grid_attach_aligned (GTK_GRID (grid2), 0, 1, _("Bit depth:"), 0.0, 0.5, combo, 2); + + store = gimp_int_store_new (_("Slow"), HEIFPLUGIN_ENCODER_SPEED_SLOW, + _("Balanced"), HEIFPLUGIN_ENCODER_SPEED_BALANCED, + _("Fast"), HEIFPLUGIN_ENCODER_SPEED_FASTER, + NULL); + + combo = gimp_prop_int_combo_box_new (config, "encoder-speed", + GIMP_INT_STORE (store)); + g_object_unref (store); + gimp_grid_attach_aligned (GTK_GRID (grid2), 0, 2, + _("Speed:"), 0.0, 0.5, + combo, 2); #endif #if LIBHEIF_HAVE_VERSION(1,4,0)