[gimp/wip/Jehan/PSD-multi-layer] plug-ins: multi-layer support in PSD load/export.
- From: Jehan <jehanp src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gimp/wip/Jehan/PSD-multi-layer] plug-ins: multi-layer support in PSD load/export.
- Date: Wed, 24 Feb 2021 09:33:34 +0000 (UTC)
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]