[gimp/wip/Jehan/PSD-multi-layer] plug-ins: multi-layer support in PSD load/export.




commit 0f0e63fff314be6abbdbeefbeb8ec2f1f62a5c10
Author: Jehan <jehan girinstud io>
Date:   Sun Feb 21 23:24:54 2021 +0100

    plug-ins: multi-layer support in PSD load/export.
    
    - Store the Layer ID (lyid) block. Use GIMP's layer tattoo as a PSD
      layer ID, hence mirroring PSD load processing (we were already reading
      this block into our layer tattoos but always exporting with no ID).
    - Add support for the Layer Selection ID(s) block (0x042D) both on
      import and export in order to store and restore the multi-layer
      selection.
      We were previously using the Layer state information block (0x0400) to
      store the active layer, but it doesn't seem to be usable for multiple
      layer selection. Actually it is even doubtful if this was working fine
      even for single layer selection but I can't be sure (I could only test
      in non-Photoshop software available to me). So the new logics is:
      * If more than 1 layer is selected, store only the Layer Selection
        ID(s) block.
      * If exactly 1 layer is selected, store both the Layer Selection ID(s)
        and Layer state information blocks.
      * Otherwise (no layers selected) do not store any of these blocks.

 plug-ins/file-psd/psd-image-res-load.c | 52 ++++++++++++++++++++
 plug-ins/file-psd/psd-load.c           | 44 +++++++++++++----
 plug-ins/file-psd/psd-save.c           | 89 +++++++++++++++++++---------------
 plug-ins/file-psd/psd.h                |  3 +-
 4 files changed, 138 insertions(+), 50 deletions(-)
---
diff --git a/plug-ins/file-psd/psd-image-res-load.c b/plug-ins/file-psd/psd-image-res-load.c
index 7be157e730..5cfb34b518 100644
--- a/plug-ins/file-psd/psd-image-res-load.c
+++ b/plug-ins/file-psd/psd-image-res-load.c
@@ -223,6 +223,12 @@ static gint     load_resource_1058     (const PSDimageres     *res_a,
                                         FILE                  *f,
                                         GError               **error);
 
