[gimp] plug-ins: metadata import/export for JPEG XL format
- From: Daniel Novomeský <dnovomesky src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gimp] plug-ins: metadata import/export for JPEG XL format
- Date: Wed, 5 Oct 2022 15:37:54 +0000 (UTC)
commit eac694905fc8144389faab23f945738031b82331
Author: Daniel Novomeský <dnovomesky gmail com>
Date: Tue Oct 4 17:02:08 2022 +0200
plug-ins: metadata import/export for JPEG XL format
Changes require libjxl 0.7.0
configure.ac | 2 +-
meson.build | 2 +-
plug-ins/common/Makefile.am | 1 +
plug-ins/common/file-jpegxl.c | 441 ++++++++++++++++++++++++++++++++++++-----
plug-ins/common/meson.build | 2 +-
plug-ins/common/plugin-defs.pl | 2 +-
6 files changed, 397 insertions(+), 53 deletions(-)
---
diff --git a/configure.ac b/configure.ac
index b7f1a78a09..713feb0b0b 100644
--- a/configure.ac
+++ b/configure.ac
@@ -77,7 +77,7 @@ m4_define([json_glib_required_version], [1.2.6])
m4_define([lcms_required_version], [2.8])
m4_define([libgudev_required_version], [167])
m4_define([libheif_required_version], [1.3.2])
-m4_define([libjxl_required_version], [0.6.1])
+m4_define([libjxl_required_version], [0.7.0])
m4_define([liblzma_required_version], [5.0.0])
m4_define([libmypaint_required_version], [1.3.0])
m4_define([libpng_required_version], [1.6.25])
diff --git a/meson.build b/meson.build
index 6f5a504344..149065b6d9 100644
--- a/meson.build
+++ b/meson.build
@@ -834,7 +834,7 @@ if openjpeg.found()
MIMEtypes += [ 'image/jp2', 'image/jpeg2000', 'image/jpx', ]
endif
-jpegxl_minver = '0.6.1'
+jpegxl_minver = '0.7.0'
libjxl = dependency('libjxl',
version: '>='+jpegxl_minver,
required: get_option('jpeg-xl')
diff --git a/plug-ins/common/Makefile.am b/plug-ins/common/Makefile.am
index 85ae6be80a..095a489677 100644
--- a/plug-ins/common/Makefile.am
+++ b/plug-ins/common/Makefile.am
@@ -844,6 +844,7 @@ file_jpegxl_LDADD = \
$(GEGL_LIBS) \
$(JXL_THREADS_LIBS) \
$(JXL_LIBS) \
+ $(GEXIV2_LIBS) \
$(RT_LIBS) \
$(INTLLIBS) \
$(file_jpegxl_RC)
diff --git a/plug-ins/common/file-jpegxl.c b/plug-ins/common/file-jpegxl.c
index 8dcd013c46..7b40523a74 100644
--- a/plug-ins/common/file-jpegxl.c
+++ b/plug-ins/common/file-jpegxl.c
@@ -20,6 +20,7 @@
#include "config.h"
+#include <gexiv2/gexiv2.h>
#include <glib/gstdio.h>
#include <jxl/decode.h>
@@ -191,6 +192,18 @@ jpegxl_create_procedure (GimpPlugIn *plug_in,
_("Store ICC profile to exported JXL file"),
FALSE,
G_PARAM_READWRITE);
+
+ GIMP_PROC_ARG_BOOLEAN (procedure, "save-exif",
+ _("Save Exi_f"),
+ _("Toggle saving Exif data"),
+ gimp_export_exif (),
+ G_PARAM_READWRITE);
+
+ GIMP_PROC_ARG_BOOLEAN (procedure, "save-xmp",
+ _("Save _XMP"),
+ _("Toggle saving XMP data"),
+ gimp_export_xmp (),
+ G_PARAM_READWRITE);
}
return procedure;
@@ -300,6 +313,8 @@ load_image (GFile *file,
return NULL;
}
+ JxlDecoderCloseInput (decoder);
+
if (JxlDecoderSubscribeEvents (decoder, JXL_DEC_BASIC_INFO | JXL_DEC_COLOR_ENCODING | JXL_DEC_FULL_IMAGE)
!= JXL_DEC_SUCCESS)
{
g_set_error (error, G_FILE_ERROR, 0,
@@ -627,6 +642,208 @@ load_image (GFile *file,
{
g_object_unref (profile);
}
+
+ if (basicinfo.have_container)
+ {
+ JxlDecoderReleaseInput (decoder);
+ JxlDecoderRewind (decoder);
+
+ if (JxlDecoderSetInput (decoder, memory, inputFileSize) != JXL_DEC_SUCCESS)
+ {
+ g_printerr ("%s: JxlDecoderSetInput failed after JxlDecoderRewind\n", G_STRFUNC);
+ }
+ else
+ {
+ JxlDecoderCloseInput (decoder);
+ if (JxlDecoderSubscribeEvents (decoder, JXL_DEC_BOX) != JXL_DEC_SUCCESS)
+ {
+ g_printerr ("%s: JxlDecoderSubscribeEvents for JXL_DEC_BOX failed\n", G_STRFUNC);
+ }
+ else
+ {
+ gboolean search_exif = TRUE;
+ gboolean search_xmp = TRUE;
+ gboolean success_exif = FALSE;
+ gboolean success_xmp = FALSE;
+ JxlBoxType box_type = { 0, 0, 0, 0 };
+ GByteArray *exif_box = NULL;
+ GByteArray *xml_box = NULL;
+ size_t exif_remains = 0;
+ size_t xml_remains = 0;
+
+ while (search_exif || search_xmp)
+ {
+ status = JxlDecoderProcessInput (decoder);
+ switch (status)
+ {
+ case JXL_DEC_SUCCESS:
+ if (box_type[0] == 'E' && box_type[1] == 'x' && box_type[2] == 'i' && box_type[3] ==
'f' && search_exif)
+ {
+ exif_remains = JxlDecoderReleaseBoxBuffer (decoder);
+ g_byte_array_set_size (exif_box, exif_box->len - exif_remains);
+ success_exif = TRUE;
+ }
+ else if (box_type[0] == 'x' && box_type[1] == 'm' && box_type[2] == 'l' && box_type[3]
== ' ' && search_xmp)
+ {
+ xml_remains = JxlDecoderReleaseBoxBuffer (decoder);
+ g_byte_array_set_size (xml_box, xml_box->len - xml_remains);
+ success_xmp = TRUE;
+ }
+
+ search_exif = FALSE;
+ search_xmp = FALSE;
+ break;
+ case JXL_DEC_ERROR:
+ search_exif = FALSE;
+ search_xmp = FALSE;
+ g_printerr ("%s: Metadata decoding error\n", G_STRFUNC);
+ break;
+ case JXL_DEC_NEED_MORE_INPUT:
+ search_exif = FALSE;
+ search_xmp = FALSE;
+ g_printerr ("%s: JXL metadata are probably incomplete\n", G_STRFUNC);
+ break;
+ case JXL_DEC_BOX:
+ JxlDecoderSetDecompressBoxes (decoder, JXL_TRUE);
+
+ if (box_type[0] == 'E' && box_type[1] == 'x' && box_type[2] == 'i' && box_type[3] ==
'f' && search_exif && exif_box)
+ {
+ exif_remains = JxlDecoderReleaseBoxBuffer (decoder);
+ g_byte_array_set_size (exif_box, exif_box->len - exif_remains);
+
+ search_exif = FALSE;
+ success_exif = TRUE;
+ }
+ else if (box_type[0] == 'x' && box_type[1] == 'm' && box_type[2] == 'l' && box_type[3]
== ' ' && search_xmp && xml_box)
+ {
+ xml_remains = JxlDecoderReleaseBoxBuffer (decoder);
+ g_byte_array_set_size (xml_box, xml_box->len - xml_remains);
+
+ search_xmp = FALSE;
+ success_xmp = TRUE;
+ }
+
+ if (JxlDecoderGetBoxType (decoder, box_type, JXL_TRUE) == JXL_DEC_SUCCESS)
+ {
+ if (box_type[0] == 'E' && box_type[1] == 'x' && box_type[2] == 'i' && box_type[3]
== 'f' && search_exif)
+ {
+ exif_box = g_byte_array_sized_new (4096);
+ g_byte_array_set_size (exif_box, 4096);
+
+ JxlDecoderSetBoxBuffer (decoder, exif_box->data, exif_box->len);
+ }
+ else if (box_type[0] == 'x' && box_type[1] == 'm' && box_type[2] == 'l' &&
box_type[3] == ' ' && search_xmp)
+ {
+ xml_box = g_byte_array_sized_new (4096);
+ g_byte_array_set_size (xml_box, 4096);
+
+ JxlDecoderSetBoxBuffer (decoder, xml_box->data, xml_box->len);
+ }
+ }
+ else
+ {
+ search_exif = FALSE;
+ search_xmp = FALSE;
+ g_printerr ("%s: Error in JxlDecoderGetBoxType\n", G_STRFUNC);
+ }
+ break;
+ case JXL_DEC_BOX_NEED_MORE_OUTPUT:
+ if (box_type[0] == 'E' && box_type[1] == 'x' && box_type[2] == 'i' && box_type[3] ==
'f' && search_exif)
+ {
+ exif_remains = JxlDecoderReleaseBoxBuffer (decoder);
+ g_byte_array_set_size (exif_box, exif_box->len + 4096);
+ JxlDecoderSetBoxBuffer (decoder, exif_box->data + exif_box->len - (4096 +
exif_remains), 4096 + exif_remains);
+ }
+ else if (box_type[0] == 'x' && box_type[1] == 'm' && box_type[2] == 'l' && box_type[3]
== ' ' && search_xmp)
+ {
+ xml_remains = JxlDecoderReleaseBoxBuffer (decoder);
+ g_byte_array_set_size (xml_box, xml_box->len + 4096);
+ JxlDecoderSetBoxBuffer (decoder, xml_box->data + xml_box->len - (4096 +
xml_remains), 4096 + xml_remains);
+ }
+ else
+ {
+ search_exif = FALSE;
+ search_xmp = FALSE;
+ }
+ break;
+ default:
+ break;
+ }
+ }
+
+ if (success_exif || success_xmp)
+ {
+ GimpMetadata *metadata = gimp_metadata_new ();
+
+ if (success_exif && exif_box)
+ {
+ const guint8 tiffHeaderBE[4] = { 'M', 'M', 0, 42 };
+ const guint8 tiffHeaderLE[4] = { 'I', 'I', 42, 0 };
+ const guint8 *tiffheader = exif_box->data;
+ glong new_exif_size = exif_box->len;
+
+ while (new_exif_size >= 4) /*Searching for TIFF Header*/
+ {
+ if (tiffheader[0] == tiffHeaderBE[0] && tiffheader[1] == tiffHeaderBE[1] &&
+ tiffheader[2] == tiffHeaderBE[2] && tiffheader[3] == tiffHeaderBE[3])
+ {
+ break;
+ }
+ if (tiffheader[0] == tiffHeaderLE[0] && tiffheader[1] == tiffHeaderLE[1] &&
+ tiffheader[2] == tiffHeaderLE[2] && tiffheader[3] == tiffHeaderLE[3])
+ {
+ break;
+ }
+ new_exif_size--;
+ tiffheader++;
+ }
+
+ if (new_exif_size > 4) /* TIFF header + some data found*/
+ {
+ if (! gexiv2_metadata_open_buf (GEXIV2_METADATA (metadata), tiffheader,
new_exif_size, error))
+ {
+ g_printerr ("%s: Failed to set EXIF metadata: %s\n", G_STRFUNC,
(*error)->message);
+ g_clear_error (error);
+ }
+ }
+ else
+ {
+ g_printerr ("%s: EXIF metadata not set\n", G_STRFUNC);
+ }
+ }
+
+ if (success_xmp && xml_box)
+ {
+ if (! gimp_metadata_set_from_xmp (metadata, xml_box->data, xml_box->len, error))
+ {
+ g_printerr ("%s: Failed to set XMP metadata: %s\n", G_STRFUNC, (*error)->message);
+ g_clear_error (error);
+ }
+ }
+
+ gexiv2_metadata_try_set_orientation (GEXIV2_METADATA (metadata),
+ GEXIV2_ORIENTATION_NORMAL, NULL);
+ gexiv2_metadata_try_set_metadata_pixel_width (GEXIV2_METADATA (metadata),
+ basicinfo.xsize, NULL);
+ gexiv2_metadata_try_set_metadata_pixel_height (GEXIV2_METADATA (metadata),
+ basicinfo.ysize, NULL);
+ gimp_image_metadata_load_finish (image, "image/jxl", metadata,
+ GIMP_METADATA_LOAD_COMMENT |
GIMP_METADATA_LOAD_RESOLUTION);
+ }
+
+ if (exif_box)
+ {
+ g_byte_array_free (exif_box, TRUE);
+ }
+
+ if (xml_box)
+ {
+ g_byte_array_free (xml_box, TRUE);
+ }
+ }
+ }
+ }
+
JxlThreadParallelRunnerDestroy (runner);
JxlDecoderDestroy (decoder);
g_free (memory);
@@ -674,47 +891,49 @@ jpegxl_load (GimpProcedure *procedure,
return return_vals;
}
-
static gboolean
-save_image (GFile *file,
- GimpProcedureConfig *config,
- GimpImage *image,
- GimpDrawable *drawable,
- GError **error)
+save_image (GFile *file,
+ GimpProcedureConfig *config,
+ GimpImage *image,
+ GimpDrawable *drawable,
+ GimpMetadata *metadata,
+ GError **error)
{
- JxlEncoder *encoder;
- void *runner;
- JxlEncoderOptions *encoder_options;
- JxlPixelFormat pixel_format;
- JxlBasicInfo output_info;
- JxlColorEncoding color_profile;
- JxlEncoderStatus status;
- size_t buffer_size;
-
- GByteArray *compressed;
-
- FILE *outfile;
- GeglBuffer *buffer;
- GimpImageType drawable_type;
-
- gint drawable_width;
- gint drawable_height;
- gpointer picture_buffer;
-
- GimpColorProfile *profile = NULL;
- const Babl *file_format = NULL;
- const Babl *space = NULL;
- gboolean out_linear = FALSE;
-
- size_t offset = 0;
- uint8_t *next_out;
- size_t avail_out;
-
- gdouble compression = 1.0;
- gboolean lossless = FALSE;
- gint speed = 7;
- gint bit_depth = 8;
- gboolean uses_original_profile = FALSE;
+ JxlEncoder *encoder;
+ void *runner;
+ JxlEncoderFrameSettings *encoder_options;
+ JxlPixelFormat pixel_format;
+ JxlBasicInfo output_info;
+ JxlColorEncoding color_profile;
+ JxlEncoderStatus status;
+ size_t buffer_size;
+
+ GByteArray *compressed;
+
+ FILE *outfile;
+ GeglBuffer *buffer;
+ GimpImageType drawable_type;
+
+ gint drawable_width;
+ gint drawable_height;
+ gpointer picture_buffer;
+
+ GimpColorProfile *profile = NULL;
+ const Babl *file_format = NULL;
+ const Babl *space = NULL;
+ gboolean out_linear = FALSE;
+
+ size_t offset = 0;
+ uint8_t *next_out;
+ size_t avail_out;
+
+ gdouble compression = 1.0;
+ gboolean lossless = FALSE;
+ gint speed = 7;
+ gint bit_depth = 8;
+ gboolean uses_original_profile = FALSE;
+ gboolean save_exif = FALSE;
+ gboolean save_xmp = FALSE;
gimp_progress_init_printf (_("Exporting '%s'"),
gimp_file_get_utf8_name (file));
@@ -725,6 +944,8 @@ save_image (GFile *file,
"speed", &speed,
"save-bit-depth", &bit_depth,
"uses-original-profile", &uses_original_profile,
+ "save-exif", &save_exif,
+ "save-xmp", &save_xmp,
NULL);
if (lossless)
@@ -792,7 +1013,6 @@ save_image (GFile *file,
output_info.xsize = drawable_width;
output_info.ysize = drawable_height;
output_info.exponent_bits_per_sample = 0;
- output_info.intensity_target = 255.0f;
output_info.orientation = JXL_ORIENT_IDENTITY;
output_info.animation.tps_numerator = 10;
output_info.animation.tps_denominator = 1;
@@ -911,6 +1131,22 @@ save_image (GFile *file,
return FALSE;
}
+ if ( (output_info.bits_per_sample > 12 && (output_info.uses_original_profile || output_info.alpha_bits >
12)) || (metadata && (save_exif || save_xmp)))
+ {
+ output_info.have_container = JXL_TRUE;
+ JxlEncoderUseContainer (encoder, JXL_TRUE);
+
+ if (output_info.bits_per_sample > 12 && (output_info.uses_original_profile || output_info.alpha_bits >
12))
+ {
+ JxlEncoderSetCodestreamLevel (encoder, 10);
+ }
+
+ if (metadata && (save_exif || save_xmp))
+ {
+ JxlEncoderUseBoxes (encoder);
+ }
+ }
+
runner = JxlThreadParallelRunnerCreate (NULL, gimp_get_num_processors ());
if (JxlEncoderSetParallelRunner (encoder, JxlThreadParallelRunner, runner) != JXL_ENC_SUCCESS)
{
@@ -981,23 +1217,23 @@ save_image (GFile *file,
}
}
- encoder_options = JxlEncoderOptionsCreate (encoder, NULL);
+ encoder_options = JxlEncoderFrameSettingsCreate (encoder, NULL);
if (lossless)
{
- JxlEncoderOptionsSetDistance (encoder_options, 0);
- JxlEncoderOptionsSetLossless (encoder_options, JXL_TRUE);
+ JxlEncoderSetFrameDistance (encoder_options, 0);
+ JxlEncoderSetFrameLossless (encoder_options, JXL_TRUE);
}
else
{
- JxlEncoderOptionsSetDistance (encoder_options, compression);
- JxlEncoderOptionsSetLossless (encoder_options, JXL_FALSE);
+ JxlEncoderSetFrameDistance (encoder_options, compression);
+ JxlEncoderSetFrameLossless (encoder_options, JXL_FALSE);
}
- status = JxlEncoderOptionsSetEffort (encoder_options, speed);
+ status = JxlEncoderFrameSettingsSetOption (encoder_options, JXL_ENC_FRAME_SETTING_EFFORT, speed);
if (status != JXL_ENC_SUCCESS)
{
- g_printerr ("JxlEncoderOptionsSetEffort failed to set effort %d", speed);
+ g_printerr ("JxlEncoderFrameSettingsSetOption failed to set effort %d", speed);
}
gimp_progress_update (0.5);
@@ -1013,6 +1249,104 @@ save_image (GFile *file,
return FALSE;
}
+ gimp_progress_update (0.65);
+
+ if (metadata && (save_exif || save_xmp))
+ {
+ GimpMetadata *filtered_metadata;
+ GimpMetadataSaveFlags metadata_flags = 0;
+
+ if (save_exif)
+ {
+ metadata_flags |= GIMP_METADATA_SAVE_EXIF;
+ }
+
+ if (save_xmp)
+ {
+ metadata_flags |= GIMP_METADATA_SAVE_XMP;
+ }
+
+ filtered_metadata = gimp_image_metadata_save_filter (image, "image/jxl", metadata, metadata_flags,
NULL, error);
+ if (! filtered_metadata)
+ {
+ if (error && *error)
+ {
+ g_printerr ("%s: error filtering metadata: %s",
+ G_STRFUNC, (*error)->message);
+ g_clear_error (error);
+ }
+ }
+ else
+ {
+ GExiv2Metadata *filtered_g2metadata = GEXIV2_METADATA (filtered_metadata);
+
+ /* EXIF metadata */
+ if (save_exif && gexiv2_metadata_has_exif (filtered_g2metadata))
+ {
+ GBytes *raw_exif_data;
+
+ raw_exif_data = gexiv2_metadata_get_exif_data (filtered_g2metadata, GEXIV2_BYTE_ORDER_LITTLE,
error);
+ if (raw_exif_data)
+ {
+ gsize exif_size = 0;
+ gconstpointer exif_buffer = g_bytes_get_data (raw_exif_data, &exif_size);
+
+ if (exif_size >= 4)
+ {
+ const JxlBoxType exif_box_type = { 'E', 'x', 'i', 'f' };
+ uint8_t *content = g_new (uint8_t, exif_size + 4);
+
+ content[0] = 0;
+ content[1] = 0;
+ content[2] = 0;
+ content[3] = 0;
+ memcpy (content + 4, exif_buffer, exif_size);
+
+ if (JxlEncoderAddBox (encoder, exif_box_type, content, exif_size + 4, JXL_FALSE) !=
JXL_ENC_SUCCESS)
+ {
+ g_printerr ("%s: Failed to save EXIF metadata.\n", G_STRFUNC);
+ }
+
+ g_free (content);
+ }
+ g_bytes_unref (raw_exif_data);
+ }
+ else
+ {
+ if (error && *error)
+ {
+ g_printerr ("%s: error preparing EXIF metadata: %s",
+ G_STRFUNC, (*error)->message);
+ g_clear_error (error);
+ }
+ }
+ }
+
+ /* XMP metadata */
+ if (save_xmp && gexiv2_metadata_has_xmp (filtered_g2metadata))
+ {
+ gchar *xmp_packet;
+
+ xmp_packet = gexiv2_metadata_try_generate_xmp_packet (filtered_g2metadata,
GEXIV2_USE_COMPACT_FORMAT | GEXIV2_OMIT_ALL_FORMATTING, 0, NULL);
+ if (xmp_packet)
+ {
+ int xmp_size = strlen (xmp_packet);
+ if (xmp_size > 0)
+ {
+ const JxlBoxType xml_box_type = { 'x', 'm', 'l', ' ' };
+ if (JxlEncoderAddBox (encoder, xml_box_type, (const uint8_t *) xmp_packet, xmp_size,
JXL_FALSE) != JXL_ENC_SUCCESS)
+ {
+ g_printerr ("%s: Failed to save XMP metadata.\n", G_STRFUNC);
+ }
+ }
+ g_free (xmp_packet);
+ }
+ }
+
+ g_object_unref (filtered_metadata);
+ }
+ }
+
JxlEncoderCloseInput (encoder);
gimp_progress_update (0.7);
@@ -1139,6 +1473,7 @@ save_dialog (GimpImage *image,
"lossless", "compression",
"speed", "save-bit-depth",
"uses-original-profile",
+ "save-exif", "save-xmp",
NULL);
run = gimp_procedure_dialog_run (GIMP_PROCEDURE_DIALOG (dialog));
@@ -1212,11 +1547,19 @@ jpegxl_save (GimpProcedure *procedure,
if (status == GIMP_PDB_SUCCESS)
{
- if (! save_image (file, config,
- image, drawables[0], &error))
+ GimpMetadataSaveFlags metadata_flags;
+
+ GimpMetadata *metadata = gimp_image_metadata_save_prepare (image, "image/jxl", &metadata_flags);
+
+ if (! save_image (file, config, image, drawables[0], metadata, &error))
{
status = GIMP_PDB_EXECUTION_ERROR;
}
+
+ if (metadata)
+ {
+ g_object_unref (metadata);
+ }
}
diff --git a/plug-ins/common/meson.build b/plug-ins/common/meson.build
index 07a9e25899..c01ff92f88 100644
--- a/plug-ins/common/meson.build
+++ b/plug-ins/common/meson.build
@@ -107,7 +107,7 @@ endif
if libjxl.found() and libjxl_threads.found()
common_plugins_list += {
'name': 'file-jpegxl',
- 'deps': [ gtk3, gegl, libjxl, libjxl_threads, ],
+ 'deps': [ gtk3, gegl, gexiv2, libjxl, libjxl_threads, ],
}
endif
diff --git a/plug-ins/common/plugin-defs.pl b/plug-ins/common/plugin-defs.pl
index 140fba934c..2d0aaf2f03 100644
--- a/plug-ins/common/plugin-defs.pl
+++ b/plug-ins/common/plugin-defs.pl
@@ -32,7 +32,7 @@
'file-heif' => { ui => 1, optional => 1, gegl => 1, libdep => 'GEXIV2:LCMS', libs => 'LIBHEIF_LIBS',
cflags => 'LIBHEIF_CFLAGS' },
'file-html-table' => { ui => 1, gegl => 1 },
'file-jp2-load' => { ui => 1, optional => 1, gegl => 1, libs => 'OPENJPEG_LIBS', cflags =>
'OPENJPEG_CFLAGS' },
- 'file-jpegxl' => { ui => 1, optional => 1, gegl => 1, libdep => 'JXL:JXL_THREADS', cflags =>
'JXL_CFLAGS' },
+ 'file-jpegxl' => { ui => 1, optional => 1, gegl => 1, libdep => 'GEXIV2:JXL:JXL_THREADS', cflags =>
'JXL_CFLAGS' },
'file-mng' => { ui => 1, gegl => 1, optional => 1, libs => 'MNG_LIBS', cflags => 'MNG_CFLAGS' },
'file-pat' => { ui => 1, gegl => 1 },
'file-pcx' => { ui => 1, gegl => 1 },
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]