[gimp/alxsa-psd-paths-export] plug-ins: Export PSD with paths




commit f27dca441b611ee6259c9ea1e14656fbd111eeb0
Author: Alx Sa <cmyk student gmail com>
Date:   Fri Sep 30 18:27:17 2022 +0000

    plug-ins: Export PSD with paths
    
    Ports PSD path export from file-tiff-save.c so that paths are carried
    over in PSD project files as well.

 plug-ins/file-psd/psd-save.c | 172 +++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 172 insertions(+)
---
diff --git a/plug-ins/file-psd/psd-save.c b/plug-ins/file-psd/psd-save.c
index 2b942e0b4d..0392b233f3 100644
--- a/plug-ins/file-psd/psd-save.c
+++ b/plug-ins/file-psd/psd-save.c
@@ -145,6 +145,9 @@ static void          save_resources       (GOutputStream  *output,
                                            gboolean        export_cmyk,
                                            gboolean        export_duotone);
 
+static void          save_paths           (GOutputStream  *output,
+                                           GimpImage      *image);
+
 static void          save_layer_and_mask  (GOutputStream  *output,
                                            GimpImage      *image,
                                            gboolean        export_cmyk);
@@ -153,6 +156,9 @@ static void          save_data            (GOutputStream  *output,
                                            GimpImage      *image,
                                            gboolean        export_cmyk);
 
+static void          double_to_psd_fixed  (gdouble         value,
+                                           gchar          *target);
+
 static void          xfwrite              (GOutputStream  *output,
                                            gconstpointer   buf,
                                            gsize           len,
@@ -844,6 +850,9 @@ save_resources (GOutputStream  *output,
                         (int) sizeof (gint16));
     }
 
