/* PIKA - Photo and Image Kooker Application * a rebranding of The GNU Image Manipulation Program (created with heckimp) * A derived work which may be trivial. However, any changes may be (C)2023 by Aldercone Studio * * Original copyright, applying to most contents (license remains unchanged): * Copyright (C) 1995 Spencer Kimball and Peter Mattis * * PIKA 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 . */ #include "config.h" #include #include #include #include #include #include #include "psd.h" #include "psd-util.h" #include "psd-image-res-load.h" #include "psd-layer-res-load.h" #include "psd-load.h" #include "libpika/stdplugins-intl.h" #define COMP_MODE_SIZE sizeof(guint16) /* Local function prototypes */ static gint read_header_block (PSDimage *img_a, GInputStream *input, GError **error); static gint read_color_mode_block (PSDimage *img_a, GInputStream *input, GError **error); static gint read_image_resource_block (PSDimage *img_a, GInputStream *input, GError **error); static PSDlayer ** read_layer_block (PSDimage *img_a, GInputStream *input, GError **error); static PSDlayer ** read_layer_info (PSDimage *img_a, GInputStream *input, GError **error); static gint read_merged_image_block (PSDimage *img_a, GInputStream *input, GError **error); static PikaImage * create_pika_image (PSDimage *img_a, GFile *file); static gint add_color_map (PikaImage *image, PSDimage *img_a); static gint add_image_resources (PikaImage *image, PSDimage *img_a, GInputStream *input, gboolean *resolution_loaded, gboolean *profile_loaded, GError **error); static gint add_layers (PikaImage *image, PSDimage *img_a, PSDlayer **lyr_a, GInputStream *input, GError **error); static gint add_merged_image (PikaImage *image, PSDimage *img_a, GInputStream *input, GError **error); /* Local utility function prototypes */ static PikaLayer * add_clipping_group (PikaImage *image, PikaLayer *parent); static gchar * get_psd_color_mode_name (PSDColorMode mode); static void psd_to_pika_color_map (guchar *map256); static PikaImageType get_pika_image_type (PikaImageBaseType image_base_type, gboolean alpha); static void free_lyr_a (PSDlayer **lyr_a, gint layer_count); static void free_lyr_chn (PSDchannel **lyr_chn, gint channel_count); static gboolean read_RLE_channel (PSDimage *img_a, PSDchannel *lyr_chn, guint64 channel_data_len, GInputStream *input, GError **error); static gint read_channel_data (PSDchannel *channel, guint16 bps, guint16 compression, const guint32 *rle_pack_len, GInputStream *input, guint32 comp_len, GError **error); static void decode_32_bit_predictor (gchar *src, gchar *dst, guint32 rows, guint32 columns); 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); static void initialize_unsupported (PSDSupport *supported_features); /* Main file load function */ PikaImage * load_image (GFile *file, gboolean merged_image_only, gboolean *resolution_loaded, gboolean *profile_loaded, PSDSupport *unsupported_features, GError **load_error) { GInputStream *input; PSDimage img_a; PSDlayer **lyr_a; PikaImage *image = NULL; GError *error = NULL; img_a.ibm_pc_format = FALSE; img_a.cmyk_transform = img_a.cmyk_transform_alpha = NULL; img_a.cmyk_profile = NULL; initialize_unsupported (unsupported_features); img_a.unsupported_features = unsupported_features; /* ----- Open PSD file ----- */ input = G_INPUT_STREAM (g_file_read (file, NULL, &error)); if (! input) { if (! error) g_set_error (load_error, G_FILE_ERROR, g_file_error_from_errno (errno), _("Could not open '%s' for reading: %s"), pika_file_get_utf8_name (file), g_strerror (errno)); else { g_set_error (load_error, G_FILE_ERROR, G_FILE_ERROR_FAILED, _("Could not open '%s' for reading: %s"), pika_file_get_utf8_name (file), error->message); g_error_free (error); } return NULL; } pika_progress_init_printf (_("Opening '%s'"), pika_file_get_utf8_name (file)); IFDBG(1) g_debug ("Open file %s", pika_file_get_utf8_name (file)); img_a.merged_image_only = merged_image_only; /* ----- Read the PSD file Header block ----- */ IFDBG(2) g_debug ("Read header block at offset %" G_GOFFSET_FORMAT, PSD_TELL(input)); if (read_header_block (&img_a, input, &error) < 0) goto load_error; pika_progress_update (0.1); /* ----- Read the PSD file Color Mode block ----- */ IFDBG(2) g_debug ("Read color mode block at offset %" G_GOFFSET_FORMAT, PSD_TELL(input)); if (read_color_mode_block (&img_a, input, &error) < 0) goto load_error; pika_progress_update (0.2); /* ----- Read the PSD file Image Resource block ----- */ IFDBG(2) g_debug ("Read image resource block at offset %" G_GOFFSET_FORMAT, PSD_TELL(input)); if (read_image_resource_block (&img_a, input, &error) < 0) goto load_error; pika_progress_update (0.3); /* ----- Read the PSD file Layer & Mask block ----- */ IFDBG(2) g_debug ("Read layer & mask block at offset %" G_GOFFSET_FORMAT, PSD_TELL(input)); img_a.mask_layer_len = 0; lyr_a = read_layer_block (&img_a, input, &error); if (merged_image_only) { if (error) { g_debug ("Error loading layer block: %s", error->message); g_clear_error (&error); } } else if (error) { goto load_error; } else if (lyr_a == NULL) { if (img_a.num_layers > 0) { g_warning ("No error message but loading layer failed. This should not happen!"); goto load_error; } } pika_progress_update (0.4); /* ----- Read the PSD file Merged Image Data block ----- */ IFDBG(2) g_debug ("Read merged image and extra alpha channel block at offset %" G_GOFFSET_FORMAT, PSD_TELL(input)); if (read_merged_image_block (&img_a, input, &error) < 0) goto load_error; pika_progress_update (0.5); /* ----- Create PIKA image ----- */ IFDBG(2) g_debug ("Create PIKA image"); image = create_pika_image (&img_a, file); if (! image) goto load_error; pika_progress_update (0.6); /* ----- Add color map ----- */ IFDBG(2) g_debug ("Add color map"); if (add_color_map (image, &img_a) < 0) goto load_error; pika_progress_update (0.7); /* ----- Add image resources ----- */ IFDBG(2) g_debug ("Add image resources"); if (add_image_resources (image, &img_a, input, resolution_loaded, profile_loaded, &error) < 0) goto load_error; pika_progress_update (0.8); /* ----- Add layers -----*/ IFDBG(2) g_debug ("Add layers"); if (add_layers (image, &img_a, lyr_a, input, &error) < 0) goto load_error; pika_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, &img_a, input, &error) < 0) goto load_error; pika_progress_update (1.0); IFDBG(2) g_debug ("Close file & return, image id: %d", pika_image_get_id (image)); IFDBG(1) g_debug ("\n----------------------------------------" "----------------------------------------\n"); pika_image_clean_all (image); pika_image_undo_enable (image); g_object_unref (input); return image; /* ----- 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) pika_image_delete (image); /* Close file if Open */ g_object_unref (input); return NULL; } /* Loading metadata for external file formats */ PikaImage * load_image_metadata (GFile *file, gint data_length, PikaImage *image, gboolean for_layers, gboolean is_cmyk, PSDSupport *unsupported_features, GError **error) { GInputStream *input; PSDimage img_a; gboolean profile_loaded; gboolean resolution_loaded; /* Convert metadata file to PSD format */ input = G_INPUT_STREAM (g_file_read (file, NULL, error)); if (! input) { g_object_unref (input); return image; } /* Load metadata for external file format */ img_a.image_res_len = data_length; img_a.image_res_start = 0; img_a.version = 1; img_a.layer_selection = NULL; img_a.columns = pika_image_get_width (image); img_a.rows = pika_image_get_height (image); img_a.base_type = pika_image_get_base_type (image); img_a.merged_image_only = FALSE; img_a.ibm_pc_format = FALSE; 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; img_a.cmyk_profile = NULL; initialize_unsupported (unsupported_features); img_a.unsupported_features = unsupported_features; if (! for_layers) { PSDimageres res_a; while (PSD_TELL (input) < img_a.image_res_start + img_a.image_res_len) { if (get_image_resource_header (&res_a, input, error) < 0) break; 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"); break; } if (load_image_resource (&res_a, image, &img_a, input, &resolution_loaded, &profile_loaded, error) < 0) break; } } else { PSDlayer **lyr_a = NULL; gchar sig[4]; gchar key[4]; if (psd_read (input, &sig, 4, error) < 4) { g_object_unref (input); return image; } if (psd_read (input, &key, 4, error) < 4) { g_object_unref (input); return image; } /* Treat labels/ints as Little Endian */ if (memcmp (sig, "MIB8", 4) == 0) img_a.ibm_pc_format = TRUE; /* Setting up PSDImage structure */ if (memcmp (key, "Layr", 4) == 0 || memcmp (key, "ryaL", 4) == 0) { img_a.bps = 8; } else if (memcmp (key, "Lr16", 4) == 0 || memcmp (key, "61rL", 4) == 0) { img_a.bps = 16; } else if (memcmp (key, "Lr32", 4) == 0 || memcmp (key, "23rL", 4) == 0) { img_a.bps = 32; } else { /* Get BPC from existing image */ switch (pika_image_get_precision (image)) { case PIKA_PRECISION_U8_LINEAR: case PIKA_PRECISION_U8_NON_LINEAR: case PIKA_PRECISION_U8_PERCEPTUAL: img_a.bps = 8; break; case PIKA_PRECISION_U16_LINEAR: case PIKA_PRECISION_U16_NON_LINEAR: case PIKA_PRECISION_U16_PERCEPTUAL: case PIKA_PRECISION_HALF_LINEAR: case PIKA_PRECISION_HALF_NON_LINEAR: case PIKA_PRECISION_HALF_PERCEPTUAL: img_a.bps = 16; break; case PIKA_PRECISION_U32_LINEAR: case PIKA_PRECISION_U32_NON_LINEAR: case PIKA_PRECISION_U32_PERCEPTUAL: case PIKA_PRECISION_FLOAT_LINEAR: case PIKA_PRECISION_FLOAT_NON_LINEAR: case PIKA_PRECISION_FLOAT_PERCEPTUAL: img_a.bps = 32; break; default: g_set_error (error, G_FILE_ERROR, G_FILE_ERROR_FAILED, _("Invalid PSD metadata layer format")); return image; } } switch (pika_image_get_base_type (image)) { case PIKA_RGB: img_a.color_mode = PSD_RGB; break; case PIKA_GRAY: img_a.color_mode = PSD_GRAYSCALE; break; case PIKA_INDEXED: img_a.color_mode = PSD_INDEXED; break; } if (is_cmyk) img_a.color_mode = PSD_CMYK; /* Set layer block size from metadata */ img_a.mask_layer_len = data_length; lyr_a = read_layer_block (&img_a, input, error); if (! lyr_a) { g_object_unref (input); return image; } add_layers (image, &img_a, lyr_a, input, error); } g_object_unref (input); return image; } /* Local functions */ static gint read_header_block (PSDimage *img_a, GInputStream *input, GError **error) { gchar sig[4]; gchar buf[6]; if (psd_read (input, sig, 4, error) < 4 || psd_read (input, &img_a->version, 2, error) < 2 || psd_read (input, buf, 6, error) < 6 || psd_read (input, &img_a->channels, 2, error) < 2 || psd_read (input, &img_a->rows, 4, error) < 4 || psd_read (input, &img_a->columns, 4, error) < 4 || psd_read (input, &img_a->bps, 2, error) < 2 || psd_read (input, &img_a->color_mode, 2, error) < 2) { psd_set_error (error); return -1; } img_a->version = GUINT16_FROM_BE (img_a->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, img_a->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 (img_a->version != 1 && img_a->version != 2) { g_set_error (error, G_FILE_ERROR, G_FILE_ERROR_FAILED, _("Unsupported file format version: %d"), img_a->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; } if (img_a->rows < 1 || img_a->rows > PIKA_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 > PIKA_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_LAB && 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 || img_a->color_mode == PSD_LAB) { if (img_a->bps != 8 && (img_a->bps != 16 || img_a->color_mode == PSD_LAB)) { 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, GInputStream *input, GError **error) { static guchar cmap[] = { 0, 0, 0, 255, 255, 255 }; guint32 block_len = 0; img_a->color_map_entries = 0; img_a->color_map_len = 0; if (psd_read (input, &block_len, 4, error) < 4) { psd_set_error (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 (psd_read (input, img_a->color_map, block_len, error) < block_len) { psd_set_error (error); return -1; } else { psd_to_pika_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 (psd_read (input, img_a->color_map, block_len, error) < block_len) { psd_set_error (error); return -1; } } else { /* Apparently it's possible to have a non zero block_len here. */ if (! psd_seek (input, block_len, G_SEEK_CUR, error)) { psd_set_error (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, GInputStream *input, GError **error) { guint64 block_len = 0; guint64 block_end; if (psd_read (input, &block_len, 4, error) < 4) { psd_set_error (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 = PSD_TELL(input); block_end = img_a->image_res_start + img_a->image_res_len; if (! psd_seek (input, block_end, G_SEEK_SET, error)) { psd_set_error (error); return -1; } return 0; } static void free_lyr_a (PSDlayer **lyr_a, gint layer_count) { gint lidx; for (lidx = 0; lidx < layer_count; ++lidx) if (lyr_a[lidx]) g_free (lyr_a[lidx]); g_free (lyr_a); } static PSDlayer ** read_layer_info (PSDimage *img_a, GInputStream *input, GError **error) { PSDlayer **lyr_a = NULL; guint64 block_len; guint64 block_rem; gint32 read_len; gint32 write_len; gint lidx; /* Layer index */ gint cidx; /* Channel index */ IFDBG(1) g_debug ("Reading layer info at offset %" G_GOFFSET_FORMAT, PSD_TELL(input)); /* Get number of layers */ if (psd_read (input, &img_a->num_layers, 2, error) < 2) { psd_set_error (error); img_a->num_layers = -1; return NULL; } if (! img_a->ibm_pc_format) img_a->num_layers = GINT16_FROM_BE (img_a->num_layers); IFDBG(2) g_debug ("Number of layers: %d (negative means transparency present)", 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_new0 (PSDlayer *, img_a->num_layers); for (lidx = 0; lidx < img_a->num_layers; ++lidx) { IFDBG(3) g_debug ("Read info for layer %d at offset %" G_GOFFSET_FORMAT, lidx, PSD_TELL(input)); /* 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 (psd_read (input, &lyr_a[lidx]->top, 4, error) < 4 || psd_read (input, &lyr_a[lidx]->left, 4, error) < 4 || psd_read (input, &lyr_a[lidx]->bottom, 4, error) < 4 || psd_read (input, &lyr_a[lidx]->right, 4, error) < 4 || psd_read (input, &lyr_a[lidx]->num_channels, 2, error) < 2) { psd_set_error (error); free_lyr_a (lyr_a, img_a->num_layers); return NULL; } if (! img_a->ibm_pc_format) { 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); free_lyr_a (lyr_a, img_a->num_layers); 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 (psd_read (input, &lyr_a[lidx]->chn_info[cidx].channel_id, 2, error) < 2 || ! psd_read_len (input, &lyr_a[lidx]->chn_info[cidx].data_len, img_a->version, error)) { psd_set_error (error); free_lyr_a (lyr_a, img_a->num_layers); return NULL; } if (! img_a->ibm_pc_format) lyr_a[lidx]->chn_info[cidx].channel_id = GINT16_FROM_BE (lyr_a[lidx]->chn_info[cidx].channel_id); else lyr_a[lidx]->chn_info[cidx].data_len = GUINT32_FROM_LE (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 %" G_GSIZE_FORMAT, lyr_a[lidx]->chn_info[cidx].channel_id, lyr_a[lidx]->chn_info[cidx].data_len); } if (psd_read (input, lyr_a[lidx]->mode_key, 4, error) < 4 || psd_read (input, lyr_a[lidx]->blend_mode, 4, error) < 4 || psd_read (input, &lyr_a[lidx]->opacity, 1, error) < 1 || psd_read (input, &lyr_a[lidx]->clipping, 1, error) < 1 || psd_read (input, &lyr_a[lidx]->flags, 1, error) < 1 || psd_read (input, &lyr_a[lidx]->filler, 1, error) < 1 || psd_read (input, &lyr_a[lidx]->extra_len, 4, error) < 4) { psd_set_error (error); free_lyr_a (lyr_a, img_a->num_layers); return NULL; } /* Reverse relevant IBM PC Format entries */ if (img_a->ibm_pc_format) { gchar blend_mode[4]; for (gint i = 0; i < 4; i++) blend_mode[i] = lyr_a[lidx]->blend_mode[3 - i]; for (gint i = 0; i < 4; i++) lyr_a[lidx]->blend_mode[i] = blend_mode[i]; lyr_a[lidx]->extra_len = GUINT32_FROM_LE (lyr_a[lidx]->extra_len); } /* Not sure if 8B64 is possible here but it won't hurt to check. */ if (memcmp (lyr_a[lidx]->mode_key, "8BIM", 4) != 0 && memcmp (lyr_a[lidx]->mode_key, "MIB8", 4) != 0 && memcmp (lyr_a[lidx]->mode_key, "8B64", 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!")); free_lyr_a (lyr_a, img_a->num_layers); 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; if (! img_a->ibm_pc_format) 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: %" G_GSIZE_FORMAT "\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 ("Offset: %" G_GOFFSET_FORMAT ", Remaining length %" G_GSIZE_FORMAT, PSD_TELL(input), block_rem); if (! lyr_a[lidx]->layer_flags.irrelevant) { if (lyr_a[lidx]->bottom < lyr_a[lidx]->top || lyr_a[lidx]->bottom - lyr_a[lidx]->top > PIKA_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); free_lyr_a (lyr_a, img_a->num_layers); return NULL; } if (lyr_a[lidx]->right < lyr_a[lidx]->left || lyr_a[lidx]->right - lyr_a[lidx]->left > PIKA_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); free_lyr_a (lyr_a, img_a->num_layers); 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); free_lyr_a (lyr_a, img_a->num_layers); return NULL; } } /* Layer mask data */ if (psd_read (input, &block_len, 4, error) < 4) { psd_set_error (error); free_lyr_a (lyr_a, img_a->num_layers); return NULL; } if (! img_a->ibm_pc_format) block_len = GUINT32_FROM_BE (block_len); else block_len = GUINT32_FROM_LE (block_len); IFDBG(3) g_debug ("Layer mask record size %" G_GOFFSET_FORMAT, 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.")); /* Translations have problems with using G_GSIZE_FORMAT, let's use g_debug. */ g_debug ("Unsupported or invalid mask info size: %" G_GSIZE_FORMAT, block_len); free_lyr_a (lyr_a, img_a->num_layers); return NULL; } block_rem -= (block_len + 4); IFDBG(3) g_debug ("Offset: %" G_GOFFSET_FORMAT ", Remaining length %" G_GSIZE_FORMAT, PSD_TELL(input), 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 (psd_read (input, &lyr_a[lidx]->layer_mask.top, 4, error) < 4 || psd_read (input, &lyr_a[lidx]->layer_mask.left, 4, error) < 4 || psd_read (input, &lyr_a[lidx]->layer_mask.bottom, 4, error) < 4 || psd_read (input, &lyr_a[lidx]->layer_mask.right, 4, error) < 4 || psd_read (input, &lyr_a[lidx]->layer_mask.def_color, 1, error) < 1 || psd_read (input, &lyr_a[lidx]->layer_mask.flags, 1, error) < 1) { psd_set_error (error); free_lyr_a (lyr_a, img_a->num_layers); return NULL; } if (! img_a->ibm_pc_format) { 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 (psd_read (input, &lyr_a[lidx]->layer_mask.extra_flags, 1, error) < 1 || psd_read (input, &lyr_a[lidx]->layer_mask.extra_def_color, 1, error) < 1 || psd_read (input, &lyr_a[lidx]->layer_mask_extra.top, 4, error) < 4 || psd_read (input, &lyr_a[lidx]->layer_mask_extra.left, 4, error) < 4 || psd_read (input, &lyr_a[lidx]->layer_mask_extra.bottom, 4, error) < 4 || psd_read (input, &lyr_a[lidx]->layer_mask_extra.right, 4, error) < 4) { psd_set_error (error); free_lyr_a (lyr_a, img_a->num_layers); return NULL; } block_len -= 18; if (! img_a->ibm_pc_format) { 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 (psd_read (input, &lyr_a[lidx]->layer_mask.mask_params, 1, error) < 1) { psd_set_error (error); free_lyr_a (lyr_a, img_a->num_layers); 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 (! psd_seek (input, extra_bytes, G_SEEK_CUR, error)) { psd_set_error (error); free_lyr_a (lyr_a, img_a->num_layers); 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 %" G_GOFFSET_FORMAT " bytes of unknown layer mask data", block_len); } if (! psd_seek (input, block_len, G_SEEK_CUR, error)) { psd_set_error (error); free_lyr_a (lyr_a, img_a->num_layers); 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 > PIKA_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); free_lyr_a (lyr_a, img_a->num_layers); 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 > PIKA_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); free_lyr_a (lyr_a, img_a->num_layers); 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); free_lyr_a (lyr_a, img_a->num_layers); return NULL; } } } /* Layer blending ranges */ /* FIXME */ if (psd_read (input, &block_len, 4, error) < 4) { psd_set_error (error); free_lyr_a (lyr_a, img_a->num_layers); return NULL; } if (! img_a->ibm_pc_format) block_len = GUINT32_FROM_BE (block_len); block_rem -= (block_len + 4); IFDBG(3) g_debug ("Blending ranges size %" G_GSIZE_FORMAT " (not imported)", block_len); IFDBG(3) g_debug ("Offset: %" G_GOFFSET_FORMAT ", Remaining length %" G_GSIZE_FORMAT, PSD_TELL(input), block_rem); if (block_len > 0) { if (! psd_seek (input, block_len, G_SEEK_CUR, error)) { psd_set_error (error); free_lyr_a (lyr_a, img_a->num_layers); return NULL; } } lyr_a[lidx]->name = fread_pascal_string (&read_len, &write_len, 4, input, error); if (*error) { free_lyr_a (lyr_a, img_a->num_layers); return NULL; } block_rem -= read_len; IFDBG(3) g_debug ("Offset: %" G_GOFFSET_FORMAT ", Remaining length %" G_GSIZE_FORMAT, PSD_TELL(input), block_rem); /* Adjustment layer info */ /* FIXME */ while (block_rem > 7) { gint header_size; IFDBG(3) g_debug ("Offset: %" G_GOFFSET_FORMAT ", Remaining length %" G_GSIZE_FORMAT, PSD_TELL(input), block_rem); header_size = get_layer_resource_header (&res_a, img_a->version, input, error); if (header_size < 0) { psd_set_error (error); free_lyr_a (lyr_a, img_a->num_layers); return NULL; } block_rem -= header_size; 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 %"G_GOFFSET_FORMAT".", 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!")); free_lyr_a (lyr_a, img_a->num_layers); return NULL; } lyr_a[lidx]->unsupported_features = img_a->unsupported_features; if (load_layer_resource (&res_a, lyr_a[lidx], input, error) < 0) { free_lyr_a (lyr_a, img_a->num_layers); return NULL; } block_rem -= res_a.data_len; IFDBG(3) g_debug ("Remaining length in block: %" G_GSIZE_FORMAT, block_rem); } if (block_rem > 0) { if (! psd_seek (input, block_rem, G_SEEK_CUR, error)) { psd_set_error (error); free_lyr_a (lyr_a, img_a->num_layers); return NULL; } } } img_a->layer_data_start = PSD_TELL(input); if (! psd_seek (input, img_a->layer_data_len, G_SEEK_CUR, error)) { psd_set_error (error); free_lyr_a (lyr_a, img_a->num_layers); return NULL; } IFDBG(1) g_debug ("Layer image data block start %" G_GOFFSET_FORMAT ", size %" G_GSIZE_FORMAT ", end: %" G_GOFFSET_FORMAT, img_a->layer_data_start, img_a->layer_data_len, PSD_TELL(input)); } return lyr_a; } static PSDlayer ** read_layer_block (PSDimage *img_a, GInputStream *input, GError **error) { PSDlayer **lyr_a = NULL; guint64 block_len; guint64 block_end; gint block_len_size = (img_a->version == 1 ? 4 : 8); /* If layer data is being loaded for non-PSD files (like TIFF), * then mask_layer_len will have its size preloaded */ if (img_a->mask_layer_len == 0) { if (! psd_read_len (input, &block_len, img_a->version, error)) { img_a->num_layers = -1; return NULL; } else { img_a->mask_layer_len = block_len; } } IFDBG(1) g_debug ("Layer and mask block size = %" G_GOFFSET_FORMAT, img_a->mask_layer_len); img_a->transparency = FALSE; img_a->layer_data_len = 0; img_a->num_layers = 0; if (!img_a->mask_layer_len) { img_a->num_layers = 0; return NULL; } else { gsize total_len = img_a->mask_layer_len; img_a->mask_layer_start = PSD_TELL(input); block_end = img_a->mask_layer_start + img_a->mask_layer_len; /* Layer info */ if (! psd_read_len (input, &block_len, img_a->version, error)) { lyr_a = NULL; return NULL; } else { if (img_a->ibm_pc_format) block_len = GUINT32_FROM_BE (block_len); IFDBG(1) g_debug ("Layer info size = %" G_GOFFSET_FORMAT, block_len); /* To make computations easier add the size of block_len */ block_len += block_len_size; if (block_len > block_len_size) { lyr_a = read_layer_info (img_a, input, error); if (error && *error) return NULL; if (img_a->mask_layer_start + block_len != PSD_TELL(input)) { g_debug ("Unexpected offset after reading layer info: %" G_GOFFSET_FORMAT " instead of %" G_GOFFSET_FORMAT, PSD_TELL(input), img_a->mask_layer_start + block_len); /* Correct the offset. */ if (! psd_seek (input, img_a->mask_layer_start + block_len, G_SEEK_SET, error)) return NULL; } } total_len -= block_len; } IFDBG(3) g_debug ("Offset: %" G_GOFFSET_FORMAT ", remaining len: %" G_GSIZE_FORMAT, PSD_TELL(input), total_len); if (total_len >= 4) { /* Global layer mask info */ if (psd_read (input, &block_len, 4, error) == 4) { if (! img_a->ibm_pc_format) block_len = GUINT32_FROM_BE (block_len); else block_len = GUINT32_TO_LE (block_len); IFDBG(1) g_debug ("Global layer mask info size = %" G_GOFFSET_FORMAT, block_len); if (block_len > total_len) { g_debug ("Invalid global layer mask size. Skipping remainder of layer data."); total_len = 0; } else { /* read_global_layer_mask_info (img_a, f, error); */ /* For now skipping ... */ if (block_len > 0) if (! psd_seek (input, block_len, G_SEEK_CUR, error)) { psd_set_error (error); return NULL; } total_len -= block_len + 4; } } else { psd_set_error (error); return NULL; } } /* Additional Layer Information */ g_debug ("Reading layer resources..."); while (total_len >= 12) { gsize adjusted_len; gint header_size; PSDlayerres res_a; IFDBG(3) g_debug ("Offset: %" G_GOFFSET_FORMAT ", remaining len: %" G_GSIZE_FORMAT, PSD_TELL(input), total_len); header_size = get_layer_resource_header (&res_a, img_a->version, input, error); if (header_size < 0) { psd_set_error (error); return NULL; } total_len -= header_size; if (res_a.data_len > total_len) { /* Not fatal just skip remainder of this block. */ g_debug ("Invalid resource block length: %" G_GSIZE_FORMAT " is larger than maximum %" G_GSIZE_FORMAT, res_a.data_len, total_len); break; } if (res_a.data_len > 0) { if (memcmp (res_a.key, "Lr16", 4) == 0 || memcmp (res_a.key, "Lr32", 4) == 0) { lyr_a = read_layer_info (img_a, input, error); if (lyr_a == NULL) { img_a->num_layers = 0; } if (error && *error) return NULL; } else { /* FIXME Loading layer resources here to a specific layer is probably * not right. We need to figure out what to do instead. * Most likely these resources are not connected to a layer so * lyr_a being NULL should not matter. e.g. PATT (pattern) * e.g. 0layers.psb has resources when lyr_a == NULL */ if (load_layer_resource (&res_a, NULL, input, error) < 0) { return NULL; } } } /* Seems the length here needs to be a multiple of 4? */ adjusted_len = (res_a.data_len + 3) / 4 * 4; total_len -= adjusted_len; /* Seek to correct offset */ if (! psd_seek (input, res_a.data_start + adjusted_len, G_SEEK_SET, error)) { g_debug ("Failed to seek to start of next layer resource at offset %" G_GOFFSET_FORMAT, res_a.data_start + adjusted_len); psd_set_error (error); return NULL; } } /* Skip to end of block */ if (! psd_seek (input, block_end, G_SEEK_SET, error)) { psd_set_error (error); return NULL; } IFDBG(3) g_debug ("Finished read_layer_block. Now at offset: %" G_GOFFSET_FORMAT, PSD_TELL(input)); } return lyr_a; } static gint read_merged_image_block (PSDimage *img_a, GInputStream *input, GError **error) { img_a->merged_image_start = PSD_TELL(input); if (! psd_seek (input, 0, G_SEEK_END, error)) { psd_set_error (error); return -1; } img_a->merged_image_len = PSD_TELL(input) - img_a->merged_image_start; IFDBG(1) g_debug ("Merged image data block: Start: %" G_GOFFSET_FORMAT ", len: %" G_GOFFSET_FORMAT, img_a->merged_image_start, img_a->merged_image_len); return 0; } static PikaImage * create_pika_image (PSDimage *img_a, GFile *file) { PikaImage *image = NULL; PikaPrecision precision; switch (img_a->color_mode) { case PSD_MULTICHANNEL: case PSD_GRAYSCALE: case PSD_DUOTONE: img_a->base_type = PIKA_GRAY; break; case PSD_BITMAP: case PSD_INDEXED: img_a->base_type = PIKA_INDEXED; break; case PSD_LAB: case PSD_CMYK: case PSD_RGB: img_a->base_type = PIKA_RGB; break; default: /* Color mode already validated - should not be here */ g_warning ("Invalid color mode"); return NULL; break; } switch (img_a->bps) { case 32: precision = PIKA_PRECISION_FLOAT_NON_LINEAR; break; case 16: if (img_a->color_mode == PSD_CMYK) precision = PIKA_PRECISION_FLOAT_NON_LINEAR; else precision = PIKA_PRECISION_U16_NON_LINEAR; break; case 8: case 1: if (img_a->color_mode == PSD_CMYK || img_a->color_mode == PSD_LAB) precision = PIKA_PRECISION_FLOAT_NON_LINEAR; else precision = PIKA_PRECISION_U8_NON_LINEAR; break; default: /* Precision not supported */ g_warning ("Invalid precision"); return NULL; break; } /* Create pika image */ IFDBG(2) g_debug ("Create image"); image = pika_image_new_with_precision (img_a->columns, img_a->rows, img_a->base_type, precision); pika_image_undo_disable (image); return image; } static gint add_color_map (PikaImage *image, PSDimage *img_a) { PikaParasite *parasite; if (img_a->color_map_len) { if (img_a->color_mode != PSD_DUOTONE) { pika_image_set_colormap (image, 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 = pika_parasite_new (PSD_PARASITE_DUOTONE_DATA, 0, img_a->color_map_len, img_a->color_map); pika_image_attach_parasite (image, parasite); pika_parasite_free (parasite); } g_free (img_a->color_map); } return 0; } static gint add_image_resources (PikaImage *image, PSDimage *img_a, GInputStream *input, gboolean *resolution_loaded, gboolean *profile_loaded, GError **error) { PSDimageres res_a; if (! psd_seek (input, img_a->image_res_start, G_SEEK_SET, error)) { psd_set_error (error); g_debug ("Failed to seek in add_image_resources"); return -1; } /* Initialise image resource variables */ img_a->no_icc = FALSE; img_a->layer_state = 0; img_a->layer_selection = NULL; img_a->alpha_names = NULL; img_a->alpha_display_info = NULL; img_a->alpha_display_count = 0; img_a->alpha_id = NULL; img_a->alpha_id_count = 0; img_a->quick_mask_id = 0; while (PSD_TELL(input) < img_a->image_res_start + img_a->image_res_len) { if (get_image_resource_header (&res_a, input, 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, img_a, input, 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, GError **error) { const Babl *fish = NULL; const Babl *base_format = NULL; const Babl *space = NULL; if (img_a->cmyk_profile && pika_color_profile_is_cmyk (img_a->cmyk_profile)) { /* If no CMYK profile attached, babl_format_with_space () * will ignore the NULL space value and process the image * as if it were just using babl_format () */ space = pika_color_profile_get_space (img_a->cmyk_profile, PIKA_COLOR_RENDERING_INTENT_RELATIVE_COLORIMETRIC, error); } if (img_a->bps == 8) base_format = babl_format_with_space (alpha ? "cmykA u8" : "cmyk u8", space); else base_format = babl_format_with_space (alpha ? "cmykA u16" : "cmyk u16", space); if (! alpha) fish = babl_fish (base_format, "R'G'B' float"); else fish = babl_fish (base_format, "R'G'B'A float"); babl_process (fish, src, dst, width * height); return (guchar*) dst; } static guchar * psd_convert_lab_to_srgb (PSDimage *img_a, guchar *dst, guchar *src, gint width, gint height, gboolean alpha) { const Babl *fish; if (alpha) fish = babl_fish ("CIE Lab alpha u8", "R'G'B'A float"); else fish = babl_fish ("CIE Lab u8", "R'G'B' float"); babl_process (fish, src, dst, width * height); return (guchar*) dst; } static gboolean read_RLE_channel (PSDimage *img_a, PSDchannel *lyr_chn, guint64 channel_data_len, GInputStream *input, GError **error) { gint rle_count_size = (img_a->version == 1 ? 2 : 4); gint rle_row_size = lyr_chn->rows * rle_count_size; guint32 *rle_pack_len; gint rowi; IFDBG(4) g_debug ("RLE channel length %" G_GSIZE_FORMAT ", RLE length data: %d, " "RLE data block: %" G_GSIZE_FORMAT, channel_data_len - 2, rle_row_size, (channel_data_len - 2 - rle_row_size)); rle_pack_len = g_malloc (lyr_chn->rows * 4); /* Always 4 since this is the data size in memory. */ for (rowi = 0; rowi < lyr_chn->rows; ++rowi) { if (psd_read (input, &rle_pack_len[rowi], rle_count_size, error) < rle_count_size) { psd_set_error (error); g_free (rle_pack_len); return FALSE; } if (img_a->version == 1) rle_pack_len[rowi] = img_a->ibm_pc_format ? GUINT16_FROM_LE (rle_pack_len[rowi]) : GUINT16_FROM_BE (rle_pack_len[rowi]); else rle_pack_len[rowi] = img_a->ibm_pc_format ? GUINT32_FROM_LE (rle_pack_len[rowi]) : GUINT32_FROM_BE (rle_pack_len[rowi]); } if (read_channel_data (lyr_chn, img_a->bps, PSD_COMP_RLE, rle_pack_len, input, 0, error) < 1) { psd_set_error (error); g_free (rle_pack_len); return FALSE; } g_free (rle_pack_len); return TRUE; } static void free_lyr_chn (PSDchannel **lyr_chn, gint channel_count) { gint cidx; for (cidx = 0; cidx < channel_count; ++cidx) if (lyr_chn[cidx]) g_free (lyr_chn[cidx]); g_free (lyr_chn); } static gint add_layers (PikaImage *image, PSDimage *img_a, PSDlayer **lyr_a, GInputStream *input, GError **error) { PSDchannel **lyr_chn; GArray *parent_group_stack; PikaLayer *parent_group = NULL; PikaLayer *clipping_group = NULL; guint16 alpha_chn; guint16 user_mask_chn; guint16 layer_channels, base_channels; guint16 channel_idx[MAX_CHANNELS]; 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 */ PikaLayer *layer = NULL; PikaLayerMask *mask = NULL; GList *selected_layers = NULL; gint lidx; /* Layer index */ gint cidx; /* Channel index */ gint gidx; /* Clipping group start index */ gboolean alpha; gboolean user_mask; gboolean empty; gboolean empty_mask; gboolean use_clipping_group; GeglBuffer *buffer; PikaImageType 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 (! psd_seek (input, img_a->layer_data_start, G_SEEK_SET, error)) { psd_set_error (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) { lyr_a[lidx]->unsupported_features = img_a->unsupported_features; if (lyr_a[lidx]->clipping == 1) { /* Photoshop handles layers with clipping differently than PIKA 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 (PikaLayer *)); g_array_append_val (parent_group_stack, parent_group); for (lidx = 0; lidx < img_a->num_layers; ++lidx) { IFDBG(2) g_debug ("Process Layer No %d (%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 (! psd_seek (input, lyr_a[lidx]->chn_info[cidx].data_len, G_SEEK_CUR, error)) { psd_set_error (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_new0 (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 (! psd_seek (input, lyr_a[lidx]->chn_info[cidx].data_len, G_SEEK_CUR, error)) { psd_set_error (error); free_lyr_chn (lyr_chn, lyr_a[lidx]->num_channels); 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 (psd_read (input, &comp_mode, COMP_MODE_SIZE, error) < COMP_MODE_SIZE) { psd_set_error (error); free_lyr_chn (lyr_chn, lyr_a[lidx]->num_channels); return -1; } if (! img_a->ibm_pc_format) comp_mode = GUINT16_FROM_BE (comp_mode); else comp_mode = GUINT16_FROM_LE (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: %" G_GSIZE_FORMAT, lyr_a[lidx]->chn_info[cidx].data_len - 2); if (read_channel_data (lyr_chn[cidx], img_a->bps, PSD_COMP_RAW, NULL, input, 0, error) < 1) { psd_set_error (error); free_lyr_chn (lyr_chn, lyr_a[lidx]->num_channels); return -1; } break; case PSD_COMP_RLE: /* Packbits */ if (! read_RLE_channel (img_a, lyr_chn[cidx], lyr_a[lidx]->chn_info[cidx].data_len, input, error)) { psd_set_error (error); free_lyr_chn (lyr_chn, lyr_a[lidx]->num_channels); return -1; } break; case PSD_COMP_ZIP: /* ? */ case PSD_COMP_ZIP_PRED: if (read_channel_data (lyr_chn[cidx], img_a->bps, comp_mode, NULL, input, lyr_a[lidx]->chn_info[cidx].data_len - 2, error) < 1) { psd_set_error (error); free_lyr_chn (lyr_chn, lyr_a[lidx]->num_channels); return -1; } break; default: g_set_error (error, G_FILE_ERROR, G_FILE_ERROR_FAILED, _("Unsupported compression mode: %d"), comp_mode); free_lyr_chn (lyr_chn, lyr_a[lidx]->num_channels); 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 = g_array_index (parent_group_stack, PikaLayer *, parent_group_stack->len - 1); else parent_group = NULL; /* 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) { IFDBG(3) g_debug ("Channel: %d - id: %d", cidx, lyr_chn[cidx]->id); if (lyr_chn[cidx]->id == PSD_CHANNEL_MASK || lyr_chn[cidx]->id == PSD_CHANNEL_EXTRA_MASK) { /* According to the specs the extra mask (which they call * real user supplied layer mask) is used "when both a user * mask and a vector mask are present". * I haven't seen an example that has the extra mask, so not * sure which of the masks would appear first. * For now assuming that the extra mask will be first. */ if (! user_mask) { user_mask = TRUE; user_mask_chn = cidx; } else { /* Not using this mask, make sure it gets freed. */ g_free (lyr_chn[cidx]->data); } } 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 marker layers are used to * assemble the layer structure in a single pass */ IFDBG(2) g_debug ("Create placeholder group layer"); layer = pika_layer_group_new (image); /* add this group layer as the new parent */ g_array_append_val (parent_group_stack, layer); } else /* group-type == 1 || group_type == 2 */ { if (parent_group_stack->len) { layer = g_array_index (parent_group_stack, PikaLayer *, parent_group_stack->len - 1); IFDBG(2) g_debug ("End group layer id %d.", pika_item_get_id (PIKA_ITEM (layer))); /* 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); pika_drawable_get_offsets (PIKA_DRAWABLE (layer), &l_x, &l_y); l_w = pika_drawable_get_width (PIKA_DRAWABLE (layer)); l_h = pika_drawable_get_height (PIKA_DRAWABLE (layer)); } else { IFDBG(1) g_debug ("WARNING: Unmatched group layer start marker."); layer = NULL; } } } else { if (lyr_a[lidx]->clipping_group_type == 1) { clipping_group = add_clipping_group (image, parent_group); } if (empty) { IFDBG(2) g_debug ("Create blank layer"); } 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_pika_image_type (img_a->base_type, TRUE); IFDBG(3) g_debug ("Layer type %d", image_type); layer = pika_layer_new (image, lyr_a[lidx]->name, l_w, l_h, image_type, 100, PIKA_LAYER_MODE_NORMAL); } if (layer != NULL) { /* 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. */ if (lyr_a[lidx]->name) pika_item_set_name (PIKA_ITEM (layer), 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_pika_blend_mode (lyr_a[lidx], &mode_info); pika_layer_set_mode (layer, mode_info.mode); pika_layer_set_blend_space (layer, mode_info.blend_space); pika_layer_set_composite_space (layer, mode_info.composite_space); pika_layer_set_composite_mode (layer, mode_info.composite_mode); /* Opacity */ pika_layer_set_opacity (layer, lyr_a[lidx]->opacity * 100.0 / 255.0); /* Flags */ pika_layer_set_lock_alpha (layer, lyr_a[lidx]->layer_flags.trans_prot); pika_item_set_visible (PIKA_ITEM (layer), 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) { pika_item_set_visible (PIKA_ITEM (layer), FALSE); } #endif /* Position */ if (l_x != 0 || l_y != 0) pika_layer_set_offsets (layer, l_x, l_y); /* Color tag */ pika_item_set_color_tag (PIKA_ITEM (layer), psd_to_pika_layer_color_tag (lyr_a[lidx]->color_tag[0])); /* Tattoo */ if (lyr_a[lidx]->id) pika_item_set_tattoo (PIKA_ITEM (layer), lyr_a[lidx]->id); /* For layer groups, expand or collapse the group */ if (lyr_a[lidx]->group_type != 0) { pika_item_set_expanded (PIKA_ITEM (layer), lyr_a[lidx]->group_type == 1); } } /* Remember the selected layers: * - Layer Selection ID(s) (0x042D) are prioritary; * - Layer state information (0x0400) is used instead * otherwise. */ if (img_a->layer_selection) { if (g_list_find (img_a->layer_selection, GINT_TO_POINTER (lyr_a[lidx]->id)) != NULL) { selected_layers = g_list_prepend (selected_layers, layer); } } else if (lidx == img_a->layer_state) { selected_layers = g_list_prepend (selected_layers, layer); } /* Set the layer data */ if (lyr_a[lidx]->group_type == 0) { IFDBG(3) g_debug ("Draw layer"); if (empty) { pika_drawable_fill (PIKA_DRAWABLE (layer), PIKA_FILL_TRANSPARENT); } else { GeglBufferIterator *iter; bps = img_a->bps / 8; if (bps == 0) bps++; buffer = pika_drawable_get_buffer (PIKA_DRAWABLE (layer)); 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 || img_a->color_mode == PSD_LAB) { dst0 = gegl_scratch_alloc (base_channels * bps * 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, error); gegl_scratch_free (dst0); } else if (img_a->color_mode == PSD_LAB) { psd_convert_lab_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 = pika_layer_create_mask (layer, PIKA_ADD_MASK_WHITE); else mask = pika_layer_create_mask (layer, PIKA_ADD_MASK_BLACK); pika_layer_add_mask (layer, mask); pika_layer_set_apply_mask (layer, ! 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 = pika_layer_create_mask (layer, PIKA_ADD_MASK_WHITE); else mask = pika_layer_create_mask (layer, PIKA_ADD_MASK_BLACK); bps = img_a->bps / 8; if (bps == 0) bps++; IFDBG(3) g_debug ("New layer mask %d", pika_item_get_id (PIKA_ITEM (mask))); pika_layer_add_mask (layer, mask); buffer = pika_drawable_get_buffer (PIKA_DRAWABLE (mask)); 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); pika_layer_set_apply_mask (layer, ! 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) { pika_image_insert_layer (image, layer, clipping_group, 0); if (lyr_a[lidx]->clipping_group_type == 2) { /* End of our clipping group. */ clipping_group = NULL; } } else { pika_image_insert_layer (image, layer, parent_group, 0); } } } free_lyr_chn (lyr_chn, lyr_a[lidx]->num_channels); } 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 selected layers */ pika_image_take_selected_layers (image, selected_layers); g_list_free (img_a->layer_selection); return 0; } static gint add_merged_image (PikaImage *image, PSDimage *img_a, GInputStream *input, GError **error) { PSDchannel chn_a[MAX_CHANNELS]; gchar *alpha_name; guchar *pixels; guint16 comp_mode = 0; guint16 base_channels; guint16 extra_channels; guint16 total_channels; guint16 bps; guint32 *rle_pack_len[MAX_CHANNELS]; guint32 alpha_id; gint32 layer_size; PikaLayer *layer = NULL; PikaChannel *channel = NULL; gint16 alpha_opacity; gint cidx; /* Channel index */ gint rowi; /* Row index */ gint offset; gint i; gboolean alpha_visible; gboolean alpha_channel = FALSE; gboolean original_mode_CMYK = FALSE; GeglBuffer *buffer; PikaImageType image_type; PikaRGB 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->merged_image_only && img_a->color_mode == PSD_CMYK && img_a->num_layers > 0) { /* In this case there is no conversion. Merged image is RGB. */ img_a->color_mode = PSD_RGB; original_mode_CMYK = TRUE; if (! img_a->transparency) { total_channels--; } } if (extra_channels > 0) { if (img_a->transparency) { extra_channels--; } else if (img_a->alpha_names) { gchar *alpha_name; /* If first alpha_name is Transparency consider it the alpha channel. */ alpha_name = g_ptr_array_index (img_a->alpha_names, 0); if (! g_strcmp0 (alpha_name, "Transparency")) { alpha_channel = TRUE; extra_channels--; IFDBG(3) g_debug ("Merged image alpha channel present."); } } } base_channels = total_channels - extra_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; if (! psd_seek (input, block_start, G_SEEK_SET, error) || psd_read (input, &comp_mode, COMP_MODE_SIZE, error) < COMP_MODE_SIZE) { psd_set_error (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, input, 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 */ { gint rle_count_size = (img_a->version == 1 ? 2 : 4); gint rle_row_size = img_a->rows * rle_count_size; IFDBG(3) g_debug ("RLE length data: %d, RLE data block: %d", total_channels * rle_row_size, block_len - (total_channels * rle_row_size)); 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 * 4); /* Always 4 since this is the data size in memory. */ for (rowi = 0; rowi < img_a->rows; ++rowi) { if (psd_read (input, &rle_pack_len[cidx][rowi], rle_count_size, error) < rle_count_size) { psd_set_error (error); return -1; } if (img_a->version == 1) rle_pack_len[cidx][rowi] = GUINT16_FROM_BE (rle_pack_len[cidx][rowi]); else rle_pack_len[cidx][rowi] = GUINT32_FROM_BE (rle_pack_len[cidx][rowi]); } } /* Skip channel length data for unloaded channels */ if (! psd_seek (input, (img_a->channels - total_channels) * rle_row_size, G_SEEK_CUR, error)) { psd_set_error (error); return -1; } for (cidx = 0; cidx < total_channels; ++cidx) { if (read_channel_data (&chn_a[cidx], img_a->bps, PSD_COMP_RLE, rle_pack_len[cidx], input, 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_pika_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 = pika_layer_new (image, _("Background"), img_a->columns, img_a->rows, image_type, 100, pika_image_get_default_new_layer_mode (image)); pika_image_insert_layer (image, layer, NULL, 0); buffer = pika_drawable_get_buffer (PIKA_DRAWABLE (layer)); 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, img_a->transparency || alpha_channel, error); g_free (pixels); pixels = dst0; } else if (img_a->color_mode == PSD_LAB) { guchar *dst0; dst0 = g_malloc (base_channels * layer_size * sizeof(float)); psd_convert_lab_to_srgb (img_a, dst0, pixels, img_a->columns, img_a->rows, img_a->transparency || alpha_channel); g_free (pixels); pixels = dst0; } else if (original_mode_CMYK && img_a->transparency) { gint irow; guchar *dst0, *dst; guchar *data; dst0 = g_malloc (base_channels * layer_size * sizeof(guchar)); dst = dst0; data = pixels; /* CMYKA layers: 5 channels but merged image is RGBA * with RGB in the first 3 layers and A in 5th layer. * Move A to 4th layer. */ for (irow = 0; irow < img_a->rows; irow++) { gint icol; for (icol = 0; icol < img_a->columns; icol++) { dst[0] = data[0]; dst[1] = data[1]; dst[2] = data[2]; dst[3] = data[4]; dst += 4; data += 5; } } 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; IFDBG(4) g_debug ("Unblend merged transparency"); 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) { 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 (PIKA_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]->pika_color; alpha_opacity = img_a->alpha_display_info[i + offset]->opacity; } else { pika_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 = pika_channel_new (image, alpha_name, chn_a[cidx].columns, chn_a[cidx].rows, alpha_opacity, &alpha_rgb); pika_image_insert_channel (image, channel, NULL, i); g_free (alpha_name); buffer = pika_drawable_get_buffer (PIKA_DRAWABLE (channel)); if (alpha_id) pika_item_set_tattoo (PIKA_ITEM (channel), alpha_id); pika_item_set_visible (PIKA_ITEM (channel), 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 pika image tattoo state */ return 0; } /* Local utility functions */ static PikaLayer * add_clipping_group (PikaImage *image, PikaLayer *parent) { PikaLayer * clipping_group = NULL; /* We need to create a group because PIKA 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 = pika_layer_group_new (image); pika_item_set_name (PIKA_ITEM (clipping_group), "Group added by PIKA"); pika_layer_set_blend_space (clipping_group, PIKA_LAYER_COLOR_SPACE_RGB_PERCEPTUAL); pika_layer_set_composite_space (clipping_group, PIKA_LAYER_COLOR_SPACE_RGB_PERCEPTUAL); pika_layer_set_composite_mode (clipping_group, PIKA_LAYER_COMPOSITE_UNION); pika_image_insert_layer (image, clipping_group, parent, 0); return clipping_group; } 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_pika_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 PikaImageType get_pika_image_type (PikaImageBaseType image_base_type, gboolean alpha) { PikaImageType image_type; switch (image_base_type) { case PIKA_GRAY: image_type = (alpha) ? PIKA_GRAYA_IMAGE : PIKA_GRAY_IMAGE; break; case PIKA_INDEXED: image_type = (alpha) ? PIKA_INDEXEDA_IMAGE : PIKA_INDEXED_IMAGE; break; case PIKA_RGB: image_type = (alpha) ? PIKA_RGBA_IMAGE : PIKA_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 guint32 *rle_pack_len, GInputStream *input, guint32 comp_len, GError **error) { gchar *raw_data = NULL; 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(4) 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 (psd_read (input, raw_data, readline_len * channel->rows, error) < readline_len * channel->rows) { psd_set_error (error); g_free (raw_data); 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 (PSD_TELL(input) + rle_pack_len[i] > block_end) { psd_set_error (TRUE, errno, error); return -1; } */ if (psd_read (input, src, rle_pack_len[i], error) < rle_pack_len[i]) { psd_set_error (error); gegl_scratch_free (src); g_free (raw_data); 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 (psd_read (input, src, comp_len, error) < comp_len) { psd_set_error (error); g_free (src); g_free (raw_data); 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); g_free (raw_data); return -1; } g_free (src); break; } } /* Convert channel data to PIKA format */ switch (bps) { case 32: { guint32 *data; guint64 pos; if (compression == PSD_COMP_ZIP_PRED) { IFDBG(3) g_debug ("Converting 32 bit predictor data"); channel->data = (gchar *) g_malloc0 (channel->rows * channel->columns * 4); decode_32_bit_predictor (raw_data, channel->data, channel->rows, channel->columns); } else { IFDBG(3) g_debug ("32 bit channel data without predictor"); channel->data = raw_data; raw_data = NULL; } data = (guint32*) channel->data; for (pos = 0; pos < channel->rows * channel->columns; ++pos) data[pos] = GUINT32_FROM_BE (data[pos]); 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) { IFDBG(3) g_debug ("Converting 16 bit predictor data"); 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) { IFDBG(3) g_debug ("Converting 8 bit predictor data"); 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: g_free (raw_data); return -1; break; } g_free (raw_data); return 1; } /* * For reference on zip predictor see: * - TIFFTN3d1.pdf * - psd_tools */ static void decode_32_bit_predictor (gchar *src, gchar *dst, guint32 rows, guint32 columns) { guint32 rowsize; guint64 row, dstpos; rowsize = columns * 4; /* decode delta */ for (row = 0; row < rows; ++row) { guint32 j; guint64 offset; offset = row * rowsize; for (j = 0; j < rowsize-1; ++j) { guint64 pos; pos = offset + j; src[pos + 1] += src[pos]; } } /* restore byte order */ dstpos = 0; for (row = 0; row < rows * rowsize; row += rowsize) { guint64 offset; for (offset = row; offset < row + columns; offset++) { guint64 x; for (x = offset; x < offset + rowsize; x += columns) { dst[dstpos] = src[x]; dstpos++; } } } } 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(4) 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_pika_image_type (img_a->base_type, alpha)) { case PIKA_GRAY_IMAGE: switch (img_a->bps) { case 32: format = babl_format ("Y' float"); 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 PIKA_GRAYA_IMAGE: switch (img_a->bps) { case 32: format = babl_format ("Y'A float"); 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 PIKA_RGB_IMAGE: switch (img_a->bps) { case 32: format = babl_format ("R'G'B' float"); break; case 16: format = babl_format (img_a->color_mode == PSD_CMYK ? "R'G'B' float" : "R'G'B' u16"); break; case 8: case 1: format = babl_format ((img_a->color_mode == PSD_CMYK || img_a->color_mode == PSD_LAB) ? "R'G'B' float" : "R'G'B' u8"); break; default: return NULL; break; } break; case PIKA_RGBA_IMAGE: switch (img_a->bps) { case 32: format = babl_format ("R'G'B'A float"); break; case 16: format = babl_format (img_a->color_mode == PSD_CMYK ? "R'G'B'A float" : "R'G'B'A u16"); break; case 8: case 1: format = babl_format ((img_a->color_mode == PSD_CMYK || img_a->color_mode == PSD_LAB) ? "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 float"); break; case 16: format = babl_format ("Y u16"); break; case 8: case 1: /* see pika_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 float"); break; case 16: format = babl_format ("Y u16"); break; case 8: case 1: format = babl_format ("Y u8"); break; default: break; } return format; } static void initialize_unsupported (PSDSupport *unsupported_features) { unsupported_features->show_gui = FALSE; unsupported_features->duotone_mode = FALSE; unsupported_features->adjustment_layer = FALSE; unsupported_features->fill_layer = FALSE; unsupported_features->text_layer = FALSE; unsupported_features->linked_layer = FALSE; unsupported_features->vector_mask = FALSE; unsupported_features->smart_object = FALSE; unsupported_features->stroke = FALSE; unsupported_features->layer_effect = FALSE; unsupported_features->layer_comp = FALSE; unsupported_features->psd_metadata = FALSE; } void load_dialog (const gchar *title, PSDSupport *unsupported_features) { GtkWidget *dialog; GtkWidget *label; GtkWidget *vbox; dialog = pika_dialog_new (title, "import-psd", NULL, 0, NULL, NULL, _("_OK"), GTK_RESPONSE_OK, NULL); pika_window_set_transient (GTK_WINDOW (dialog)); vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 2); gtk_container_set_border_width (GTK_CONTAINER (vbox), 12); gtk_box_pack_start (GTK_BOX (gtk_dialog_get_content_area (GTK_DIALOG (dialog))), vbox, TRUE, TRUE, 0); gtk_widget_show (vbox); /* Duotone import notification */ if (unsupported_features->duotone_mode) { gchar *label_text; label_text = g_strdup_printf ("%s\n%s", _("Duotone Import"), _("Image will be imported as Grayscale.\n" "Duotone color space data has been saved\n" "and can be reapplied on export.")); label = gtk_label_new (NULL); gtk_label_set_markup (GTK_LABEL (label), label_text); gtk_label_set_selectable (GTK_LABEL (label), TRUE); gtk_label_set_justify (GTK_LABEL (label), GTK_JUSTIFY_LEFT); gtk_label_set_line_wrap (GTK_LABEL (label), TRUE); gtk_label_set_yalign (GTK_LABEL (label), 0.0); gtk_box_pack_start (GTK_BOX (vbox), label, TRUE, TRUE, 0); gtk_widget_show (label); g_free (label_text); } if (unsupported_features->show_gui) { GtkWidget *scrolled_window; GtkWidget *title_label; gchar *message = g_strdup (""); gchar *title = g_strdup_printf ("%s\n%s\n", _("Compatibility Notice"), _("This PSD file contains features " "that\nare not yet fully supported " "in PIKA:")); title_label = gtk_label_new (NULL); gtk_label_set_markup (GTK_LABEL (title_label), title); gtk_label_set_justify (GTK_LABEL (title_label), GTK_JUSTIFY_LEFT); gtk_label_set_line_wrap (GTK_LABEL (title_label), TRUE); gtk_label_set_yalign (GTK_LABEL (title_label), 0.0); gtk_box_pack_start (GTK_BOX (vbox), title_label, FALSE, FALSE, 0); gtk_widget_show (title_label); #define ADD_UNSUPPORTED_MESSAGE(message, text) \ { \ gchar *old_message = message; \ message = g_strdup_printf ("%s\n\xE2\x80\xA2 %s", old_message, \ _(text)); \ g_free (old_message); \ } \ if (unsupported_features->adjustment_layer) ADD_UNSUPPORTED_MESSAGE (message, "Adjustment layers are not yet " "supported and will be dropped."); if (unsupported_features->psd_metadata) ADD_UNSUPPORTED_MESSAGE (message, "Metadata non-raster layers are " "not yet supported and will be dropped."); if (unsupported_features->fill_layer) ADD_UNSUPPORTED_MESSAGE (message, "Fill layers are partially " "supported and will be converted " "to raster layers."); if (unsupported_features->text_layer) ADD_UNSUPPORTED_MESSAGE (message, "Text layers are partially " "supported and will be converted " "to raster layers."); if (unsupported_features->linked_layer) ADD_UNSUPPORTED_MESSAGE (message, "Linked layers are not yet " "supported and will be dropped."); if (unsupported_features->vector_mask) ADD_UNSUPPORTED_MESSAGE (message, "Vector masks are partially " "supported and will be converted " "to raster layers."); if (unsupported_features->stroke) ADD_UNSUPPORTED_MESSAGE (message, "Vector strokes are not yet " "supported and will be dropped."); if (unsupported_features->layer_effect) ADD_UNSUPPORTED_MESSAGE (message, "Layer effects are not yet " "supported and will be dropped."); if (unsupported_features->smart_object) ADD_UNSUPPORTED_MESSAGE (message, "Smart objects are not yet " "supported and will be dropped."); if (unsupported_features->layer_comp) ADD_UNSUPPORTED_MESSAGE (message, /* Translators: short for layer compositions */ "Layer comps are not yet " "supported and will be dropped."); #undef ADD_UNSUPPORTED_MESSAGE scrolled_window = gtk_scrolled_window_new (NULL, NULL); gtk_widget_set_size_request (scrolled_window, -1, 100); gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (scrolled_window), GTK_SHADOW_OUT); gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scrolled_window), GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC); gtk_box_pack_start (GTK_BOX (vbox), scrolled_window, TRUE, TRUE, 0); gtk_widget_show (scrolled_window); label = gtk_label_new (NULL); gtk_label_set_markup (GTK_LABEL (label), message); gtk_label_set_justify (GTK_LABEL (label), GTK_JUSTIFY_LEFT); gtk_label_set_line_wrap (GTK_LABEL (label), TRUE); gtk_label_set_yalign (GTK_LABEL (label), 0.0); gtk_container_add (GTK_CONTAINER (scrolled_window), label); gtk_widget_show (label); g_free (title); g_free (message); } gtk_widget_show (dialog); /* run the dialog */ pika_dialog_run (PIKA_DIALOG (dialog)); gtk_widget_destroy (dialog); }