gimp/plug-ins/file-psd/psd-image-res-load.c

1784 lines
61 KiB
C
Raw Permalink Normal View History

/* 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,
GimpImage *image,
GInputStream *input,
GError **error);
static gint load_resource_ps_only (const PSDimageres *res_a,
GimpImage *image,
GInputStream *input,
GError **error);
static gint load_resource_1005 (const PSDimageres *res_a,
GimpImage *image,
GInputStream *input,
GError **error);
static gint load_resource_1006 (const PSDimageres *res_a,
GimpImage *image,
PSDimage *img_a,
GInputStream *input,
GError **error);
static gint load_resource_1007 (const PSDimageres *res_a,
GimpImage *image,
PSDimage *img_a,
GInputStream *input,
GError **error);
static gint load_resource_1008 (const PSDimageres *res_a,
GimpImage *image,
GInputStream *input,
GError **error);
static gint load_resource_1022 (const PSDimageres *res_a,
GimpImage *image,
PSDimage *img_a,
GInputStream *input,
GError **error);
static gint load_resource_1024 (const PSDimageres *res_a,
GimpImage *image,
PSDimage *img_a,
GInputStream *input,
GError **error);
static gint load_resource_1028 (const PSDimageres *res_a,
GimpImage *image,
GInputStream *input,
GError **error);
static gint load_resource_1032 (const PSDimageres *res_a,
GimpImage *image,
GInputStream *input,
GError **error);
/* 1033 - Thumbnail needs special handling since it calls the jpeg library
* which needs a classic FILE. */
static gint load_resource_1033 (const PSDimageres *res_a,
GimpImage *image,
GFile *file,
GInputStream *input,
GError **error);
static gint load_resource_1039 (const PSDimageres *res_a,
PSDimage *img_a,
GimpImage *image,
GInputStream *input,
GError **error);
static gint load_resource_1045 (const PSDimageres *res_a,
GimpImage *image,
PSDimage *img_a,
GInputStream *input,
GError **error);
static gint load_resource_1046 (const PSDimageres *res_a,
GimpImage *image,
GInputStream *input,
GError **error);
static gint load_resource_1053 (const PSDimageres *res_a,
GimpImage *image,
PSDimage *img_a,
GInputStream *input,
GError **error);
static gint load_resource_1058 (const PSDimageres *res_a,
GimpImage *image,
GInputStream *input,
GError **error);
static gint load_resource_1069 (const PSDimageres *res_a,
GimpImage *image,
PSDimage *img_a,
GInputStream *input,
GError **error);
static gint load_resource_1077 (const PSDimageres *res_a,
GimpImage *image,
PSDimage *img_a,
GInputStream *input,
GError **error);
static gint load_resource_2000 (const PSDimageres *res_a,
GimpImage *image,
GInputStream *input,
GError **error);
static gint load_resource_2999 (const PSDimageres *res_a,
GimpImage *image,
GInputStream *input,
GError **error);
/* Public Functions */
gint
get_image_resource_header (PSDimageres *res_a,
GInputStream *input,
GError **error)
{
gint32 read_len;
gint32 write_len;
gchar *name;
if (psd_read (input, &res_a->type, 4, error) < 4 ||
psd_read (input, &res_a->id, 2, error) < 2)
{
psd_set_error (error);
return -1;
}
res_a->id = GUINT16_FROM_BE (res_a->id);
name = fread_pascal_string (&read_len, &write_len, 2, input, 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 (psd_read (input, &res_a->data_len, 4, error) < 4)
{
psd_set_error (error);
return -1;
}
res_a->data_len = GUINT32_FROM_BE (res_a->data_len);
res_a->data_start = g_seekable_tell (G_SEEKABLE (input));
IFDBG(2) g_debug ("Type: %.4s, id: %d, start: %" G_GOFFSET_FORMAT ", len: %" G_GSIZE_FORMAT,
res_a->type, res_a->id, res_a->data_start, res_a->data_len);
return 0;
}
gint
load_image_resource (PSDimageres *res_a,
GimpImage *image,
PSDimage *img_a,
GInputStream *input,
gboolean *resolution_loaded,
gboolean *profile_loaded,
GError **error)
{
gint pad;
/* Set file position to start of image resource data block */
if (! psd_seek (input, res_a->data_start, G_SEEK_SET, error))
{
psd_set_error (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, input, error);
break;
case PSD_RESN_INFO:
if (! load_resource_1005 (res_a, image, input, error))
*resolution_loaded = TRUE;
break;
case PSD_ALPHA_NAMES:
load_resource_1006 (res_a, image, img_a, input, error);
break;
case PSD_DISPLAY_INFO:
load_resource_1007 (res_a, image, img_a, input, error);
break;
case PSD_CAPTION:
load_resource_1008 (res_a, image, input, error);
break;
case PSD_QUICK_MASK:
if (! img_a->merged_image_only)
load_resource_1022 (res_a, image, img_a, input, error);
break;
case PSD_LAYER_STATE:
if (! img_a->merged_image_only)
load_resource_1024 (res_a, image, img_a, input, error);
break;
case PSD_WORKING_PATH:
if (! img_a->merged_image_only)
load_resource_2000 (res_a, image, input, error);
break;
case PSD_IPTC_NAA_DATA:
load_resource_1028 (res_a, image, input, error);
break;
case PSD_GRID_GUIDE:
if (! img_a->merged_image_only)
load_resource_1032 (res_a, image, input, error);
break;
case PSD_ICC_PROFILE:
if (! load_resource_1039 (res_a, img_a, image, input, error))
*profile_loaded = TRUE;
break;
case PSD_ALPHA_NAMES_UNI:
load_resource_1045 (res_a, image, img_a, input, error);
break;
case PSD_IDX_COL_TAB_CNT:
load_resource_1046 (res_a, image, input, error);
break;
case PSD_ALPHA_ID:
if (! img_a->merged_image_only)
load_resource_1053 (res_a, image, img_a, input, error);
break;
case PSD_EXIF_DATA:
load_resource_1058 (res_a, image, input, error);
break;
case PSD_LAYER_SELECT_ID:
if (! img_a->merged_image_only)
load_resource_1069 (res_a, image, img_a, input, error);
break;
case PSD_XMP_DATA:
break;
case PSD_DISPLAY_INFO_NEW:
load_resource_1077 (res_a, image, img_a, input, error);
break;
case PSD_CLIPPING_PATH:
load_resource_2999 (res_a, image, input, error);
break;
case PSD_LAYER_COMPS:
img_a->unsupported_features->layer_comp = TRUE;
img_a->unsupported_features->show_gui = TRUE;
load_resource_unknown (res_a, image, input, error);
break;
default:
if (res_a->id >= 2000 &&
res_a->id < 2999)
load_resource_2000 (res_a, image, input, error);
else
load_resource_unknown (res_a, image, input, 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 (! psd_seek (input, res_a->data_start + res_a->data_len + pad, G_SEEK_SET, error))
{
psd_set_error (error);
return -1;
}
return 0;
}
gint
load_thumbnail_resource (PSDimageres *res_a,
GimpImage *image,
GFile *file,
GInputStream *input,
GError **error)
{
gint rtn = 0;
gint pad;
/* Set file position to start of image resource data block */
if (! psd_seek (input, res_a->data_start, G_SEEK_SET, error))
{
psd_set_error (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, file, input, 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 (psd_seek (input, res_a->data_start + res_a->data_len + pad, G_SEEK_SET, error))
{
psd_set_error (error);
return -1;
}
return rtn;
}
/* Private Functions */
static gint
load_resource_unknown (const PSDimageres *res_a,
GimpImage *image,
GInputStream *input,
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 && psd_read (input, data, res_a->data_len, error) < res_a->data_len)
{
psd_set_error (error);
2010-11-10 02:03:47 +08:00
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, parasite);
gimp_parasite_free (parasite);
g_free (data);
g_free (name);
return 0;
}
static gint
load_resource_ps_only (const PSDimageres *res_a,
GimpImage *image,
GInputStream *input,
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 (psd_read (input, data, res_a->data_len, error) < res_a->data_len)
{
psd_set_error (error);
2010-11-10 02:03:47 +08:00
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, parasite);
gimp_parasite_free (parasite);
g_free (data);
g_free (name);
return 0;
}
static gint
load_resource_1005 (const PSDimageres *res_a,
GimpImage *image,
GInputStream *input,
GError **error)
{
/* Load image resolution and unit of measure */
/* FIXME width unit and height unit unused at present */
Issue #8900 and #9923: reimplementing GimpUnit as a proper class. This fixes all our GObject Introspection issues with GimpUnit which was both an enum and an int-derived type of user-defined units *completing* the enum values. GIR clearly didn't like this! Now GimpUnit is a proper class and units are unique objects, allowing to compare them with an identity test (i.e. `unit == gimp_unit_pixel ()` tells us if unit is the pixel unit or not), which makes it easy to use, just like with int, yet adding also methods, making for nicer introspected API. As an aside, this also fixes #10738, by having all the built-in units retrievable even if libgimpbase had not been properly initialized with gimp_base_init(). I haven't checked in details how GIR works to introspect, but it looks like it loads the library to inspect and runs functions, hence triggering some CRITICALS because virtual methods (supposed to be initialized with gimp_base_init() run by libgimp) are not set. This new code won't trigger any critical because the vtable method are now not necessary, at least for all built-in units. Note that GimpUnit is still in libgimpbase. It could have been moved to libgimp in order to avoid any virtual method table (since we need to keep core and libgimp side's units in sync, PDB is required), but too many libgimpwidgets widgets were already using GimpUnit. And technically most of GimpUnit logic doesn't require PDB (only the creation/sync part). This is one of the reasons why user-created GimpUnit list is handled and stored differently from other types of objects. Globally this simplifies the code a lot too and we don't need separate implementations of various utils for core and libgimp, which means less prone to errors.
2024-07-26 02:55:21 +08:00
ResolutionInfo res_info;
GimpUnit *image_unit;
IFDBG(2) g_debug ("Process image resource block 1005: Resolution Info");
if (psd_read (input, &res_info.hRes, 4, error) < 4 ||
psd_read (input, &res_info.hResUnit, 2, error) < 2 ||
psd_read (input, &res_info.widthUnit, 2, error) < 2 ||
psd_read (input, &res_info.vRes, 4, error) < 4 ||
psd_read (input, &res_info.vResUnit, 2, error) < 2 ||
psd_read (input, &res_info.heightUnit, 2, error) < 2)
{
psd_set_error (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,
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:
Issue #8900 and #9923: reimplementing GimpUnit as a proper class. This fixes all our GObject Introspection issues with GimpUnit which was both an enum and an int-derived type of user-defined units *completing* the enum values. GIR clearly didn't like this! Now GimpUnit is a proper class and units are unique objects, allowing to compare them with an identity test (i.e. `unit == gimp_unit_pixel ()` tells us if unit is the pixel unit or not), which makes it easy to use, just like with int, yet adding also methods, making for nicer introspected API. As an aside, this also fixes #10738, by having all the built-in units retrievable even if libgimpbase had not been properly initialized with gimp_base_init(). I haven't checked in details how GIR works to introspect, but it looks like it loads the library to inspect and runs functions, hence triggering some CRITICALS because virtual methods (supposed to be initialized with gimp_base_init() run by libgimp) are not set. This new code won't trigger any critical because the vtable method are now not necessary, at least for all built-in units. Note that GimpUnit is still in libgimpbase. It could have been moved to libgimp in order to avoid any virtual method table (since we need to keep core and libgimp side's units in sync, PDB is required), but too many libgimpwidgets widgets were already using GimpUnit. And technically most of GimpUnit logic doesn't require PDB (only the creation/sync part). This is one of the reasons why user-created GimpUnit list is handled and stored differently from other types of objects. Globally this simplifies the code a lot too and we don't need separate implementations of various utils for core and libgimp, which means less prone to errors.
2024-07-26 02:55:21 +08:00
image_unit = gimp_unit_inch ();
break;
case PSD_RES_CM:
Issue #8900 and #9923: reimplementing GimpUnit as a proper class. This fixes all our GObject Introspection issues with GimpUnit which was both an enum and an int-derived type of user-defined units *completing* the enum values. GIR clearly didn't like this! Now GimpUnit is a proper class and units are unique objects, allowing to compare them with an identity test (i.e. `unit == gimp_unit_pixel ()` tells us if unit is the pixel unit or not), which makes it easy to use, just like with int, yet adding also methods, making for nicer introspected API. As an aside, this also fixes #10738, by having all the built-in units retrievable even if libgimpbase had not been properly initialized with gimp_base_init(). I haven't checked in details how GIR works to introspect, but it looks like it loads the library to inspect and runs functions, hence triggering some CRITICALS because virtual methods (supposed to be initialized with gimp_base_init() run by libgimp) are not set. This new code won't trigger any critical because the vtable method are now not necessary, at least for all built-in units. Note that GimpUnit is still in libgimpbase. It could have been moved to libgimp in order to avoid any virtual method table (since we need to keep core and libgimp side's units in sync, PDB is required), but too many libgimpwidgets widgets were already using GimpUnit. And technically most of GimpUnit logic doesn't require PDB (only the creation/sync part). This is one of the reasons why user-created GimpUnit list is handled and stored differently from other types of objects. Globally this simplifies the code a lot too and we don't need separate implementations of various utils for core and libgimp, which means less prone to errors.
2024-07-26 02:55:21 +08:00
image_unit = gimp_unit_mm ();
break;
default:
Issue #8900 and #9923: reimplementing GimpUnit as a proper class. This fixes all our GObject Introspection issues with GimpUnit which was both an enum and an int-derived type of user-defined units *completing* the enum values. GIR clearly didn't like this! Now GimpUnit is a proper class and units are unique objects, allowing to compare them with an identity test (i.e. `unit == gimp_unit_pixel ()` tells us if unit is the pixel unit or not), which makes it easy to use, just like with int, yet adding also methods, making for nicer introspected API. As an aside, this also fixes #10738, by having all the built-in units retrievable even if libgimpbase had not been properly initialized with gimp_base_init(). I haven't checked in details how GIR works to introspect, but it looks like it loads the library to inspect and runs functions, hence triggering some CRITICALS because virtual methods (supposed to be initialized with gimp_base_init() run by libgimp) are not set. This new code won't trigger any critical because the vtable method are now not necessary, at least for all built-in units. Note that GimpUnit is still in libgimpbase. It could have been moved to libgimp in order to avoid any virtual method table (since we need to keep core and libgimp side's units in sync, PDB is required), but too many libgimpwidgets widgets were already using GimpUnit. And technically most of GimpUnit logic doesn't require PDB (only the creation/sync part). This is one of the reasons why user-created GimpUnit list is handled and stored differently from other types of objects. Globally this simplifies the code a lot too and we don't need separate implementations of various utils for core and libgimp, which means less prone to errors.
2024-07-26 02:55:21 +08:00
image_unit = gimp_unit_inch ();
}
gimp_image_set_unit (image, image_unit);
return 0;
}
static gint
load_resource_1006 (const PSDimageres *res_a,
GimpImage *image,
PSDimage *img_a,
GInputStream *input,
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, input, 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,
GimpImage *image,
PSDimage *img_a,
GInputStream *input,
GError **error)
{
/* Load alpha channel display info */
DisplayInfo dsp_info;
CMColor ps_color;
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)
{
GeglColor *color;
if (psd_read (input, &dsp_info.colorSpace, 2, error) < 2 ||
psd_read (input, &dsp_info.color, 8, error) < 8 ||
psd_read (input, &dsp_info.opacity, 2, error) < 2 ||
psd_read (input, &dsp_info.kind, 1, error) < 1 ||
psd_read (input, &dsp_info.padding, 1, error) < 1)
{
psd_set_error (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);
color = gegl_color_new ("red");
switch (dsp_info.colorSpace)
{
case PSD_CS_RGB:
/* TODO: which space should we use? */
gegl_color_set_rgba_with_space (color,
ps_color.rgb.red / 65535.0,
ps_color.rgb.green / 65535.0,
ps_color.rgb.blue / 65535.0,
1.0, NULL);
break;
case PSD_CS_HSB:
{
gfloat hsv[3] =
{
ps_color.hsv.hue / 65535.0f,
ps_color.hsv.saturation / 65535.0f,
ps_color.hsv.value / 65535.0f
};
gegl_color_set_pixel (color, babl_format ("HSV float"), hsv);
}
break;
case PSD_CS_CMYK:
/* TODO: again, which space? */
gegl_color_set_cmyk (color,
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,
1.0, NULL);
break;
case PSD_CS_GRAYSCALE:
{
gdouble gray = ps_color.gray.gray / 10000.0;
/* TODO: which space? */
gegl_color_set_pixel (color, babl_format_with_space ("Y' double", NULL), &gray);
}
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);
}
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, opacity: %d, kind: %d",
dsp_info.colorSpace, dsp_info.opacity, dsp_info.kind);
img_a->alpha_display_info[cidx] = g_malloc0 (sizeof (PSDchanneldata));
img_a->alpha_display_info[cidx]->gimp_color = color;
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,
GimpImage *image,
GInputStream *input,
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, input, 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, parasite);
gimp_parasite_free (parasite);
g_free (caption);
return 0;
}
static gint
load_resource_1022 (const PSDimageres *res_a,
GimpImage *image,
PSDimage *img_a,
GInputStream *input,
GError **error)
{
/* Load quick mask info */
gboolean quick_mask_empty = TRUE; /* Quick mask initially empty */
IFDBG(2) g_debug ("Process image resource block: 1022: Quick Mask");
if (psd_read (input, &img_a->quick_mask_id, 2, error) < 2 ||
psd_read (input, &quick_mask_empty, 1, error) < 1)
{
psd_set_error (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,
GimpImage *image,
PSDimage *img_a,
GInputStream *input,
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 (psd_read (input, &img_a->layer_state, 2, error) < 2)
{
psd_set_error (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,
GimpImage *image,
GInputStream *input,
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 (psd_read (input, res_data, res_a->data_len, error) < res_a->data_len)
{
psd_set_error (error);
2010-11-10 02:03:47 +08:00
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, 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, 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,
GimpImage *image,
GInputStream *input,
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 (psd_read (input, &hdr.fVersion, 4, error) < 4 ||
psd_read (input, &hdr.fGridCycleV, 4, error) < 4 ||
psd_read (input, &hdr.fGridCycleH, 4, error) < 4 ||
psd_read (input, &hdr.fGuideCount, 4, error) < 4)
{
psd_set_error (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 (psd_read (input, &guide.fLocation, 4, error) < 4 ||
psd_read (input, &guide.fDirection, 1, error) < 1)
{
psd_set_error (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, guide.fLocation);
else
gimp_image_add_hguide (image, guide.fLocation);
}
return 0;
}
static gint
load_resource_1033 (const PSDimageres *res_a,
GimpImage *image,
GFile *file,
GInputStream *input,
GError **error)
{
/* Load thumbnail image */
struct jpeg_decompress_struct cinfo;
struct jpeg_error_mgr jerr;
FILE *f;
ThumbnailInfo thumb_info;
GeglBuffer *buffer;
const Babl *format;
GimpLayer *layer;
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 (psd_read (input, &thumb_info.format, 4, error) < 4 ||
psd_read (input, &thumb_info.width, 4, error) < 4 ||
psd_read (input, &thumb_info.height, 4, error) < 4 ||
psd_read (input, &thumb_info.widthbytes, 4, error) < 4 ||
psd_read (input, &thumb_info.size, 4, error) < 4 ||
psd_read (input, &thumb_info.compressedsize, 4, error) < 4 ||
psd_read (input, &thumb_info.bitspixel, 2, error) < 2 ||
psd_read (input, &thumb_info.planes, 2, error) < 2)
{
psd_set_error (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 */
/* Open input also as a FILE. */
f = g_fopen (g_file_peek_path (file), "rb");
if (! f)
return -1;
/* Now seek to the same position as we have in input. */
fseek(f, g_seekable_tell (G_SEEKABLE (input)), SEEK_SET);
/* 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, cinfo.output_width, cinfo.output_height, 0, 0);
layer = gimp_layer_new (image, _("Background"),
cinfo.output_width,
cinfo.output_height,
GIMP_RGB_IMAGE,
100,
gimp_image_get_default_new_layer_mode (image));
buffer = gimp_drawable_get_buffer (GIMP_DRAWABLE (layer));
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);
/* Close FILE */
fclose (f);
/* 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, layer, NULL, 0);
g_object_unref (buffer);
return 0;
}
static gint
load_resource_1039 (const PSDimageres *res_a,
PSDimage *img_a,
GimpImage *image,
GInputStream *input,
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 (psd_read (input, icc_profile, res_a->data_len, error) < res_a->data_len)
{
psd_set_error (error);
2010-11-10 02:03:47 +08:00
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;
/* Store CMYK profile in GimpImage if attached */
gimp_image_set_simulation_profile (image, img_a->cmyk_profile);
}
else if (img_a->color_mode == PSD_LAB)
{
g_debug ("LAB color profile ignored.");
g_object_unref (profile);
}
else
{
gimp_image_set_color_profile (image, profile);
g_object_unref (profile);
}
}
g_free (icc_profile);
return 0;
}
static gint
load_resource_1045 (const PSDimageres *res_a,
GimpImage *image,
PSDimage *img_a,
GInputStream *input,
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, FALSE, input,
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,
GimpImage *image,
GInputStream *input,
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 (psd_read (input, &index_count, 2, error) < 2)
{
psd_set_error (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, NULL, &cmap_count);
if (cmap && index_count < cmap_count)
gimp_image_set_colormap (image, cmap, index_count);
g_free (cmap);
}
return 0;
}
static gint
load_resource_1053 (const PSDimageres *res_a,
GimpImage *image,
PSDimage *img_a,
GInputStream *input,
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 (psd_read (input, &img_a->alpha_id[cidx], 4, error) < 4)
{
psd_set_error (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,
GimpImage *image,
GInputStream *input,
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 (psd_read (input, res_data, res_a->data_len, error) < res_a->data_len)
{
psd_set_error (error);
2010-11-10 02:03:47 +08:00
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, parasite);
gimp_parasite_free (parasite);
g_free (name);
g_free (res_data);
return 0;
}
static gint
load_resource_1069 (const PSDimageres *res_a,
GimpImage *image,
PSDimage *img_a,
GInputStream *input,
GError **error)
{
guint16 layer_count = 0;
gint i;
IFDBG(2) g_debug ("Process image resource block: 1069: Layer Selection ID(s)");
if (psd_read (input, &layer_count, 2, error) < 2)
{
psd_set_error (error);
return -1;
}
layer_count = GUINT16_FROM_BE (layer_count);
/* This should probably not happen, but just in case the block is
* duplicated, let's just free the previous selection.
*/
g_list_free (img_a->layer_selection);
img_a->layer_selection = NULL;
for (i = 0; i < layer_count; i++)
{
guint32 layer_id = 0;
if (psd_read (input, &layer_id, 4, error) < 4)
{
psd_set_error (error);
return -1;
}
layer_id = GUINT32_FROM_BE (layer_id);
img_a->layer_selection = g_list_prepend (img_a->layer_selection, GINT_TO_POINTER (layer_id));
}
return 0;
}
static gint
load_resource_1077 (const PSDimageres *res_a,
GimpImage *image,
PSDimage *img_a,
GInputStream *input,
GError **error)
{
/* Load alpha channel display info */
DisplayInfoNew dsp_info;
CMColor ps_color;
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. */
if (! psd_seek (input, 4, G_SEEK_CUR, error))
return -1;
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)
{
GeglColor *color;
if (psd_read (input, &dsp_info.colorSpace, 2, error) < 2 ||
psd_read (input, &dsp_info.color, 8, error) < 8 ||
psd_read (input, &dsp_info.opacity, 2, error) < 2 ||
psd_read (input, &dsp_info.mode, 1, error) < 1)
{
psd_set_error (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);
color = gegl_color_new ("red");
switch (dsp_info.colorSpace)
{
case PSD_CS_RGB:
/* TODO: which space? */
gegl_color_set_rgba_with_space (color,
ps_color.rgb.red / 65535.0,
ps_color.rgb.green / 65535.0,
ps_color.rgb.blue / 65535.0,
1.0, NULL);
break;
case PSD_CS_HSB:
{
gfloat hsv[3] =
{
ps_color.hsv.hue / 65535.0f,
ps_color.hsv.saturation / 65535.0f,
ps_color.hsv.value / 65535.0f
};
gegl_color_set_pixel (color, babl_format ("HSV float"), hsv);
}
break;
case PSD_CS_CMYK:
/* TODO: which space? */
gegl_color_set_cmyk (color,
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,
1.0, NULL);
break;
case PSD_CS_GRAYSCALE:
{
gdouble gray = ps_color.gray.gray / 10000.0;
/* TODO: which space? */
gegl_color_set_pixel (color, babl_format_with_space ("Y' double", NULL), &gray);
}
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);
}
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, opacity: %d, mode: %d",
dsp_info.colorSpace, dsp_info.opacity, dsp_info.mode);
img_a->alpha_display_info[cidx] = g_malloc0 (sizeof (PSDchanneldata));
img_a->alpha_display_info[cidx]->gimp_color = color;
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,
GimpImage *image,
GInputStream *input,
GError **error)
{
gdouble *controlpoints;
gint32 x[3];
gint32 y[3];
GimpPath *path = NULL;
gint16 type = 0;
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 (psd_read (input, &type, 2, error) < 2)
{
psd_set_error (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 (! psd_seek (input, 24, G_SEEK_CUR, error))
{
psd_set_error (error);
return -1;
}
path_rec--;
if (path_rec ==0)
return 0;
image_width = gimp_image_get_width (image);
image_height = gimp_image_get_height (image);
/* Create path */
if (res_a->id == PSD_WORKING_PATH)
{
/* use "Working Path" for the path name to match the Photoshop display */
path = gimp_path_new (image, "Working Path");
}
else
{
/* Use the name stored in the PSD to name the path */
path = gimp_path_new (image, res_a->name);
}
gimp_image_insert_path (image, path, NULL, -1);
while (path_rec > 0)
{
if (psd_read (input, &type, 2, error) < 2)
{
psd_set_error (error);
return -1;
}
type = GINT16_FROM_BE (type);
IFDBG(3) g_debug ("Path record type %d", type);
if (type == PSD_PATH_FILL_RULE)
{
if (! psd_seek (input, 24, G_SEEK_CUR, error))
{
psd_set_error (error);
return -1;
}
}
else if (type == PSD_PATH_FILL_INIT)
{
if (psd_read (input, &init_fill, 2, error) < 2)
{
psd_set_error (error);
return -1;
}
if (! psd_seek (input, 22, G_SEEK_CUR, error))
{
psd_set_error (error);
return -1;
}
}
else if (type == PSD_PATH_CL_LEN
|| type == PSD_PATH_OP_LEN)
{
if (psd_read (input, &num_rec, 2, error) < 2)
{
psd_set_error (error);
return -1;
}
num_rec = GINT16_FROM_BE (num_rec);
IFDBG(3) g_debug ("Num path records %d", num_rec);
if (num_rec > path_rec)
{
psd_set_error (error);
return - 1;
}
if (type == PSD_PATH_CL_LEN)
closed = TRUE;
else
closed = FALSE;
cntr = 0;
controlpoints = g_malloc (sizeof (gdouble) * num_rec * 6);
if (! psd_seek (input, 22, G_SEEK_CUR, error))
{
psd_set_error (error);
2010-11-10 02:03:47 +08:00
g_free (controlpoints);
return -1;
}
while (num_rec > 0)
{
if (psd_read (input, &type, 2, error) < 2)
{
psd_set_error (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 (psd_read (input, &y[0], 4, error) < 4 ||
psd_read (input, &x[0], 4, error) < 4 ||
psd_read (input, &y[1], 4, error) < 4 ||
psd_read (input, &x[1], 4, error) < 4 ||
psd_read (input, &y[2], 4, error) < 4 ||
psd_read (input, &x[2], 4, error) < 4)
{
psd_set_error (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 (! psd_seek (input, 24, G_SEEK_CUR, error))
{
psd_set_error (error);
return -1;
}
}
path_rec--;
num_rec--;
}
/* Add sub-path */
gimp_path_stroke_new_from_points (path,
GIMP_PATH_STROKE_TYPE_BEZIER,
cntr, controlpoints, closed);
g_free (controlpoints);
}
else
{
if (! psd_seek (input, 24, G_SEEK_CUR, error))
{
psd_set_error (error);
return -1;
}
}
path_rec--;
}
return 0;
}
static gint
load_resource_2999 (const PSDimageres *res_a,
GimpImage *image,
GInputStream *input,
GError **error)
{
gchar *path_name;
gint16 path_flatness_int = 0;
gint16 path_flatness_fixed = 0;
gfloat path_flatness;
GimpParasite *parasite;
gint32 read_len;
gint32 write_len;
path_name = fread_pascal_string (&read_len, &write_len, 2, input, error);
if (*error)
return -1;
/* Convert from fixed to floating point */
if (psd_read (input, &path_flatness_int, 2, error) < 2 ||
psd_read (input, &path_flatness_fixed, 2, error) < 2)
{
psd_set_error (error);
return -1;
}
path_flatness_fixed = GINT16_FROM_BE (path_flatness_fixed);
path_flatness_int = GINT16_FROM_BE (path_flatness_int);
/* Converting from Adobe fixed point value to float */
path_flatness = (path_flatness_fixed - 0.5f) / 65536.0;
path_flatness += path_flatness_int;
/* Adobe path flatness range is 0.2 to 100.0 */
path_flatness = CLAMP (path_flatness, 0.2f, 100.0f);
/* Save to image parasite */
parasite = gimp_parasite_new (PSD_PARASITE_CLIPPING_PATH, 0,
read_len, path_name);
gimp_image_attach_parasite (image, parasite);
gimp_parasite_free (parasite);
parasite = gimp_parasite_new (PSD_PARASITE_PATH_FLATNESS, 0,
sizeof (gfloat), &path_flatness);
gimp_image_attach_parasite (image, parasite);
gimp_parasite_free (parasite);
/* Adobe says they ignore the last two bytes, the fill rule */
if (psd_read (input, &path_flatness_fixed, 2, error) < 2)
{
psd_set_error (error);
return -1;
}
g_free (path_name);
return 0;
}