+  /* --------------- Write paths ------------------- */
+  save_paths (output, image);
+
   /* --------------- Write resolution data ------------------- */
   {
     gdouble  xres = 0, yres = 0;
@@ -1066,6 +1075,169 @@ get_compress_channel_data (guchar  *channel_data,
   return len;
 }
 
+/* Ported /from plug-ins/file-tiff/file-tiff-save.c */
+static void
+double_to_psd_fixed (gdouble  value,
+                     gchar   *target)
+{
+  gdouble in, frac;
+  gint    i, f;
+
+  frac = modf (value, &in);
+  if (frac < 0)
+    {
+      in -= 1;
+      frac += 1;
+    }
+
+  i = (gint) CLAMP (in, -16, 15);
+  f = CLAMP ((gint) (frac * 0xFFFFFF), 0, 0xFFFFFF);
+
+  target[0] = i & 0xFF;
+  target[1] = (f >> 16) & 0xFF;
+  target[2] = (f >>  8) & 0xFF;
+  target[3] = f & 0xFF;
+}
+
+/* Ported from /plug-ins/file-tiff/file-tiff-save.c */
+static void
+save_paths (GOutputStream  *output,
+            GimpImage      *image)
+{
+  gshort  id     = 0x07D0; /* Photoshop paths have IDs >= 2000 */
+  gdouble width  = gimp_image_get_width (image);
+  gdouble height = gimp_image_get_height (image);
+  GList  *vectors;
+  GList  *iter;
+  gint    v;
+  gint    num_strokes;
+  gint   *strokes;
+  gint    s;
+
+  vectors = gimp_image_list_vectors (image);
+
+  if (! vectors)
+    return;
+
+  /* Only up to 997 paths supported */
+  for (iter = vectors, v = 0;
+       iter && v <= 997;
+       iter = g_list_next (iter), v++)
+    {
+      GString *data;
+      gchar   *name, *nameend;
+      gsize    len;
+      gint     lenpos;
+      gchar    pointrecord[26] = { 0, };
+      gchar   *tmpname;
+      GError  *err = NULL;
+
+      data = g_string_new ("8BIM");
+      g_string_append_c (data, id / 256);
+      g_string_append_c (data, id % 256);
+
+      /*
+       * - use iso8859-1 if possible
+       * - otherwise use UTF-8, prepended with \xef\xbb\xbf (Byte-Order-Mark)
+       */
+      name = gimp_item_get_name (iter->data);
+      tmpname = g_convert (name, -1, "iso8859-1", "utf-8", NULL, &len, &err);
+
+      if (tmpname && err == NULL)
+        {
+          g_string_append_c (data, MIN (len, 255));
+          g_string_append_len (data, tmpname, MIN (len, 255));
+          g_free (tmpname);
+        }
+      else
+        {
+          /* conversion failed, we fall back to UTF-8 */
+          len = g_utf8_strlen (name, 255 - 3);  /* need three marker-bytes */
+
+          nameend = g_utf8_offset_to_pointer (name, len);
+          len = nameend - name; /* in bytes */
+          g_assert (len + 3 <= 255);
+
+          g_string_append_c (data, len + 3);
+          g_string_append_len (data, "\xEF\xBB\xBF", 3); /* Unicode 0xfeff */
+          g_string_append_len (data, name, len);
+
+          if (tmpname)
+            g_free (tmpname);
+        }
+
+      if (data->len % 2)  /* padding to even size */
+        g_string_append_c (data, 0);
+      g_free (name);
+
+      lenpos = data->len;
+      g_string_append_len (data, "\0\0\0\0", 4); /* will be filled in later */
+      len = data->len; /* to calculate the data size later */
+
+      pointrecord[1] = 6;  /* fill rule record */
+      g_string_append_len (data, pointrecord, 26);
+
+      strokes = gimp_vectors_get_strokes (iter->data, &num_strokes);
+
+      for (s = 0; s < num_strokes; s++)
+        {
+          GimpVectorsStrokeType type;
+          gdouble  *points;
+          gint      num_points;
+          gboolean  closed;
+          gint      p = 0;
+
+          type = gimp_vectors_stroke_get_points (iter->data, strokes[s],
+                                                 &num_points, &points, &closed);
+
+          if (type != GIMP_VECTORS_STROKE_TYPE_BEZIER ||
+              num_points > 65535 ||
+              num_points % 6)
+            {
+              g_printerr ("psd-save: unsupported stroke type: "
+                          "%d (%d points)\n", type, num_points);
+              continue;
+            }
+
+          memset (pointrecord, 0, 26);
+          pointrecord[1] = closed ? 0 : 3;
+          pointrecord[2] = (num_points / 6) / 256;
+          pointrecord[3] = (num_points / 6) % 256;
+          g_string_append_len (data, pointrecord, 26);
+
+          for (p = 0; p < num_points; p += 6)
+            {
+              pointrecord[1] = closed ? 2 : 5;
+
+              double_to_psd_fixed (points[p+1] / height, pointrecord + 2);
+              double_to_psd_fixed (points[p+0] / width,  pointrecord + 6);
+              double_to_psd_fixed (points[p+3] / height, pointrecord + 10);
+              double_to_psd_fixed (points[p+2] / width,  pointrecord + 14);
+              double_to_psd_fixed (points[p+5] / height, pointrecord + 18);
+              double_to_psd_fixed (points[p+4] / width,  pointrecord + 22);
+
+              g_string_append_len (data, pointrecord, 26);
+            }
+        }
+
+      g_free (strokes);
+
+      /* fix up the length */
+
+      len = data->len - len;
+      data->str[lenpos + 0] = (len & 0xFF000000) >> 24;
+      data->str[lenpos + 1] = (len & 0x00FF0000) >> 16;
+      data->str[lenpos + 2] = (len & 0x0000FF00) >>  8;
+      data->str[lenpos + 3] = (len & 0x000000FF) >>  0;
+
+      xfwrite (output, data->str, data->len, "path resources data");
+      g_string_free (data, TRUE);
+      id += 0x01;
+    }
+
+  g_list_free (vectors);
+}
+
 static void
 save_layer_and_mask (GOutputStream  *output,
                      GimpImage      *image,


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]