Commit e51d5d48 authored by Jacob Boerema's avatar Jacob Boerema
Browse files

plug-ins: add extra layer groups when loading PSD images with clipping layers

Together with the previous commit this mostly fixes #5438.

PhotoShop handles clipping layers in a different way than GIMP. The only
way to have it look the same is by adding extra layer groups.
PSD layers that have clipping set, combined with the first non clipping
layer below it are grouped together in a new layer group.
Doing this results in the same image as the PSD merged image unless there
are other PSD elements in play that we don't handle yet.

See e.g. the NSFW image in the mentioned issue where the purple hair color
is a little darker than the merged image.
parent e7f85f7b
Pipeline #342493 passed with stages
in 31 minutes and 12 seconds
......@@ -85,6 +85,9 @@ static gint add_merged_image (GimpImage *image,
GError **error);
/* Local utility function prototypes */
static GimpLayer * add_clipping_group (GimpImage *image,
GimpLayer *parent);
static gchar * get_psd_color_mode_name (PSDColorMode mode);
static void psd_to_gimp_color_map (guchar *map256);
......@@ -1622,6 +1625,7 @@ add_layers (GimpImage *image,
PSDchannel **lyr_chn;
GArray *parent_group_stack;
GimpLayer *parent_group = NULL;
GimpLayer *clipping_group = NULL;
guint16 alpha_chn;
guint16 user_mask_chn;
guint16 layer_channels;
......@@ -1640,10 +1644,12 @@ add_layers (GimpImage *image,
GList *selected_layers = NULL;
gint lidx; /* Layer index */
gint cidx; /* Channel index */
gint gidx; /* Clipping group start index */
gboolean alpha;
gboolean user_mask;
gboolean empty;
gboolean empty_mask;
gboolean use_clipping_group;
GeglBuffer *buffer;
GimpImageType image_type;
LayerModeInfo mode_info;
......@@ -1664,13 +1670,70 @@ add_layers (GimpImage *image,
return -1;
}
IFDBG(3) g_debug ("Pre process layers...");
use_clipping_group = FALSE;
gidx = -1;
for (lidx = 0; lidx < img_a->num_layers; ++lidx)
{
if (lyr_a[lidx]->clipping == 1)
{
/* Photoshop handles layers with clipping differently than GIMP does.
* To correctly show these layers we need to make a new group
* starting with the first non-clipping layer and including all
* the clipping layers above it.
*/
if (lidx > 0)
{
if (gidx == -1)
{
use_clipping_group = TRUE;
/* Looking at the results we should ignore layer groups */
if (lyr_a[lidx-1]->group_type == 0)
gidx = lidx - 1;
else
gidx = lidx;
lyr_a[gidx]->clipping_group_type = 1; /* start clipping group */
IFDBG(3) g_debug ("Layer: %s - start of clipping group", lyr_a[gidx]->name);
}
else if (lidx + 1 == img_a->num_layers && use_clipping_group)
{
/* end clipping group at the top of the layer stack */
lyr_a[lidx]->clipping_group_type = 2; /* end clipping group */
IFDBG(3) g_debug ("Layer: %s - end of clipping group", lyr_a[lidx]->name);
use_clipping_group = FALSE;
gidx = -1;
}
else
{
lyr_a[lidx]->clipping_group_type = 0;
}
}
}
else if (use_clipping_group)
{
/* end clipping group */
lyr_a[lidx-1]->clipping_group_type = 2;
IFDBG(3) g_debug ("Layer: %s - end of clipping group", lyr_a[lidx-1]->name);
use_clipping_group = FALSE;
gidx = -1;
}
else
{
lyr_a[lidx]->clipping_group_type = 0;
}
}
/* set the root of the group hierarchy */
parent_group_stack = g_array_new (FALSE, FALSE, sizeof (GimpLayer *));
g_array_append_val (parent_group_stack, parent_group);
for (lidx = 0; lidx < img_a->num_layers; ++lidx)
{
IFDBG(2) g_debug ("Process Layer No %d.", lidx);
IFDBG(2) g_debug ("Process Layer No %d (%s).", lidx, lyr_a[lidx]->name);
if (lyr_a[lidx]->drop)
{
......@@ -1875,6 +1938,9 @@ add_layers (GimpImage *image,
layer_channels++;
}
IFDBG(4) g_debug ("Create the layer (group type: %d, clipping group type: %d)",
lyr_a[lidx]->group_type, lyr_a[lidx]->clipping_group_type);
/* Create the layer */
if (lyr_a[lidx]->group_type != 0)
{
......@@ -1916,6 +1982,11 @@ add_layers (GimpImage *image,
}
else
{
if (lyr_a[lidx]->clipping_group_type == 1)
{
clipping_group = add_clipping_group (image, parent_group);
}
if (empty)
{
IFDBG(2) g_debug ("Create blank layer");
......@@ -2196,7 +2267,20 @@ add_layers (GimpImage *image,
if (lyr_a[lidx]->group_type == 0 || /* normal layer */
lyr_a[lidx]->group_type == 3 /* group layer end marker */)
{
gimp_image_insert_layer (image, layer, parent_group, 0);
if (clipping_group)
{
gimp_image_insert_layer (image, layer, clipping_group, 0);
if (lyr_a[lidx]->clipping_group_type == 2)
{
/* End of our clipping group. */
clipping_group = NULL;
}
}
else
{
gimp_image_insert_layer (image, layer, parent_group, 0);
}
}
}
......@@ -2650,6 +2734,28 @@ add_merged_image (GimpImage *image,
/* Local utility functions */
static GimpLayer *
add_clipping_group (GimpImage *image,
GimpLayer *parent)
{
GimpLayer * clipping_group = NULL;
/* We need to create a group because GIMP handles clipping and
* composition mode in a different manner than PS. */
IFDBG(2) g_debug ("Creating a layer group to handle PS transparency clipping correctly.");
clipping_group = gimp_layer_group_new (image);
gimp_item_set_name (GIMP_ITEM (clipping_group), "Group added by GIMP");
gimp_layer_set_blend_space (clipping_group, GIMP_LAYER_COLOR_SPACE_RGB_PERCEPTUAL);
gimp_layer_set_composite_space (clipping_group, GIMP_LAYER_COLOR_SPACE_RGB_PERCEPTUAL);
gimp_layer_set_composite_mode (clipping_group, GIMP_LAYER_COMPOSITE_UNION);
gimp_image_insert_layer (image, clipping_group, parent, 0);
return clipping_group;
}
static gchar *
get_psd_color_mode_name (PSDColorMode mode)
{
......
......@@ -1169,8 +1169,10 @@ save_layer_and_mask (GOutputStream *output,
IFDBG(1) g_debug ("\t\tOpacity: %u", layerOpacity);
write_gchar (output, layerOpacity, "Opacity");
/* Apparently this field is not used in GIMP */
write_gchar (output, 0, "Clipping");
if (gimp_layer_get_composite_mode (psd_layer->layer) == GIMP_LAYER_COMPOSITE_CLIP_TO_BACKDROP)
write_gchar (output, 1, "Clipping");
else
write_gchar (output, 0, "Clipping");
flags = 0;
if (gimp_layer_get_lock_alpha (psd_layer->layer)) flags |= 1;
......
......@@ -589,6 +589,7 @@ typedef struct
gchar blend_mode[4]; /* Blend mode */
guchar opacity; /* Opacity - 0 = transparent ... 255 = opaque */
guchar clipping; /* Clipping */
guchar clipping_group_type; /* Used to track group needed for clipping (1 = group start, 2 = group end) */
guchar flags; /* Layer flags */
guchar filler; /* Filler */
guint64 extra_len; /* Extra data length */
......
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment