summaryrefslogtreecommitdiffstats
path: root/plug-ins/file-psd/psd-load.c
diff options
context:
space:
mode:
Diffstat (limited to 'plug-ins/file-psd/psd-load.c')
-rw-r--r--plug-ins/file-psd/psd-load.c2807
1 files changed, 2807 insertions, 0 deletions
diff --git a/plug-ins/file-psd/psd-load.c b/plug-ins/file-psd/psd-load.c
new file mode 100644
index 0000000..b03f8cf
--- /dev/null
+++ b/plug-ins/file-psd/psd-load.c
@@ -0,0 +1,2807 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * GIMP PSD Plug-in
+ * Copyright 2007 by John Marshall
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+#include "config.h"
+
+#include <string.h>
+#include <errno.h>
+
+#include <glib/gstdio.h>
+#include <zlib.h>
+#include <libgimp/gimp.h>
+
+#include "psd.h"
+#include "psd-util.h"
+#include "psd-image-res-load.h"
+#include "psd-layer-res-load.h"
+#include "psd-load.h"
+
+#include "libgimp/stdplugins-intl.h"
+
+
+#define COMP_MODE_SIZE sizeof(guint16)
+
+
+/* Local function prototypes */
+static gint read_header_block (PSDimage *img_a,
+ FILE *f,
+ GError **error);
+
+static gint read_color_mode_block (PSDimage *img_a,
+ FILE *f,
+ GError **error);
+
+static gint read_image_resource_block (PSDimage *img_a,
+ FILE *f,
+ GError **error);
+
+static PSDlayer ** read_layer_block (PSDimage *img_a,
+ FILE *f,
+ GError **error);
+
+static gint read_merged_image_block (PSDimage *img_a,
+ FILE *f,
+ GError **error);
+
+static gint32 create_gimp_image (PSDimage *img_a,
+ const gchar *filename);
+
+static gint add_color_map (gint32 image_id,
+ PSDimage *img_a);
+
+static gint add_image_resources (gint32 image_id,
+ PSDimage *img_a,
+ FILE *f,
+ gboolean *resolution_loaded,
+ gboolean *profile_loaded,
+ GError **error);
+
+static gint add_layers (gint32 image_id,
+ PSDimage *img_a,
+ PSDlayer **lyr_a,
+ FILE *f,
+ GError **error);
+
+static gint add_merged_image (gint32 image_id,
+ PSDimage *img_a,
+ FILE *f,
+ GError **error);
+
+/* Local utility function prototypes */
+static gint32 add_clipping_group (gint32 image_id,
+ gint32 parent_id);
+
+static gchar * get_psd_color_mode_name (PSDColorMode mode);
+
+static void psd_to_gimp_color_map (guchar *map256);
+
+static GimpImageType get_gimp_image_type (GimpImageBaseType image_base_type,
+ gboolean alpha);
+
+static gint read_channel_data (PSDchannel *channel,
+ guint16 bps,
+ guint16 compression,
+ const guint16 *rle_pack_len,
+ FILE *f,
+ guint32 comp_len,
+ GError **error);
+
+static void convert_1_bit (const gchar *src,
+ gchar *dst,
+ guint32 rows,
+ guint32 columns);
+
+static const Babl* get_layer_format (PSDimage *img_a,
+ gboolean alpha);
+static const Babl* get_channel_format (PSDimage *img_a);
+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,
+ gboolean *profile_loaded,
+ GError **load_error)
+{
+ FILE *f;
+ GStatBuf st;
+ PSDimage img_a;
+ PSDlayer **lyr_a;
+ gint32 image_id = -1;
+ GError *error = NULL;
+
+ img_a.cmyk_transform = img_a.cmyk_transform_alpha = NULL;
+ img_a.cmyk_profile = NULL;
+ /* ----- Open PSD file ----- */
+ if (g_stat (filename, &st) == -1)
+ return -1;
+
+ gimp_progress_init_printf (_("Opening '%s'"),
+ gimp_filename_to_utf8 (filename));
+
+ IFDBG(1) g_debug ("Open file %s", gimp_filename_to_utf8 (filename));
+ f = g_fopen (filename, "rb");
+ if (f == NULL)
+ {
+ g_set_error (load_error, G_FILE_ERROR, g_file_error_from_errno (errno),
+ _("Could not open '%s' for reading: %s"),
+ gimp_filename_to_utf8 (filename), g_strerror (errno));
+ 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)
+ goto load_error;
+ gimp_progress_update (0.1);
+
+ /* ----- Read the PSD file Color Mode block ----- */
+ IFDBG(2) g_debug ("Read color mode block");
+ if (read_color_mode_block (&img_a, f, &error) < 0)
+ goto load_error;
+ gimp_progress_update (0.2);
+
+ /* ----- Read the PSD file Image Resource block ----- */
+ IFDBG(2) g_debug ("Read image resource block");
+ if (read_image_resource_block (&img_a, f, &error) < 0)
+ goto load_error;
+ gimp_progress_update (0.3);
+
+ /* ----- 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.merged_image_only && img_a.num_layers != 0 && lyr_a == NULL)
+ goto load_error;
+ gimp_progress_update (0.4);
+
+ /* ----- Read the PSD file Merged Image Data block ----- */
+ IFDBG(2) g_debug ("Read merged image and extra alpha channel block");
+ if (read_merged_image_block (&img_a, f, &error) < 0)
+ goto load_error;
+ gimp_progress_update (0.5);
+
+ /* ----- Create GIMP image ----- */
+ IFDBG(2) g_debug ("Create GIMP image");
+ image_id = create_gimp_image (&img_a, filename);
+ if (image_id < 0)
+ goto load_error;
+ gimp_progress_update (0.6);
+
+ /* ----- Add color map ----- */
+ IFDBG(2) g_debug ("Add color map");
+ if (add_color_map (image_id, &img_a) < 0)
+ goto load_error;
+ gimp_progress_update (0.7);
+
+ /* ----- Add image resources ----- */
+ IFDBG(2) g_debug ("Add image resources");
+ if (add_image_resources (image_id, &img_a, f,
+ resolution_loaded, profile_loaded,
+ &error) < 0)
+ goto load_error;
+ gimp_progress_update (0.8);
+
+ /* ----- Add layers -----*/
+ IFDBG(2) g_debug ("Add layers");
+ if (add_layers (image_id, &img_a, lyr_a, f, &error) < 0)
+ goto load_error;
+ gimp_progress_update (0.9);
+
+ /* ----- Add merged image data and extra alpha channels ----- */
+ IFDBG(2) g_debug ("Add merged image data and extra alpha channels");
+ if (add_merged_image (image_id, &img_a, f, &error) < 0)
+ goto load_error;
+ gimp_progress_update (1.0);
+
+ IFDBG(2) g_debug ("Close file & return, image id: %d", image_id);
+ IFDBG(1) g_debug ("\n----------------------------------------"
+ "----------------------------------------\n");
+
+ gimp_image_clean_all (image_id);
+ gimp_image_undo_enable (image_id);
+ fclose (f);
+ return image_id;
+
+ /* ----- Process load errors ----- */
+ load_error:
+ if (error)
+ {
+ g_set_error (load_error, G_FILE_ERROR, G_FILE_ERROR_FAILED,
+ _("Error loading PSD file: %s"), error->message);
+ g_error_free (error);
+ }
+
+ /* Delete partially loaded image */
+ if (image_id > 0)
+ gimp_image_delete (image_id);
+
+ /* Close file if Open */
+ if (! (f == NULL))
+ fclose (f);
+
+ return -1;
+}
+
+
+/* Local functions */
+
+static gint
+read_header_block (PSDimage *img_a,
+ FILE *f,
+ GError **error)
+{
+ guint16 version;
+ gchar sig[4];
+ gchar buf[6];
+
+ if (fread (sig, 4, 1, f) < 1
+ || fread (&version, 2, 1, f) < 1
+ || fread (buf, 6, 1, f) < 1
+ || fread (&img_a->channels, 2, 1, f) < 1
+ || fread (&img_a->rows, 4, 1, f) < 1
+ || fread (&img_a->columns, 4, 1, f) < 1
+ || fread (&img_a->bps, 2, 1, f) < 1
+ || fread (&img_a->color_mode, 2, 1, f) < 1)
+ {
+ psd_set_error (feof (f), errno, error);
+ return -1;
+ }
+ version = GUINT16_FROM_BE (version);
+ img_a->channels = GUINT16_FROM_BE (img_a->channels);
+ img_a->rows = GUINT32_FROM_BE (img_a->rows);
+ img_a->columns = GUINT32_FROM_BE (img_a->columns);
+ img_a->bps = GUINT16_FROM_BE (img_a->bps);
+ img_a->color_mode = GUINT16_FROM_BE (img_a->color_mode);
+
+ IFDBG(1) g_debug ("\n\n\tSig: %.4s\n\tVer: %d\n\tChannels: "
+ "%d\n\tSize: %dx%d\n\tBPS: %d\n\tMode: %d\n",
+ sig, version, img_a->channels,
+ img_a->columns, img_a->rows,
+ img_a->bps, img_a->color_mode);
+
+ if (memcmp (sig, "8BPS", 4) != 0)
+ {
+ g_set_error (error, G_FILE_ERROR, G_FILE_ERROR_FAILED,
+ _("Not a valid Photoshop document file"));
+ return -1;
+ }
+
+ if (version != 1)
+ {
+ g_set_error (error, G_FILE_ERROR, G_FILE_ERROR_FAILED,
+ _("Unsupported file format version: %d"), version);
+ return -1;
+ }
+
+ if (img_a->channels > MAX_CHANNELS)
+ {
+ g_set_error (error, G_FILE_ERROR, G_FILE_ERROR_FAILED,
+ _("Too many channels in file: %d"), img_a->channels);
+ return -1;
+ }
+
+ /* Photoshop CS (version 8) supports 300000 x 300000, but this
+ is currently larger than GIMP_MAX_IMAGE_SIZE */
+
+ if (img_a->rows < 1 || img_a->rows > GIMP_MAX_IMAGE_SIZE)
+ {
+ g_set_error (error, G_FILE_ERROR, G_FILE_ERROR_FAILED,
+ _("Unsupported or invalid image height: %d"),
+ img_a->rows);
+ return -1;
+ }
+
+ if (img_a->columns < 1 || img_a->columns > GIMP_MAX_IMAGE_SIZE)
+ {
+ g_set_error (error, G_FILE_ERROR, G_FILE_ERROR_FAILED,
+ _("Unsupported or invalid image width: %d"),
+ img_a->columns);
+ return -1;
+ }
+
+ /* img_a->rows is sanitized above, so a division by zero is avoided here */
+ if (img_a->columns > G_MAXINT32 / img_a->rows)
+ {
+ g_set_error (error, G_FILE_ERROR, G_FILE_ERROR_FAILED,
+ _("Unsupported or invalid image size: %dx%d"),
+ img_a->columns, img_a->rows);
+ return -1;
+ }
+
+ if (img_a->color_mode != PSD_BITMAP
+ && img_a->color_mode != PSD_GRAYSCALE
+ && img_a->color_mode != PSD_INDEXED
+ && img_a->color_mode != PSD_RGB
+ && img_a->color_mode != PSD_MULTICHANNEL
+ && img_a->color_mode != PSD_CMYK
+ && img_a->color_mode != PSD_DUOTONE)
+ {
+ g_set_error (error, G_FILE_ERROR, G_FILE_ERROR_FAILED,
+ _("Unsupported color mode: %s"),
+ get_psd_color_mode_name (img_a->color_mode));
+ return -1;
+ }
+
+ if (img_a->color_mode == PSD_CMYK)
+ {
+ if (img_a->bps != 8)
+ {
+ g_set_error (error, G_FILE_ERROR, G_FILE_ERROR_FAILED,
+ _("Unsupported color mode: %s"),
+ get_psd_color_mode_name (img_a->color_mode));
+ return -1;
+ }
+ }
+
+ /* Warning for unsupported bit depth */
+ switch (img_a->bps)
+ {
+ case 32:
+ IFDBG(3) g_debug ("32 Bit Data");
+ break;
+
+ case 16:
+ IFDBG(3) g_debug ("16 Bit Data");
+ break;
+
+ case 8:
+ IFDBG(3) g_debug ("8 Bit Data");
+ break;
+
+ case 1:
+ IFDBG(3) g_debug ("1 Bit Data");
+ break;
+
+ default:
+ g_set_error (error, G_FILE_ERROR, G_FILE_ERROR_FAILED,
+ _("Unsupported bit depth: %d"), img_a->bps);
+ return -1;
+ break;
+ }
+
+ return 0;
+}
+
+static gint
+read_color_mode_block (PSDimage *img_a,
+ FILE *f,
+ GError **error)
+{
+ static guchar cmap[] = { 0, 0, 0, 255, 255, 255 };
+ guint32 block_len;
+
+ img_a->color_map_entries = 0;
+ img_a->color_map_len = 0;
+ if (fread (&block_len, 4, 1, f) < 1)
+ {
+ psd_set_error (feof (f), errno, error);
+ return -1;
+ }
+ block_len = GUINT32_FROM_BE (block_len);
+
+ IFDBG(1) g_debug ("Color map block size = %d", block_len);
+
+ if (block_len == 0)
+ {
+ if (img_a->color_mode == PSD_INDEXED ||
+ img_a->color_mode == PSD_DUOTONE )
+ {
+ IFDBG(1) g_debug ("No color block for indexed or duotone image");
+ g_set_error (error, G_FILE_ERROR, G_FILE_ERROR_FAILED,
+ _("The file is corrupt!"));
+ return -1;
+ }
+ }
+ else if (img_a->color_mode == PSD_INDEXED)
+ {
+ if (block_len != 768)
+ {
+ IFDBG(1) g_debug ("Invalid color block size for indexed image");
+ g_set_error (error, G_FILE_ERROR, G_FILE_ERROR_FAILED,
+ _("The file is corrupt!"));
+ return -1;
+ }
+ else
+ {
+ img_a->color_map_len = block_len;
+ img_a->color_map = g_malloc (img_a->color_map_len);
+ if (fread (img_a->color_map, block_len, 1, f) < 1)
+ {
+ psd_set_error (feof (f), errno, error);
+ return -1;
+ }
+ else
+ {
+ psd_to_gimp_color_map (img_a->color_map);
+ img_a->color_map_entries = 256;
+ }
+ }
+ }
+ else if (img_a->color_mode == PSD_DUOTONE)
+ {
+ img_a->color_map_len = block_len;
+ img_a->color_map = g_malloc (img_a->color_map_len);
+ if (fread (img_a->color_map, block_len, 1, f) < 1)
+ {
+ psd_set_error (feof (f), errno, error);
+ return -1;
+ }
+ }
+
+ /* Create color map for bitmap image */
+ if (img_a->color_mode == PSD_BITMAP)
+ {
+ img_a->color_map_len = 6;
+ img_a->color_map = g_malloc (img_a->color_map_len);
+ memcpy (img_a->color_map, cmap, img_a->color_map_len);
+ img_a->color_map_entries = 2;
+ }
+ IFDBG(2) g_debug ("Color map data length %d", img_a->color_map_len);
+
+ return 0;
+}
+
+static gint
+read_image_resource_block (PSDimage *img_a,
+ FILE *f,
+ GError **error)
+{
+ guint32 block_len;
+ guint32 block_end;
+
+ if (fread (&block_len, 4, 1, f) < 1)
+ {
+ psd_set_error (feof (f), errno, error);
+ return -1;
+ }
+ img_a->image_res_len = GUINT32_FROM_BE (block_len);
+
+ IFDBG(1) g_debug ("Image resource block size = %d", (int)img_a->image_res_len);
+
+ img_a->image_res_start = ftell (f);
+ block_end = img_a->image_res_start + img_a->image_res_len;
+
+ if (fseek (f, block_end, SEEK_SET) < 0)
+ {
+ psd_set_error (feof (f), errno, error);
+ return -1;
+ }
+
+ return 0;
+}
+
+static PSDlayer **
+read_layer_info (PSDimage *img_a,
+ FILE *f,
+ GError **error)
+{
+ PSDlayer **lyr_a = NULL;
+ guint32 block_len;
+ guint32 block_rem;
+ gint32 read_len;
+ gint32 write_len;
+ gint lidx; /* Layer index */
+ gint cidx; /* Channel index */
+
+ /* Get number of layers */
+ if (fread (&img_a->num_layers, 2, 1, f) < 1)
+ {
+ psd_set_error (feof (f), errno, error);
+ img_a->num_layers = -1;
+ return NULL;
+ }
+
+ img_a->num_layers = GINT16_FROM_BE (img_a->num_layers);
+ IFDBG(2) g_debug ("Number of layers: %d", img_a->num_layers);
+
+ if (img_a->num_layers < 0)
+ {
+ img_a->transparency = TRUE;
+ img_a->num_layers = -img_a->num_layers;
+ }
+
+ if (! img_a->merged_image_only && img_a->num_layers)
+ {
+ /* Read layer records */
+ PSDlayerres res_a;
+
+ /* Create pointer array for the layer records */
+ lyr_a = g_new (PSDlayer *, img_a->num_layers);
+
+ for (lidx = 0; lidx < img_a->num_layers; ++lidx)
+ {
+ /* Allocate layer record */
+ lyr_a[lidx] = (PSDlayer *) g_malloc (sizeof (PSDlayer) );
+
+ /* Initialise record */
+ lyr_a[lidx]->drop = FALSE;
+ lyr_a[lidx]->id = 0;
+ lyr_a[lidx]->group_type = 0;
+
+ if (fread (&lyr_a[lidx]->top, 4, 1, f) < 1
+ || fread (&lyr_a[lidx]->left, 4, 1, f) < 1
+ || fread (&lyr_a[lidx]->bottom, 4, 1, f) < 1
+ || fread (&lyr_a[lidx]->right, 4, 1, f) < 1
+ || fread (&lyr_a[lidx]->num_channels, 2, 1, f) < 1)
+ {
+ psd_set_error (feof (f), errno, error);
+ return NULL;
+ }
+
+ lyr_a[lidx]->top = GINT32_FROM_BE (lyr_a[lidx]->top);
+ lyr_a[lidx]->left = GINT32_FROM_BE (lyr_a[lidx]->left);
+ lyr_a[lidx]->bottom = GINT32_FROM_BE (lyr_a[lidx]->bottom);
+ lyr_a[lidx]->right = GINT32_FROM_BE (lyr_a[lidx]->right);
+ lyr_a[lidx]->num_channels = GUINT16_FROM_BE (lyr_a[lidx]->num_channels);
+
+ if (lyr_a[lidx]->num_channels > MAX_CHANNELS)
+ {
+ g_set_error (error, G_FILE_ERROR, G_FILE_ERROR_FAILED,
+ _("Too many channels in layer: %d"),
+ lyr_a[lidx]->num_channels);
+ return NULL;
+ }
+ if (lyr_a[lidx]->bottom < lyr_a[lidx]->top ||
+ lyr_a[lidx]->bottom - lyr_a[lidx]->top > GIMP_MAX_IMAGE_SIZE)
+ {
+ g_set_error (error, G_FILE_ERROR, G_FILE_ERROR_FAILED,
+ _("Unsupported or invalid layer height: %d"),
+ lyr_a[lidx]->bottom - lyr_a[lidx]->top);
+ return NULL;
+ }
+ if (lyr_a[lidx]->right < lyr_a[lidx]->left ||
+ lyr_a[lidx]->right - lyr_a[lidx]->left > GIMP_MAX_IMAGE_SIZE)
+ {
+ g_set_error (error, G_FILE_ERROR, G_FILE_ERROR_FAILED,
+ _("Unsupported or invalid layer width: %d"),
+ lyr_a[lidx]->right - lyr_a[lidx]->left);
+ return NULL;
+ }
+
+ if ((lyr_a[lidx]->right - lyr_a[lidx]->left) >
+ G_MAXINT32 / MAX (lyr_a[lidx]->bottom - lyr_a[lidx]->top, 1))
+ {
+ g_set_error (error, G_FILE_ERROR, G_FILE_ERROR_FAILED,
+ _("Unsupported or invalid layer size: %dx%d"),
+ lyr_a[lidx]->right - lyr_a[lidx]->left,
+ lyr_a[lidx]->bottom - lyr_a[lidx]->top);
+ return NULL;
+ }
+
+ IFDBG(2) g_debug ("Layer %d, Coords %d %d %d %d, channels %d, ",
+ lidx, lyr_a[lidx]->left, lyr_a[lidx]->top,
+ lyr_a[lidx]->right, lyr_a[lidx]->bottom,
+ lyr_a[lidx]->num_channels);
+
+ lyr_a[lidx]->chn_info = g_new (ChannelLengthInfo, lyr_a[lidx]->num_channels);
+
+ for (cidx = 0; cidx < lyr_a[lidx]->num_channels; ++cidx)
+ {
+ if (fread (&lyr_a[lidx]->chn_info[cidx].channel_id, 2, 1, f) < 1
+ || fread (&lyr_a[lidx]->chn_info[cidx].data_len, 4, 1, f) < 1)
+ {
+ psd_set_error (feof (f), errno, error);
+ return NULL;
+ }
+ lyr_a[lidx]->chn_info[cidx].channel_id =
+ GINT16_FROM_BE (lyr_a[lidx]->chn_info[cidx].channel_id);
+ lyr_a[lidx]->chn_info[cidx].data_len =
+ GUINT32_FROM_BE (lyr_a[lidx]->chn_info[cidx].data_len);
+ img_a->layer_data_len += lyr_a[lidx]->chn_info[cidx].data_len;
+ IFDBG(3) g_debug ("Channel ID %d, data len %d",
+ lyr_a[lidx]->chn_info[cidx].channel_id,
+ lyr_a[lidx]->chn_info[cidx].data_len);
+ }
+
+ if (fread (lyr_a[lidx]->mode_key, 4, 1, f) < 1
+ || fread (lyr_a[lidx]->blend_mode, 4, 1, f) < 1
+ || fread (&lyr_a[lidx]->opacity, 1, 1, f) < 1
+ || fread (&lyr_a[lidx]->clipping, 1, 1, f) < 1
+ || fread (&lyr_a[lidx]->flags, 1, 1, f) < 1
+ || fread (&lyr_a[lidx]->filler, 1, 1, f) < 1
+ || fread (&lyr_a[lidx]->extra_len, 4, 1, f) < 1)
+ {
+ psd_set_error (feof (f), errno, error);
+ return NULL;
+ }
+ if (memcmp (lyr_a[lidx]->mode_key, "8BIM", 4) != 0)
+ {
+ IFDBG(1) g_debug ("Incorrect layer mode signature %.4s",
+ lyr_a[lidx]->mode_key);
+ g_set_error (error, G_FILE_ERROR, G_FILE_ERROR_FAILED,
+ _("The file is corrupt!"));
+ return NULL;
+ }
+
+ lyr_a[lidx]->layer_flags.trans_prot = lyr_a[lidx]->flags & 1 ? TRUE : FALSE;
+ lyr_a[lidx]->layer_flags.visible = lyr_a[lidx]->flags & 2 ? FALSE : TRUE;
+
+ if (lyr_a[lidx]->flags & 8)
+ lyr_a[lidx]->layer_flags.irrelevant = lyr_a[lidx]->flags & 16 ? TRUE : FALSE;
+ else
+ lyr_a[lidx]->layer_flags.irrelevant = FALSE;
+
+ lyr_a[lidx]->extra_len = GUINT32_FROM_BE (lyr_a[lidx]->extra_len);
+ block_rem = lyr_a[lidx]->extra_len;
+ IFDBG(2) g_debug ("\n\tLayer mode sig: %.4s\n\tBlend mode: %.4s\n\t"
+ "Opacity: %d\n\tClipping: %d\n\tExtra data len: %d\n\t"
+ "Alpha lock: %d\n\tVisible: %d\n\tIrrelevant: %d",
+ lyr_a[lidx]->mode_key,
+ lyr_a[lidx]->blend_mode,
+ lyr_a[lidx]->opacity,
+ lyr_a[lidx]->clipping,
+ lyr_a[lidx]->extra_len,
+ lyr_a[lidx]->layer_flags.trans_prot,
+ lyr_a[lidx]->layer_flags.visible,
+ lyr_a[lidx]->layer_flags.irrelevant);
+ IFDBG(3) g_debug ("Remaining length %d", block_rem);
+
+ /* Layer mask data */
+ if (fread (&block_len, 4, 1, f) < 1)
+ {
+ psd_set_error (feof (f), errno, error);
+ return NULL;
+ }
+ block_len = GUINT32_FROM_BE (block_len);
+ IFDBG(3) g_debug ("Layer mask record size %u", block_len);
+ if (block_len + 4 > block_rem)
+ {
+ g_set_error (error, G_FILE_ERROR, G_FILE_ERROR_FAILED,
+ _("Unsupported or invalid mask info size: %d"),
+ block_len);
+ return NULL;
+ }
+
+ block_rem -= (block_len + 4);
+ IFDBG(3) g_debug ("Remaining length %d", block_rem);
+
+ lyr_a[lidx]->layer_mask_extra.top = 0;
+ lyr_a[lidx]->layer_mask_extra.left = 0;
+ lyr_a[lidx]->layer_mask_extra.bottom = 0;
+ lyr_a[lidx]->layer_mask_extra.right = 0;
+ lyr_a[lidx]->layer_mask.top = 0;
+ lyr_a[lidx]->layer_mask.left = 0;
+ lyr_a[lidx]->layer_mask.bottom = 0;
+ lyr_a[lidx]->layer_mask.right = 0;
+ lyr_a[lidx]->layer_mask.def_color = 0;
+ lyr_a[lidx]->layer_mask.extra_def_color = 0;
+ lyr_a[lidx]->layer_mask.flags = 0;
+ lyr_a[lidx]->layer_mask.extra_flags = 0;
+ lyr_a[lidx]->layer_mask.mask_params = 0;
+ lyr_a[lidx]->layer_mask.mask_flags.relative_pos = FALSE;
+ lyr_a[lidx]->layer_mask.mask_flags.disabled = FALSE;
+ lyr_a[lidx]->layer_mask.mask_flags.invert = FALSE;
+ lyr_a[lidx]->layer_mask.mask_flags.rendered = FALSE;
+ lyr_a[lidx]->layer_mask.mask_flags.params_present = FALSE;
+
+ if (block_len > 0)
+ {
+ if (fread (&lyr_a[lidx]->layer_mask.top, 4, 1, f) < 1
+ || fread (&lyr_a[lidx]->layer_mask.left, 4, 1, f) < 1
+ || fread (&lyr_a[lidx]->layer_mask.bottom, 4, 1, f) < 1
+ || fread (&lyr_a[lidx]->layer_mask.right, 4, 1, f) < 1
+ || fread (&lyr_a[lidx]->layer_mask.def_color, 1, 1, f) < 1
+ || fread (&lyr_a[lidx]->layer_mask.flags, 1, 1, f) < 1)
+ {
+ psd_set_error (feof (f), errno, error);
+ return NULL;
+ }
+
+ lyr_a[lidx]->layer_mask.top =
+ GINT32_FROM_BE (lyr_a[lidx]->layer_mask.top);
+ lyr_a[lidx]->layer_mask.left =
+ GINT32_FROM_BE (lyr_a[lidx]->layer_mask.left);
+ lyr_a[lidx]->layer_mask.bottom =
+ GINT32_FROM_BE (lyr_a[lidx]->layer_mask.bottom);
+ lyr_a[lidx]->layer_mask.right =
+ GINT32_FROM_BE (lyr_a[lidx]->layer_mask.right);
+ lyr_a[lidx]->layer_mask.mask_flags.relative_pos =
+ lyr_a[lidx]->layer_mask.flags & 1 ? TRUE : FALSE;
+ lyr_a[lidx]->layer_mask.mask_flags.disabled =
+ lyr_a[lidx]->layer_mask.flags & 2 ? TRUE : FALSE;
+ lyr_a[lidx]->layer_mask.mask_flags.invert =
+ lyr_a[lidx]->layer_mask.flags & 4 ? TRUE : FALSE;
+ lyr_a[lidx]->layer_mask.mask_flags.rendered =
+ lyr_a[lidx]->layer_mask.flags & 8 ? TRUE : FALSE;
+ lyr_a[lidx]->layer_mask.mask_flags.params_present =
+ lyr_a[lidx]->layer_mask.flags & 16 ? TRUE : FALSE;
+ IFDBG(3)
+ {
+ if (lyr_a[lidx]->layer_mask.flags & 32) g_debug ("Layer mask flags bit 5 set.");
+ if (lyr_a[lidx]->layer_mask.flags & 64) g_debug ("Layer mask flags bit 6 set.");
+ if (lyr_a[lidx]->layer_mask.flags & 128) g_debug ("Layer mask flags bit 7 set.");
+ }
+
+ block_len -= 18;
+
+ /* According to psd-tools this part comes before reading the
+ * mask parameters. However if all mask parameter flags are
+ * set the size of that would also be more than 18. I'm not
+ * sure if all those flags could be set at the same time or
+ * how to distinguish them. */
+ if (block_len >= 18)
+ {
+ if (fread (&lyr_a[lidx]->layer_mask.extra_flags, 1, 1, f) < 1
+ || fread (&lyr_a[lidx]->layer_mask.extra_def_color, 1, 1, f) < 1
+ || fread (&lyr_a[lidx]->layer_mask_extra.top, 4, 1, f) < 1
+ || fread (&lyr_a[lidx]->layer_mask_extra.left, 4, 1, f) < 1
+ || fread (&lyr_a[lidx]->layer_mask_extra.bottom, 4, 1, f) < 1
+ || fread (&lyr_a[lidx]->layer_mask_extra.right, 4, 1, f) < 1)
+ {
+ psd_set_error (feof (f), errno, error);
+ return NULL;
+ }
+ block_len -= 18;
+
+ lyr_a[lidx]->layer_mask_extra.top =
+ GINT32_FROM_BE (lyr_a[lidx]->layer_mask_extra.top);
+ lyr_a[lidx]->layer_mask_extra.left =
+ GINT32_FROM_BE (lyr_a[lidx]->layer_mask_extra.left);
+ lyr_a[lidx]->layer_mask_extra.bottom =
+ GINT32_FROM_BE (lyr_a[lidx]->layer_mask_extra.bottom);
+ lyr_a[lidx]->layer_mask_extra.right =
+ GINT32_FROM_BE (lyr_a[lidx]->layer_mask_extra.right);
+
+ IFDBG(2) g_debug ("Rect enclosing Layer mask %d %d %d %d",
+ lyr_a[lidx]->layer_mask_extra.left,
+ lyr_a[lidx]->layer_mask_extra.top,
+ lyr_a[lidx]->layer_mask_extra.right,
+ lyr_a[lidx]->layer_mask_extra.bottom);
+ }
+
+ if (block_len > 2 && lyr_a[lidx]->layer_mask.mask_flags.params_present)
+ {
+ gint extra_bytes = 0;
+
+ if (fread (&lyr_a[lidx]->layer_mask.mask_params, 1, 1, f) < 1)
+ {
+ psd_set_error (feof (f), errno, error);
+ return NULL;
+ }
+ block_len--;
+ IFDBG(3) g_debug ("Mask params: %u", lyr_a[lidx]->layer_mask.mask_params);
+
+ /* FIXME Extra bytes with user/vector mask density and feather follow.
+ * We currently can't handle that so we will skip it. */
+ extra_bytes += (lyr_a[lidx]->layer_mask.mask_params & 1 ? 1 : 0);
+ extra_bytes += (lyr_a[lidx]->layer_mask.mask_params & 2 ? 8 : 0);
+ extra_bytes += (lyr_a[lidx]->layer_mask.mask_params & 4 ? 1 : 0);
+ extra_bytes += (lyr_a[lidx]->layer_mask.mask_params & 8 ? 8 : 0);
+ IFDBG(3) g_debug ("Extra bytes according to mask parameters %d", extra_bytes);
+ if (fseek (f, extra_bytes, SEEK_CUR) < 0)
+ {
+ psd_set_error (feof (f), errno, error);
+ return NULL;
+ }
+ block_len -= extra_bytes;
+ }
+
+ if (block_len > 0)
+ {
+ /* We have some remaining unknown mask data.
+ * If size is less than 4 it is most likely padding. */
+ IFDBG(1)
+ {
+ if (block_len > 3)
+ g_debug ("Skipping %u bytes of unknown layer mask data", block_len);
+ }
+
+ if (fseek (f, block_len, SEEK_CUR) < 0)
+ {
+ psd_set_error (feof (f), errno, error);
+ return NULL;
+ }
+ }
+
+ IFDBG(2) g_debug ("Layer mask coords %d %d %d %d",
+ lyr_a[lidx]->layer_mask.left,
+ lyr_a[lidx]->layer_mask.top,
+ lyr_a[lidx]->layer_mask.right,
+ lyr_a[lidx]->layer_mask.bottom);
+
+ IFDBG(3) g_debug ("Default mask color %d, real color %d",
+ lyr_a[lidx]->layer_mask.def_color,
+ lyr_a[lidx]->layer_mask.extra_def_color);
+
+ IFDBG(3) g_debug ("Mask flags %u, real flags %u, mask params %u",
+ lyr_a[lidx]->layer_mask.flags,
+ lyr_a[lidx]->layer_mask.extra_flags,
+ lyr_a[lidx]->layer_mask.mask_params);
+
+ /* Rendered masks can have invalid dimensions: 0, 0, 0, -1 */
+ if (! lyr_a[lidx]->layer_mask.mask_flags.rendered)
+ {
+ /* sanity checks */
+ if (lyr_a[lidx]->layer_mask.bottom < lyr_a[lidx]->layer_mask.top ||
+ lyr_a[lidx]->layer_mask.bottom - lyr_a[lidx]->layer_mask.top > GIMP_MAX_IMAGE_SIZE)
+ {
+ g_set_error (error, G_FILE_ERROR, G_FILE_ERROR_FAILED,
+ _("Unsupported or invalid layer mask height: %d"),
+ lyr_a[lidx]->layer_mask.bottom - lyr_a[lidx]->layer_mask.top);
+ return NULL;
+ }
+ if (lyr_a[lidx]->layer_mask.right < lyr_a[lidx]->layer_mask.left ||
+ lyr_a[lidx]->layer_mask.right - lyr_a[lidx]->layer_mask.left > GIMP_MAX_IMAGE_SIZE)
+ {
+ g_set_error (error, G_FILE_ERROR, G_FILE_ERROR_FAILED,
+ _("Unsupported or invalid layer mask width: %d"),
+ lyr_a[lidx]->layer_mask.right - lyr_a[lidx]->layer_mask.left);
+ return NULL;
+ }
+
+ if ((lyr_a[lidx]->layer_mask.right - lyr_a[lidx]->layer_mask.left) >
+ G_MAXINT32 / MAX (lyr_a[lidx]->layer_mask.bottom - lyr_a[lidx]->layer_mask.top, 1))
+ {
+ g_set_error (error, G_FILE_ERROR, G_FILE_ERROR_FAILED,
+ _("Unsupported or invalid layer mask size: %dx%d"),
+ lyr_a[lidx]->layer_mask.right - lyr_a[lidx]->layer_mask.left,
+ lyr_a[lidx]->layer_mask.bottom - lyr_a[lidx]->layer_mask.top);
+ return NULL;
+ }
+ }
+ }
+
+ /* Layer blending ranges */ /* FIXME */
+ if (fread (&block_len, 4, 1, f) < 1)
+ {
+ psd_set_error (feof (f), errno, error);
+ return NULL;
+ }
+
+ block_len = GUINT32_FROM_BE (block_len);
+ block_rem -= (block_len + 4);
+ IFDBG(3) g_debug ("Remaining length %d", block_rem);
+
+ if (block_len > 0)
+ {
+ if (fseek (f, block_len, SEEK_CUR) < 0)
+ {
+ psd_set_error (feof (f), errno, error);
+ return NULL;
+ }
+ }
+
+ lyr_a[lidx]->name = fread_pascal_string (&read_len, &write_len,
+ 4, f, error);
+ if (*error)
+ return NULL;
+
+ block_rem -= read_len;
+ IFDBG(3) g_debug ("Remaining length %d", block_rem);
+
+ /* Adjustment layer info */ /* FIXME */
+
+ while (block_rem > 7)
+ {
+ if (get_layer_resource_header (&res_a, f, error) < 0)
+ return NULL;
+
+ block_rem -= 12;
+
+ if (res_a.data_len % 2 != 0)
+ {
+ /* Warn the user about an invalid length value but
+ * try to recover graciously. See bug #771558.
+ */
+ g_printerr ("psd-load: Layer extra data length should "
+ "be even, but it is %d.", res_a.data_len);
+ }
+
+ if (res_a.data_len > block_rem)
+ {
+ IFDBG(1) g_debug ("Unexpected end of layer resource data");
+ g_set_error (error, G_FILE_ERROR, G_FILE_ERROR_FAILED,
+ _("The file is corrupt!"));
+ return NULL;
+ }
+
+ if (load_layer_resource (&res_a, lyr_a[lidx], f, error) < 0)
+ return NULL;
+ block_rem -= res_a.data_len;
+ }
+ if (block_rem > 0)
+ {
+ if (fseek (f, block_rem, SEEK_CUR) < 0)
+ {
+ psd_set_error (feof (f), errno, error);
+ return NULL;
+ }
+ }
+ }
+
+ img_a->layer_data_start = ftell(f);
+ if (fseek (f, img_a->layer_data_len, SEEK_CUR) < 0)
+ {
+ psd_set_error (feof (f), errno, error);
+ return NULL;
+ }
+
+ IFDBG(1) g_debug ("Layer image data block size %d",
+ img_a->layer_data_len);
+ }
+
+ return lyr_a;
+}
+
+
+static PSDlayer **
+read_layer_block (PSDimage *img_a,
+ FILE *f,
+ GError **error)
+{
+ PSDlayer **lyr_a = NULL;
+ guint32 block_len;
+ guint32 block_end;
+
+ if (fread (&block_len, 4, 1, f) < 1)
+ {
+ psd_set_error (feof (f), errno, error);
+ img_a->num_layers = -1;
+ return NULL;
+ }
+
+ img_a->mask_layer_len = GUINT32_FROM_BE (block_len);
+
+ IFDBG(1) g_debug ("Layer and mask block size = %d", img_a->mask_layer_len);
+
+ img_a->transparency = FALSE;
+ img_a->layer_data_len = 0;
+
+ if (!img_a->mask_layer_len)
+ {
+ img_a->num_layers = 0;
+ return NULL;
+ }
+ else
+ {
+ guint32 total_len = img_a->mask_layer_len;
+
+ img_a->mask_layer_start = ftell (f);
+ block_end = img_a->mask_layer_start + img_a->mask_layer_len;
+
+ /* Layer info */
+ if (fread (&block_len, 4, 1, f) == 1 && block_len)
+ {
+ block_len = GUINT32_FROM_BE (block_len);
+ IFDBG(1) g_debug ("Layer info size = %d", block_len);
+
+ lyr_a = read_layer_info (img_a, f, error);
+
+ total_len -= block_len;
+ }
+ else
+ {
+ img_a->num_layers = 0;
+ lyr_a = NULL;
+ }
+
+ /* Global layer mask info */
+ if (fread (&block_len, 4, 1, f) == 1 && block_len)
+ {
+ block_len = GUINT32_FROM_BE (block_len);
+ IFDBG(1) g_debug ("Global layer mask info size = %d", block_len);
+
+ /* read_global_layer_mask_info (img_a, f, error); */
+ fseek (f, block_len, SEEK_CUR);
+
+ total_len -= block_len;
+ }
+
+ /* Additional Layer Information */
+ if (total_len > 12)
+ {
+ gchar signature_key[8];
+
+ if (fread (&signature_key, 4, 2, f) == 2 &&
+ (memcmp (signature_key, "8BIMLr16", 8) == 0 ||
+ memcmp (signature_key, "8BIMLr32", 8) == 0) &&
+ fread (&block_len, 4, 1, f) == 1 && block_len)
+ lyr_a = read_layer_info (img_a, f, error);
+ }
+
+ /* Skip to end of block */
+ if (fseek (f, block_end, SEEK_SET) < 0)
+ {
+ psd_set_error (feof (f), errno, error);
+ return NULL;
+ }
+ }
+
+ return lyr_a;
+}
+
+static gint
+read_merged_image_block (PSDimage *img_a,
+ FILE *f,
+ GError **error)
+{
+ img_a->merged_image_start = ftell(f);
+ if (fseek (f, 0, SEEK_END) < 0)
+ {
+ psd_set_error (feof (f), errno, error);
+ return -1;
+ }
+
+ img_a->merged_image_len = ftell(f) - img_a->merged_image_start;
+
+ IFDBG(1) g_debug ("Merged image data block: Start: %d, len: %d",
+ img_a->merged_image_start, img_a->merged_image_len);
+
+ return 0;
+}
+
+static gint32
+create_gimp_image (PSDimage *img_a,
+ const gchar *filename)
+{
+ gint32 image_id = -1;
+ GimpPrecision precision;
+
+ switch (img_a->color_mode)
+ {
+ case PSD_MULTICHANNEL:
+ case PSD_GRAYSCALE:
+ case PSD_DUOTONE:
+ img_a->base_type = GIMP_GRAY;
+ break;
+
+ case PSD_BITMAP:
+ case PSD_INDEXED:
+ img_a->base_type = GIMP_INDEXED;
+ break;
+
+ case PSD_CMYK:
+ case PSD_RGB:
+ img_a->base_type = GIMP_RGB;
+ break;
+
+ default:
+ /* Color mode already validated - should not be here */
+ g_warning ("Invalid color mode");
+ return -1;
+ break;
+ }
+
+ switch (img_a->bps)
+ {
+ case 32:
+ precision = GIMP_PRECISION_U32_GAMMA;
+ break;
+
+ case 16:
+ precision = GIMP_PRECISION_U16_GAMMA;
+ break;
+
+ case 8:
+ case 1:
+ if (img_a->color_mode == PSD_CMYK)
+ precision = GIMP_PRECISION_FLOAT_GAMMA;
+ else
+ precision = GIMP_PRECISION_U8_GAMMA;
+ break;
+
+ default:
+ /* Precision not supported */
+ g_warning ("Invalid precision");
+ return -1;
+ break;
+ }
+
+ /* Create gimp image */
+ IFDBG(2) g_debug ("Create image");
+ image_id = gimp_image_new_with_precision (img_a->columns, img_a->rows,
+ img_a->base_type, precision);
+ gimp_image_set_filename (image_id, filename);
+ gimp_image_undo_disable (image_id);
+
+ return image_id;
+}
+
+static gint
+add_color_map (gint32 image_id,
+ PSDimage *img_a)
+{
+ GimpParasite *parasite;
+
+ if (img_a->color_map_len)
+ {
+ if (img_a->color_mode != PSD_DUOTONE)
+ {
+ gimp_image_set_colormap (image_id, img_a->color_map,
+ img_a->color_map_entries);
+ }
+ else
+ {
+ /* Add parasite for Duotone color data */
+ IFDBG(2) g_debug ("Add Duotone color data parasite");
+ parasite = gimp_parasite_new (PSD_PARASITE_DUOTONE_DATA, 0,
+ img_a->color_map_len, img_a->color_map);
+ gimp_image_attach_parasite (image_id, parasite);
+ gimp_parasite_free (parasite);
+ }
+ g_free (img_a->color_map);
+ }
+
+ return 0;
+}
+
+static gint
+add_image_resources (gint32 image_id,
+ PSDimage *img_a,
+ FILE *f,
+ gboolean *resolution_loaded,
+ gboolean *profile_loaded,
+ GError **error)
+{
+ PSDimageres res_a;
+
+ if (fseek (f, img_a->image_res_start, SEEK_SET) < 0)
+ {
+ psd_set_error (feof (f), errno, error);
+ return -1;
+ }
+
+ /* Initialise image resource variables */
+ img_a->no_icc = FALSE;
+ img_a->layer_state = 0;
+ img_a->alpha_names = NULL;
+ img_a->alpha_display_info = NULL;
+ img_a->alpha_display_count = 0;
+ img_a->alpha_id = NULL;
+ img_a->alpha_id_count = 0;
+ img_a->quick_mask_id = 0;
+
+ while (ftell (f) < img_a->image_res_start + img_a->image_res_len)
+ {
+ if (get_image_resource_header (&res_a, f, error) < 0)
+ return -1;
+
+ if (res_a.data_start + res_a.data_len >
+ img_a->image_res_start + img_a->image_res_len)
+ {
+ IFDBG(1) g_debug ("Unexpected end of image resource data");
+ return 0;
+ }
+
+ if (load_image_resource (&res_a, image_id, img_a, f,
+ resolution_loaded, profile_loaded,
+ error) < 0)
+ return -1;
+ }
+
+ return 0;
+}
+
+static guchar *
+psd_convert_cmyk_to_srgb (PSDimage *img_a,
+ guchar *dst,
+ guchar *src,
+ gint width,
+ gint height,
+ gboolean alpha)
+{
+ if (img_a->cmyk_profile)
+ {
+ if (alpha)
+ {
+ if (! img_a->cmyk_transform_alpha)
+ {
+ GimpColorProfile *srgb = gimp_color_profile_new_rgb_srgb ();
+
+ img_a->cmyk_transform_alpha = gimp_color_transform_new (img_a->cmyk_profile, babl_format ("cmykA u8"),
+ srgb, babl_format ("R'G'B'A float"),
+ 0, 0);
+
+ g_object_unref (srgb);
+ }
+
+ gimp_color_transform_process_pixels (img_a->cmyk_transform_alpha,
+ babl_format ("cmykA u8"),
+ src,
+ babl_format ("R'G'B'A float"),
+ dst,
+ width * height);
+ }
+ else
+ {
+ if (! img_a->cmyk_transform)
+ {
+ GimpColorProfile *srgb = gimp_color_profile_new_rgb_srgb ();
+
+ img_a->cmyk_transform = gimp_color_transform_new (img_a->cmyk_profile, babl_format ("cmyk u8"),
+ srgb, babl_format ("R'G'B' float"),
+ 0, 0);
+
+ g_object_unref (srgb);
+ }
+
+ gimp_color_transform_process_pixels (img_a->cmyk_transform,
+ babl_format ("cmyk u8"),
+ src,
+ babl_format ("R'G'B' float"),
+ dst,
+ width * height);
+ }
+ }
+ else
+ {
+ const Babl *fish;
+
+ if (alpha)
+ fish = babl_fish ("cmykA u8", "R'G'B'A float");
+ else
+ fish = babl_fish ("cmyk u8", "R'G'B' float");
+
+ babl_process (fish, src, dst, width * height);
+ }
+
+ return (guchar*) dst;
+}
+
+static gint
+add_layers (gint32 image_id,
+ PSDimage *img_a,
+ PSDlayer **lyr_a,
+ FILE *f,
+ GError **error)
+{
+ PSDchannel **lyr_chn;
+ GArray *parent_group_stack;
+ gint32 parent_group_id = -1;
+ gint32 clipping_group_id = -1;
+ guint16 alpha_chn;
+ guint16 user_mask_chn;
+ guint16 layer_channels, base_channels;
+ guint16 channel_idx[MAX_CHANNELS];
+ guint16 *rle_pack_len;
+ guint16 bps;
+ gint32 l_x; /* Layer x */
+ gint32 l_y; /* Layer y */
+ gint32 l_w; /* Layer width */
+ gint32 l_h; /* Layer height */
+ gint32 lm_x; /* Layer mask x */
+ gint32 lm_y; /* Layer mask y */
+ gint32 lm_w; /* Layer mask width */
+ gint32 lm_h; /* Layer mask height */
+ gint32 layer_id = -1;
+ gint32 mask_id = -1;
+ gint32 active_layer_id = -1;
+ gint lidx; /* Layer index */
+ gint cidx; /* Channel index */
+ gint gidx; /* Clipping group start index */
+ gint rowi; /* Row index */
+ gboolean alpha;
+ gboolean user_mask;
+ gboolean empty;
+ gboolean empty_mask;
+ gboolean use_clipping_group;
+ GeglBuffer *buffer;
+ GimpImageType image_type;
+ LayerModeInfo mode_info;
+
+
+ IFDBG(2) g_debug ("Number of layers: %d", img_a->num_layers);
+
+ if (img_a->merged_image_only || img_a->num_layers == 0)
+ {
+ IFDBG(2) g_debug ("No layers to process");
+ return 0;
+ }
+
+ /* Layered image - Photoshop 3 style */
+ if (fseek (f, img_a->layer_data_start, SEEK_SET) < 0)
+ {
+ psd_set_error (feof (f), errno, error);
+ 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 (gint32));
+ g_array_append_val (parent_group_stack, parent_group_id);
+
+ for (lidx = 0; lidx < img_a->num_layers; ++lidx)
+ {
+ IFDBG(2) g_debug ("Process Layer No %d (%s).", lidx, lyr_a[lidx]->name);
+
+ if (lyr_a[lidx]->drop)
+ {
+ IFDBG(2) g_debug ("Drop layer %d", lidx);
+
+ /* Step past layer data */
+ for (cidx = 0; cidx < lyr_a[lidx]->num_channels; ++cidx)
+ {
+ if (fseek (f, lyr_a[lidx]->chn_info[cidx].data_len, SEEK_CUR) < 0)
+ {
+ psd_set_error (feof (f), errno, error);
+ return -1;
+ }
+ }
+ }
+ else
+ {
+ /* Empty layer */
+ if (lyr_a[lidx]->bottom - lyr_a[lidx]->top == 0
+ || lyr_a[lidx]->right - lyr_a[lidx]->left == 0)
+ empty = TRUE;
+ else
+ empty = FALSE;
+
+ /* Empty mask */
+ if (lyr_a[lidx]->layer_mask.bottom - lyr_a[lidx]->layer_mask.top == 0
+ || lyr_a[lidx]->layer_mask.right - lyr_a[lidx]->layer_mask.left == 0)
+ empty_mask = TRUE;
+ else
+ empty_mask = FALSE;
+
+ IFDBG(3) g_debug ("Empty mask %d, size %d %d", empty_mask,
+ lyr_a[lidx]->layer_mask.bottom - lyr_a[lidx]->layer_mask.top,
+ lyr_a[lidx]->layer_mask.right - lyr_a[lidx]->layer_mask.left);
+
+ /* Load layer channel data */
+ IFDBG(2) g_debug ("Number of channels: %d", lyr_a[lidx]->num_channels);
+ /* Create pointer array for the channel records */
+ lyr_chn = g_new (PSDchannel *, lyr_a[lidx]->num_channels);
+ for (cidx = 0; cidx < lyr_a[lidx]->num_channels; ++cidx)
+ {
+ guint16 comp_mode = PSD_COMP_RAW;
+
+ /* Allocate channel record */
+ lyr_chn[cidx] = g_malloc (sizeof (PSDchannel) );
+
+ lyr_chn[cidx]->id = lyr_a[lidx]->chn_info[cidx].channel_id;
+ lyr_chn[cidx]->rows = lyr_a[lidx]->bottom - lyr_a[lidx]->top;
+ lyr_chn[cidx]->columns = lyr_a[lidx]->right - lyr_a[lidx]->left;
+ lyr_chn[cidx]->data = NULL;
+
+ if (lyr_chn[cidx]->id == PSD_CHANNEL_EXTRA_MASK)
+ {
+ if (fseek (f, lyr_a[lidx]->chn_info[cidx].data_len, SEEK_CUR) != 0)
+ {
+ psd_set_error (feof (f), errno, error);
+ return -1;
+ }
+
+ continue;
+ }
+ else if (lyr_chn[cidx]->id == PSD_CHANNEL_MASK)
+ {
+ /* Works around a bug in panotools psd files where the layer mask
+ size is given as 0 but data exists. Set mask size to layer size.
+ */
+ if (empty_mask && lyr_a[lidx]->chn_info[cidx].data_len - 2 > 0)
+ {
+ empty_mask = FALSE;
+ if (lyr_a[lidx]->layer_mask.top == lyr_a[lidx]->layer_mask.bottom)
+ {
+ lyr_a[lidx]->layer_mask.top = lyr_a[lidx]->top;
+ lyr_a[lidx]->layer_mask.bottom = lyr_a[lidx]->bottom;
+ }
+ if (lyr_a[lidx]->layer_mask.right == lyr_a[lidx]->layer_mask.left)
+ {
+ lyr_a[lidx]->layer_mask.right = lyr_a[lidx]->right;
+ lyr_a[lidx]->layer_mask.left = lyr_a[lidx]->left;
+ }
+ }
+ lyr_chn[cidx]->rows = (lyr_a[lidx]->layer_mask.bottom -
+ lyr_a[lidx]->layer_mask.top);
+ lyr_chn[cidx]->columns = (lyr_a[lidx]->layer_mask.right -
+ lyr_a[lidx]->layer_mask.left);
+ }
+
+ IFDBG(3) g_debug ("Channel id %d, %dx%d",
+ lyr_chn[cidx]->id,
+ lyr_chn[cidx]->columns,
+ lyr_chn[cidx]->rows);
+
+ /* Only read channel data if there is any channel
+ * data. Note that the channel data can contain a
+ * compression method but no actual data.
+ */
+ if (lyr_a[lidx]->chn_info[cidx].data_len >= COMP_MODE_SIZE)
+ {
+ if (fread (&comp_mode, COMP_MODE_SIZE, 1, f) < 1)
+ {
+ psd_set_error (feof (f), errno, error);
+ return -1;
+ }
+ comp_mode = GUINT16_FROM_BE (comp_mode);
+ IFDBG(3) g_debug ("Compression mode: %d", comp_mode);
+ }
+ if (lyr_a[lidx]->chn_info[cidx].data_len > COMP_MODE_SIZE)
+ {
+ switch (comp_mode)
+ {
+ case PSD_COMP_RAW: /* Planar raw data */
+ IFDBG(3) g_debug ("Raw data length: %d",
+ lyr_a[lidx]->chn_info[cidx].data_len - 2);
+ if (read_channel_data (lyr_chn[cidx], img_a->bps,
+ PSD_COMP_RAW, NULL, f, 0,
+ error) < 1)
+ return -1;
+ break;
+
+ case PSD_COMP_RLE: /* Packbits */
+ IFDBG(3) g_debug ("RLE channel length %d, RLE length data: %d, "
+ "RLE data block: %d",
+ lyr_a[lidx]->chn_info[cidx].data_len - 2,
+ lyr_chn[cidx]->rows * 2,
+ (lyr_a[lidx]->chn_info[cidx].data_len - 2 -
+ lyr_chn[cidx]->rows * 2));
+ rle_pack_len = g_malloc (lyr_chn[cidx]->rows * 2);
+ for (rowi = 0; rowi < lyr_chn[cidx]->rows; ++rowi)
+ {
+ if (fread (&rle_pack_len[rowi], 2, 1, f) < 1)
+ {
+ psd_set_error (feof (f), errno, error);
+ g_free (rle_pack_len);
+ return -1;
+ }
+ rle_pack_len[rowi] = GUINT16_FROM_BE (rle_pack_len[rowi]);
+ }
+
+ IFDBG(3) g_debug ("RLE decode - data");
+ if (read_channel_data (lyr_chn[cidx], img_a->bps,
+ PSD_COMP_RLE, rle_pack_len, f, 0,
+ error) < 1)
+ return -1;
+
+ g_free (rle_pack_len);
+ break;
+
+ case PSD_COMP_ZIP: /* ? */
+ case PSD_COMP_ZIP_PRED:
+ if (read_channel_data (lyr_chn[cidx], img_a->bps,
+ comp_mode, NULL, f,
+ lyr_a[lidx]->chn_info[cidx].data_len - 2,
+ error) < 1)
+ return -1;
+ break;
+
+ default:
+ g_set_error (error, G_FILE_ERROR, G_FILE_ERROR_FAILED,
+ _("Unsupported compression mode: %d"), comp_mode);
+ return -1;
+ break;
+ }
+ }
+ }
+
+ /* Draw layer */
+
+ alpha = FALSE;
+ alpha_chn = -1;
+ user_mask = FALSE;
+ user_mask_chn = -1;
+ layer_channels = 0;
+ l_x = 0;
+ l_y = 0;
+ l_w = img_a->columns;
+ l_h = img_a->rows;
+ if (parent_group_stack->len > 0)
+ parent_group_id = g_array_index (parent_group_stack, gint32,
+ parent_group_stack->len - 1);
+ else
+ parent_group_id = -1; /* root */
+
+ if (img_a->color_mode == PSD_CMYK)
+ base_channels = 4;
+ else if (img_a->color_mode == PSD_RGB || img_a->color_mode == PSD_LAB)
+ base_channels = 3;
+ else
+ base_channels = 1;
+
+ IFDBG(3) g_debug ("Re-hash channel indices");
+ for (cidx = 0; cidx < lyr_a[lidx]->num_channels; ++cidx)
+ {
+ if (lyr_chn[cidx]->id == PSD_CHANNEL_MASK)
+ {
+ user_mask = TRUE;
+ user_mask_chn = cidx;
+ }
+ else if (lyr_chn[cidx]->id == PSD_CHANNEL_ALPHA)
+ {
+ alpha = TRUE;
+ alpha_chn = cidx;
+ }
+ else if (lyr_chn[cidx]->data)
+ {
+ if (layer_channels < base_channels)
+ {
+ channel_idx[layer_channels] = cidx; /* Assumes in sane order */
+ layer_channels++; /* RGB, Lab, CMYK etc. */
+ }
+ else
+ {
+ /* channel_idx[base_channels] is reserved for alpha channel,
+ * but this layer apparently has extra channels.
+ * From the one example I have (see #8411) it looks like
+ * that channel is the same as the alpha channel. */
+ IFDBG(2) g_debug ("This layer has an extra channel (id: %d)", lyr_chn[cidx]->id);
+ channel_idx[layer_channels+1] = cidx; /* Assumes in sane order */
+ layer_channels += 2; /* RGB, Lab, CMYK etc. */
+ }
+ }
+ else
+ {
+ IFDBG(4) g_debug ("Channel %d (id: %d) has no data", cidx, lyr_chn[cidx]->id);
+ }
+ }
+
+ if (alpha)
+ {
+ if (layer_channels <= base_channels)
+ {
+ channel_idx[layer_channels] = alpha_chn;
+ layer_channels++;
+ }
+ else
+ {
+ channel_idx[base_channels] = alpha_chn;
+ }
+ base_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)
+ {
+ if (lyr_a[lidx]->group_type == 3)
+ {
+ /* the </Layer group> marker layers are used to
+ * assemble the layer structure in a single pass
+ */
+ IFDBG(2) g_debug ("Create placeholder group layer");
+ layer_id = gimp_layer_group_new (image_id);
+ /* add this group layer as the new parent */
+ g_array_append_val (parent_group_stack, layer_id);
+ }
+ else /* group-type == 1 || group_type == 2 */
+ {
+ if (parent_group_stack->len)
+ {
+ layer_id = g_array_index (parent_group_stack, gint32,
+ parent_group_stack->len - 1);
+ IFDBG(2) g_debug ("End group layer id %d.", layer_id);
+ /* since the layers are stored in reverse, the group
+ * layer start marker actually means we're done with
+ * that layer group
+ */
+ g_array_remove_index (parent_group_stack,
+ parent_group_stack->len - 1);
+
+ gimp_drawable_offsets (layer_id, &l_x, &l_y);
+
+ l_w = gimp_drawable_width (layer_id);
+ l_h = gimp_drawable_height (layer_id);
+ }
+ else
+ {
+ IFDBG(1) g_debug ("WARNING: Unmatched group layer start marker.");
+ layer_id = -1;
+ }
+ }
+ }
+ else
+ {
+
+ if (lyr_a[lidx]->clipping_group_type == 1)
+ {
+ clipping_group_id = add_clipping_group (image_id, parent_group_id);
+ }
+
+ if (empty)
+ {
+ IFDBG(2) g_debug ("Create blank layer");
+ }
+ else
+ {
+ IFDBG(2) g_debug ("Create normal layer");
+ l_x = lyr_a[lidx]->left;
+ l_y = lyr_a[lidx]->top;
+ l_w = lyr_a[lidx]->right - lyr_a[lidx]->left;
+ l_h = lyr_a[lidx]->bottom - lyr_a[lidx]->top;
+ }
+
+ image_type = get_gimp_image_type (img_a->base_type, TRUE);
+ IFDBG(3) g_debug ("Layer type %d", image_type);
+
+ layer_id = gimp_layer_new (image_id, lyr_a[lidx]->name,
+ l_w, l_h, image_type,
+ 100, GIMP_LAYER_MODE_NORMAL);
+ }
+
+ if (layer_id != -1)
+ {
+ /* Set the layer name. Note that we do this even for group-end
+ * markers, to avoid having the default group name collide with
+ * subsequent layers; the real group name is set by the group
+ * start marker.
+ */
+ gimp_item_set_name (layer_id, lyr_a[lidx]->name);
+
+ /* Set the layer properties (skip this for layer group end
+ * markers; we set their properties when processing the start
+ * marker.)
+ */
+ if (lyr_a[lidx]->group_type != 3)
+ {
+ /* Mode */
+ psd_to_gimp_blend_mode (lyr_a[lidx], &mode_info);
+ gimp_layer_set_mode (layer_id, mode_info.mode);
+ gimp_layer_set_blend_space (layer_id, mode_info.blend_space);
+ gimp_layer_set_composite_space (layer_id, mode_info.composite_space);
+ gimp_layer_set_composite_mode (layer_id, mode_info.composite_mode);
+
+ /* Opacity */
+ gimp_layer_set_opacity (layer_id,
+ lyr_a[lidx]->opacity * 100.0 / 255.0);
+
+ /* Flags */
+ gimp_layer_set_lock_alpha (layer_id, lyr_a[lidx]->layer_flags.trans_prot);
+ gimp_item_set_visible (layer_id, lyr_a[lidx]->layer_flags.visible);
+#if 0
+ /* according to the spec, the 'irrelevant' flag indicates
+ * that the layer's "pixel data is irrelevant to the
+ * appearance of the document". what this seems to mean is
+ * not that the layer doesn't contribute to the image, but
+ * rather that its appearance can be entirely derived from
+ * sources other than the pixel data, such as vector data.
+ * in particular, this doesn't mean that the layer is
+ * invisible. since we only support raster layers atm, we can
+ * just ignore this flag.
+ */
+ if (lyr_a[lidx]->layer_flags.irrelevant &&
+ lyr_a[lidx]->group_type == 0)
+ {
+ gimp_item_set_visible (layer_id, FALSE);
+ }
+#endif
+
+ /* Position */
+ if (l_x != 0 || l_y != 0)
+ gimp_layer_set_offsets (layer_id, l_x, l_y);
+
+ /* Color tag */
+ gimp_item_set_color_tag (layer_id,
+ psd_to_gimp_layer_color_tag (lyr_a[lidx]->color_tag[0]));
+
+ /* Tattoo */
+ if (lyr_a[lidx]->id)
+ gimp_item_set_tattoo (layer_id, lyr_a[lidx]->id);
+
+ /* For layer groups, expand or collapse the group */
+ if (lyr_a[lidx]->group_type != 0)
+ {
+ gimp_item_set_expanded (layer_id,
+ lyr_a[lidx]->group_type == 1);
+ }
+ }
+
+ /* Remember the active layer ID */
+ if (lidx == img_a->layer_state)
+ {
+ active_layer_id = layer_id;
+ }
+
+ /* Set the layer data */
+ if (lyr_a[lidx]->group_type == 0)
+ {
+ IFDBG(3) g_debug ("Draw layer");
+
+ if (empty)
+ {
+ gimp_drawable_fill (layer_id, GIMP_FILL_TRANSPARENT);
+ }
+ else
+ {
+ GeglBufferIterator *iter;
+
+ bps = img_a->bps / 8;
+ if (bps == 0)
+ bps++;
+
+ buffer = gimp_drawable_get_buffer (layer_id);
+
+ iter = gegl_buffer_iterator_new (
+ buffer, NULL, 0, get_layer_format (img_a, alpha),
+ GEGL_ACCESS_WRITE, GEGL_ABYSS_NONE, 1);
+
+ while (gegl_buffer_iterator_next (iter))
+ {
+ const GeglRectangle *roi = &iter->items[0].roi;
+ guint8 *dst0 = iter->items[0].data;
+ gint src_step = bps;
+ gint dst_step = bps * base_channels;
+
+ if (img_a->color_mode == PSD_CMYK)
+ {
+ dst0 = gegl_scratch_alloc (base_channels *
+ iter->length);
+ }
+
+ for (cidx = 0; cidx < base_channels; ++cidx)
+ {
+ gint b;
+
+ if (roi->x == 0 && roi->y == 0)
+ IFDBG(3) g_debug ("Start channel %d", channel_idx[cidx]);
+
+ for (b = 0; b < bps; ++b)
+ {
+ guint8 *dst;
+ gint y;
+
+ dst = &dst0[cidx * bps + b];
+
+ for (y = 0; y < roi->height; y++)
+ {
+ const guint8 *src;
+ gint x;
+
+ src = (const guint8 *)
+ &lyr_chn[channel_idx[cidx]]->data[
+ ((roi->y + y) * l_w +
+ roi->x) * bps +
+ b];
+
+ for (x = 0; x < roi->width; ++x)
+ {
+ *dst = *src;
+
+ src += src_step;
+ dst += dst_step;
+ }
+ }
+ }
+ }
+
+ if (img_a->color_mode == PSD_CMYK)
+ {
+ psd_convert_cmyk_to_srgb (
+ img_a,
+ iter->items[0].data, dst0,
+ roi->width, roi->height,
+ alpha);
+
+ gegl_scratch_free (dst0);
+ }
+ }
+
+ for (cidx = 0; cidx < layer_channels; ++cidx)
+ g_free (lyr_chn[channel_idx[cidx]]->data);
+
+ g_object_unref (buffer);
+ }
+ }
+
+ /* Layer mask */
+ if (user_mask && lyr_a[lidx]->group_type != 3)
+ {
+ if (empty_mask)
+ {
+ IFDBG(3) g_debug ("Create empty mask");
+ if (lyr_a[lidx]->layer_mask.def_color == 255)
+ mask_id = gimp_layer_create_mask (layer_id,
+ GIMP_ADD_MASK_WHITE);
+ else
+ mask_id = gimp_layer_create_mask (layer_id,
+ GIMP_ADD_MASK_BLACK);
+ gimp_layer_add_mask (layer_id, mask_id);
+ gimp_layer_set_apply_mask (layer_id,
+ ! lyr_a[lidx]->layer_mask.mask_flags.disabled);
+ }
+ else
+ {
+ GeglRectangle mask_rect;
+
+ /* Load layer mask data */
+ lm_x = lyr_a[lidx]->layer_mask.left - l_x;
+ lm_y = lyr_a[lidx]->layer_mask.top - l_y;
+ lm_w = lyr_a[lidx]->layer_mask.right - lyr_a[lidx]->layer_mask.left;
+ lm_h = lyr_a[lidx]->layer_mask.bottom - lyr_a[lidx]->layer_mask.top;
+ IFDBG(3) g_debug ("Mask channel index %d", user_mask_chn);
+ IFDBG(3) g_debug ("Original Mask %d %d %d %d", lm_x, lm_y, lm_w, lm_h);
+ /* Crop mask at layer boundary, and draw layer mask data,
+ * if any
+ */
+ if (gegl_rectangle_intersect (
+ &mask_rect,
+ GEGL_RECTANGLE (0, 0, l_w, l_h),
+ GEGL_RECTANGLE (lm_x, lm_y, lm_w, lm_h)))
+ {
+ IFDBG(3) g_debug ("Layer %d %d %d %d", l_x, l_y, l_w, l_h);
+ IFDBG(3) g_debug ("Mask %d %d %d %d",
+ mask_rect.x, mask_rect.y,
+ mask_rect.width, mask_rect.height);
+
+ if (lyr_a[lidx]->layer_mask.def_color == 255)
+ mask_id = gimp_layer_create_mask (layer_id,
+ GIMP_ADD_MASK_WHITE);
+ else
+ mask_id = gimp_layer_create_mask (layer_id,
+ GIMP_ADD_MASK_BLACK);
+
+ bps = img_a->bps / 8;
+ if (bps == 0)
+ bps++;
+
+ IFDBG(3) g_debug ("New layer mask %d", mask_id);
+ gimp_layer_add_mask (layer_id, mask_id);
+ buffer = gimp_drawable_get_buffer (mask_id);
+ gegl_buffer_set (buffer,
+ &mask_rect,
+ 0, get_mask_format (img_a),
+ lyr_chn[user_mask_chn]->data + (
+ (mask_rect.y - lm_y) * lm_w +
+ (mask_rect.x - lm_x)) * bps,
+ lm_w * bps);
+ g_object_unref (buffer);
+ gimp_layer_set_apply_mask (layer_id,
+ ! lyr_a[lidx]->layer_mask.mask_flags.disabled);
+ }
+ g_free (lyr_chn[user_mask_chn]->data);
+ }
+ }
+
+ /* Insert the layer */
+ if (lyr_a[lidx]->group_type == 0 || /* normal layer */
+ lyr_a[lidx]->group_type == 3 /* group layer end marker */)
+ {
+ if (clipping_group_id != -1)
+ {
+ gimp_image_insert_layer (image_id, layer_id, clipping_group_id, 0);
+
+ if (lyr_a[lidx]->clipping_group_type == 2)
+ {
+ /* End of our clipping group. */
+ clipping_group_id = -1;
+ }
+ }
+ else
+ {
+ gimp_image_insert_layer (image_id, layer_id, parent_group_id, 0);
+ }
+
+ }
+ }
+
+ for (cidx = 0; cidx < lyr_a[lidx]->num_channels; ++cidx)
+ if (lyr_chn[cidx])
+ g_free (lyr_chn[cidx]);
+ g_free (lyr_chn);
+ }
+ g_free (lyr_a[lidx]->chn_info);
+ g_free (lyr_a[lidx]->name);
+ g_free (lyr_a[lidx]);
+ }
+ g_free (lyr_a);
+ g_array_free (parent_group_stack, FALSE);
+
+ /* Set the active layer */
+ if (active_layer_id >= 0)
+ gimp_image_set_active_layer (image_id, active_layer_id);
+
+ return 0;
+}
+
+static gint
+add_merged_image (gint32 image_id,
+ PSDimage *img_a,
+ FILE *f,
+ GError **error)
+{
+ PSDchannel chn_a[MAX_CHANNELS];
+ gchar *alpha_name;
+ guchar *pixels;
+ guint16 comp_mode;
+ guint16 base_channels;
+ guint16 extra_channels;
+ guint16 total_channels;
+ guint16 bps;
+ guint16 *rle_pack_len[MAX_CHANNELS];
+ guint32 alpha_id;
+ gint32 layer_size;
+ gint32 layer_id = -1;
+ gint32 channel_id = -1;
+ gint16 alpha_opacity;
+ gint cidx; /* Channel index */
+ gint rowi; /* Row index */
+ gint offset;
+ gint i;
+ gboolean alpha_visible;
+ gboolean alpha_channel = FALSE;
+ GeglBuffer *buffer;
+ GimpImageType image_type;
+ GimpRGB alpha_rgb;
+
+ total_channels = img_a->channels;
+ extra_channels = 0;
+ bps = img_a->bps / 8;
+ if (bps == 0)
+ bps++;
+
+ if (img_a->num_layers > 0 && img_a->color_mode == PSD_CMYK)
+ {
+ /* In this case there is no conversion. Merged image is RGB. */
+ img_a->color_mode = PSD_RGB;
+ if (! img_a->transparency)
+ total_channels--;
+ }
+
+ if ((img_a->color_mode == PSD_BITMAP ||
+ img_a->color_mode == PSD_MULTICHANNEL ||
+ img_a->color_mode == PSD_GRAYSCALE ||
+ img_a->color_mode == PSD_DUOTONE ||
+ img_a->color_mode == PSD_INDEXED) &&
+ total_channels > 1)
+ {
+ extra_channels = total_channels - 1;
+ }
+ else if ((img_a->color_mode == PSD_RGB ||
+ img_a->color_mode == PSD_LAB) &&
+ total_channels > 3)
+ {
+ extra_channels = total_channels - 3;
+ }
+ else if ((img_a->color_mode == PSD_CMYK) &&
+ total_channels > 4)
+ {
+ extra_channels = total_channels - 4;
+ }
+ if (img_a->transparency && extra_channels > 0)
+ extra_channels--;
+ base_channels = total_channels - extra_channels;
+
+ if (img_a->merged_image_only)
+ {
+ if (! img_a->transparency && extra_channels > 0)
+ {
+ alpha_channel = TRUE;
+ base_channels += 1;
+ }
+ extra_channels = 0;
+ total_channels = base_channels;
+ }
+
+ /* ----- Read merged image & extra channel pixel data ----- */
+ if (img_a->merged_image_only ||
+ img_a->num_layers == 0 ||
+ extra_channels > 0)
+ {
+ guint32 block_len;
+ guint32 block_start;
+
+ block_start = img_a->merged_image_start;
+ block_len = img_a->merged_image_len;
+
+ fseek (f, block_start, SEEK_SET);
+
+ if (fread (&comp_mode, COMP_MODE_SIZE, 1, f) < 1)
+ {
+ psd_set_error (feof (f), errno, error);
+ return -1;
+ }
+ comp_mode = GUINT16_FROM_BE (comp_mode);
+
+ switch (comp_mode)
+ {
+ case PSD_COMP_RAW: /* Planar raw data */
+ IFDBG(3) g_debug ("Raw data length: %d", block_len);
+ for (cidx = 0; cidx < total_channels; ++cidx)
+ {
+ chn_a[cidx].columns = img_a->columns;
+ chn_a[cidx].rows = img_a->rows;
+ if (read_channel_data (&chn_a[cidx], img_a->bps,
+ PSD_COMP_RAW, NULL, f, 0,
+ error) < 1)
+ return -1;
+ }
+ break;
+
+ case PSD_COMP_RLE: /* Packbits */
+ /* Image data is stored as packed scanlines in planar order
+ with all compressed length counters stored first */
+ IFDBG(3) g_debug ("RLE length data: %d, RLE data block: %d",
+ total_channels * img_a->rows * 2,
+ block_len - (total_channels * img_a->rows * 2));
+ for (cidx = 0; cidx < total_channels; ++cidx)
+ {
+ chn_a[cidx].columns = img_a->columns;
+ chn_a[cidx].rows = img_a->rows;
+ rle_pack_len[cidx] = g_malloc (img_a->rows * 2);
+ for (rowi = 0; rowi < img_a->rows; ++rowi)
+ {
+ if (fread (&rle_pack_len[cidx][rowi], 2, 1, f) < 1)
+ {
+ psd_set_error (feof (f), errno, error);
+ return -1;
+ }
+ rle_pack_len[cidx][rowi] = GUINT16_FROM_BE (rle_pack_len[cidx][rowi]);
+ }
+ }
+
+ /* 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)
+ {
+ if (read_channel_data (&chn_a[cidx], img_a->bps,
+ PSD_COMP_RLE, rle_pack_len[cidx], f, 0,
+ error) < 1)
+ return -1;
+ g_free (rle_pack_len[cidx]);
+ }
+ break;
+
+ case PSD_COMP_ZIP: /* ? */
+ case PSD_COMP_ZIP_PRED:
+ default:
+ g_set_error (error, G_FILE_ERROR, G_FILE_ERROR_FAILED,
+ _("Unsupported compression mode: %d"), comp_mode);
+ return -1;
+ break;
+ }
+ }
+
+ /* ----- Draw merged image ----- */
+ 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 || alpha_channel);
+
+ layer_size = img_a->columns * img_a->rows;
+ pixels = g_malloc (layer_size * base_channels * bps);
+ for (cidx = 0; cidx < base_channels; ++cidx)
+ {
+ for (i = 0; i < layer_size; ++i)
+ {
+ memcpy (&pixels[((i * base_channels) + cidx) * bps],
+ &chn_a[cidx].data[i * bps], bps);
+ }
+ g_free (chn_a[cidx].data);
+ }
+
+ /* Add background layer */
+ IFDBG(2) g_debug ("Draw merged image");
+ layer_id = gimp_layer_new (image_id, _("Background"),
+ img_a->columns, img_a->rows,
+ image_type,
+ 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);
+ if (img_a->color_mode == PSD_CMYK)
+ {
+ guchar *dst0;
+
+ dst0 = g_malloc (base_channels * layer_size * sizeof(float));
+ psd_convert_cmyk_to_srgb ( img_a,
+ dst0, pixels,
+ img_a->columns, img_a->rows,
+ alpha_channel);
+ g_free (pixels);
+ pixels = dst0;
+ }
+
+ gegl_buffer_set (buffer,
+ GEGL_RECTANGLE (0, 0,
+ gegl_buffer_get_width (buffer),
+ gegl_buffer_get_height (buffer)),
+ 0, get_layer_format (img_a,
+ img_a->transparency ||
+ alpha_channel),
+ pixels, GEGL_AUTO_ROWSTRIDE);
+
+ if (img_a->color_mode == PSD_CMYK)
+ img_a->color_mode = PSD_RGB;
+
+ /* 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, 1);
+
+ while (gegl_buffer_iterator_next (iter))
+ {
+ gfloat *data = iter->items[0].data;
+
+ 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);
+ }
+ else
+ {
+ /* Free merged image data for layered image */
+ if (extra_channels)
+ for (cidx = 0; cidx < base_channels; ++cidx)
+ 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 */
+ && image_id > -1)
+ {
+ IFDBG(2) g_debug ("Add extra channels");
+ pixels = g_malloc(0);
+
+ /* Get channel resource data */
+ if (img_a->transparency)
+ offset = 1;
+ else
+ offset = 0;
+
+ /* Draw channels */
+ IFDBG(2) g_debug ("Number of channels: %d", extra_channels);
+ for (i = 0; i < extra_channels; ++i)
+ {
+ /* Alpha channel name */
+ alpha_name = NULL;
+ alpha_visible = FALSE;
+ /* Quick mask channel*/
+ if (img_a->quick_mask_id)
+ if (i == img_a->quick_mask_id - base_channels + offset)
+ {
+ /* Free "Quick Mask" channel name */
+ alpha_name = g_ptr_array_index (img_a->alpha_names, i + offset);
+ if (alpha_name)
+ g_free (alpha_name);
+ alpha_name = g_strdup (GIMP_IMAGE_QUICK_MASK_NAME);
+ alpha_visible = TRUE;
+ }
+ if (! alpha_name && img_a->alpha_names)
+ if (offset < img_a->alpha_names->len
+ && i + offset <= img_a->alpha_names->len)
+ alpha_name = g_ptr_array_index (img_a->alpha_names, i + offset);
+ if (! alpha_name)
+ alpha_name = g_strdup (_("Extra"));
+
+ if (offset < img_a->alpha_id_count &&
+ offset + i <= img_a->alpha_id_count)
+ alpha_id = img_a->alpha_id[i + offset];
+ else
+ alpha_id = 0;
+ if (offset < img_a->alpha_display_count &&
+ i + offset <= img_a->alpha_display_count)
+ {
+ alpha_rgb = img_a->alpha_display_info[i + offset]->gimp_color;
+ alpha_opacity = img_a->alpha_display_info[i + offset]->opacity;
+ }
+ else
+ {
+ gimp_rgba_set (&alpha_rgb, 1.0, 0.0, 0.0, 1.0);
+ alpha_opacity = 50;
+ }
+
+ cidx = base_channels + i;
+ pixels = g_realloc (pixels, chn_a[cidx].columns * chn_a[cidx].rows * bps);
+ memcpy (pixels, chn_a[cidx].data, chn_a[cidx].columns * chn_a[cidx].rows * bps);
+ channel_id = gimp_channel_new (image_id, alpha_name,
+ chn_a[cidx].columns, chn_a[cidx].rows,
+ alpha_opacity, &alpha_rgb);
+ gimp_image_insert_channel (image_id, channel_id, -1, i);
+ g_free (alpha_name);
+ buffer = gimp_drawable_get_buffer (channel_id);
+ if (alpha_id)
+ gimp_item_set_tattoo (channel_id, alpha_id);
+ gimp_item_set_visible (channel_id, alpha_visible);
+ gegl_buffer_set (buffer,
+ GEGL_RECTANGLE (0, 0,
+ gegl_buffer_get_width (buffer),
+ gegl_buffer_get_height (buffer)),
+ 0, get_channel_format (img_a),
+ pixels, GEGL_AUTO_ROWSTRIDE);
+ g_object_unref (buffer);
+ g_free (chn_a[cidx].data);
+ }
+
+ g_free (pixels);
+ if (img_a->alpha_names)
+ g_ptr_array_free (img_a->alpha_names, TRUE);
+
+ if (img_a->alpha_id)
+ g_free (img_a->alpha_id);
+
+ if (img_a->alpha_display_info)
+ {
+ for (cidx = 0; cidx < img_a->alpha_display_count; ++cidx)
+ g_free (img_a->alpha_display_info[cidx]);
+ g_free (img_a->alpha_display_info);
+ }
+ }
+
+ /* FIXME gimp image tattoo state */
+
+ return 0;
+}
+
+
+/* Local utility functions */
+static gint32
+add_clipping_group (gint32 image_id,
+ gint32 parent_id)
+{
+ gint32 clipping_group_id = -1;
+
+ /* 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_id = gimp_layer_group_new (image_id);
+
+ gimp_item_set_name (clipping_group_id, "Group added by GIMP");
+ gimp_layer_set_blend_space (clipping_group_id, GIMP_LAYER_COLOR_SPACE_RGB_PERCEPTUAL);
+ gimp_layer_set_composite_space (clipping_group_id, GIMP_LAYER_COLOR_SPACE_RGB_PERCEPTUAL);
+ gimp_layer_set_composite_mode (clipping_group_id, GIMP_LAYER_COMPOSITE_UNION);
+
+ gimp_image_insert_layer (image_id, clipping_group_id, parent_id, 0);
+
+ return clipping_group_id;
+}
+
+static gchar *
+get_psd_color_mode_name (PSDColorMode mode)
+{
+ static gchar * const psd_color_mode_names[] =
+ {
+ "BITMAP",
+ "GRAYSCALE",
+ "INDEXED",
+ "RGB",
+ "CMYK",
+ "UNKNOWN (5)",
+ "UNKNOWN (6)",
+ "MULTICHANNEL",
+ "DUOTONE",
+ "LAB"
+ };
+
+ static gchar *err_name = NULL;
+
+ if (mode >= PSD_BITMAP && mode <= PSD_LAB)
+ return psd_color_mode_names[mode];
+
+ g_free (err_name);
+ err_name = g_strdup_printf ("UNKNOWN (%d)", mode);
+
+ return err_name;
+}
+
+static void
+psd_to_gimp_color_map (guchar *map256)
+{
+ guchar *tmpmap;
+ gint i;
+
+ tmpmap = g_malloc (3 * 256);
+
+ for (i = 0; i < 256; ++i)
+ {
+ tmpmap[i*3 ] = map256[i];
+ tmpmap[i*3+1] = map256[i+256];
+ tmpmap[i*3+2] = map256[i+512];
+ }
+
+ memcpy (map256, tmpmap, 3 * 256);
+ g_free (tmpmap);
+}
+
+static GimpImageType
+get_gimp_image_type (GimpImageBaseType image_base_type,
+ gboolean alpha)
+{
+ GimpImageType image_type;
+
+ switch (image_base_type)
+ {
+ case GIMP_GRAY:
+ image_type = (alpha) ? GIMP_GRAYA_IMAGE : GIMP_GRAY_IMAGE;
+ break;
+
+ case GIMP_INDEXED:
+ image_type = (alpha) ? GIMP_INDEXEDA_IMAGE : GIMP_INDEXED_IMAGE;
+ break;
+
+ case GIMP_RGB:
+ image_type = (alpha) ? GIMP_RGBA_IMAGE : GIMP_RGB_IMAGE;
+ break;
+
+ default:
+ image_type = -1;
+ break;
+ }
+
+ return image_type;
+}
+
+static voidpf
+zzalloc (voidpf opaque, uInt items, uInt size)
+{
+ /* overflow check missing */
+ return g_malloc (items * size);
+}
+
+static void
+zzfree (voidpf opaque, voidpf address)
+{
+ g_free (address);
+}
+
+static gint
+read_channel_data (PSDchannel *channel,
+ guint16 bps,
+ guint16 compression,
+ const guint16 *rle_pack_len,
+ FILE *f,
+ guint32 comp_len,
+ GError **error)
+{
+ gchar *raw_data;
+ gchar *src;
+ guint32 readline_len;
+ gint i, j;
+
+ if (bps == 1)
+ readline_len = ((channel->columns + 7) / 8);
+ else
+ readline_len = (channel->columns * bps / 8);
+
+ IFDBG(3) g_debug ("raw data size %d x %d = %d", readline_len,
+ channel->rows, readline_len * channel->rows);
+
+ /* sanity check, int overflow check (avoid divisions by zero) */
+ if ((channel->rows == 0) || (channel->columns == 0) ||
+ (channel->rows > G_MAXINT32 / channel->columns / MAX (bps / 8, 1)))
+ {
+ g_set_error (error, G_FILE_ERROR, G_FILE_ERROR_FAILED,
+ _("Unsupported or invalid channel size"));
+ return -1;
+ }
+
+ raw_data = g_malloc (readline_len * channel->rows);
+ switch (compression)
+ {
+ case PSD_COMP_RAW:
+ if (fread (raw_data, readline_len, channel->rows, f) < 1)
+ {
+ psd_set_error (feof (f), errno, error);
+ return -1;
+ }
+ break;
+
+ case PSD_COMP_RLE:
+ for (i = 0; i < channel->rows; ++i)
+ {
+ src = gegl_scratch_alloc (rle_pack_len[i]);
+/* FIXME check for over-run
+ if (ftell (f) + rle_pack_len[i] > block_end)
+ {
+ psd_set_error (TRUE, errno, error);
+ return -1;
+ }
+*/
+ if (fread (src, rle_pack_len[i], 1, f) < 1)
+ {
+ psd_set_error (feof (f), errno, error);
+ gegl_scratch_free (src);
+ return -1;
+ }
+ /* FIXME check for errors returned from decode packbits */
+ decode_packbits (src, raw_data + i * readline_len,
+ rle_pack_len[i], readline_len);
+ gegl_scratch_free (src);
+ }
+ break;
+ case PSD_COMP_ZIP:
+ case PSD_COMP_ZIP_PRED:
+ {
+ z_stream zs;
+
+ src = g_malloc (comp_len);
+ if (fread (src, comp_len, 1, f) < 1)
+ {
+ psd_set_error (feof (f), errno, error);
+ g_free (src);
+ return -1;
+ }
+
+ zs.next_in = (guchar*) src;
+ zs.avail_in = comp_len;
+ zs.next_out = (guchar*) raw_data;
+ zs.avail_out = readline_len * channel->rows;
+ zs.zalloc = zzalloc;
+ zs.zfree = zzfree;
+
+ if (inflateInit (&zs) == Z_OK &&
+ inflate (&zs, Z_FINISH) == Z_STREAM_END)
+ {
+ inflateEnd (&zs);
+ }
+ else
+ {
+ g_set_error (error, G_FILE_ERROR, G_FILE_ERROR_FAILED,
+ _("Failed to decompress data"));
+ g_free (src);
+ return -1;
+ }
+
+ g_free (src);
+ break;
+ }
+ }
+
+ /* Convert channel data to GIMP format */
+ switch (bps)
+ {
+ case 32:
+ {
+ guint32 *data = (guint32*) raw_data;
+
+ channel->data = raw_data;
+ raw_data = NULL;
+
+ for (i = 0; i < channel->rows * channel->columns; ++i)
+ data[i] = GUINT32_FROM_BE (data[i]);
+
+ if (compression == PSD_COMP_ZIP_PRED)
+ {
+ for (i = 0; i < channel->rows; ++i)
+ for (j = 1; j < channel->columns; ++j)
+ data[i * channel->columns + j] += data[i * channel->columns + j - 1];
+ }
+ break;
+ }
+
+ case 16:
+ {
+ guint16 *data = (guint16*) raw_data;
+
+ channel->data = raw_data;
+ raw_data = NULL;
+
+ for (i = 0; i < channel->rows * channel->columns; ++i)
+ data[i] = GUINT16_FROM_BE (data[i]);
+
+ if (compression == PSD_COMP_ZIP_PRED)
+ {
+ for (i = 0; i < channel->rows; ++i)
+ for (j = 1; j < channel->columns; ++j)
+ data[i * channel->columns + j] += data[i * channel->columns + j - 1];
+ }
+ break;
+ }
+
+ case 8:
+ channel->data = raw_data;
+ raw_data = NULL;
+
+ if (compression == PSD_COMP_ZIP_PRED)
+ {
+ for (i = 0; i < channel->rows; ++i)
+ for (j = 1; j < channel->columns; ++j)
+ channel->data[i * channel->columns + j] += channel->data[i * channel->columns + j - 1];
+ }
+ break;
+
+ case 1:
+ channel->data = (gchar *) g_malloc (channel->rows * channel->columns);
+ convert_1_bit (raw_data, channel->data, channel->rows, channel->columns);
+ break;
+
+ default:
+ return -1;
+ break;
+ }
+
+ g_free (raw_data);
+
+ return 1;
+}
+
+static void
+convert_1_bit (const gchar *src,
+ gchar *dst,
+ guint32 rows,
+ guint32 columns)
+{
+/* Convert bits to bytes left to right by row.
+ Rows are padded out to a byte boundary.
+*/
+ guint32 row_pos = 0;
+ gint i, j;
+
+ IFDBG(3) g_debug ("Start 1 bit conversion");
+
+ for (i = 0; i < rows * ((columns + 7) / 8); ++i)
+ {
+ guchar mask = 0x80;
+ for (j = 0; j < 8 && row_pos < columns; ++j)
+ {
+ *dst = (*src & mask) ? 0 : 1;
+ IFDBG(3) g_debug ("byte %d, bit %d, offset %d, src %d, dst %d",
+ i , j, row_pos, *src, *dst);
+ dst++;
+ mask >>= 1;
+ row_pos++;
+ }
+ if (row_pos >= columns)
+ row_pos = 0;
+ src++;
+ }
+ IFDBG(3) g_debug ("End 1 bit conversion");
+}
+
+static const Babl*
+get_layer_format (PSDimage *img_a,
+ gboolean alpha)
+{
+ const Babl *format = NULL;
+
+ switch (get_gimp_image_type (img_a->base_type, alpha))
+ {
+ case GIMP_GRAY_IMAGE:
+ switch (img_a->bps)
+ {
+ case 32:
+ format = babl_format ("Y' u32");
+ break;
+
+ case 16:
+ format = babl_format ("Y' u16");
+ break;
+
+ case 8:
+ case 1:
+ format = babl_format ("Y' u8");
+ break;
+
+ default:
+ return NULL;
+ break;
+ }
+ break;
+
+ case GIMP_GRAYA_IMAGE:
+ switch (img_a->bps)
+ {
+ case 32:
+ format = babl_format ("Y'A u32");
+ break;
+
+ case 16:
+ format = babl_format ("Y'A u16");
+ break;
+
+ case 8:
+ case 1:
+ format = babl_format ("Y'A u8");
+ break;
+
+ default:
+ return NULL;
+ break;
+ }
+ break;
+
+ case GIMP_RGB_IMAGE:
+ switch (img_a->bps)
+ {
+ case 32:
+ format = babl_format ("R'G'B' u32");
+ break;
+
+ case 16:
+ format = babl_format ("R'G'B' u16");
+ break;
+
+ case 8:
+ case 1:
+ format = babl_format (img_a->color_mode == PSD_CMYK ? "R'G'B' float" : "R'G'B' u8");
+ break;
+
+ default:
+ return NULL;
+ break;
+ }
+ break;
+
+ case GIMP_RGBA_IMAGE:
+ switch (img_a->bps)
+ {
+ case 32:
+ format = babl_format ("R'G'B'A u32");
+ break;
+
+ case 16:
+ format = babl_format ("R'G'B'A u16");
+ break;
+
+ case 8:
+ case 1:
+ format = babl_format (img_a->color_mode == PSD_CMYK ? "R'G'B'A float" : "R'G'B'A u8");
+ break;
+
+ default:
+ return NULL;
+ break;
+ }
+ break;
+
+ default:
+ return NULL;
+ break;
+ }
+
+ return format;
+}
+
+static const Babl*
+get_channel_format (PSDimage *img_a)
+{
+ const Babl *format = NULL;
+
+ switch (img_a->bps)
+ {
+ case 32:
+ format = babl_format ("Y u32");
+ break;
+
+ case 16:
+ format = babl_format ("Y u16");
+ break;
+
+ case 8:
+ case 1:
+ /* see gimp_image_get_channel_format() */
+ format = babl_format ("Y' u8");
+ break;
+
+ default:
+ break;
+ }
+
+ return format;
+}
+
+static const Babl*
+get_mask_format (PSDimage *img_a)
+{
+ const Babl *format = NULL;
+
+ switch (img_a->bps)
+ {
+ case 32:
+ format = babl_format ("Y u32");
+ break;
+
+ case 16:
+ format = babl_format ("Y u16");
+ break;
+
+ case 8:
+ case 1:
+ format = babl_format ("Y u8");
+ break;
+
+ default:
+ break;
+ }
+
+ return format;
+}