[gimp/alxsa-jpeg-clipping-path-import] plug-ins: Load JPEG clipping paths




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]