diff options
Diffstat (limited to '')
-rw-r--r-- | plug-ins/file-psd/psd-image-res-load.c | 1611 |
1 files changed, 1611 insertions, 0 deletions
diff --git a/plug-ins/file-psd/psd-image-res-load.c b/plug-ins/file-psd/psd-image-res-load.c new file mode 100644 index 0000000..a634bc6 --- /dev/null +++ b/plug-ins/file-psd/psd-image-res-load.c @@ -0,0 +1,1611 @@ +/* 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/>. + */ + +/* ----- Known Image Resource Block Types ----- + All image resources not otherwise handled, including unknown types + are added as image parasites. + The data is attached as-is from the file (i.e. in big endian order). + + PSD_PS2_IMAGE_INFO = 1000, Dropped * 0x03e8 - Obsolete - ps 2.0 image info * + PSD_MAC_PRINT_INFO = 1001, PS Only * 0x03e9 - Optional - Mac print manager print info record * + PSD_PS2_COLOR_TAB = 1003, Dropped * 0x03eb - Obsolete - ps 2.0 indexed color table * + PSD_RESN_INFO = 1005, Loaded * 0x03ed - ResolutionInfo structure * + PSD_ALPHA_NAMES = 1006, Loaded * 0x03ee - Alpha channel names * + PSD_DISPLAY_INFO = 1007, Loaded * 0x03ef - DisplayInfo structure * + PSD_CAPTION = 1008, Loaded * 0x03f0 - Optional - Caption string * + PSD_BORDER_INFO = 1009, * 0x03f1 - Border info * + PSD_BACKGROUND_COL = 1010, * 0x03f2 - Background color * + PSD_PRINT_FLAGS = 1011, * 0x03f3 - Print flags * + PSD_GREY_HALFTONE = 1012, * 0x03f4 - Greyscale and multichannel halftoning info * + PSD_COLOR_HALFTONE = 1013, * 0x03f5 - Color halftoning info * + PSD_DUOTONE_HALFTONE = 1014, * 0x03f6 - Duotone halftoning info * + PSD_GREY_XFER = 1015, * 0x03f7 - Greyscale and multichannel transfer functions * + PSD_COLOR_XFER = 1016, * 0x03f8 - Color transfer functions * + PSD_DUOTONE_XFER = 1017, * 0x03f9 - Duotone transfer functions * + PSD_DUOTONE_INFO = 1018, * 0x03fa - Duotone image information * + PSD_EFFECTIVE_BW = 1019, * 0x03fb - Effective black & white values for dot range * + PSD_OBSOLETE_01 = 1020, Dropped * 0x03fc - Obsolete * + PSD_EPS_OPT = 1021, * 0x03fd - EPS options * + PSD_QUICK_MASK = 1022, Loaded * 0x03fe - Quick mask info * + PSD_OBSOLETE_02 = 1023, Dropped * 0x03ff - Obsolete * + PSD_LAYER_STATE = 1024, Loaded * 0x0400 - Layer state info * + PSD_WORKING_PATH = 1025, * 0x0401 - Working path (not saved) * + PSD_LAYER_GROUP = 1026, * 0x0402 - Layers group info * + PSD_OBSOLETE_03 = 1027, Dropped * 0x0403 - Obsolete * + PSD_IPTC_NAA_DATA = 1028, Loaded * 0x0404 - IPTC-NAA record (IMV4.pdf) * + PSD_IMAGE_MODE_RAW = 1029, * 0x0405 - Image mode for raw format files * + PSD_JPEG_QUAL = 1030, PS Only * 0x0406 - JPEG quality * + PSD_GRID_GUIDE = 1032, Loaded * 0x0408 - Grid & guide info * + PSD_THUMB_RES = 1033, Special * 0x0409 - Thumbnail resource * + PSD_COPYRIGHT_FLG = 1034, * 0x040a - Copyright flag * + PSD_URL = 1035, * 0x040b - URL string * + PSD_THUMB_RES2 = 1036, Special * 0x040c - Thumbnail resource * + PSD_GLOBAL_ANGLE = 1037, * 0x040d - Global angle * + PSD_COLOR_SAMPLER = 1038, * 0x040e - Color samplers resource * + PSD_ICC_PROFILE = 1039, Loaded * 0x040f - ICC Profile * + PSD_WATERMARK = 1040, * 0x0410 - Watermark * + PSD_ICC_UNTAGGED = 1041, * 0x0411 - Do not use ICC profile flag * + PSD_EFFECTS_VISIBLE = 1042, * 0x0412 - Show hide all effects layers * + PSD_SPOT_HALFTONE = 1043, * 0x0413 - Spot halftone * + PSD_DOC_IDS = 1044, * 0x0414 - Document specific IDs * + PSD_ALPHA_NAMES_UNI = 1045, Loaded * 0x0415 - Unicode alpha names * + PSD_IDX_COL_TAB_CNT = 1046, Loaded * 0x0416 - Indexed color table count * + PSD_IDX_TRANSPARENT = 1047, * 0x0417 - Index of transparent color (if any) * + PSD_GLOBAL_ALT = 1049, * 0x0419 - Global altitude * + PSD_SLICES = 1050, * 0x041a - Slices * + PSD_WORKFLOW_URL_UNI = 1051, * 0x041b - Workflow URL - Unicode string * + PSD_JUMP_TO_XPEP = 1052, * 0x041c - Jump to XPEP (?) * + PSD_ALPHA_ID = 1053, Loaded * 0x041d - Alpha IDs * + PSD_URL_LIST_UNI = 1054, * 0x041e - URL list - unicode * + PSD_VERSION_INFO = 1057, * 0x0421 - Version info * + PSD_EXIF_DATA = 1058, Loaded * 0x0422 - Exif data block 1 * + PSD_EXIF_DATA_3 = 1059 * 0X0423 - Exif data block 3 (?) * + PSD_XMP_DATA = 1060, Loaded * 0x0424 - XMP data block * + PSD_CAPTION_DIGEST = 1061, * 0x0425 - Caption digest * + PSD_PRINT_SCALE = 1062, * 0x0426 - Print scale * + PSD_PIXEL_AR = 1064, * 0x0428 - Pixel aspect ratio * + PSD_LAYER_COMPS = 1065, * 0x0429 - Layer comps * + PSD_ALT_DUOTONE_COLOR = 1066, * 0x042A - Alternative Duotone colors * + PSD_ALT_SPOT_COLOR = 1067, * 0x042B - Alternative Spot colors * + PSD_LAYER_SELECT_ID = 1069, * 0x042D - Layer selection ID * + PSD_HDR_TONING_INFO = 1070, * 0x042E - HDR toning information * + PSD_PRINT_INFO_SCALE = 1071, * 0x042F - Print scale * + PSD_LAYER_GROUP_E_ID = 1072, * 0x0430 - Layer group(s) enabled ID * + PSD_COLOR_SAMPLER_NEW = 1073, * 0x0431 - Color sampler resource for ps CS3 and higher PSD files * + PSD_MEASURE_SCALE = 1074, * 0x0432 - Measurement scale * + PSD_TIMELINE_INFO = 1075, * 0x0433 - Timeline information * + PSD_SHEET_DISCLOSE = 1076, * 0x0434 - Sheet discloser * + PSD_DISPLAY_INFO_NEW = 1077, Loaded * 0x0435 - DisplayInfo structure for ps CS3 and higher PSD files * + PSD_ONION_SKINS = 1078, * 0x0436 - Onion skins * + PSD_COUNT_INFO = 1080, * 0x0438 - Count information* + PSD_PRINT_INFO = 1082, * 0x043A - Print information added in ps CS5* + PSD_PRINT_STYLE = 1083, * 0x043B - Print style * + PSD_MAC_NSPRINTINFO = 1084, * 0x043C - Mac NSPrintInfo* + PSD_WIN_DEVMODE = 1085, * 0x043D - Windows DEVMODE * + PSD_AUTO_SAVE_PATH = 1086, * 0x043E - Auto save file path * + PSD_AUTO_SAVE_FORMAT = 1087, * 0x043F - Auto save format * + PSD_PATH_INFO_FIRST = 2000, Loaded * 0x07d0 - First path info block * + PSD_PATH_INFO_LAST = 2998, Loaded * 0x0bb6 - Last path info block * + PSD_CLIPPING_PATH = 2999, * 0x0bb7 - Name of clipping path * + PSD_PLUGIN_R_FIRST = 4000, * 0x0FA0 - First plugin resource * + PSD_PLUGIN_R_LAST = 4999, * 0x1387 - Last plugin resource * + PSD_IMAGEREADY_VARS = 7000, PS Only * 0x1B58 - Imageready variables * + PSD_IMAGEREADY_DATA = 7001, PS Only * 0x1B59 - Imageready data sets * + PSD_LIGHTROOM_WORK = 8000, PS Only * 0x1F40 - Lightroom workflow * + PSD_PRINT_FLAGS_2 = 10000 * 0x2710 - Print flags * +*/ + +#include "config.h" + +#include <string.h> +#include <errno.h> + +#include <glib/gstdio.h> +#include <libgimp/gimp.h> + +#include <jpeglib.h> +#include <jerror.h> + +#ifdef HAVE_IPTCDATA +#include <libiptcdata/iptc-data.h> +#endif /* HAVE_IPTCDATA */ + +#include "psd.h" +#include "psd-util.h" +#include "psd-image-res-load.h" + +#include "libgimp/stdplugins-intl.h" + +#define EXIF_HEADER_SIZE 8 + +/* Local function prototypes */ +static gint load_resource_unknown (const PSDimageres *res_a, + gint32 image_id, + FILE *f, + GError **error); + +static gint load_resource_ps_only (const PSDimageres *res_a, + gint32 image_id, + FILE *f, + GError **error); + +static gint load_resource_1005 (const PSDimageres *res_a, + gint32 image_id, + FILE *f, + GError **error); + +static gint load_resource_1006 (const PSDimageres *res_a, + gint32 image_id, + PSDimage *img_a, + FILE *f, + GError **error); + +static gint load_resource_1007 (const PSDimageres *res_a, + gint32 image_id, + PSDimage *img_a, + FILE *f, + GError **error); + +static gint load_resource_1008 (const PSDimageres *res_a, + gint32 image_id, + FILE *f, + GError **error); + +static gint load_resource_1022 (const PSDimageres *res_a, + gint32 image_id, + PSDimage *img_a, + FILE *f, + GError **error); + +static gint load_resource_1024 (const PSDimageres *res_a, + gint32 image_id, + PSDimage *img_a, + FILE *f, + GError **error); + +static gint load_resource_1028 (const PSDimageres *res_a, + gint32 image_id, + FILE *f, + GError **error); + +static gint load_resource_1032 (const PSDimageres *res_a, + gint32 image_id, + FILE *f, + GError **error); + +static gint load_resource_1033 (const PSDimageres *res_a, + gint32 image_id, + FILE *f, + GError **error); + +static gint load_resource_1039 (const PSDimageres *res_a, + PSDimage *img_a, + gint32 image_id, + FILE *f, + GError **error); + +static gint load_resource_1045 (const PSDimageres *res_a, + gint32 image_id, + PSDimage *img_a, + FILE *f, + GError **error); + +static gint load_resource_1046 (const PSDimageres *res_a, + gint32 image_id, + FILE *f, + GError **error); + +static gint load_resource_1053 (const PSDimageres *res_a, + gint32 image_id, + PSDimage *img_a, + FILE *f, + GError **error); + +static gint load_resource_1058 (const PSDimageres *res_a, + gint32 image_id, + FILE *f, + GError **error); + +static gint load_resource_1077 (const PSDimageres *res_a, + gint32 image_id, + PSDimage *img_a, + FILE *f, + GError **error); + +static gint load_resource_2000 (const PSDimageres *res_a, + gint32 image_id, + FILE *f, + GError **error); + +/* Public Functions */ +gint +get_image_resource_header (PSDimageres *res_a, + FILE *f, + GError **error) +{ + gint32 read_len; + gint32 write_len; + gchar *name; + + if (fread (&res_a->type, 4, 1, f) < 1 + || fread (&res_a->id, 2, 1, f) < 1) + { + psd_set_error (feof (f), errno, error); + return -1; + } + res_a->id = GUINT16_FROM_BE (res_a->id); + name = fread_pascal_string (&read_len, &write_len, 2, f, error); + if (*error) + return -1; + if (name != NULL) + g_strlcpy (res_a->name, name, write_len + 1); + else + res_a->name[0] = 0x0; + g_free (name); + if (fread (&res_a->data_len, 4, 1, f) < 1) + { + psd_set_error (feof (f), errno, error); + return -1; + } + res_a->data_len = GUINT32_FROM_BE (res_a->data_len); + res_a->data_start = ftell (f); + + IFDBG(2) g_debug ("Type: %.4s, id: %d, start: %d, len: %d", + res_a->type, res_a->id, res_a->data_start, res_a->data_len); + + return 0; +} + +gint +load_image_resource (PSDimageres *res_a, + gint32 image_id, + PSDimage *img_a, + FILE *f, + gboolean *resolution_loaded, + gboolean *profile_loaded, + GError **error) +{ + gint pad; + + /* Set file position to start of image resource data block */ + if (fseek (f, res_a->data_start, SEEK_SET) < 0) + { + psd_set_error (feof (f), errno, error); + return -1; + } + + /* Process image resource blocks */ + if (memcmp (res_a->type, "8BIM", 4) != 0 && + memcmp (res_a->type, "MeSa", 4) !=0) + { + IFDBG(1) g_debug ("Unknown image resource type signature %.4s", + res_a->type); + } + else + { + switch (res_a->id) + { + case PSD_PS2_IMAGE_INFO: + case PSD_PS2_COLOR_TAB: + case PSD_OBSOLETE_01: + case PSD_OBSOLETE_02: + case PSD_OBSOLETE_03: + /* Drop obsolete image resource blocks */ + IFDBG(2) g_debug ("Obsolete image resource block: %d", + res_a->id); + break; + + case PSD_THUMB_RES: + case PSD_THUMB_RES2: + /* Drop thumbnails from standard file load */ + IFDBG(2) g_debug ("Thumbnail resource block: %d", + res_a->id); + break; + + case PSD_MAC_PRINT_INFO: + case PSD_JPEG_QUAL: + /* Save photoshop resources with no meaning for GIMP + as image parasites */ + load_resource_ps_only (res_a, image_id, f, error); + break; + + case PSD_RESN_INFO: + if (! load_resource_1005 (res_a, image_id, f, error)) + *resolution_loaded = TRUE; + break; + + case PSD_ALPHA_NAMES: + if (! img_a->merged_image_only) + load_resource_1006 (res_a, image_id, img_a, f, error); + break; + + case PSD_DISPLAY_INFO: + load_resource_1007 (res_a, image_id, img_a, f, error); + break; + + case PSD_CAPTION: + load_resource_1008 (res_a, image_id, f, error); + break; + + case PSD_QUICK_MASK: + if (! img_a->merged_image_only) + load_resource_1022 (res_a, image_id, img_a, f, error); + break; + + case PSD_LAYER_STATE: + if (! img_a->merged_image_only) + load_resource_1024 (res_a, image_id, img_a, f, error); + break; + + case PSD_WORKING_PATH: + if (! img_a->merged_image_only) + load_resource_2000 (res_a, image_id, f, error); + break; + + case PSD_IPTC_NAA_DATA: + load_resource_1028 (res_a, image_id, f, error); + break; + + case PSD_GRID_GUIDE: + if (! img_a->merged_image_only) + load_resource_1032 (res_a, image_id, f, error); + break; + + case PSD_ICC_PROFILE: + if (! load_resource_1039 (res_a, img_a, image_id, f, error)) + *profile_loaded = TRUE; + break; + + case PSD_ALPHA_NAMES_UNI: + if (! img_a->merged_image_only) + load_resource_1045 (res_a, image_id, img_a, f, error); + break; + + case PSD_IDX_COL_TAB_CNT: + load_resource_1046 (res_a, image_id, f, error); + break; + + case PSD_ALPHA_ID: + if (! img_a->merged_image_only) + load_resource_1053 (res_a, image_id, img_a, f, error); + break; + + case PSD_EXIF_DATA: + load_resource_1058 (res_a, image_id, f, error); + break; + + case PSD_XMP_DATA: + break; + + case PSD_DISPLAY_INFO_NEW: + load_resource_1077 (res_a, image_id, img_a, f, error); + break; + + default: + if (res_a->id >= 2000 && + res_a->id < 2999) + load_resource_2000 (res_a, image_id, f, error); + else + load_resource_unknown (res_a, image_id, f, error); + } + } + + /* Image blocks are null padded to even length */ + if (res_a->data_len % 2 == 0) + pad = 0; + else + pad = 1; + + /* Set file position to end of image resource block */ + if (fseek (f, res_a->data_start + res_a->data_len + pad, SEEK_SET) < 0) + { + psd_set_error (feof (f), errno, error); + return -1; + } + + return 0; +} + +gint +load_thumbnail_resource (PSDimageres *res_a, + gint32 image_id, + FILE *f, + GError **error) +{ + gint rtn = 0; + gint pad; + + /* Set file position to start of image resource data block */ + if (fseek (f, res_a->data_start, SEEK_SET) < 0) + { + psd_set_error (feof (f), errno, error); + return -1; + } + + /* Process image resource blocks */ + if (res_a->id == PSD_THUMB_RES + || res_a->id == PSD_THUMB_RES2) + { + /* Load thumbnails from standard file load */ + load_resource_1033 (res_a, image_id, f, error); + rtn = 1; + } + + /* Image blocks are null padded to even length */ + if (res_a->data_len % 2 == 0) + pad = 0; + else + pad = 1; + + /* Set file position to end of image resource block */ + if (fseek (f, res_a->data_start + res_a->data_len + pad, SEEK_SET) < 0) + { + psd_set_error (feof (f), errno, error); + return -1; + } + + return rtn; +} + +/* Private Functions */ + +static gint +load_resource_unknown (const PSDimageres *res_a, + gint32 image_id, + FILE *f, + GError **error) +{ + /* Unknown image resources attached as parasites to re-save later */ + GimpParasite *parasite; + gchar *data; + gchar *name; + + IFDBG(2) g_debug ("Process unknown image resource block: %d", res_a->id); + + data = g_malloc (res_a->data_len); + if (res_a->data_len > 0 && fread (data, res_a->data_len, 1, f) < 1) + { + psd_set_error (feof (f), errno, error); + g_free (data); + return -1; + } + + name = g_strdup_printf ("psd-image-resource-%.4s-%.4x", + res_a->type, res_a->id); + IFDBG(2) g_debug ("Parasite name: %s", name); + + parasite = gimp_parasite_new (name, 0, res_a->data_len, data); + gimp_image_attach_parasite (image_id, parasite); + gimp_parasite_free (parasite); + g_free (data); + g_free (name); + + return 0; +} + +static gint +load_resource_ps_only (const PSDimageres *res_a, + gint32 image_id, + FILE *f, + GError **error) +{ + /* Save photoshop resources with no meaning for GIMP as image parasites + to re-save later */ + GimpParasite *parasite; + gchar *data; + gchar *name; + + IFDBG(3) g_debug ("Process image resource block: %d", res_a->id); + + data = g_malloc (res_a->data_len); + if (fread (data, res_a->data_len, 1, f) < 1) + { + psd_set_error (feof (f), errno, error); + g_free (data); + return -1; + } + + name = g_strdup_printf ("psd-image-resource-%.4s-%.4x", + res_a->type, res_a->id); + IFDBG(2) g_debug ("Parasite name: %s", name); + + parasite = gimp_parasite_new (name, 0, res_a->data_len, data); + gimp_image_attach_parasite (image_id, parasite); + gimp_parasite_free (parasite); + g_free (data); + g_free (name); + + return 0; +} + +static gint +load_resource_1005 (const PSDimageres *res_a, + gint32 image_id, + FILE *f, + GError **error) +{ + /* Load image resolution and unit of measure */ + + /* FIXME width unit and height unit unused at present */ + + ResolutionInfo res_info; + GimpUnit image_unit; + + IFDBG(2) g_debug ("Process image resource block 1005: Resolution Info"); + + if (fread (&res_info.hRes, 4, 1, f) < 1 + || fread (&res_info.hResUnit, 2, 1, f) < 1 + || fread (&res_info.widthUnit, 2, 1, f) < 1 + || fread (&res_info.vRes, 4, 1, f) < 1 + || fread (&res_info.vResUnit, 2, 1, f) < 1 + || fread (&res_info.heightUnit, 2, 1, f) < 1) + { + psd_set_error (feof (f), errno, error); + return -1; + } + res_info.hRes = GINT32_FROM_BE (res_info.hRes); + res_info.hResUnit = GINT16_FROM_BE (res_info.hResUnit); + res_info.widthUnit = GINT16_FROM_BE (res_info.widthUnit); + res_info.vRes = GINT32_FROM_BE (res_info.vRes); + res_info.vResUnit = GINT16_FROM_BE (res_info.vResUnit); + res_info.heightUnit = GINT16_FROM_BE (res_info.heightUnit); + + IFDBG(3) g_debug ("Resolution: %d, %d, %d, %d, %d, %d", + res_info.hRes, + res_info.hResUnit, + res_info.widthUnit, + res_info.vRes, + res_info.vResUnit, + res_info.heightUnit); + + /* Resolution always recorded as pixels / inch in a fixed point implied + decimal int32 with 16 bits before point and 16 after (i.e. cast as + double and divide resolution by 2^16 */ + gimp_image_set_resolution (image_id, + res_info.hRes / 65536.0, res_info.vRes / 65536.0); + + /* GIMP only has one display unit so use ps horizontal resolution unit */ + switch (res_info.hResUnit) + { + case PSD_RES_INCH: + image_unit = GIMP_UNIT_INCH; + break; + case PSD_RES_CM: + image_unit = GIMP_UNIT_MM; + break; + default: + image_unit = GIMP_UNIT_INCH; + } + + gimp_image_set_unit (image_id, image_unit); + + return 0; +} + +static gint +load_resource_1006 (const PSDimageres *res_a, + gint32 image_id, + PSDimage *img_a, + FILE *f, + GError **error) +{ + /* Load alpha channel names stored as a series of pascal strings + unpadded between strings */ + + gchar *str; + gint32 block_rem; + gint32 read_len; + gint32 write_len; + + IFDBG(2) g_debug ("Process image resource block 1006: Alpha Channel Names"); + + if (img_a->alpha_names) + { + IFDBG(3) g_debug ("Alpha names loaded from unicode resource block"); + return 0; + } + + img_a->alpha_names = g_ptr_array_new (); + + block_rem = res_a->data_len; + while (block_rem > 1) + { + str = fread_pascal_string (&read_len, &write_len, 1, f, error); + if (*error) + return -1; + IFDBG(3) g_debug ("String: %s, %d, %d", str, read_len, write_len); + if (write_len >= 0) + { + g_ptr_array_add (img_a->alpha_names, (gpointer) str); + } + block_rem -= read_len; + } + + return 0; +} + +static gint +load_resource_1007 (const PSDimageres *res_a, + gint32 image_id, + PSDimage *img_a, + FILE *f, + GError **error) +{ + /* Load alpha channel display info */ + + DisplayInfo dsp_info; + CMColor ps_color; + GimpRGB gimp_rgb; + GimpHSV gimp_hsv; + GimpCMYK gimp_cmyk; + gint16 tot_rec; + gint cidx; + + IFDBG(2) g_debug ("Process image resource block 1007: Display Info"); + tot_rec = res_a->data_len / 14; + if (tot_rec == 0) + return 0; + + img_a->alpha_display_info = g_new (PSDchanneldata *, tot_rec); + img_a->alpha_display_count = tot_rec; + for (cidx = 0; cidx < tot_rec; ++cidx) + { + if (fread (&dsp_info.colorSpace, 2, 1, f) < 1 + || fread (&dsp_info.color, 8, 1, f) < 1 + || fread (&dsp_info.opacity, 2, 1, f) < 1 + || fread (&dsp_info.kind, 1, 1, f) < 1 + || fread (&dsp_info.padding, 1, 1, f) < 1) + { + psd_set_error (feof (f), errno, error); + return -1; + } + dsp_info.colorSpace = GINT16_FROM_BE (dsp_info.colorSpace); + ps_color.cmyk.cyan = GUINT16_FROM_BE (dsp_info.color[0]); + ps_color.cmyk.magenta = GUINT16_FROM_BE (dsp_info.color[1]); + ps_color.cmyk.yellow = GUINT16_FROM_BE (dsp_info.color[2]); + ps_color.cmyk.black = GUINT16_FROM_BE (dsp_info.color[3]); + dsp_info.opacity = GINT16_FROM_BE (dsp_info.opacity); + + switch (dsp_info.colorSpace) + { + case PSD_CS_RGB: + gimp_rgb_set (&gimp_rgb, ps_color.rgb.red / 65535.0, + ps_color.rgb.green / 65535.0, + ps_color.rgb.blue / 65535.0); + break; + + case PSD_CS_HSB: + gimp_hsv_set (&gimp_hsv, ps_color.hsv.hue / 65535.0, + ps_color.hsv.saturation / 65535.0, + ps_color.hsv.value / 65535.0); + gimp_hsv_to_rgb (&gimp_hsv, &gimp_rgb); + break; + + case PSD_CS_CMYK: + gimp_cmyk_set (&gimp_cmyk, 1.0 - ps_color.cmyk.cyan / 65535.0, + 1.0 - ps_color.cmyk.magenta / 65535.0, + 1.0 - ps_color.cmyk.yellow / 65535.0, + 1.0 - ps_color.cmyk.black / 65535.0); + gimp_cmyk_to_rgb (&gimp_cmyk, &gimp_rgb); + break; + + case PSD_CS_GRAYSCALE: + gimp_rgb_set (&gimp_rgb, ps_color.gray.gray / 10000.0, + ps_color.gray.gray / 10000.0, + ps_color.gray.gray / 10000.0); + break; + + case PSD_CS_FOCOLTONE: + case PSD_CS_TRUMATCH: + case PSD_CS_HKS: + case PSD_CS_LAB: + case PSD_CS_PANTONE: + case PSD_CS_TOYO: + case PSD_CS_DIC: + case PSD_CS_ANPA: + default: + if (CONVERSION_WARNINGS) + g_message ("Unsupported color space: %d", + dsp_info.colorSpace); + gimp_rgb_set (&gimp_rgb, 1.0, 0.0, 0.0); + } + + gimp_rgb_set_alpha (&gimp_rgb, 1.0); + + IFDBG(2) g_debug ("PS cSpace: %d, col: %d %d %d %d, opacity: %d, kind: %d", + dsp_info.colorSpace, ps_color.cmyk.cyan, ps_color.cmyk.magenta, + ps_color.cmyk.yellow, ps_color.cmyk.black, dsp_info.opacity, + dsp_info.kind); + + IFDBG(2) g_debug ("cSpace: %d, col: %g %g %g, opacity: %d, kind: %d", + dsp_info.colorSpace, gimp_rgb.r * 255 , gimp_rgb.g * 255, + gimp_rgb.b * 255, dsp_info.opacity, dsp_info.kind); + + img_a->alpha_display_info[cidx] = g_malloc0 (sizeof (PSDchanneldata)); + img_a->alpha_display_info[cidx]->gimp_color = gimp_rgb; + img_a->alpha_display_info[cidx]->opacity = dsp_info.opacity; + img_a->alpha_display_info[cidx]->ps_kind = dsp_info.kind; + img_a->alpha_display_info[cidx]->ps_cspace = dsp_info.colorSpace; + img_a->alpha_display_info[cidx]->ps_color = ps_color; + } + + return 0; +} + +static gint +load_resource_1008 (const PSDimageres *res_a, + gint32 image_id, + FILE *f, + GError **error) +{ + /* Load image caption */ + GimpParasite *parasite; + gchar *caption; + gint32 read_len; + gint32 write_len; + + IFDBG(2) g_debug ("Process image resource block: 1008: Caption"); + caption = fread_pascal_string (&read_len, &write_len, 1, f, error); + if (*error) + return -1; + + IFDBG(3) g_debug ("Caption: %s", caption); + parasite = gimp_parasite_new (GIMP_PARASITE_COMMENT, GIMP_PARASITE_PERSISTENT, + write_len, caption); + gimp_image_attach_parasite (image_id, parasite); + gimp_parasite_free (parasite); + g_free (caption); + + return 0; +} + +static gint +load_resource_1022 (const PSDimageres *res_a, + gint32 image_id, + PSDimage *img_a, + FILE *f, + GError **error) +{ + /* Load quick mask info */ + gboolean quick_mask_empty; /* Quick mask initially empty */ + + IFDBG(2) g_debug ("Process image resource block: 1022: Quick Mask"); + + if (fread (&img_a->quick_mask_id, 2, 1, f) < 1 + || fread (&quick_mask_empty, 1, 1, f) < 1) + { + psd_set_error (feof (f), errno, error); + return -1; + } + img_a->quick_mask_id = GUINT16_FROM_BE (img_a->quick_mask_id); + + IFDBG(3) g_debug ("Quick mask channel: %d, empty: %d", + img_a->quick_mask_id, + quick_mask_empty); + + return 0; +} + +static gint +load_resource_1024 (const PSDimageres *res_a, + gint32 image_id, + PSDimage *img_a, + FILE *f, + GError **error) +{ + /* Load image layer state - current active layer counting from bottom up */ + IFDBG(2) g_debug ("Process image resource block: 1024: Layer State"); + + if (fread (&img_a->layer_state, 2, 1, f) < 1) + { + psd_set_error (feof (f), errno, error); + return -1; + } + img_a->layer_state = GUINT16_FROM_BE (img_a->layer_state); + + return 0; +} + +static gint +load_resource_1028 (const PSDimageres *res_a, + gint32 image_id, + FILE *f, + GError **error) +{ + /* Load IPTC data block */ + +#ifdef HAVE_IPTCDATA + IptcData *iptc_data; + guchar *iptc_buf; + guint iptc_buf_len; +#else + gchar *name; +#endif /* HAVE_IPTCDATA */ + + GimpParasite *parasite; + gchar *res_data; + + IFDBG(2) g_debug ("Process image resource block: 1028: IPTC data"); + + res_data = g_malloc (res_a->data_len); + if (fread (res_data, res_a->data_len, 1, f) < 1) + { + psd_set_error (feof (f), errno, error); + g_free (res_data); + return -1; + } + +#ifdef HAVE_IPTCDATA + /* Load IPTC data structure */ + iptc_data = iptc_data_new_from_data (res_data, res_a->data_len); + IFDBG (3) iptc_data_dump (iptc_data, 0); + + /* Store resource data as a GIMP IPTC parasite */ + IFDBG (2) g_debug ("Processing IPTC data as GIMP IPTC parasite"); + /* Serialize IPTC data */ + iptc_data_save (iptc_data, &iptc_buf, &iptc_buf_len); + if (iptc_buf_len > 0) + { + parasite = gimp_parasite_new (GIMP_PARASITE_IPTC, + GIMP_PARASITE_PERSISTENT, + iptc_buf_len, iptc_buf); + gimp_image_attach_parasite (image_id, parasite); + gimp_parasite_free (parasite); + } + + iptc_data_unref (iptc_data); + g_free (iptc_buf); + +#else + /* Store resource data as a standard psd parasite */ + IFDBG (2) g_debug ("Processing IPTC data as psd parasite"); + name = g_strdup_printf ("psd-image-resource-%.4s-%.4x", + res_a->type, res_a->id); + IFDBG(3) g_debug ("Parasite name: %s", name); + + parasite = gimp_parasite_new (name, 0, res_a->data_len, res_data); + gimp_image_attach_parasite (image_id, parasite); + gimp_parasite_free (parasite); + g_free (name); + +#endif /* HAVE_IPTCDATA */ + + g_free (res_data); + return 0; +} + +static gint +load_resource_1032 (const PSDimageres *res_a, + gint32 image_id, + FILE *f, + GError **error) +{ + /* Load grid and guides */ + + /* Grid info is not used (CS2 or earlier) */ + + GuideHeader hdr; + GuideResource guide; + gint i; + + IFDBG(2) g_debug ("Process image resource block 1032: Grid and Guide Info"); + + if (fread (&hdr.fVersion, 4, 1, f) < 1 + || fread (&hdr.fGridCycleV, 4, 1, f) < 1 + || fread (&hdr.fGridCycleH, 4, 1, f) < 1 + || fread (&hdr.fGuideCount, 4, 1, f) < 1) + { + psd_set_error (feof (f), errno, error); + return -1; + } + hdr.fVersion = GUINT32_FROM_BE (hdr.fVersion); + hdr.fGridCycleV = GUINT32_FROM_BE (hdr.fGridCycleV); + hdr.fGridCycleH = GUINT32_FROM_BE (hdr.fGridCycleH); + hdr.fGuideCount = GUINT32_FROM_BE (hdr.fGuideCount); + + IFDBG(3) g_debug ("Grids & Guides: %d, %d, %d, %d", + hdr.fVersion, + hdr.fGridCycleV, + hdr.fGridCycleH, + hdr.fGuideCount); + + for (i = 0; i < hdr.fGuideCount; ++i) + { + if (fread (&guide.fLocation, 4, 1, f) < 1 + || fread (&guide.fDirection, 1, 1, f) < 1) + { + psd_set_error (feof (f), errno, error); + return -1; + } + guide.fLocation = GUINT32_FROM_BE (guide.fLocation); + guide.fLocation /= 32; + + IFDBG(3) g_debug ("Guide: %d px, %d", + guide.fLocation, + guide.fDirection); + + if (guide.fDirection == PSD_VERTICAL) + gimp_image_add_vguide (image_id, guide.fLocation); + else + gimp_image_add_hguide (image_id, guide.fLocation); + } + + return 0; +} + +static gint +load_resource_1033 (const PSDimageres *res_a, + gint32 image_id, + FILE *f, + GError **error) +{ + /* Load thumbnail image */ + + struct jpeg_decompress_struct cinfo; + struct jpeg_error_mgr jerr; + + ThumbnailInfo thumb_info; + GeglBuffer *buffer; + const Babl *format; + gint32 layer_id; + guchar *buf; + guchar *rgb_buf; + guchar **rowbuf; + gint i; + + IFDBG(2) g_debug ("Process image resource block %d: Thumbnail Image", res_a->id); + + /* Read thumbnail resource header info */ + if (fread (&thumb_info.format, 4, 1, f) < 1 + || fread (&thumb_info.width, 4, 1, f) < 1 + || fread (&thumb_info.height, 4, 1, f) < 1 + || fread (&thumb_info.widthbytes, 4, 1, f) < 1 + || fread (&thumb_info.size, 4, 1, f) < 1 + || fread (&thumb_info.compressedsize, 4, 1, f) < 1 + || fread (&thumb_info.bitspixel, 2, 1, f) < 1 + || fread (&thumb_info.planes, 2, 1, f) < 1) + { + psd_set_error (feof (f), errno, error); + return -1; + } + thumb_info.format = GINT32_FROM_BE (thumb_info.format); + thumb_info.width = GINT32_FROM_BE (thumb_info.width); + thumb_info.height = GINT32_FROM_BE (thumb_info.height); + thumb_info.widthbytes = GINT32_FROM_BE (thumb_info.widthbytes); + thumb_info.size = GINT32_FROM_BE (thumb_info.size); + thumb_info.compressedsize = GINT32_FROM_BE (thumb_info.compressedsize); + thumb_info.bitspixel = GINT16_FROM_BE (thumb_info.bitspixel); + thumb_info.planes = GINT16_FROM_BE (thumb_info.planes); + + IFDBG(2) g_debug ("\nThumbnail:\n" + "\tFormat: %d\n" + "\tDimensions: %d x %d\n", + thumb_info.format, + thumb_info.width, + thumb_info.height); + + if (thumb_info.format != 1) + { + IFDBG(1) g_debug ("Unknown thumbnail format %d", thumb_info.format); + return -1; + } + + /* Load Jpeg RGB thumbnail info */ + + /* Step 1: Allocate and initialize JPEG decompression object */ + cinfo.err = jpeg_std_error (&jerr); + jpeg_create_decompress (&cinfo); + + /* Step 2: specify data source (eg, a file) */ + jpeg_stdio_src(&cinfo, f); + + /* Step 3: read file parameters with jpeg_read_header() */ + jpeg_read_header (&cinfo, TRUE); + + /* Step 4: set parameters for decompression */ + + + /* Step 5: Start decompressor */ + jpeg_start_decompress (&cinfo); + + /* temporary buffers */ + buf = g_new (guchar, cinfo.output_height * cinfo.output_width + * cinfo.output_components); + if (res_a->id == PSD_THUMB_RES) + rgb_buf = g_new (guchar, cinfo.output_height * cinfo.output_width + * cinfo.output_components); + else + rgb_buf = NULL; + rowbuf = g_new (guchar *, cinfo.output_height); + + for (i = 0; i < cinfo.output_height; ++i) + rowbuf[i] = buf + cinfo.output_width * cinfo.output_components * i; + + /* Create image layer */ + gimp_image_resize (image_id, cinfo.output_width, cinfo.output_height, 0, 0); + layer_id = gimp_layer_new (image_id, _("Background"), + cinfo.output_width, + cinfo.output_height, + GIMP_RGB_IMAGE, + 100, + gimp_image_get_default_new_layer_mode (image_id)); + buffer = gimp_drawable_get_buffer (layer_id); + format = babl_format ("R'G'B' u8"); + + /* Step 6: while (scan lines remain to be read) */ + /* jpeg_read_scanlines(...); */ + while (cinfo.output_scanline < cinfo.output_height) + { + jpeg_read_scanlines (&cinfo, + (JSAMPARRAY) &rowbuf[cinfo.output_scanline], 1); + } + + if (res_a->id == PSD_THUMB_RES) /* Order is BGR for resource 1033 */ + { + guchar *dst = rgb_buf; + guchar *src = buf; + + for (i = 0; i < gegl_buffer_get_width (buffer) * gegl_buffer_get_height (buffer); ++i) + { + guchar r, g, b; + + r = *(src++); + g = *(src++); + b = *(src++); + *(dst++) = b; + *(dst++) = g; + *(dst++) = r; + } + } + + gegl_buffer_set (buffer, GEGL_RECTANGLE (0, 0, + gegl_buffer_get_width (buffer), + gegl_buffer_get_height (buffer)), + 0, format, rgb_buf ? rgb_buf : buf, GEGL_AUTO_ROWSTRIDE); + + /* Step 7: Finish decompression */ + jpeg_finish_decompress (&cinfo); + /* We can ignore the return value since suspension is not possible + * with the stdio data source. + */ + + /* Step 8: Release JPEG decompression object */ + jpeg_destroy_decompress (&cinfo); + + /* free up the temporary buffers */ + g_free (rowbuf); + g_free (buf); + g_free (rgb_buf); + + /* At this point you may want to check to see whether any + * corrupt-data warnings occurred (test whether + * jerr.num_warnings is nonzero). + */ + gimp_image_insert_layer (image_id, layer_id, -1, 0); + g_object_unref (buffer); + + return 0; +} + +static gint +load_resource_1039 (const PSDimageres *res_a, + PSDimage *img_a, + gint32 image_id, + FILE *f, + GError **error) +{ + /* Load ICC profile */ + GimpColorProfile *profile; + gchar *icc_profile; + + IFDBG(2) g_debug ("Process image resource block: 1039: ICC Profile"); + + icc_profile = g_malloc (res_a->data_len); + if (fread (icc_profile, res_a->data_len, 1, f) < 1) + { + psd_set_error (feof (f), errno, error); + g_free (icc_profile); + return -1; + } + + profile = gimp_color_profile_new_from_icc_profile ((guint8 *) icc_profile, + res_a->data_len, + NULL); + if (profile) + { + if (img_a->color_mode == PSD_CMYK && + gimp_color_profile_is_cmyk (profile)) + { + img_a->cmyk_profile = profile; + } + else + { + gimp_image_set_color_profile (image_id, profile); + g_object_unref (profile); + } + } + + g_free (icc_profile); + + return 0; +} + +static gint +load_resource_1045 (const PSDimageres *res_a, + gint32 image_id, + PSDimage *img_a, + FILE *f, + GError **error) +{ + /* Load alpha channel names stored as a series of unicode strings + in a GPtrArray */ + + gchar *str; + gint32 block_rem; + gint32 read_len; + gint32 write_len; + + IFDBG(2) g_debug ("Process image resource block 1045: Unicode Alpha Channel Names"); + + if (img_a->alpha_names) + { + gint i; + IFDBG(3) g_debug ("Deleting localised alpha channel names"); + for (i = 0; i < img_a->alpha_names->len; ++i) + { + str = g_ptr_array_index (img_a->alpha_names, i); + g_free (str); + } + g_ptr_array_free (img_a->alpha_names, TRUE); + } + + img_a->alpha_names = g_ptr_array_new (); + + block_rem = res_a->data_len; + while (block_rem > 1) + { + str = fread_unicode_string (&read_len, &write_len, 1, f, error); + if (*error) + return -1; + + IFDBG(3) g_debug ("String: %s, %d, %d", str, read_len, write_len); + if (write_len >= 0) + { + g_ptr_array_add (img_a->alpha_names, (gpointer) str); + } + block_rem -= read_len; + } + + return 0; +} + +static gint +load_resource_1046 (const PSDimageres *res_a, + gint32 image_id, + FILE *f, + GError **error) +{ + /* Load indexed color table count */ + guchar *cmap; + gint32 cmap_count = 0; + gint16 index_count = 0; + + IFDBG(2) g_debug ("Process image resource block: 1046: Indexed Color Table Count"); + + if (fread (&index_count, 2, 1, f) < 1) + { + psd_set_error (feof (f), errno, error); + return -1; + } + index_count = GINT16_FROM_BE (index_count); + + IFDBG(3) g_debug ("Indexed color table count: %d", index_count); + /* FIXME - check that we have indexed image */ + if (index_count && index_count < 256) + { + cmap = gimp_image_get_colormap (image_id, &cmap_count); + if (cmap && index_count < cmap_count) + gimp_image_set_colormap (image_id, cmap, index_count); + g_free (cmap); + } + return 0; +} + +static gint +load_resource_1053 (const PSDimageres *res_a, + gint32 image_id, + PSDimage *img_a, + FILE *f, + GError **error) +{ + /* Load image alpha channel ids (tattoos) */ + gint16 tot_rec; + gint16 cidx; + + IFDBG(2) g_debug ("Process image resource block: 1053: Channel ID"); + + tot_rec = res_a->data_len / 4; + if (tot_rec ==0) + return 0; + + img_a->alpha_id = g_malloc (sizeof (img_a->alpha_id) * tot_rec); + img_a->alpha_id_count = tot_rec; + for (cidx = 0; cidx < tot_rec; ++cidx) + { + if (fread (&img_a->alpha_id[cidx], 4, 1, f) < 1) + { + psd_set_error (feof (f), errno, error); + return -1; + } + img_a->alpha_id[cidx] = GUINT32_FROM_BE (img_a->alpha_id[cidx]); + + IFDBG(3) g_debug ("Channel id: %d", img_a->alpha_id[cidx]); + } + + return 0; +} + +static gint +load_resource_1058 (const PSDimageres *res_a, + gint32 image_id, + FILE *f, + GError **error) +{ + gchar *name; + + GimpParasite *parasite; + gchar *res_data; + + IFDBG(2) g_debug ("Process image resource block: 1058: Exif data"); + + res_data = g_malloc (res_a->data_len); + if (fread (res_data, res_a->data_len, 1, f) < 1) + { + psd_set_error (feof (f), errno, error); + g_free (res_data); + return -1; + } + + /* Store resource data as a standard psd parasite */ + IFDBG (2) g_debug ("Processing exif data as psd parasite"); + name = g_strdup_printf ("psd-image-resource-%.4s-%.4x", + res_a->type, res_a->id); + IFDBG(3) g_debug ("Parasite name: %s", name); + + parasite = gimp_parasite_new (name, 0, res_a->data_len, res_data); + gimp_image_attach_parasite (image_id, parasite); + gimp_parasite_free (parasite); + g_free (name); + + g_free (res_data); + return 0; +} + +static gint +load_resource_1077 (const PSDimageres *res_a, + gint32 image_id, + PSDimage *img_a, + FILE *f, + GError **error) +{ + /* Load alpha channel display info */ + + DisplayInfoNew dsp_info; + CMColor ps_color; + GimpRGB gimp_rgb; + GimpHSV gimp_hsv; + GimpCMYK gimp_cmyk; + gint16 tot_rec; + gint cidx; + + IFDBG(2) g_debug ("Process image resource block 1077: Display Info New"); + + /* For now, skip first 4 bytes since intention is unclear. Seems to be + a version number that is always one, but who knows. */ + fseek (f, 4, SEEK_CUR); + + tot_rec = res_a->data_len / 13; + if (tot_rec == 0) + return 0; + + img_a->alpha_display_info = g_new (PSDchanneldata *, tot_rec); + img_a->alpha_display_count = tot_rec; + for (cidx = 0; cidx < tot_rec; ++cidx) + { + if (fread (&dsp_info.colorSpace, 2, 1, f) < 1 + || fread (&dsp_info.color, 8, 1, f) < 1 + || fread (&dsp_info.opacity, 2, 1, f) < 1 + || fread (&dsp_info.mode, 1, 1, f) < 1) + { + psd_set_error (feof (f), errno, error); + return -1; + } + dsp_info.colorSpace = GINT16_FROM_BE (dsp_info.colorSpace); + ps_color.cmyk.cyan = GUINT16_FROM_BE (dsp_info.color[0]); + ps_color.cmyk.magenta = GUINT16_FROM_BE (dsp_info.color[1]); + ps_color.cmyk.yellow = GUINT16_FROM_BE (dsp_info.color[2]); + ps_color.cmyk.black = GUINT16_FROM_BE (dsp_info.color[3]); + dsp_info.opacity = GINT16_FROM_BE (dsp_info.opacity); + + switch (dsp_info.colorSpace) + { + case PSD_CS_RGB: + gimp_rgb_set (&gimp_rgb, ps_color.rgb.red / 65535.0, + ps_color.rgb.green / 65535.0, + ps_color.rgb.blue / 65535.0); + break; + + case PSD_CS_HSB: + gimp_hsv_set (&gimp_hsv, ps_color.hsv.hue / 65535.0, + ps_color.hsv.saturation / 65535.0, + ps_color.hsv.value / 65535.0); + gimp_hsv_to_rgb (&gimp_hsv, &gimp_rgb); + break; + + case PSD_CS_CMYK: + gimp_cmyk_set (&gimp_cmyk, 1.0 - ps_color.cmyk.cyan / 65535.0, + 1.0 - ps_color.cmyk.magenta / 65535.0, + 1.0 - ps_color.cmyk.yellow / 65535.0, + 1.0 - ps_color.cmyk.black / 65535.0); + gimp_cmyk_to_rgb (&gimp_cmyk, &gimp_rgb); + break; + + case PSD_CS_GRAYSCALE: + gimp_rgb_set (&gimp_rgb, ps_color.gray.gray / 10000.0, + ps_color.gray.gray / 10000.0, + ps_color.gray.gray / 10000.0); + break; + + case PSD_CS_FOCOLTONE: + case PSD_CS_TRUMATCH: + case PSD_CS_HKS: + case PSD_CS_LAB: + case PSD_CS_PANTONE: + case PSD_CS_TOYO: + case PSD_CS_DIC: + case PSD_CS_ANPA: + default: + if (CONVERSION_WARNINGS) + g_message ("Unsupported color space: %d", + dsp_info.colorSpace); + gimp_rgb_set (&gimp_rgb, 1.0, 0.0, 0.0); + } + + gimp_rgb_set_alpha (&gimp_rgb, 1.0); + + IFDBG(2) g_debug ("PS cSpace: %d, col: %d %d %d %d, opacity: %d, mode: %d", + dsp_info.colorSpace, ps_color.cmyk.cyan, ps_color.cmyk.magenta, + ps_color.cmyk.yellow, ps_color.cmyk.black, dsp_info.opacity, + dsp_info.mode); + + IFDBG(2) g_debug ("cSpace: %d, col: %g %g %g, opacity: %d, mode: %d", + dsp_info.colorSpace, gimp_rgb.r * 255 , gimp_rgb.g * 255, + gimp_rgb.b * 255, dsp_info.opacity, dsp_info.mode); + + img_a->alpha_display_info[cidx] = g_malloc0 (sizeof (PSDchanneldata)); + img_a->alpha_display_info[cidx]->gimp_color = gimp_rgb; + img_a->alpha_display_info[cidx]->opacity = dsp_info.opacity; + img_a->alpha_display_info[cidx]->ps_mode = dsp_info.mode; + img_a->alpha_display_info[cidx]->ps_cspace = dsp_info.colorSpace; + img_a->alpha_display_info[cidx]->ps_color = ps_color; + } + + return 0; +} + +static gint +load_resource_2000 (const PSDimageres *res_a, + gint32 image_id, + FILE *f, + GError **error) +{ + gdouble *controlpoints; + gint32 x[3]; + gint32 y[3]; + gint32 vector_id = -1; + gint16 type; + gint16 init_fill; + gint16 num_rec; + gint16 path_rec; + gint16 cntr; + gint image_width; + gint image_height; + gint i; + gboolean closed; + + /* Load path data from image resources 2000-2998 */ + + IFDBG(2) g_debug ("Process image resource block: %d :Path data", res_a->id); + path_rec = res_a->data_len / 26; + if (path_rec ==0) + return 0; + + if (fread (&type, 2, 1, f) < 1) + { + psd_set_error (feof (f), errno, error); + return -1; + } + type = GINT16_FROM_BE (type); + if (type != PSD_PATH_FILL_RULE) + { + IFDBG(1) g_debug ("Unexpected path record type: %d", type); + return -1; + } + + if (fseek (f, 24, SEEK_CUR) < 0) + { + psd_set_error (feof (f), errno, error); + return -1; + } + + path_rec--; + if (path_rec ==0) + return 0; + + image_width = gimp_image_width (image_id); + image_height = gimp_image_height (image_id); + + /* Create path */ + if (res_a->id == PSD_WORKING_PATH) + { + /* use "Working Path" for the path name to match the Photoshop display */ + vector_id = gimp_vectors_new (image_id, "Working Path"); + } + else + { + /* Use the name stored in the PSD to name the path */ + vector_id = gimp_vectors_new (image_id, res_a->name); + } + + gimp_image_insert_vectors (image_id, vector_id, -1, -1); + + while (path_rec > 0) + { + if (fread (&type, 2, 1, f) < 1) + { + psd_set_error (feof (f), errno, error); + return -1; + } + type = GINT16_FROM_BE (type); + IFDBG(3) g_debug ("Path record type %d", type); + + if (type == PSD_PATH_FILL_RULE) + { + if (fseek (f, 24, SEEK_CUR) < 0) + { + psd_set_error (feof (f), errno, error); + return -1; + } + } + + else if (type == PSD_PATH_FILL_INIT) + { + if (fread (&init_fill, 2, 1, f) < 1) + { + psd_set_error (feof (f), errno, error); + return -1; + } + + if (fseek (f, 22, SEEK_CUR) < 0) + { + psd_set_error (feof (f), errno, error); + return -1; + } + } + + else if (type == PSD_PATH_CL_LEN + || type == PSD_PATH_OP_LEN) + { + if (fread (&num_rec, 2, 1, f) < 1) + { + psd_set_error (feof (f), errno, error); + return -1; + } + num_rec = GINT16_FROM_BE (num_rec); + if (num_rec > path_rec) + { + psd_set_error (feof (f), errno, error); + return - 1; + } + IFDBG(3) g_debug ("Num path records %d", num_rec); + + if (type == PSD_PATH_CL_LEN) + closed = TRUE; + else + closed = FALSE; + cntr = 0; + controlpoints = g_malloc (sizeof (gdouble) * num_rec * 6); + if (fseek (f, 22, SEEK_CUR) < 0) + { + psd_set_error (feof (f), errno, error); + g_free (controlpoints); + return -1; + } + + while (num_rec > 0) + { + if (fread (&type, 2, 1, f) < 1) + { + psd_set_error (feof (f), errno, error); + return -1; + } + type = GINT16_FROM_BE (type); + IFDBG(3) g_debug ("Path record type %d", type); + + if (type == PSD_PATH_CL_LNK + || type == PSD_PATH_CL_UNLNK + || type == PSD_PATH_OP_LNK + || type == PSD_PATH_OP_UNLNK) + { + if (fread (&y[0], 4, 1, f) < 1 + || fread (&x[0], 4, 1, f) < 1 + || fread (&y[1], 4, 1, f) < 1 + || fread (&x[1], 4, 1, f) < 1 + || fread (&y[2], 4, 1, f) < 1 + || fread (&x[2], 4, 1, f) < 1) + { + psd_set_error (feof (f), errno, error); + return -1; + } + for (i = 0; i < 3; ++i) + { + x[i] = GINT32_FROM_BE (x[i]); + controlpoints[cntr] = x[i] / 16777216.0 * image_width; + cntr++; + y[i] = GINT32_FROM_BE (y[i]); + controlpoints[cntr] = y[i] / 16777216.0 * image_height; + cntr++; + } + IFDBG(3) g_debug ("Path points (%d,%d), (%d,%d), (%d,%d)", + x[0], y[0], x[1], y[1], x[2], y[2]); + } + else + { + IFDBG(1) g_debug ("Unexpected path type record %d", type); + if (fseek (f, 24, SEEK_CUR) < 0) + { + psd_set_error (feof (f), errno, error); + return -1; + } + } + path_rec--; + num_rec--; + } + /* Add sub-path */ + gimp_vectors_stroke_new_from_points (vector_id, + GIMP_VECTORS_STROKE_TYPE_BEZIER, + cntr, controlpoints, closed); + g_free (controlpoints); + } + + else + { + if (fseek (f, 24, SEEK_CUR) < 0) + { + psd_set_error (feof (f), errno, error); + return -1; + } + } + + path_rec--; + } + + return 0; +} |