[gimp] Improved HEIF plug-in



commit 64bfa57eec9c38bae417e658a78f73a2d7c41ccc
Author: Daniel Novomesky <dnovomesky gmail com>
Date:   Thu Oct 15 17:38:24 2020 +0200

    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)


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