diff options
Diffstat (limited to 'plug-ins/file-psd/psd-load.c')
-rw-r--r-- | plug-ins/file-psd/psd-load.c | 2577 |
1 files changed, 2577 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..25b08a9 --- /dev/null +++ b/plug-ins/file-psd/psd-load.c @@ -0,0 +1,2577 @@ +/* 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 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); + 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.mask_flags.relative_pos = FALSE; + lyr_a[lidx]->layer_mask.mask_flags.disabled = FALSE; + lyr_a[lidx]->layer_mask.mask_flags.invert = FALSE; + + switch (block_len) + { + case 0: + break; + + case 20: + 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 + || fread (&lyr_a[lidx]->layer_mask.extra_def_color, 1, 1, f) < 1 + || fread (&lyr_a[lidx]->layer_mask.extra_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; + break; + case 36: /* If we have a 36 byte mask record assume second data set is correct */ + 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 + || fread (&lyr_a[lidx]->layer_mask.extra_def_color, 1, 1, f) < 1 + || fread (&lyr_a[lidx]->layer_mask.extra_flags, 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; + } + 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); + 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; + break; + + default: + IFDBG(1) g_debug ("Unknown layer mask record size ... skipping"); + if (fseek (f, block_len, SEEK_CUR) < 0) + { + psd_set_error (feof (f), errno, error); + return NULL; + } + } + + /* 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; + } + + 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, %d", + lyr_a[lidx]->layer_mask.def_color, + lyr_a[lidx]->layer_mask.extra_def_color); + + /* 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_alpha, + 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; + guint16 alpha_chn; + guint16 user_mask_chn; + guint16 layer_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 rowi; /* Row index */ + gboolean alpha; + gboolean user_mask; + gboolean empty; + gboolean empty_mask; + 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; + } + + /* 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.", lidx); + + 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 */ + + 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) + { + channel_idx[layer_channels] = cidx; /* Assumes in sane order */ + layer_channels++; /* RGB, Lab, CMYK etc. */ + } + } + + if (alpha) + { + channel_idx[layer_channels] = alpha_chn; + layer_channels++; + } + + /* 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 (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]->blend_mode, &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 * layer_channels; + + if (img_a->color_mode == PSD_CMYK) + { + dst0 = gegl_scratch_alloc (layer_channels * + iter->length); + } + + for (cidx = 0; cidx < layer_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 */) + { + 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; + 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->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) + { + 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); + + 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) + img_a->color_mode = PSD_RGB; + + gegl_buffer_set (buffer, + GEGL_RECTANGLE (0, 0, + gegl_buffer_get_width (buffer), + gegl_buffer_get_height (buffer)), + 0, get_layer_format (img_a, (base_channels % 2) == 0), + 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, 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 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; +} |