[gimp/alxsa-jpeg-clipping-path-import] plug-ins: Load JPEG clipping paths
- From: Alx Sa <sawyeralex src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gimp/alxsa-jpeg-clipping-path-import] plug-ins: Load JPEG clipping paths
- Date: Sun, 9 Oct 2022 17:36:28 +0000 (UTC)
commit ffba7e646ecd5f9699f7277f43120a9fbcb54b93
Author: Alx Sa <cmyk student gmail com>
Date: Sun Oct 9 17:16:28 2022 +0000
plug-ins: Load JPEG clipping paths
Extracts APP13 metadata, which Photoshop uses to store a clipping path.
If it exists, the path is converted into a GimpVectors path.
plug-ins/file-jpeg/jpeg-load.c | 196 ++++++++++++++++++++++++++++++++++++++++-
plug-ins/file-jpeg/jpeg-load.h | 13 +++
2 files changed, 206 insertions(+), 3 deletions(-)
---
diff --git a/plug-ins/file-jpeg/jpeg-load.c b/plug-ins/file-jpeg/jpeg-load.c
index ef7fa4eb92..50addd3ad2 100644
--- a/plug-ins/file-jpeg/jpeg-load.c
+++ b/plug-ins/file-jpeg/jpeg-load.c
@@ -40,11 +40,17 @@
#include "jpeg-settings.h"
#include "jpeg-load.h"
-static gboolean jpeg_load_resolution (GimpImage *image,
+static gboolean jpeg_load_resolution (GimpImage *image,
struct jpeg_decompress_struct
- *cinfo);
+ *cinfo);
+
+static void jpeg_load_sanitize_comment (gchar *comment);
+
+static gboolean load_paths (GimpImage *image,
+ const guchar *data,
+ gint data_len,
+ gint start);
-static void jpeg_load_sanitize_comment (gchar *comment);
GimpImage * volatile preview_image;
@@ -139,6 +145,9 @@ load_image (GFile *file,
/* - step 2.3: tell the lib to save APP2 data (ICC profiles) */
jpeg_save_markers (&cinfo, JPEG_APP0 + 2, 0xffff);
+
+ /* - step 2.4: tell the lib to save APP13 data (clipping path) */
+ jpeg_save_markers (&cinfo, JPEG_APP0 + 13, 0xffff);
}
/* Step 3: read file parameters with jpeg_read_header() */
@@ -219,6 +228,8 @@ load_image (GFile *file,
else
{
GString *comment_buffer = NULL;
+ guchar *photoshop_data = NULL;
+ guint photoshop_len = 0;
guint8 *icc_data = NULL;
guint icc_length = 0;
@@ -266,6 +277,16 @@ load_image (GFile *file,
#ifdef GIMP_UNSTABLE
g_print ("jpeg-load: found Exif block (%d bytes)\n",
(gint) (len - sizeof (JPEG_APP_HEADER_EXIF)));
+#endif
+ }
+ else if ((marker->marker == JPEG_APP0 + 13))
+ {
+ photoshop_data = g_new (guchar, len);
+ photoshop_len = len;
+ memcpy (photoshop_data, (guchar *) marker->data, len);
+#ifdef GIMP_UNSTABLE
+ g_print ("jpeg-load: found Photoshop block (%d bytes) %s\n",
+ (gint) (len - sizeof (JPEG_APP_HEADER_EXIF)), data);
#endif
}
}
@@ -318,6 +339,29 @@ load_image (GFile *file,
g_free (icc_data);
+ /* Step 5.4: check for clipping path in APP13 markers */
+ if (photoshop_data)
+ {
+ for (int i = 0; i < ((gint) photoshop_len) - 6; i += 2)
+ {
+ if (photoshop_data[i] == '8' && photoshop_data[i + 1] == 'B' &&
+ photoshop_data[i + 2] == 'I' && photoshop_data[i + 3] == 'M')
+ {
+ gshort resource_id = (photoshop_data[i + 4] << 8)
+ + photoshop_data[i + 5];
+
+ if (resource_id == 0x07D0)
+ {
+ if (! load_paths (image, photoshop_data, photoshop_len, i + 6))
+ g_message ("Unable to load JPEG clipping path");
+
+ break;
+ }
+ }
+ }
+ g_free (photoshop_data);
+ }
+
/* Do not attach the "jpeg-save-options" parasite to the image
* because this conflicts with the global defaults (bug #75398).
*/
@@ -526,6 +570,152 @@ jpeg_load_sanitize_comment (gchar *comment)
}
}
+/* Adapted from /plug-ins/file-psd/psd-image-res-load.c */
+static gboolean
+load_paths (GimpImage *image,
+ const guchar *data,
+ gint data_len,
+ gint start)
+{
+ gint name_length;
+ gchar *name;
+ GimpVectors *vectors = NULL;
+ gint image_width;
+ gint image_height;
+ gint data_size;
+ gint16 type;
+ gdouble *controlpoints;
+ gint32 x[3];
+ gint32 y[3];
+ gint16 num_rec;
+ gint16 cntr;
+ gboolean closed;
+
+ name_length = data[start++];
+ name = g_new (gchar, name_length + 1);
+ if (data_len < (start + name_length))
+ return FALSE;
+
+ strncpy (name, (const gchar *) data + start, name_length);
+ name[name_length] = '\0';
+ start += name_length;
+
+ /* Even-length path names are padded */
+ if (name_length % 2 == 0)
+ start++;
+
+ vectors = gimp_vectors_new (image, name);
+ gimp_image_insert_vectors (image, vectors, NULL, -1);
+ g_free (name);
+
+ if (data_len < (start + 4))
+ return FALSE;
+ data_size = (data[start] << 24) + (data[start + 1] << 16)
+ + (data[start + 2] << 8) + data[start + 3];
+ start += 4;
+
+ /* First path must be 0x0006, with 24 0x00s as padding */
+ if (data_len < (start + 26))
+ return FALSE;
+ type = (data[start] << 8) + data[start + 1];
+ if (type != 6)
+ return FALSE;
+
+ start += 2;
+ for (int i = 0; i < 24; i++)
+ if (data[start + i] != 0)
+ return FALSE;
+
+ start += 24;
+ /* All path blocks are 26 bytes */
+ data_size -= 26;
+
+ image_width = gimp_image_get_width (image);
+ image_height = gimp_image_get_height (image);
+
+ /* Load the path control points */
+ while (data_size > 0 && (start + 26) < data_len)
+ {
+ type = (data[start] << 8) + data[start + 1];
+ start += 2;
+
+ /* Skip "fill" blocks */
+ if (type == PSD_PATH_FILL_RULE)
+ start += 24;
+ else if (type == PSD_PATH_FILL_INIT)
+ start += 24;
+ else if (type == PSD_PATH_CL_LEN || type == PSD_PATH_OP_LEN)
+ {
+ num_rec = (data[start] << 8) + data[start + 1];
+ start += 2;
+
+ if (type == PSD_PATH_CL_LEN)
+ closed = TRUE;
+ else
+ closed = FALSE;
+
+ cntr = 0;
+ controlpoints = g_malloc (sizeof (gdouble) * num_rec * 6);
+
+ /* Moving to next 26 byte block */
+ start += 22;
+
+ while (num_rec > 0 && (start + 26) < data_len)
+ {
+ type = (data[start] << 8) + data[start + 1];
+ start += 2;
+
+ if (type == PSD_PATH_CL_LNK ||
+ type == PSD_PATH_CL_UNLNK ||
+ type == PSD_PATH_OP_LNK ||
+ type == PSD_PATH_OP_UNLNK)
+ {
+ y[0] = (data[start] << 24) + (data[start + 1] << 16)
+ + (data[start + 2] << 8) + data[start + 3];
+ start += 4;
+ x[0] = (data[start] << 24) + (data[start + 1] << 16)
+ + (data[start + 2] << 8) + data[start + 3];
+ start += 4;
+ y[1] = (data[start] << 24) + (data[start + 1] << 16)
+ + (data[start + 2] << 8) + data[start + 3];
+ start += 4;
+ x[1] = (data[start] << 24) + (data[start + 1] << 16)
+ + (data[start + 2] << 8) + data[start + 3];
+ start += 4;
+ y[2] = (data[start] << 24) + (data[start + 1] << 16)
+ + (data[start + 2] << 8) + data[start + 3];
+ start += 4;
+ x[2] = (data[start] << 24) + (data[start + 1] << 16)
+ + (data[start + 2] << 8) + data[start + 3];
+ start += 4;
+
+ for (int i = 0; i < 3; ++i)
+ {
+ controlpoints[cntr] = x[i] / 16777216.0 * image_width;
+ cntr++;
+
+ controlpoints[cntr] = y[i] / 16777216.0 * image_height;
+ cntr++;
+ }
+ }
+ else
+ {
+ return FALSE;
+ }
+ num_rec--;
+ data_size -= 26;
+ }
+ gimp_vectors_stroke_new_from_points (vectors,
+ GIMP_VECTORS_STROKE_TYPE_BEZIER,
+ cntr, controlpoints, closed);
+ g_free (controlpoints);
+ }
+ data_size -= 26;
+ }
+
+ return TRUE;
+}
+
GimpImage *
load_thumbnail_image (GFile *file,
gint *width,
diff --git a/plug-ins/file-jpeg/jpeg-load.h b/plug-ins/file-jpeg/jpeg-load.h
index ef2332e70f..7bcfb1c28e 100644
--- a/plug-ins/file-jpeg/jpeg-load.h
+++ b/plug-ins/file-jpeg/jpeg-load.h
@@ -18,6 +18,19 @@
#ifndef __JPEG_LOAD_H__
#define __JPEG_LOAD_H__
+/* Path record types */
+typedef enum {
+ PSD_PATH_CL_LEN = 0, /* Closed sub-path length record */
+ PSD_PATH_CL_LNK = 1, /* Closed sub-path Bezier knot, linked */
+ PSD_PATH_CL_UNLNK = 2, /* Closed sub-path Bezier knot, unlinked */
+ PSD_PATH_OP_LEN = 3, /* Open sub-path length record */
+ PSD_PATH_OP_LNK = 4, /* Open sub-path Bezier knot, linked */
+ PSD_PATH_OP_UNLNK = 5, /* Open sub-path Bezier knot, unlinked */
+ PSD_PATH_FILL_RULE = 6, /* Path fill rule record */
+ PSD_PATH_CLIPBOARD = 7, /* Path clipboard record */
+ PSD_PATH_FILL_INIT = 8 /* Path initial fill record */
+} PSDpathtype;
+
GimpImage * load_image (GFile *file,
GimpRunMode runmode,
gboolean preview,
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]