Update upstream

This commit is contained in:
2023-12-02 11:03:24 -08:00
parent 84ea557696
commit d472f6348d
129 changed files with 17814 additions and 14162 deletions

View File

@ -352,7 +352,8 @@ static struct
gint delayTime;
gint inputFlag;
gint disposal;
} Gif89 = { -1, -1, -1, 0 };
gint num_loops;
} Gif89 = { -1, -1, -1, 0, -1 };
static void read_error (const gchar *error_type,
PikaImage *image,
@ -399,9 +400,10 @@ load_image (GFile *file,
gint grayScale;
gboolean useGlobalColormap;
gint bitPixel;
gint imageCount = 0;
PikaImage *image = NULL;
gint imageCount = 0;
PikaImage *image = NULL;
gboolean status;
gboolean saved_parasite = FALSE;
pika_progress_init_printf (_("Opening '%s'"),
pika_file_get_utf8_name (file));
@ -590,6 +592,25 @@ load_image (GFile *file,
/* If we are loading a thumbnail, we stop after the first frame. */
if (thumbnail)
break;
/* If there is more than one frame, we add a parasite so that
* we know to export as an animation on overwrite */
if (Gif89.num_loops > -1 && ! saved_parasite)
{
PikaParasite *parasite;
gchar *str;
str = g_strdup_printf ("%d", Gif89.num_loops);
parasite = pika_parasite_new ("gif/animated",
PIKA_PARASITE_PERSISTENT,
strlen (str) + 1,
(gpointer) str);
g_free (str);
pika_image_attach_parasite (image, parasite);
pika_parasite_free (parasite);
saved_parasite = TRUE;
}
}
fclose (fd);
@ -690,6 +711,15 @@ DoExtension (FILE *fd,
#ifdef GIFDEBUG
str = "Application Extension";
#endif
/* Animation block */
if (GetDataBlock (fd, (guchar *) buf))
{
if (strncmp ((const gchar *) buf, "NETSCAPE2.0", 8) == 0)
{
if (GetDataBlock (fd, (guchar *) buf))
Gif89.num_loops = (buf[0] << 8) | buf[1];
}
}
break;
case 0xfe: /* Comment Extension */
#ifdef GIFDEBUG

View File

@ -268,6 +268,7 @@ gif_save (PikaProcedure *procedure,
PikaExportReturn export = PIKA_EXPORT_CANCEL;
PikaImage *orig_image;
PikaImage *sanitized_image = NULL;
PikaParasite *parasite = NULL;
GError *error = NULL;
gegl_init (NULL, NULL);
@ -288,6 +289,34 @@ gif_save (PikaProcedure *procedure,
*/
sanitized_image = image;
/* If imported as an animation, set the animation configurations
* when overwriting the file */
parasite = pika_image_get_parasite (image, "gif/animated");
if (parasite)
{
gint num_loops;
gchar *parasite_data;
guint32 parasite_size;
parasite_data = (gchar *) pika_parasite_get_data (parasite, &parasite_size);
parasite_data = g_strndup (parasite_data, parasite_size);
if (sscanf (parasite_data, "%i", &num_loops) == 1)
{
gboolean loop = (num_loops == 0);
g_object_set (config,
"as-animation", TRUE,
"loop", loop,
"number-of-repeats", num_loops,
NULL);
}
pika_image_detach_parasite (image, "gif/animated");
g_free (parasite);
g_free (parasite_data);
}
if (run_mode == PIKA_RUN_INTERACTIVE)
{
if (! save_dialog (image, procedure, G_OBJECT (config)))

View File

@ -31,6 +31,7 @@
#define LOAD_PROC "file-heif-load"
#define LOAD_PROC_AV1 "file-heif-av1-load"
#define LOAD_PROC_HEJ2 "file-heif-hej2-load"
#define SAVE_PROC "file-heif-save"
#define SAVE_PROC_AV1 "file-heif-av1-save"
#define PLUG_IN_BINARY "file-heif"
@ -103,6 +104,8 @@ static PikaValueArray * heif_av1_save (PikaProcedure *pro
#endif
static PikaImage * load_image (GFile *file,
PikaMetadata *metadata,
PikaMetadataLoadFlags *flags,
gboolean interactive,
PikaPDBStatusType *status,
GError **error);
@ -172,6 +175,13 @@ heif_init_procedures (PikaPlugIn *plug_in)
}
#endif
#if LIBHEIF_HAVE_VERSION(1,17,0)
if (heif_have_decoder_for_format (heif_compression_JPEG2000))
{
list = g_list_append (list, g_strdup (LOAD_PROC_HEJ2));
}
#endif
#if LIBHEIF_HAVE_VERSION(1,13,0)
heif_deinit ();
#endif
@ -412,6 +422,33 @@ heif_create_procedure (PikaPlugIn *plug_in,
pika_export_xmp (),
G_PARAM_READWRITE);
}
#endif
#if LIBHEIF_HAVE_VERSION(1,17,0)
else if (! strcmp (name, LOAD_PROC_HEJ2))
{
procedure = pika_load_procedure_new (plug_in, name, PIKA_PDB_PROC_TYPE_PLUGIN,
heif_load, NULL, NULL);
pika_procedure_set_menu_label (procedure, _("JPEG 2000 encapsulated in HEIF"));
pika_procedure_set_documentation (procedure,
_("Loads HEJ2 images"),
_("Load JPEG 2000 image encapsulated in HEIF (HEJ2)"),
name);
pika_procedure_set_attribution (procedure,
"Daniel Novomesky <dnovomesky@gmail.com>",
"Daniel Novomesky <dnovomesky@gmail.com>",
"2023");
pika_file_procedure_set_handles_remote (PIKA_FILE_PROCEDURE (procedure), TRUE);
pika_file_procedure_set_mime_types (PIKA_FILE_PROCEDURE (procedure),
"image/hej2k");
pika_file_procedure_set_extensions (PIKA_FILE_PROCEDURE (procedure),
"hej2");
pika_file_procedure_set_magics (PIKA_FILE_PROCEDURE (procedure),
"4,string,ftypj2ki");
}
#endif
return procedure;
}
@ -442,7 +479,7 @@ heif_load (PikaProcedure *procedure,
heif_init (NULL);
#endif
image = load_image (file, interactive, &status, &error);
image = load_image (file, metadata, flags, interactive, &status, &error);
#if LIBHEIF_HAVE_VERSION(1,13,0)
heif_deinit ();
@ -836,10 +873,12 @@ nclx_to_pika_profile (const struct heif_color_profile_nclx *nclx)
#endif
PikaImage *
load_image (GFile *file,
gboolean interactive,
PikaPDBStatusType *status,
GError **error)
load_image (GFile *file,
PikaMetadata *metadata,
PikaMetadataLoadFlags *flags,
gboolean interactive,
PikaPDBStatusType *status,
GError **error)
{
GInputStream *input;
goffset file_size;
@ -1267,117 +1306,111 @@ load_image (GFile *file,
g_object_unref (buffer);
{
size_t exif_data_size = 0;
uint8_t *exif_data = NULL;
size_t xmp_data_size = 0;
uint8_t *xmp_data = NULL;
gint n_metadata;
heif_item_id metadata_id;
if (metadata)
{
size_t exif_data_size = 0;
uint8_t *exif_data = NULL;
size_t xmp_data_size = 0;
uint8_t *xmp_data = NULL;
gint n_metadata;
heif_item_id metadata_id;
n_metadata =
heif_image_handle_get_list_of_metadata_block_IDs (handle,
"Exif",
&metadata_id, 1);
if (n_metadata > 0)
{
exif_data_size = heif_image_handle_get_metadata_size (handle,
metadata_id);
exif_data = g_alloca (exif_data_size);
n_metadata = heif_image_handle_get_list_of_metadata_block_IDs (handle, "Exif",
&metadata_id, 1);
if (n_metadata > 0)
{
exif_data_size = heif_image_handle_get_metadata_size (handle, metadata_id);
err = heif_image_handle_get_metadata (handle, metadata_id, exif_data);
if (err.code != 0)
{
exif_data = NULL;
exif_data_size = 0;
}
}
exif_data = g_alloca (exif_data_size);
n_metadata =
heif_image_handle_get_list_of_metadata_block_IDs (handle,
"mime",
&metadata_id, 1);
if (n_metadata > 0)
{
if (g_strcmp0 (
heif_image_handle_get_metadata_content_type (handle, metadata_id),
"application/rdf+xml") == 0)
{
xmp_data_size = heif_image_handle_get_metadata_size (handle,
metadata_id);
xmp_data = g_alloca (xmp_data_size);
err = heif_image_handle_get_metadata (handle, metadata_id, exif_data);
if (err.code != 0)
{
exif_data = NULL;
exif_data_size = 0;
}
}
err = heif_image_handle_get_metadata (handle, metadata_id, xmp_data);
if (err.code != 0)
{
xmp_data = NULL;
xmp_data_size = 0;
}
}
}
n_metadata = heif_image_handle_get_list_of_metadata_block_IDs (handle, "mime",
&metadata_id, 1);
if (n_metadata > 0)
{
if (g_strcmp0 (heif_image_handle_get_metadata_content_type (handle, metadata_id), "application/rdf+xml")
== 0)
{
xmp_data_size = heif_image_handle_get_metadata_size (handle, metadata_id);
if (exif_data || xmp_data)
{
PikaMetadata *metadata = pika_metadata_new ();
PikaMetadataLoadFlags flags = PIKA_METADATA_LOAD_COMMENT | PIKA_METADATA_LOAD_RESOLUTION;
xmp_data = g_alloca (xmp_data_size);
if (exif_data)
{
const guint8 tiffHeaderBE[4] = { 'M', 'M', 0, 42 };
const guint8 tiffHeaderLE[4] = { 'I', 'I', 42, 0 };
GExiv2Metadata *exif_metadata = GEXIV2_METADATA (metadata);
const guint8 *tiffheader = exif_data;
glong new_exif_size = exif_data_size;
err = heif_image_handle_get_metadata (handle, metadata_id, xmp_data);
if (err.code != 0)
{
xmp_data = NULL;
xmp_data_size = 0;
}
}
}
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 (exif_data || xmp_data)
{
gexiv2_metadata_clear (GEXIV2_METADATA (metadata));
if (new_exif_size > 4) /* TIFF header + some data found*/
{
if (! gexiv2_metadata_open_buf (exif_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 (exif_data)
{
const guint8 tiffHeaderBE[4] = { 'M', 'M', 0, 42 };
const guint8 tiffHeaderLE[4] = { 'I', 'I', 42, 0 };
GExiv2Metadata *exif_metadata = GEXIV2_METADATA (metadata);
const guint8 *tiffheader = exif_data;
glong new_exif_size = exif_data_size;
if (xmp_data)
{
if (!pika_metadata_set_from_xmp (metadata, xmp_data, xmp_data_size, error))
{
g_printerr ("%s: Failed to set XMP metadata: %s\n", G_STRFUNC, (*error)->message);
g_clear_error (error);
}
}
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++;
}
gexiv2_metadata_try_set_orientation (GEXIV2_METADATA (metadata),
GEXIV2_ORIENTATION_NORMAL, NULL);
gexiv2_metadata_try_set_metadata_pixel_width (GEXIV2_METADATA (metadata),
width, NULL);
gexiv2_metadata_try_set_metadata_pixel_height (GEXIV2_METADATA (metadata),
height, NULL);
pika_image_metadata_load_finish (image, "image/heif",
metadata, flags);
}
}
if (new_exif_size > 4) /* TIFF header + some data found*/
{
if (! gexiv2_metadata_open_buf (exif_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 (xmp_data)
{
if (! pika_metadata_set_from_xmp (metadata, xmp_data, xmp_data_size, 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), width, NULL);
gexiv2_metadata_try_set_metadata_pixel_height (GEXIV2_METADATA (metadata),
height, NULL);
*flags = PIKA_METADATA_LOAD_COMMENT | PIKA_METADATA_LOAD_RESOLUTION;
}
if (profile)
g_object_unref (profile);

View File

@ -230,10 +230,10 @@ load_image (GFile *file,
{
ILBM_Image *true_image = iff_image[i];
/* Struct representing bitmap header properties */
ILBM_BitMapHeader *bitMapHeader = true_image->bitMapHeader;
ILBM_BitMapHeader *bitMapHeader;
/* Struct containing the color palette */
ILBM_ColorMap *colorMap = true_image->colorMap;
ILBM_Viewport *camg = true_image->viewport;
ILBM_ColorMap *colorMap;
ILBM_Viewport *camg;
IFF_UByte *bitplanes;
PikaImageType image_type;
guchar pika_cmap[768]; /* Max index is (2^nplanes) - 1 */
@ -244,29 +244,38 @@ load_image (GFile *file,
gint row_length;
gint pixel_size = 1;
gint y_height = 0;
gint aspect_x = 0;
gint aspect_y = 0;
gboolean ehb_mode = FALSE;
gboolean ham_mode = FALSE;
if (! true_image || ! bitMapHeader)
if (! true_image)
{
g_message (_("Invalid or missing ILBM image"));
return image;
}
if (! true_image->body)
{
g_message (_("ILBM contains no image data - likely a palette file"));
return NULL;
}
colorMap = true_image->colorMap;
camg = true_image->viewport;
/* Convert ACBM files to ILBM format */
if (ILBM_imageIsACBM (true_image))
ILBM_convertACBMToILBM (true_image);
bitMapHeader = true_image->bitMapHeader;
if (! bitMapHeader || ! true_image->body)
{
g_message (_("ILBM contains no image data - likely a palette file"));
return NULL;
}
width = bitMapHeader->w;
height = bitMapHeader->h;
nPlanes = bitMapHeader->nPlanes;
row_length = (width + 15) / 16;
pixel_size = nPlanes / 8;
aspect_x = bitMapHeader->xAspect;
aspect_y = bitMapHeader->yAspect;
/* Check for ILBM variants in CMAG chunk */
if (camg)
@ -329,8 +338,9 @@ load_image (GFile *file,
ILBM_unpackByteRun (true_image);
image = pika_image_new (width, height,
pixel_size == 1 ? PIKA_INDEXED : PIKA_RGB);
if (! image)
image = pika_image_new (width, height,
pixel_size == 1 ? PIKA_INDEXED : PIKA_RGB);
layer = pika_layer_new (image, _("Background"), width, height,
image_type, 100,
@ -341,6 +351,24 @@ load_image (GFile *file,
bitplanes = true_image->body->chunkData;
/* Setting resolution for non-square pixel aspect ratios */
if (aspect_x != aspect_y && aspect_x > 0 && aspect_y > 0)
{
gdouble image_xres;
gdouble image_yres;
gfloat ratio = (gfloat) aspect_x / aspect_y;
g_message (_("Non-square pixels. Image might look squashed if "
"Dot for Dot mode is enabled."));
pika_image_get_resolution (image, &image_xres, &image_yres);
if (ratio < 1)
image_xres = image_yres * (1 / ratio);
else
image_yres = image_xres * ratio;
pika_image_set_resolution (image, image_xres, image_yres);
}
/* Loading rows */
for (gint j = 0; j < height; j++)
{

View File

@ -396,9 +396,11 @@ create_cmyk_layer (PikaImage *image,
}
static PikaImage *
load_image (GFile *file,
PikaRunMode runmode,
GError **error)
load_image (GFile *file,
PikaRunMode runmode,
PikaMetadata *metadata,
PikaMetadataLoadFlags *flags,
GError **error)
{
FILE *inputFile = g_fopen (g_file_peek_path (file), "rb");
@ -430,6 +432,7 @@ load_image (GFile *file,
const Babl *type;
PikaPrecision precision_linear;
PikaPrecision precision_non_linear;
uint32_t i;
if (!inputFile)
{
@ -637,7 +640,7 @@ load_image (GFile *file,
}
/* Check for extra channels */
for (gint32 i = 0; i < basicinfo.num_extra_channels; i++)
for (i = 0; i < basicinfo.num_extra_channels; i++)
{
JxlExtraChannelInfo extra;
@ -936,205 +939,208 @@ load_image (GFile *file,
g_object_unref (profile);
}
if (basicinfo.have_container)
if (metadata)
{
JxlDecoderReleaseInput (decoder);
JxlDecoderRewind (decoder);
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)
if (JxlDecoderSetInput (decoder, memory, inputFileSize) != JXL_DEC_SUCCESS)
{
g_printerr ("%s: JxlDecoderSubscribeEvents for JXL_DEC_BOX failed\n", G_STRFUNC);
g_printerr ("%s: JxlDecoderSetInput failed after JxlDecoderRewind\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)
JxlDecoderCloseInput (decoder);
if (JxlDecoderSubscribeEvents (decoder, JXL_DEC_BOX) != JXL_DEC_SUCCESS)
{
status = JxlDecoderProcessInput (decoder);
switch (status)
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)
{
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)
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_box = g_byte_array_sized_new (4096);
g_byte_array_set_size (exif_box, 4096);
JxlDecoderSetBoxBuffer (decoder, exif_box->data, exif_box->len);
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_box = g_byte_array_sized_new (4096);
g_byte_array_set_size (xml_box, 4096);
JxlDecoderSetBoxBuffer (decoder, xml_box->data, xml_box->len);
xml_remains = JxlDecoderReleaseBoxBuffer (decoder);
g_byte_array_set_size (xml_box, xml_box->len - xml_remains);
success_xmp = TRUE;
}
}
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
{
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;
}
break;
default:
break;
}
}
if (success_exif || success_xmp)
{
PikaMetadata *metadata = pika_metadata_new ();
if (success_exif && exif_box)
if (success_exif || success_xmp)
{
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;
gexiv2_metadata_clear (GEXIV2_METADATA (metadata));
while (new_exif_size >= 4) /*Searching for TIFF Header*/
if (success_exif && exif_box)
{
if (tiffheader[0] == tiffHeaderBE[0] && tiffheader[1] == tiffHeaderBE[1] &&
tiffheader[2] == tiffHeaderBE[2] && tiffheader[3] == tiffHeaderBE[3])
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*/
{
break;
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 (tiffheader[0] == tiffHeaderLE[0] && tiffheader[1] == tiffHeaderLE[1] &&
tiffheader[2] == tiffHeaderLE[2] && tiffheader[3] == tiffHeaderLE[3])
if (new_exif_size > 4) /* TIFF header + some data found*/
{
break;
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);
}
new_exif_size--;
tiffheader++;
}
if (new_exif_size > 4) /* TIFF header + some data found*/
if (success_xmp && xml_box)
{
if (! gexiv2_metadata_open_buf (GEXIV2_METADATA (metadata), tiffheader, new_exif_size, error))
if (! pika_metadata_set_from_xmp (metadata, xml_box->data, xml_box->len, error))
{
g_printerr ("%s: Failed to set EXIF metadata: %s\n", G_STRFUNC, (*error)->message);
g_printerr ("%s: Failed to set XMP 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 (exif_box)
{
if (! pika_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);
}
g_byte_array_free (exif_box, TRUE);
}
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);
pika_image_metadata_load_finish (image, "image/jxl", metadata,
PIKA_METADATA_LOAD_COMMENT | PIKA_METADATA_LOAD_RESOLUTION);
}
if (exif_box)
{
g_byte_array_free (exif_box, TRUE);
}
if (xml_box)
{
g_byte_array_free (xml_box, TRUE);
if (xml_box)
{
g_byte_array_free (xml_box, TRUE);
}
}
}
}
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);
*flags = PIKA_METADATA_LOAD_COMMENT | PIKA_METADATA_LOAD_RESOLUTION;
}
JxlThreadParallelRunnerDestroy (runner);
@ -1158,7 +1164,7 @@ jpegxl_load (PikaProcedure *procedure,
gegl_init (NULL, NULL);
image = load_image (file, run_mode, &error);
image = load_image (file, run_mode, metadata, flags, &error);
if (! image)
return pika_procedure_new_return_values (procedure,

View File

@ -886,12 +886,13 @@ gui_single (PikaProcedure *procedure,
PikaProcedureConfig *config,
PikaImage *image)
{
GtkWidget *window;
GtkWidget *widget;
GList *missing_fonts;
GList *dialog_props = NULL;
gboolean run;
gint32 n_layers;
GtkWidget *window;
GtkWidget *widget;
PikaLayer **layers;
GList *missing_fonts;
GList *dialog_props = NULL;
gboolean run;
gint32 n_layers;
pika_ui_init (PLUG_IN_BINARY);
@ -910,7 +911,13 @@ gui_single (PikaProcedure *procedure,
widget = pika_procedure_dialog_fill_frame (PIKA_PROCEDURE_DIALOG (window),
"pages-frame", "layers-as-pages", FALSE,
"pages-box");
g_free (pika_image_get_layers (multi_page.images[0], &n_layers));
/* Enable "layers-as-pages" if more than one layer, or there's a single
* layer group has more than one layer */
layers = pika_image_get_layers (multi_page.images[0], &n_layers);
if (n_layers == 1 && pika_item_is_group (PIKA_ITEM (layers[0])))
g_free (pika_item_get_children (PIKA_ITEM (layers[0]), &n_layers));
g_free (layers);
gtk_widget_set_sensitive (widget, n_layers > 1);
/* Warning for missing fonts (non-embeddable with rasterization

View File

@ -65,6 +65,7 @@ typedef enum _PngExportformat
PNG_FORMAT_GRAYA16
} PngExportFormat;
static GSList *safe_to_copy_chunks;
typedef struct _Png Png;
typedef struct _PngClass PngClass;
@ -139,6 +140,9 @@ static gboolean ia_has_transparent_pixels (GeglBuffer *buffer);
static gint find_unused_ia_color (GeglBuffer *buffer,
gint *colors);
static gint read_unknown_chunk (png_structp png_ptr,
png_unknown_chunkp chunk);
G_DEFINE_TYPE (Png, png, PIKA_TYPE_PLUG_IN)
@ -602,6 +606,7 @@ load_image (GFile *file,
const Babl *file_format; /* BABL format for layer */
png_structp pp; /* PNG read pointer */
png_infop info; /* PNG info pointers */
png_voidp user_chunkp; /* PNG unknown chunk pointer */
guchar **pixels; /* Pixel rows */
guchar *pixel; /* Pixel data */
guchar alpha[256]; /* Index -> Alpha */
@ -609,6 +614,8 @@ load_image (GFile *file,
gint num_texts;
struct read_error_data error_data;
safe_to_copy_chunks = NULL;
pp = png_create_read_struct (PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
if (! pp)
{
@ -667,6 +674,11 @@ load_image (GFile *file,
png_init_io (pp, fp);
png_set_compression_buffer_size (pp, 512);
/* Set up callback to save "safe to copy" chunks */
png_set_keep_unknown_chunks (pp, PNG_HANDLE_CHUNK_IF_SAFE, NULL, 0);
user_chunkp = png_get_user_chunk_ptr (pp);
png_set_read_user_chunk_fn (pp, user_chunkp, read_unknown_chunk);
/*
* Get the image info
*/
@ -1184,6 +1196,23 @@ load_image (GFile *file,
g_object_unref (buffer);
}
/* If any safe-to-copy chunks were saved,
* store them in the image as parasite */
if (safe_to_copy_chunks)
{
GSList *iter;
for (iter = safe_to_copy_chunks; iter; iter = iter->next)
{
PikaParasite *parasite = iter->data;
pika_image_attach_parasite ((PikaImage *) image, parasite);
pika_parasite_free (parasite);
}
g_slist_free (safe_to_copy_chunks);
}
return (PikaImage *) image;
}
@ -1292,6 +1321,7 @@ save_image (GFile *file,
gint num; /* Number of rows to load */
FILE *fp; /* File pointer */
PikaColorProfile *profile = NULL; /* Color profile */
gchar **parasites; /* Safe-to-copy chunks */
gboolean out_linear; /* Save linear RGB */
GeglBuffer *buffer; /* GEGL buffer for layer */
const Babl *file_format = NULL; /* BABL format of file */
@ -1914,6 +1944,51 @@ save_image (GFile *file,
if (G_BYTE_ORDER == G_LITTLE_ENDIAN)
png_set_swap (pp);
/* Write any safe-to-copy chunks saved from import */
parasites = pika_image_get_parasite_list (image);
if (parasites)
{
gint count;
count = g_strv_length (parasites);
for (gint i = 0; i < count; i++)
{
if (strncmp (parasites[i], "png", 3) == 0)
{
PikaParasite *parasite;
parasite = pika_image_get_parasite (image, parasites[i]);
if (parasite)
{
gchar buf[1024];
gchar *chunk_name;
g_strlcpy (buf, parasites[i], sizeof (buf));
chunk_name = strchr (buf, '/');
chunk_name++;
if (chunk_name)
{
png_byte name[4];
const guint8 *data;
guint32 len;
for (gint j = 0; j < 4; j++)
name[j] = chunk_name[j];
data = (const guint8 *) pika_parasite_get_data (parasite, &len);
png_write_chunk (pp, name, data, len);
}
pika_parasite_free (parasite);
}
}
}
}
g_strfreev (parasites);
/*
* Turn on interlace handling...
*/
@ -2263,6 +2338,28 @@ respin_cmap (png_structp pp,
return get_bit_depth_for_palette (colors);
}
static gint
read_unknown_chunk (png_structp png_ptr,
png_unknown_chunkp chunk)
{
/* Chunks with a lowercase letter in the 4th byte
* are safe to copy */
if (g_ascii_islower (chunk->name[3]))
{
PikaParasite *parasite;
gchar pname[255];
g_snprintf (pname, sizeof (pname), "png/%s", chunk->name);
if ((parasite = pika_parasite_new (pname,
PIKA_PARASITE_PERSISTENT,
chunk->size, chunk->data)))
safe_to_copy_chunks = g_slist_prepend (safe_to_copy_chunks, parasite);
}
return 0;
}
static gboolean
save_dialog (PikaImage *image,
PikaProcedure *procedure,

View File

@ -944,7 +944,7 @@ process_pam_header (PNMScanner *scan,
{
/* skip unknown headers but recognize xv's thumbnail format */
CHECK_FOR_ERROR (g_ascii_isdigit (*buf), pnminfo->jmpbuf,
_("PAM: Unsupported inofficial PNM variant."));
_("PAM: Unsupported unofficial PNM variant."));
}
}
CHECK_FOR_ERROR (pnmscanner_eof (scan), pnminfo->jmpbuf,

View File

@ -77,7 +77,7 @@ struct _LicClass
#define LIC_TYPE (lic_get_type ())
#define LIC (obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), LIC_TYPE, Lic))
#define LIC(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), LIC_TYPE, Lic))
GType lic_get_type (void) G_GNUC_CONST;