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
This commit is contained in:
Daniel Novomesky 2020-10-15 17:38:24 +02:00
parent 05d0d5b123
commit 64bfa57eec
1 changed files with 233 additions and 2 deletions

View File

@ -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)