+static gint     load_resource_1069     (const PSDimageres     *res_a,
+                                        GimpImage             *image,
+                                        PSDimage              *img_a,
+                                        FILE                  *f,
+                                        GError               **error);
+
 static gint     load_resource_1077     (const PSDimageres     *res_a,
                                         GimpImage             *image,
                                         PSDimage              *img_a,
@@ -391,6 +397,11 @@ load_image_resource (PSDimageres  *res_a,
             load_resource_1058 (res_a, image, f, error);
             break;
 
+          case PSD_LAYER_SELECT_ID:
+            if (! img_a->merged_image_only)
+              load_resource_1069 (res_a, image, img_a, f, error);
+            break;
+
           case PSD_XMP_DATA:
             break;
 
@@ -1298,6 +1309,47 @@ load_resource_1058 (const PSDimageres  *res_a,
   return 0;
 }
 
+static gint
+load_resource_1069 (const PSDimageres  *res_a,
+                    GimpImage          *image,
+                    PSDimage           *img_a,
+                    FILE               *f,
+                    GError            **error)
+{
+  guint16 layer_count;
+  gint    i;
+
+  IFDBG(2) g_debug ("Process image resource block: 1069: Layer Selection ID(s)");
+
+  if (fread (&layer_count, 2, 1, f) < 1)
+    {
+      psd_set_error (feof (f), errno, error);
+      return -1;
+    }
+  layer_count = GUINT16_FROM_BE (layer_count);
+
+  /* This should probably not happen, but just in case the block is
+   * duplicated, let's just free the previous selection.
+   */
+  g_list_free (img_a->layer_selection);
+  img_a->layer_selection = NULL;
+
+  for (i = 0; i < layer_count; i++)
+    {
+      guint32 layer_id;
+
+      if (fread (&layer_id, 4, 1, f) < 1)
+        {
+          psd_set_error (feof (f), errno, error);
+          return -1;
+        }
+      layer_id = GUINT32_FROM_BE (layer_id);
+      img_a->layer_selection = g_list_prepend (img_a->layer_selection, GINT_TO_POINTER (layer_id));
+    }
+
+  return 0;
+}
+
 static gint
 load_resource_1077 (const PSDimageres  *res_a,
                     GimpImage          *image,
diff --git a/plug-ins/file-psd/psd-load.c b/plug-ins/file-psd/psd-load.c
index e5695f8403..0a50660c01 100644
--- a/plug-ins/file-psd/psd-load.c
+++ b/plug-ins/file-psd/psd-load.c
@@ -1167,6 +1167,7 @@ add_image_resources (GimpImage *image,
   /* Initialise image resource variables */
   img_a->no_icc = FALSE;
   img_a->layer_state = 0;
+  img_a->layer_selection = NULL;
   img_a->alpha_names = NULL;
   img_a->alpha_display_info = NULL;
   img_a->alpha_display_count = 0;
@@ -1287,9 +1288,9 @@ add_layers (GimpImage *image,
   gint32                lm_y;                  /* Layer mask y */
   gint32                lm_w;                  /* Layer mask width */
   gint32                lm_h;                  /* Layer mask height */
-  GimpLayer            *layer        = NULL;
-  GimpLayerMask        *mask         = NULL;
-  GimpLayer            *active_layer = NULL;
+  GimpLayer            *layer           = NULL;
+  GimpLayerMask        *mask            = NULL;
+  GList                *selected_layers = NULL;
   gint                  lidx;                  /* Layer index */
   gint                  cidx;                  /* Channel index */
   gint                  rowi;                  /* Row index */
@@ -1660,10 +1661,21 @@ add_layers (GimpImage *image,
                     }
                 }
 
-              /* Remember the active layer ID */
-              if (lidx == img_a->layer_state)
+              /* Remember the selected layers:
+               * - Layer Selection ID(s) (0x042D) are prioritary;
+               * - Layer state information (0x0400) is used instead
+               *   otherwise.
+               */
+              if (img_a->layer_selection)
                 {
-                  active_layer = layer;
+                  if (g_list_find (img_a->layer_selection, GINT_TO_POINTER (lyr_a[lidx]->id)) != NULL)
+                    {
+                      selected_layers = g_list_prepend (selected_layers, layer);
+                    }
+                }
+              else if (lidx == img_a->layer_state)
+                {
+                  selected_layers = g_list_prepend (selected_layers, layer);
                 }
 
               /* Set the layer data */
@@ -1846,9 +1858,23 @@ add_layers (GimpImage *image,
   g_free (lyr_a);
   g_array_free (parent_group_stack, FALSE);
 
-  /* Set the active layer */
-  if (active_layer != NULL)
-    gimp_image_set_active_layer (image, active_layer);
+  /* Set the selected layers */
+  if (selected_layers)
+    {
+      GimpLayer **sel_layers;
+      GList      *list;
+      gint        i;
+
+      sel_layers = g_new0 (GimpLayer *, g_list_length (selected_layers));
+      for (list = selected_layers, i = 0; list; list = list->next, i++)
+        sel_layers[i] = list->data;
+      gimp_image_set_selected_layers (image, g_list_length (selected_layers),
+                                      (const GimpLayer **) sel_layers);
+
+      g_list_free (selected_layers);
+      g_free (sel_layers);
+    }
+  g_list_free (img_a->layer_selection);
 
   return 0;
 }
diff --git a/plug-ins/file-psd/psd-save.c b/plug-ins/file-psd/psd-save.c
index 97b5194fe5..83c8beb1b1 100644
--- a/plug-ins/file-psd/psd-save.c
+++ b/plug-ins/file-psd/psd-save.c
@@ -603,9 +603,7 @@ save_resources (FILE      *fd,
   GList        *iter;
   gint          i;
   gchar        *fileName;            /* Image file name */
-  GimpLayer    *ActLayer;            /* The active layer */
-  guint         nActiveLayer = 0;    /* Number of the active layer */
-  gboolean      ActiveLayerPresent;  /* TRUE if there's an active layer */
+  GList        *SelLayers;           /* The selected layers */
 
   glong         eof_pos;             /* Position for End of file */
   glong         rsc_pos;             /* Position for Lengths of Resources section */
@@ -624,34 +622,9 @@ save_resources (FILE      *fd,
   fileName = g_file_get_path (gimp_image_get_file (image));
   IFDBG printf ("\tImage title: %s\n", fileName);
 
-  /* Get the active layer number id */
-
-  ActLayer = gimp_image_get_active_layer (image);
-  IFDBG printf ("\tCurrent layer id: %d\n",
-                gimp_item_get_id (GIMP_ITEM (ActLayer)));
-
-  ActiveLayerPresent = FALSE;
-  for (iter = PSDImageData.lLayers, i = 0;
-       iter;
-       iter = g_list_next (iter), i++)
-    {
-      if (ActLayer == ((PSD_Layer *) iter->data)->layer)
-        {
-          nActiveLayer = PSDImageData.nLayers - i - 1;
-          ActiveLayerPresent = TRUE;
-          break;
-        }
-    }
-
-  if (ActiveLayerPresent)
-    {
-      IFDBG printf ("\t\tActive layer is number %d\n", nActiveLayer);
-    }
-  else
-    {
-      IFDBG printf ("\t\tNo active layer\n");
-    }
+  /* Get the selected layers */
 
+  SelLayers = gimp_image_list_selected_layers (image);
 
   /* Here's where actual writing starts */
 
@@ -848,22 +821,53 @@ save_resources (FILE      *fd,
     write_gint16 (fd, psd_unit, "height unit");
   }
 
-  /* --------------- Write Active Layer Number --------------- */
+  /* --------------- Write Selected Layers --------------- */
 
-  if (ActiveLayerPresent)
+  if (SelLayers)
     {
-      xfwrite (fd, "8BIM", 4, "imageresources signature");
-      write_gint16 (fd, 0x0400, "0x0400 Id"); /* 1024 */
-      /* write_pascalstring (fd, Name, "Id name"); */
-      write_gint16 (fd, 0, "Id name"); /* Set to null string (two zeros) */
-      write_gint32 (fd, sizeof (gint16), "0x0400 resource size");
+      if (g_list_length (SelLayers) == 1)
+        {
+          /* Write the Layer State Information (0x0400) if and only if
+           * there is exactly one selected layer.
+           * Unless mistaken, this block does not seem used for multiple
+           * layer selected. It seems anyway redundant with the Layer
+           * Selection ID(s) block (0x042D) which is more recent
+           * (Photoshop CS2) but it's probably a good idea to store both
+           * information.
+           */
+          for (iter = PSDImageData.lLayers, i = 0;
+               iter;
+               iter = g_list_next (iter), i++)
+            {
+              if (SelLayers->data == ((PSD_Layer *) iter->data)->layer)
+                {
+                  xfwrite (fd, "8BIM", 4, "imageresources signature");
+                  write_gint16 (fd, 0x0400, "0x0400 Id"); /* 1024 */
+                  /* write_pascalstring (fd, Name, "Id name"); */
+                  write_gint16 (fd, 0, "Id name"); /* Set to null string (two zeros) */
+                  write_gint32 (fd, sizeof (gint16), "0x0400 resource size");
 
-      /* Save title as gint16 (length always even) */
+                  /* Layer State Information uses the layer index. */
+                  write_gint16 (fd, PSDImageData.nLayers - i - 1, "active layer");
 
-      write_gint16 (fd, nActiveLayer, "active layer");
+                  IFDBG printf ("\tTotal length of 0x0400 resource: %d\n", (int) sizeof (gint16));
+                  break;
+                }
+            }
+        }
 
-      IFDBG printf ("\tTotal length of 0x0400 resource: %d\n", (int) sizeof (gint16));
+      /* Write the Layer Selection ID(s) block when there is at least
+       * one selected layer or more.
+       */
+      xfwrite (fd, "8BIM", 4, "imageresources signature");
+      write_gint16 (fd, 0x042D, "0x042D Id"); /* 1069 */
+      write_gint16 (fd, 0, "Id name"); /* Set to null string (two zeros) */
+      write_gint32 (fd, sizeof (gint16) + sizeof (gint32) * g_list_length (SelLayers), "0x0400 resource 
size");
+      write_gint16 (fd, g_list_length (SelLayers), "2 bytes count");
+      for (iter = SelLayers; iter; iter = iter->next)
+        write_gint32 (fd, GPOINTER_TO_INT (gimp_item_get_tattoo (iter->data)), "4 bytes layer ID");
     }
+  g_list_free (SelLayers);
 
   /* --------------- Write ICC profile data ------------------- */
   {
@@ -1213,6 +1217,11 @@ save_layer_and_mask (FILE      *fd,
 
       g_free (layerName);
 
+      /* Layer ID */
+      xfwrite (fd, "8BIMlyid", 8, "lyid signature");
+      write_gint32 (fd, 4, "lyid size");
+      write_gint32 (fd, gimp_item_get_tattoo (GIMP_ITEM (psd_layer->layer)), "Layer ID");
+
       /* Layer color tag */
       xfwrite (fd, "8BIMlclr", 8, "sheet color signature");
       write_gint32 (fd, 8, "sheet color size");
diff --git a/plug-ins/file-psd/psd.h b/plug-ins/file-psd/psd.h
index b2fe6d885f..61f06a1ba5 100644
--- a/plug-ins/file-psd/psd.h
+++ b/plug-ins/file-psd/psd.h
@@ -668,7 +668,8 @@ typedef struct
   guint32               merged_image_start;     /* Merged image pixel data block start address */
   guint32               merged_image_len;       /* Merged image pixel data block length */
   gboolean              no_icc;                 /* Do not use ICC profile */
-  guint16               layer_state;            /* Active layer number counting from bottom up */
+  guint16               layer_state;            /* Active layer index counting from bottom up */
+  GList                *layer_selection;        /* Selected layer IDs (GIMP layer tattoos) */
   GPtrArray            *alpha_names;            /* Alpha channel names */
   PSDchanneldata      **alpha_display_info;     /* Alpha channel display info */
   guint16               alpha_display_count;    /* Number of alpha channel display info recs */


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