[gimp/gimp-2-10] Updated HEIF plug-in in 2.10 branch
- From: Jehan <jehanp src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gimp/gimp-2-10] Updated HEIF plug-in in 2.10 branch
- Date: Thu, 10 Sep 2020 18:52:38 +0000 (UTC)
commit e2f2a3f23ced5df7e285452678e849c7086b234c
Author: Daniel Novomesky <dnovomesky gmail com>
Date: Wed Sep 9 12:37:29 2020 +0200
Updated HEIF plug-in in 2.10 branch
Backported features from master:
AVIF export (when built with >= libheif 1.8.0)
High bit export
User can select save bit depth (8, 10, 12) during export
Minor fixes in import
plug-ins/common/file-heif.c | 390 +++++++++++++++++++++++++++++++++++++++-----
1 file changed, 346 insertions(+), 44 deletions(-)
---
diff --git a/plug-ins/common/file-heif.c b/plug-ins/common/file-heif.c
index 9d4db9c976..54989f1271 100644
--- a/plug-ins/common/file-heif.c
+++ b/plug-ins/common/file-heif.c
@@ -30,6 +30,7 @@
#define LOAD_PROC "file-heif-load"
#define SAVE_PROC "file-heif-save"
+#define SAVE_PROC_AV1 "file-heif-av1-save"
#define PLUG_IN_BINARY "file-heif"
@@ -40,6 +41,7 @@ struct _SaveParams
gint quality;
gboolean lossless;
gboolean save_profile;
+ gint save_bit_depth;
};
@@ -55,15 +57,17 @@ static void run (const gchar *name,
static gint32 load_image (GFile *file,
gboolean interactive,
GError **error);
-static gboolean save_image (GFile *file,
- gint32 image_ID,
- gint32 drawable_ID,
- const SaveParams *params,
- GError **error);
+static gboolean save_image (GFile *file,
+ gint32 image_ID,
+ gint32 drawable_ID,
+ const SaveParams *params,
+ GError **error,
+ enum heif_compression_format compression);
static gboolean load_dialog (struct heif_context *heif,
uint32_t *selected_image);
-static gboolean save_dialog (SaveParams *params);
+static gboolean save_dialog (SaveParams *params,
+ gint32 image_ID);
GimpPlugInInfo PLUG_IN_INFO =
@@ -126,15 +130,23 @@ query (void)
",avif"
#endif
, "");
- gimp_register_file_handler_mime (LOAD_PROC, "image/heif");
+ gimp_register_file_handler_mime (LOAD_PROC,
+ "image/heif"
+#if LIBHEIF_HAVE_VERSION(1,8,0)
+ ",image/avif"
+#endif
+ );
gimp_register_file_handler_uri (LOAD_PROC);
/* HEIF is an ISOBMFF format whose "brand" (the value after "ftyp")
* can be of various values.
* See also: https://gitlab.gnome.org/GNOME/gimp/issues/2209
*/
gimp_register_magic_load_handler (LOAD_PROC,
- "heif,heic",
- "",
+ "heif,heic"
+#if LIBHEIF_HAVE_VERSION(1,8,0)
+ ",avif"
+#endif
+ , "",
"4,string,ftypheic,4,string,ftypheix,"
"4,string,ftyphevc,4,string,ftypheim,"
"4,string,ftypheis,4,string,ftyphevm,"
@@ -161,6 +173,24 @@ query (void)
gimp_register_save_handler (SAVE_PROC, "heic,heif", "");
gimp_register_file_handler_mime (SAVE_PROC, "image/heif");
gimp_register_file_handler_uri (SAVE_PROC);
+
+#if LIBHEIF_HAVE_VERSION(1,8,0)
+ gimp_install_procedure (SAVE_PROC_AV1,
+ "Exports AVIF images",
+ "Save image in AV1 Image File Format (AVIF)",
+ "Daniel Novomesky <dnovomesky gmail com>",
+ "Daniel Novomesky <dnovomesky gmail com>",
+ "2020",
+ "HEIF/AVIF",
+ "RGB*",
+ GIMP_PLUGIN,
+ G_N_ELEMENTS (save_args), 0,
+ save_args, NULL);
+
+ gimp_register_save_handler (SAVE_PROC_AV1, "avif", "");
+ gimp_register_file_handler_mime (SAVE_PROC_AV1, "image/avif");
+ gimp_register_file_handler_uri (SAVE_PROC_AV1);
+#endif
}
#define LOAD_HEIF_CANCEL -2
@@ -242,6 +272,7 @@ run (const gchar *name,
params.lossless = FALSE;
params.quality = 50;
params.save_profile = (metadata_flags & GIMP_METADATA_SAVE_COLOR_PROFILE) != 0;
+ params.save_bit_depth = 8;
switch (run_mode)
{
@@ -268,7 +299,7 @@ run (const gchar *name,
case GIMP_RUN_INTERACTIVE:
gimp_get_data (SAVE_PROC, ¶ms);
- if (! save_dialog (¶ms))
+ if (! save_dialog (¶ms, image_ID))
status = GIMP_PDB_CANCEL;
break;
@@ -296,13 +327,16 @@ run (const gchar *name,
if (save_image (file, image_ID, drawable_ID,
¶ms,
- &error))
+ &error,
+ heif_compression_HEVC))
{
+ /* Exiv2 doesn't support HEIC format yet, following code doesn't work
if (metadata)
gimp_image_metadata_save_finish (image_ID,
"image/heif",
metadata, metadata_flags,
file, NULL);
+ */
gimp_set_data (SAVE_PROC, ¶ms, sizeof (params));
}
else
@@ -314,6 +348,93 @@ run (const gchar *name,
g_clear_object (&metadata);
}
+#if LIBHEIF_HAVE_VERSION(1,8,0)
+ else if (strcmp (name, SAVE_PROC_AV1) == 0)
+ {
+ gint32 image_ID = param[1].data.d_int32;
+ gint32 drawable_ID = param[2].data.d_int32;
+ GimpExportReturn export = GIMP_EXPORT_CANCEL;
+ SaveParams params;
+ GimpMetadata *metadata = NULL;
+ GimpMetadataSaveFlags metadata_flags;
+
+ metadata = gimp_image_metadata_save_prepare (image_ID,
+ "image/avif",
+ &metadata_flags);
+
+ params.lossless = FALSE;
+ params.quality = 50;
+ params.save_profile = (metadata_flags & GIMP_METADATA_SAVE_COLOR_PROFILE) != 0;
+ params.save_bit_depth = 8;
+
+ switch (run_mode)
+ {
+ case GIMP_RUN_INTERACTIVE:
+ case GIMP_RUN_WITH_LAST_VALS:
+ export = gimp_export_image (&image_ID, &drawable_ID, "AVIF",
+ GIMP_EXPORT_CAN_HANDLE_RGB |
+ GIMP_EXPORT_CAN_HANDLE_ALPHA);
+
+ if (export == GIMP_EXPORT_CANCEL)
+ {
+ values[0].data.d_status = GIMP_PDB_CANCEL;
+ g_clear_object (&metadata);
+ return;
+ }
+ break;
+
+ default:
+ break;
+ }
+
+ switch (run_mode)
+ {
+ case GIMP_RUN_INTERACTIVE:
+ gimp_get_data (SAVE_PROC_AV1, ¶ms);
+
+ if (! save_dialog (¶ms, image_ID))
+ status = GIMP_PDB_CANCEL;
+ break;
+
+ case GIMP_RUN_WITH_LAST_VALS:
+ gimp_get_data (SAVE_PROC_AV1, ¶ms);
+ break;
+
+ case GIMP_RUN_NONINTERACTIVE:
+ /* Make sure all the arguments are there! */
+ if (n_params != 7)
+ {
+ status = GIMP_PDB_CALLING_ERROR;
+ }
+ else
+ {
+ params.quality = (param[5].data.d_int32);
+ params.lossless = (param[6].data.d_int32);
+ }
+ break;
+ }
+
+ if (status == GIMP_PDB_SUCCESS)
+ {
+ GFile *file = g_file_new_for_uri (param[3].data.d_string);
+
+ if (save_image (file, image_ID, drawable_ID,
+ ¶ms,
+ &error,
+ heif_compression_AV1))
+ {
+ gimp_set_data (SAVE_PROC_AV1, ¶ms, sizeof (params));
+ }
+ else
+ {
+ status = GIMP_PDB_EXECUTION_ERROR;
+ }
+ g_object_unref (file);
+ }
+
+ g_clear_object (&metadata);
+ }
+#endif
else
{
status = GIMP_PDB_CALLING_ERROR;
@@ -1096,11 +1217,12 @@ write_callback (struct heif_context *ctx,
}
static gboolean
-save_image (GFile *file,
- gint32 image_ID,
- gint32 drawable_ID,
- const SaveParams *params,
- GError **error)
+save_image (GFile *file,
+ gint32 image_ID,
+ gint32 drawable_ID,
+ const SaveParams *params,
+ GError **error,
+ enum heif_compression_format compression)
{
struct heif_image *image = NULL;
struct heif_context *context = heif_context_alloc ();
@@ -1110,6 +1232,7 @@ save_image (GFile *file,
struct heif_error err;
GOutputStream *output;
GeglBuffer *buffer;
+ const gchar *encoding;
const Babl *format;
const Babl *space = NULL;
guint8 *data;
@@ -1134,12 +1257,44 @@ save_image (GFile *file,
has_alpha = gimp_drawable_has_alpha (drawable_ID);
- err = heif_image_create (width, height,
- heif_colorspace_RGB,
- has_alpha ?
- heif_chroma_interleaved_RGBA :
- heif_chroma_interleaved_RGB,
- &image);
+ switch (params->save_bit_depth)
+ {
+ case 8:
+ err = heif_image_create (width, height,
+ heif_colorspace_RGB,
+ has_alpha ?
+ heif_chroma_interleaved_RGBA :
+ heif_chroma_interleaved_RGB,
+ &image);
+ break;
+#if LIBHEIF_HAVE_VERSION(1,8,0)
+ case 10:
+ case 12:
+#if ( G_BYTE_ORDER == G_LITTLE_ENDIAN )
+ err = heif_image_create (width, height,
+ heif_colorspace_RGB,
+ has_alpha ?
+ heif_chroma_interleaved_RRGGBBAA_LE :
+ heif_chroma_interleaved_RRGGBB_LE,
+ &image);
+#else
+ err = heif_image_create (width, height,
+ heif_colorspace_RGB,
+ has_alpha ?
+ heif_chroma_interleaved_RRGGBBAA_BE :
+ heif_chroma_interleaved_RRGGBB_BE,
+ &image);
+#endif
+ break;
+#endif
+ default:
+ g_set_error (error, G_FILE_ERROR, G_FILE_ERROR_FAILED,
+ "Unsupported bit depth: %d",
+ params->save_bit_depth);
+ heif_context_free (context);
+ return FALSE;
+ break;
+ }
if (err.code != 0)
{
@@ -1221,41 +1376,138 @@ save_image (GFile *file,
if (! space)
space = gimp_drawable_get_format (drawable_ID);
- heif_image_add_plane (image, heif_channel_interleaved,
- width, height, has_alpha ? 32 : 24);
+ if (params->save_bit_depth > 8)
+ {
+ uint16_t *data16;
+ const uint16_t *src16;
+ uint16_t *dest16;
+ gint x, y, rowentries;
+ int tmp_pixelval;
- data = heif_image_get_plane (image, heif_channel_interleaved, &stride);
+ if (has_alpha)
+ {
+ rowentries = width * 4;
- buffer = gimp_drawable_get_buffer (drawable_ID);
+ if (out_linear)
+ encoding = "RGBA u16";
+ else
+ encoding = "R'G'B'A u16";
+ }
+ else /* no alpha */
+ {
+ rowentries = width * 3;
- if (has_alpha)
- {
- if (out_linear)
- format = babl_format ("RGBA u8");
- else
- format = babl_format ("R'G'B'A u8");
+ if (out_linear)
+ encoding = "RGB u16";
+ else
+ encoding = "R'G'B' u16";
+ }
+
+ data16 = g_malloc_n (height, rowentries * 2);
+ src16 = data16;
+
+ format = babl_format_with_space (encoding, space);
+
+ buffer = gimp_drawable_get_buffer (drawable_ID);
+
+ gegl_buffer_get (buffer,
+ GEGL_RECTANGLE (0, 0, width, height),
+ 1.0, format, data16, GEGL_AUTO_ROWSTRIDE, GEGL_ABYSS_NONE);
+
+ g_object_unref (buffer);
+
+ heif_image_add_plane (image, heif_channel_interleaved,
+ width, height, params->save_bit_depth);
+
+ data = heif_image_get_plane (image, heif_channel_interleaved, &stride);
+
+ switch (params->save_bit_depth)
+ {
+ case 10:
+ for (y = 0; y < height; y++)
+ {
+ dest16 = (uint16_t *) (y * stride + data);
+ for (x = 0; x < rowentries; x++)
+ {
+ tmp_pixelval = (int) ( ( (float) (*src16) / 65535.0f) * 1023.0f + 0.5f);
+ *dest16 = CLAMP (tmp_pixelval, 0, 1023);
+ dest16++;
+ src16++;
+ }
+ }
+ break;
+ case 12:
+ for (y = 0; y < height; y++)
+ {
+ dest16 = (uint16_t *) (y * stride + data);
+ for (x = 0; x < rowentries; x++)
+ {
+ tmp_pixelval = (int) ( ( (float) (*src16) / 65535.0f) * 4095.0f + 0.5f);
+ *dest16 = CLAMP (tmp_pixelval, 0, 4095);
+ dest16++;
+ src16++;
+ }
+ }
+ break;
+ default:
+ for (y = 0; y < height; y++)
+ {
+ dest16 = (uint16_t *) (y * stride + data);
+ for (x = 0; x < rowentries; x++)
+ {
+ *dest16 = *src16;
+ dest16++;
+ src16++;
+ }
+ }
+ break;
+ }
+ g_free (data16);
}
- else
+ else /* save_bit_depth == 8 */
{
- if (out_linear)
- format = babl_format ("RGB u8");
+#if LIBHEIF_HAVE_VERSION(1,8,0)
+ heif_image_add_plane (image, heif_channel_interleaved,
+ width, height, 8);
+#else
+ /* old style settings */
+ heif_image_add_plane (image, heif_channel_interleaved,
+ width, height, has_alpha ? 32 : 24);
+#endif
+
+ data = heif_image_get_plane (image, heif_channel_interleaved, &stride);
+
+ buffer = gimp_drawable_get_buffer (drawable_ID);
+
+ if (has_alpha)
+ {
+ if (out_linear)
+ encoding = "RGBA u8";
+ else
+ encoding = "R'G'B'A u8";
+ }
else
- format = babl_format ("R'G'B' u8");
- }
- format = babl_format_with_space (babl_format_get_encoding (format), space);
+ {
+ if (out_linear)
+ encoding = "RGB u8";
+ else
+ encoding = "R'G'B' u8";
+ }
+ format = babl_format_with_space (encoding, space);
- gegl_buffer_get (buffer,
- GEGL_RECTANGLE (0, 0, width, height),
- 1.0, format, data, stride, GEGL_ABYSS_NONE);
+ gegl_buffer_get (buffer,
+ GEGL_RECTANGLE (0, 0, width, height),
+ 1.0, format, data, stride, GEGL_ABYSS_NONE);
- g_object_unref (buffer);
+ g_object_unref (buffer);
+ }
gimp_progress_update (0.33);
/* encode to HEIF file */
err = heif_context_get_encoder_for_format (context,
- heif_compression_HEVC,
+ compression,
&encoder);
if (err.code != 0)
@@ -1697,7 +1949,8 @@ save_dialog_lossless_button_toggled (GtkToggleButton *source,
}
gboolean
-save_dialog (SaveParams *params)
+save_dialog (SaveParams *params,
+ gint32 image_ID)
{
GtkWidget *dialog;
GtkWidget *main_vbox;
@@ -1709,6 +1962,11 @@ save_dialog (SaveParams *params)
GtkWidget *profile_button;
#endif
GtkWidget *quality_slider;
+#if LIBHEIF_HAVE_VERSION(1,8,0)
+ GtkWidget *table;
+ GtkWidget *label2;
+ GtkWidget *combo;
+#endif
gboolean run = FALSE;
dialog = gimp_export_dialog_new (_("HEIF"), PLUG_IN_BINARY, SAVE_PROC);
@@ -1742,6 +2000,47 @@ save_dialog (SaveParams *params)
G_CALLBACK (save_dialog_lossless_button_toggled),
hbox);
+#if LIBHEIF_HAVE_VERSION(1,8,0)
+ switch (gimp_image_get_precision (image_ID))
+ {
+ case GIMP_PRECISION_U8_LINEAR:
+ case GIMP_PRECISION_U8_GAMMA:
+ /* image is 8bit depth */
+ params->save_bit_depth = 8;
+ break;
+ default:
+ /* high bit depth */
+ if (params->save_bit_depth < 12)
+ {
+ params->save_bit_depth = 10;
+ }
+ else if (params->save_bit_depth > 12)
+ {
+ params->save_bit_depth = 12;
+ }
+ break;
+ }
+
+ table = gtk_table_new (1, 2, FALSE);
+ gtk_box_pack_start (GTK_BOX (main_vbox), table, FALSE, FALSE, 0);
+ gtk_widget_show (table);
+
+ label2 = gtk_label_new ("Bit depth: ");
+ gtk_label_set_xalign (GTK_LABEL (label2), 0.0);
+ gtk_table_attach (GTK_TABLE (table), label2, 0, 1, 0, 1,
+ GTK_FILL, GTK_FILL, 0, 0);
+ gtk_widget_show (label2);
+
+ combo = gimp_int_combo_box_new ("8 bit/channel", 8,
+ "10 bit/channel (HDR)", 10,
+ "12 bit/channel (HDR)", 12,
+ NULL);
+ gtk_table_attach (GTK_TABLE (table), combo, 1, 2, 0, 1,
+ GTK_FILL | GTK_EXPAND, GTK_FILL, 0, 0);
+ gtk_widget_show (combo);
+ gimp_int_combo_box_set_active (GIMP_INT_COMBO_BOX (combo), params->save_bit_depth);
+#endif
+
#if LIBHEIF_HAVE_VERSION(1,4,0)
profile_button = gtk_check_button_new_with_mnemonic (_("Save color _profile"));
gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (profile_button),
@@ -1760,6 +2059,9 @@ save_dialog (SaveParams *params)
{
params->quality = gtk_range_get_value (GTK_RANGE (quality_slider));
params->lossless = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (lossless_button));
+#if LIBHEIF_HAVE_VERSION(1,8,0)
+ gimp_int_combo_box_get_active (GIMP_INT_COMBO_BOX (combo), ¶ms->save_bit_depth);
+#endif
}
gtk_widget_destroy (dialog);
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]