Commit 1d9a8a91 authored by Ell's avatar Ell

plug-ins: add support for loading merged image data from PSD files

PSD files may include a "merged", pre-composited, version of the
image (in Photoshop, this is the case when saving files with
"Maximize Compatibility" enabled; GIMP always saves the merged
image data in exported PSD files.)  This commit adds support for
loading the merged image version from PSDs, instead of the full
layer hierarchy.  This is useful when loading PSD files that use
features that we don't currently support, and therefore can't
render correctly, such as adjustment layers.

When loading the merged image version, we avoid loading certain
additional data from the file, such as channels, paths, and
guides, while still loading metadata, making this akin to loading
other "flat" image formats.

This option is currently exposed as an additional file type
("Photoshop image (merged)"), which has to be explicitly selected
from the file-type list when opening the image.
parent 38295dcb
......@@ -330,7 +330,8 @@ load_image_resource (PSDimageres *res_a,
break;
case PSD_ALPHA_NAMES:
load_resource_1006 (res_a, image_id, img_a, f, error);
if (! img_a->merged_image_only)
load_resource_1006 (res_a, image_id, img_a, f, error);
break;
case PSD_DISPLAY_INFO:
......@@ -342,15 +343,18 @@ load_image_resource (PSDimageres *res_a,
break;
case PSD_QUICK_MASK:
load_resource_1022 (res_a, image_id, img_a, f, error);
if (! img_a->merged_image_only)
load_resource_1022 (res_a, image_id, img_a, f, error);
break;
case PSD_LAYER_STATE:
load_resource_1024 (res_a, image_id, img_a, f, error);
if (! img_a->merged_image_only)
load_resource_1024 (res_a, image_id, img_a, f, error);
break;
case PSD_WORKING_PATH:
load_resource_2000 (res_a, image_id, f, error);
if (! img_a->merged_image_only)
load_resource_2000 (res_a, image_id, f, error);
break;
case PSD_IPTC_NAA_DATA:
......@@ -358,7 +362,8 @@ load_image_resource (PSDimageres *res_a,
break;
case PSD_GRID_GUIDE:
load_resource_1032 (res_a, image_id, f, error);
if (! img_a->merged_image_only)
load_resource_1032 (res_a, image_id, f, error);
break;
case PSD_ICC_PROFILE:
......@@ -366,7 +371,8 @@ load_image_resource (PSDimageres *res_a,
break;
case PSD_ALPHA_NAMES_UNI:
load_resource_1045 (res_a, image_id, img_a, f, error);
if (! img_a->merged_image_only)
load_resource_1045 (res_a, image_id, img_a, f, error);
break;
case PSD_IDX_COL_TAB_CNT:
......@@ -374,7 +380,8 @@ load_image_resource (PSDimageres *res_a,
break;
case PSD_ALPHA_ID:
load_resource_1053 (res_a, image_id, img_a, f, error);
if (! img_a->merged_image_only)
load_resource_1053 (res_a, image_id, img_a, f, error);
break;
case PSD_EXIF_DATA:
......
......@@ -113,6 +113,7 @@ static const Babl* get_mask_format (PSDimage *img_a);
/* Main file load function */
gint32
load_image (const gchar *filename,
gboolean merged_image_only,
gboolean *resolution_loaded,
GError **load_error)
{
......@@ -140,6 +141,8 @@ load_image (const gchar *filename,
return -1;
}
img_a.merged_image_only = merged_image_only;
/* ----- Read the PSD file Header block ----- */
IFDBG(2) g_debug ("Read header block");
if (read_header_block (&img_a, f, &error) < 0)
......@@ -161,7 +164,7 @@ load_image (const gchar *filename,
/* ----- Read the PSD file Layer & Mask block ----- */
IFDBG(2) g_debug ("Read layer & mask block");
lyr_a = read_layer_block (&img_a, f, &error);
if (img_a.num_layers != 0 && lyr_a == NULL)
if (! img_a.merged_image_only && img_a.num_layers != 0 && lyr_a == NULL)
goto load_error;
gimp_progress_update (0.4);
......@@ -496,7 +499,7 @@ read_layer_info (PSDimage *img_a,
img_a->num_layers = -img_a->num_layers;
}
if (img_a->num_layers)
if (! img_a->merged_image_only && img_a->num_layers)
{
/* Read layer records */
PSDlayerres res_a;
......@@ -1146,7 +1149,7 @@ add_layers (gint32 image_id,
IFDBG(2) g_debug ("Number of layers: %d", img_a->num_layers);
if (img_a->num_layers == 0)
if (img_a->merged_image_only || img_a->num_layers == 0)
{
IFDBG(2) g_debug ("No layers to process");
return 0;
......@@ -1734,9 +1737,16 @@ add_merged_image (gint32 image_id,
extra_channels--;
base_channels = total_channels - extra_channels;
if (img_a->merged_image_only)
{
extra_channels = 0;
total_channels = base_channels;
}
/* ----- Read merged image & extra channel pixel data ----- */
if (img_a->num_layers == 0
|| extra_channels > 0)
if (img_a->merged_image_only ||
img_a->num_layers == 0 ||
extra_channels > 0)
{
guint32 block_len;
guint32 block_start;
......@@ -1790,6 +1800,14 @@ add_merged_image (gint32 image_id,
}
}
/* Skip channel length data for unloaded channels */
if (fseek (f, (img_a->channels - total_channels) * img_a->rows * 2,
SEEK_CUR) < 0)
{
psd_set_error (feof (f), errno, error);
return -1;
}
IFDBG(3) g_debug ("RLE decode - data");
for (cidx = 0; cidx < total_channels; ++cidx)
{
......@@ -1812,7 +1830,8 @@ add_merged_image (gint32 image_id,
}
/* ----- Draw merged image ----- */
if (img_a->num_layers == 0) /* Merged image - Photoshop 2 style */
if (img_a->merged_image_only ||
img_a->num_layers == 0) /* Merged image - Photoshop 2 style */
{
image_type = get_gimp_image_type (img_a->base_type, img_a->transparency);
......@@ -1836,6 +1855,7 @@ add_merged_image (gint32 image_id,
100,
gimp_image_get_default_new_layer_mode (image_id));
gimp_image_insert_layer (image_id, layer_id, -1, 0);
buffer = gimp_drawable_get_buffer (layer_id);
gegl_buffer_set (buffer,
GEGL_RECTANGLE (0, 0,
......@@ -1843,6 +1863,36 @@ add_merged_image (gint32 image_id,
gegl_buffer_get_height (buffer)),
0, get_layer_format (img_a, img_a->transparency),
pixels, GEGL_AUTO_ROWSTRIDE);
/* Merged image data is blended against white. Unblend it. */
if (img_a->transparency)
{
GeglBufferIterator *iter;
iter = gegl_buffer_iterator_new (buffer, NULL, 0,
babl_format ("R'G'B'A float"),
GEGL_ACCESS_READWRITE,
GEGL_ABYSS_NONE);
while (gegl_buffer_iterator_next (iter))
{
gfloat *data = iter->data[0];
for (i = 0; i < iter->length; i++)
{
gint c;
if (data[3])
{
for (c = 0; c < 3; c++)
data[c] = (data[c] + data[3] - 1.0f) / data[3];
}
data += 4;
}
}
}
g_object_unref (buffer);
g_free (pixels);
}
......@@ -1854,9 +1904,19 @@ add_merged_image (gint32 image_id,
g_free (chn_a[cidx].data);
}
if (img_a->transparency)
{
/* Free "Transparency" channel name */
if (img_a->alpha_names)
{
alpha_name = g_ptr_array_index (img_a->alpha_names, 0);
if (alpha_name)
g_free (alpha_name);
}
}
/* ----- Draw extra alpha channels ----- */
if ((extra_channels /* Extra alpha channels */
|| img_a->transparency) /* Transparency alpha channel */
if (extra_channels /* Extra alpha channels */
&& image_id > -1)
{
IFDBG(2) g_debug ("Add extra channels");
......@@ -1864,17 +1924,7 @@ add_merged_image (gint32 image_id,
/* Get channel resource data */
if (img_a->transparency)
{
offset = 1;
/* Free "Transparency" channel name */
if (img_a->alpha_names)
{
alpha_name = g_ptr_array_index (img_a->alpha_names, 0);
if (alpha_name)
g_free (alpha_name);
}
}
offset = 1;
else
offset = 0;
......
......@@ -23,6 +23,7 @@
gint32 load_image (const gchar *filename,
gboolean merged_image_only,
gboolean *resolution_loaded,
GError **error);
......
......@@ -119,6 +119,27 @@ query (void)
"",
"0,string,8BPS");
/* File load (merged) */
gimp_install_procedure (LOAD_MERGED_PROC,
"Loads merged images from the Photoshop PSD file format",
"This plug-in loads the merged image data in Adobe "
"Photoshop (TM) native PSD format.",
"Ell",
"Ell",
"2018",
N_("Photoshop image (merged)"),
NULL,
GIMP_PLUGIN,
G_N_ELEMENTS (load_args),
G_N_ELEMENTS (load_return_vals),
load_args, load_return_vals);
gimp_register_file_handler_mime (LOAD_MERGED_PROC, "image/x-psd");
gimp_register_magic_load_handler (LOAD_MERGED_PROC,
"psd",
"",
"0,string,8BPS");
/* Thumbnail load */
gimp_install_procedure (LOAD_THUMB_PROC,
"Loads thumbnails from the Photoshop PSD file format",
......@@ -176,7 +197,8 @@ run (const gchar *name,
values[0].type = GIMP_PDB_STATUS;
values[0].data.d_status = GIMP_PDB_EXECUTION_ERROR;
if (strcmp (name, LOAD_PROC) == 0)
if (strcmp (name, LOAD_PROC) == 0 ||
strcmp (name, LOAD_MERGED_PROC) == 0)
{
gboolean resolution_loaded = FALSE;
gboolean interactive;
......@@ -194,6 +216,7 @@ run (const gchar *name,
}
image_ID = load_image (param[1].data.d_string,
strcmp (name, LOAD_MERGED_PROC) == 0,
&resolution_loaded, &error);
if (image_ID != -1)
......
......@@ -32,6 +32,7 @@
#define CONVERSION_WARNINGS FALSE
#define LOAD_PROC "file-psd-load"
#define LOAD_MERGED_PROC "file-psd-load-merged"
#define LOAD_THUMB_PROC "file-psd-load-thumb"
#define SAVE_PROC "file-psd-save"
#define PLUG_IN_BINARY "file-psd"
......@@ -642,6 +643,8 @@ typedef struct
/* PSD File data structures */
typedef struct
{
gboolean merged_image_only; /* Whether to load only the merged image data */
guint16 channels; /* Number of channels: 1- 56 */
gboolean transparency; /* Image has merged transparency alpha channel */
guint32 rows; /* Number of rows: 1 - 30000 */
......
Markdown is supported
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