diff options
Diffstat (limited to 'plug-ins/file-psd/psd-util.c')
-rw-r--r-- | plug-ins/file-psd/psd-util.c | 934 |
1 files changed, 934 insertions, 0 deletions
diff --git a/plug-ins/file-psd/psd-util.c b/plug-ins/file-psd/psd-util.c new file mode 100644 index 0000000..34b442d --- /dev/null +++ b/plug-ins/file-psd/psd-util.c @@ -0,0 +1,934 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1995 Spencer Kimball and Peter Mattis + * + * GIMP PSD Plug-in + * Copyright 2007 by John Marshall + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <https://www.gnu.org/licenses/>. + */ + +#include "config.h" + +#include <string.h> +#include <errno.h> + +#include <glib/gstdio.h> +#include <libgimp/gimp.h> + +#include "psd.h" +#include "psd-util.h" + +#include "libgimp/stdplugins-intl.h" + +/* Local constants */ +#define MIN_RUN 3 + +/* Local types */ +typedef struct +{ + const gchar *name; + const gchar *psd_mode; + GimpLayerMode gimp_mode; + gboolean exact; /* does the modes behave (more-or-less) the same in + * Photoshop and in GIMP? + */ +} LayerModeMapping; + +/* Local function prototypes */ +static const gchar * get_enum_value_nick (GType type, + gint value); + +/* Local variables */ + +/* mapping table between Photoshop and GIMP modes. in case a mode matches more + * than one entry (in either direction), the first entry wins. + */ +static const LayerModeMapping layer_mode_map[] = +{ +/* Name PSD GIMP Exact? */ + + /* Normal (ps3) */ + { "Normal", "norm", GIMP_LAYER_MODE_NORMAL, TRUE }, + { "Normal", "norm", GIMP_LAYER_MODE_NORMAL_LEGACY, TRUE }, + + /* Dissolve (ps3) */ + { "Dissolve", "diss", GIMP_LAYER_MODE_DISSOLVE, TRUE }, + + /* Multiply (ps3) */ + { "Multiply", "mul ", GIMP_LAYER_MODE_MULTIPLY, TRUE }, + { "Multiply", "mul ", GIMP_LAYER_MODE_MULTIPLY_LEGACY, TRUE }, + + /* Screen (ps3) */ + { "Screen", "scrn", GIMP_LAYER_MODE_SCREEN, TRUE }, + { "Screen", "scrn", GIMP_LAYER_MODE_SCREEN_LEGACY, TRUE }, + + /* Overlay (ps3) */ + { "Overlay", "over", GIMP_LAYER_MODE_OVERLAY, TRUE }, + + /* Difference (ps3) */ + { "Difference", "diff", GIMP_LAYER_MODE_DIFFERENCE, TRUE }, + { "Difference", "diff", GIMP_LAYER_MODE_DIFFERENCE_LEGACY, TRUE }, + + /* Linear Dodge (cs2) */ + { "Linear Dodge", "lddg", GIMP_LAYER_MODE_ADDITION, TRUE }, + { "Linear Dodge", "lddg", GIMP_LAYER_MODE_ADDITION_LEGACY, TRUE }, + + /* Subtract (??) */ + { "Subtract", "fsub", GIMP_LAYER_MODE_SUBTRACT, TRUE }, + { "Subtract", "fsub", GIMP_LAYER_MODE_SUBTRACT_LEGACY, TRUE }, + + /* Darken (ps3) */ + { "Darken", "dark", GIMP_LAYER_MODE_DARKEN_ONLY, TRUE }, + { "Darken", "dark", GIMP_LAYER_MODE_DARKEN_ONLY_LEGACY, TRUE }, + + /* Lighten (ps3) */ + { "Ligten", "lite", GIMP_LAYER_MODE_LIGHTEN_ONLY, TRUE }, + { "Ligten", "lite", GIMP_LAYER_MODE_LIGHTEN_ONLY_LEGACY, TRUE }, + + /* Hue (ps3) */ + { "Hue", "hue ", GIMP_LAYER_MODE_LCH_HUE, FALSE }, + { "Hue", "hue ", GIMP_LAYER_MODE_HSV_HUE, FALSE }, + { "Hue", "hue ", GIMP_LAYER_MODE_HSV_HUE_LEGACY, FALSE }, + + /* Stauration (ps3) */ + { "Saturation", "sat ", GIMP_LAYER_MODE_LCH_CHROMA, FALSE }, + { "Saturation", "sat ", GIMP_LAYER_MODE_HSV_SATURATION, FALSE }, + { "Saturation", "sat ", GIMP_LAYER_MODE_HSV_SATURATION_LEGACY, FALSE }, + + /* Color (ps3) */ + { "Color", "colr", GIMP_LAYER_MODE_LCH_COLOR, FALSE }, + { "Color", "colr", GIMP_LAYER_MODE_HSL_COLOR, FALSE }, + { "Color", "colr", GIMP_LAYER_MODE_HSL_COLOR_LEGACY, FALSE }, + + /* Luminosity (ps3) */ + { "Luminosity", "lum ", GIMP_LAYER_MODE_LCH_LIGHTNESS, FALSE }, + { "Luminosity", "lum ", GIMP_LAYER_MODE_HSV_VALUE, FALSE }, + { "Luminosity", "lum ", GIMP_LAYER_MODE_HSV_VALUE_LEGACY, FALSE }, + { "Luminosity", "lum ", GIMP_LAYER_MODE_LUMINANCE, FALSE }, + + /* Divide (??) */ + { "Divide", "fdiv", GIMP_LAYER_MODE_DIVIDE, TRUE }, + { "Divide", "fdiv", GIMP_LAYER_MODE_DIVIDE_LEGACY, TRUE }, + + /* Color Dodge (ps6) */ + { "Color Dodge", "div ", GIMP_LAYER_MODE_DODGE, TRUE }, + { "Color Dodge", "div ", GIMP_LAYER_MODE_DODGE_LEGACY, TRUE }, + + /* Color Burn (ps6) */ + { "Color Burn", "idiv", GIMP_LAYER_MODE_BURN, TRUE }, + { "Color Burn", "idiv", GIMP_LAYER_MODE_BURN_LEGACY, TRUE }, + + /* Hard Light (ps3) */ + { "Hard Light", "hLit", GIMP_LAYER_MODE_HARDLIGHT, TRUE }, + { "Hard Light", "hLit", GIMP_LAYER_MODE_HARDLIGHT_LEGACY, TRUE }, + + /* Soft Light (ps3) */ + { "Soft Light", "sLit", GIMP_LAYER_MODE_SOFTLIGHT, FALSE }, + { "Soft Light", "sLit", GIMP_LAYER_MODE_SOFTLIGHT_LEGACY, FALSE }, + { "Soft Light", "sLit", GIMP_LAYER_MODE_OVERLAY_LEGACY, FALSE }, + + /* Vivid Light (ps7)*/ + { "Vivid Light", "vLit", GIMP_LAYER_MODE_VIVID_LIGHT, TRUE }, + + /* Pin Light (ps7)*/ + { "Pin Light", "pLit", GIMP_LAYER_MODE_PIN_LIGHT, TRUE }, + + /* Linear Light (ps7)*/ + { "Linear Light", "lLit", GIMP_LAYER_MODE_LINEAR_LIGHT, TRUE }, + + /* Hard Mix (CS)*/ + { "Hard Mix", "hMix", GIMP_LAYER_MODE_HARD_MIX, TRUE }, + + /* Exclusion (ps6) */ + { "Exclusion", "smud", GIMP_LAYER_MODE_EXCLUSION, TRUE }, + + /* Linear Burn (ps7)*/ + { "Linear Burn", "lbrn", GIMP_LAYER_MODE_LINEAR_BURN, TRUE }, + + /* Darker Color (??)*/ + { "Darker Color", "dkCl", GIMP_LAYER_MODE_LUMA_DARKEN_ONLY, FALSE }, + + /* Lighter Color (??)*/ + { "Lighter Color", "lgCl", GIMP_LAYER_MODE_LUMA_LIGHTEN_ONLY, FALSE }, + + /* Pass Through (CS)*/ + { "Pass Through", "pass", GIMP_LAYER_MODE_PASS_THROUGH, TRUE }, +}; + + +/* Utility function */ +void +psd_set_error (gboolean file_eof, + gint err_no, + GError **error) +{ + if (file_eof) + { + g_set_error (error, G_FILE_ERROR, G_FILE_ERROR_FAILED, + "%s", _("Unexpected end of file")); + } + else + { + g_set_error (error, G_FILE_ERROR, g_file_error_from_errno (err_no), + "%s", g_strerror (err_no)); + } + + return; +} + +gchar * +fread_pascal_string (gint32 *bytes_read, + gint32 *bytes_written, + guint16 mod_len, + FILE *f, + GError **error) +{ + /* + * Reads a pascal string from the file padded to a multiple of mod_len + * and returns a utf-8 string. + */ + + gchar *str; + gchar *utf8_str; + guchar len; + gint32 padded_len; + + *bytes_read = 0; + *bytes_written = -1; + + if (fread (&len, 1, 1, f) < 1) + { + psd_set_error (feof (f), errno, error); + return NULL; + } + (*bytes_read)++; + IFDBG(3) g_debug ("Pascal string length %d", len); + + if (len == 0) + { + if (fseek (f, mod_len - 1, SEEK_CUR) < 0) + { + psd_set_error (feof (f), errno, error); + return NULL; + } + *bytes_read += (mod_len - 1); + *bytes_written = 0; + return NULL; + } + + str = g_malloc (len); + if (fread (str, len, 1, f) < 1) + { + psd_set_error (feof (f), errno, error); + g_free (str); + return NULL; + } + *bytes_read += len; + + if (mod_len > 0) + { + padded_len = len + 1; + while (padded_len % mod_len != 0) + { + if (fseek (f, 1, SEEK_CUR) < 0) + { + psd_set_error (feof (f), errno, error); + g_free (str); + return NULL; + } + (*bytes_read)++; + padded_len++; + } + } + + utf8_str = gimp_any_to_utf8 (str, len, NULL); + *bytes_written = strlen (utf8_str); + g_free (str); + + IFDBG(3) g_debug ("Pascal string: %s, bytes_read: %d, bytes_written: %d", + utf8_str, *bytes_read, *bytes_written); + + return utf8_str; +} + +gint32 +fwrite_pascal_string (const gchar *src, + guint16 mod_len, + FILE *f, + GError **error) +{ + /* + * Converts utf-8 string to current locale and writes as pascal + * string with padding to a multiple of mod_len. + */ + + gchar *str; + gchar *pascal_str; + gchar null_str = 0x0; + guchar pascal_len; + gint32 bytes_written = 0; + gsize len; + + if (src == NULL) + { + /* Write null string as two null bytes (0x0) */ + if (fwrite (&null_str, 1, 1, f) < 1 + || fwrite (&null_str, 1, 1, f) < 1) + { + psd_set_error (feof (f), errno, error); + return -1; + } + bytes_written += 2; + } + else + { + str = g_locale_from_utf8 (src, -1, NULL, &len, NULL); + if (len > 255) + pascal_len = 255; + else + pascal_len = len; + pascal_str = g_strndup (str, pascal_len); + g_free (str); + if (fwrite (&pascal_len, 1, 1, f) < 1 + || fwrite (pascal_str, pascal_len, 1, f) < 1) + { + psd_set_error (feof (f), errno, error); + g_free (pascal_str); + return -1; + } + bytes_written++; + bytes_written += pascal_len; + IFDBG(2) g_debug ("Pascal string: %s, bytes_written: %d", + pascal_str, bytes_written); + g_free (pascal_str); + } + + /* Pad with nulls */ + if (mod_len > 0) + { + while (bytes_written % mod_len != 0) + { + if (fwrite (&null_str, 1, 1, f) < 1) + { + psd_set_error (feof (f), errno, error); + return -1; + } + bytes_written++; + } + } + + return bytes_written; +} + +gchar * +fread_unicode_string (gint32 *bytes_read, + gint32 *bytes_written, + guint16 mod_len, + FILE *f, + GError **error) +{ + /* + * Reads a utf-16 string from the file padded to a multiple of mod_len + * and returns a utf-8 string. + */ + + gchar *utf8_str; + gunichar2 *utf16_str; + gint32 len; + gint32 i; + gint32 padded_len; + glong utf8_str_len; + + *bytes_read = 0; + *bytes_written = -1; + + if (fread (&len, 4, 1, f) < 1) + { + psd_set_error (feof (f), errno, error); + return NULL; + } + *bytes_read += 4; + len = GINT32_FROM_BE (len); + IFDBG(3) g_debug ("Unicode string length %d", len); + + if (len == 0) + { + if (fseek (f, mod_len - 1, SEEK_CUR) < 0) + { + psd_set_error (feof (f), errno, error); + return NULL; + } + *bytes_read += (mod_len - 1); + *bytes_written = 0; + return NULL; + } + + utf16_str = g_malloc (len * 2); + for (i = 0; i < len; ++i) + { + if (fread (&utf16_str[i], 2, 1, f) < 1) + { + psd_set_error (feof (f), errno, error); + g_free (utf16_str); + return NULL; + } + *bytes_read += 2; + utf16_str[i] = GINT16_FROM_BE (utf16_str[i]); + } + + if (mod_len > 0) + { + padded_len = len + 1; + while (padded_len % mod_len != 0) + { + if (fseek (f, 1, SEEK_CUR) < 0) + { + psd_set_error (feof (f), errno, error); + g_free (utf16_str); + return NULL; + } + (*bytes_read)++; + padded_len++; + } + } + + utf8_str = g_utf16_to_utf8 (utf16_str, len, NULL, &utf8_str_len, NULL); + *bytes_written = utf8_str_len; + g_free (utf16_str); + + IFDBG(3) g_debug ("Unicode string: %s, bytes_read: %d, bytes_written: %d", + utf8_str, *bytes_read, *bytes_written); + + return utf8_str; +} + +gint32 +fwrite_unicode_string (const gchar *src, + guint16 mod_len, + FILE *f, + GError **error) +{ + /* + * Converts utf-8 string to utf-16 and writes 4 byte length + * then string padding to multiple of mod_len. + */ + + gunichar2 *utf16_str; + gchar null_str = 0x0; + gint32 utf16_len = 0; + gint32 bytes_written = 0; + gint i; + glong len; + + if (src == NULL) + { + /* Write null string as four byte 0 int32 */ + if (fwrite (&utf16_len, 4, 1, f) < 1) + { + psd_set_error (feof (f), errno, error); + return -1; + } + bytes_written += 4; + } + else + { + utf16_str = g_utf8_to_utf16 (src, -1, NULL, &len, NULL); + /* Byte swap as required */ + utf16_len = len; + for (i = 0; i < utf16_len; ++i) + utf16_str[i] = GINT16_TO_BE (utf16_str[i]); + utf16_len = GINT32_TO_BE (utf16_len); + + if (fwrite (&utf16_len, 4, 1, f) < 1 + || fwrite (utf16_str, 2, utf16_len + 1, f) < utf16_len + 1) + { + psd_set_error (feof (f), errno, error); + return -1; + } + bytes_written += (4 + 2 * utf16_len + 2); + IFDBG(2) g_debug ("Unicode string: %s, bytes_written: %d", + src, bytes_written); + } + + /* Pad with nulls */ + if (mod_len > 0) + { + while (bytes_written % mod_len != 0) + { + if (fwrite (&null_str, 1, 1, f) < 1) + { + psd_set_error (feof (f), errno, error); + return -1; + } + bytes_written++; + } + } + + return bytes_written; +} + +gint +decode_packbits (const gchar *src, + gchar *dst, + guint16 packed_len, + guint32 unpacked_len) +{ + /* + * Decode a PackBits chunk. + */ + + gint n; + gint32 unpack_left = unpacked_len; + gint32 pack_left = packed_len; + gint32 error_code = 0; + gint32 return_val = 0; + + while (unpack_left > 0 && pack_left > 0) + { + n = *(const guchar *) src; + src++; + pack_left--; + + if (n == 128) /* nop */ + continue; + else if (n > 128) + n -= 256; + + if (n < 0) /* replicate next gchar |n|+ 1 times */ + { + n = 1 - n; + if (pack_left < 1) + { + IFDBG(2) g_debug ("Input buffer exhausted in replicate"); + error_code = 1; + break; + } + if (unpack_left < n) + { + IFDBG(2) g_debug ("Overrun in packbits replicate of %d chars", n - unpack_left); + error_code = 2; + break; + } + memset (dst, *src, n); + src++; + pack_left--; + dst += n; + unpack_left -= n; + } + else /* copy next n+1 gchars literally */ + { + n++; + if (pack_left < n) + { + IFDBG(2) g_debug ("Input buffer exhausted in copy"); + error_code = 3; + break; + } + if (unpack_left < n) + { + IFDBG(2) g_debug ("Output buffer exhausted in copy"); + error_code = 4; + break; + } + memcpy (dst, src, n); + src += n; + pack_left -= n; + dst += n; + unpack_left -= n; + } + } + + if (unpack_left > 0) + { + /* Pad with zeros to end of output buffer */ + memset (dst, 0, unpack_left); + } + + if (unpack_left) + { + IFDBG(2) g_debug ("Packbits decode - unpack left %d", unpack_left); + return_val -= unpack_left; + } + if (pack_left) + { + /* Some images seem to have a pad byte at the end of the packed data */ + if (error_code || pack_left != 1) + { + IFDBG(2) g_debug ("Packbits decode - pack left %d", pack_left); + return_val = pack_left; + } + } + + IFDBG(2) + if (error_code) + g_debug ("Error code %d", error_code); + + return return_val; +} + +gchar * +encode_packbits (const gchar *src, + guint32 unpacked_len, + guint16 *packed_len) +{ + /* + * Encode a PackBits chunk. + */ + + GString *dst_str; /* destination string */ + gint curr_char; /* current character */ + gchar char_buff[128]; /* buffer of already read characters */ + guchar run_len; /* number of characters in a run */ + gint32 unpack_left = unpacked_len; + + IFDBG(2) g_debug ("Encode packbits"); + + /* Initialise destination string */ + dst_str = g_string_sized_new (unpacked_len); + + /* prime the read loop */ + curr_char = *src; + src++; + run_len = 0; + + /* read input until there's nothing left */ + while (unpack_left > 0) + { + char_buff[run_len] = (gchar) curr_char; + IFDBG(2) g_debug ("buff %x, run len %d, curr char %x", + char_buff[run_len], run_len, (gchar) curr_char); + run_len++; + + if (run_len >= MIN_RUN) + { + gint i; + + /* check for run */ + for (i = 2; i <= MIN_RUN; ++i) + { + if (curr_char != char_buff[run_len - i]) + { + /* no run */ + i = 0; + break; + } + } + + if (i != 0) + { + /* we have a run write out buffer before run*/ + gint next_char; + + if (run_len > MIN_RUN) + { + /* block size - 1 followed by contents */ + g_string_append_c (dst_str, (run_len - MIN_RUN - 1)); + g_string_append_len (dst_str, char_buff, (run_len - MIN_RUN)); + IFDBG(2) g_debug ("(1) Number of chars: %d, run length tag: %d", + run_len - MIN_RUN, run_len - MIN_RUN - 1); + } + + /* determine run length (MIN_RUN so far) */ + run_len = MIN_RUN; + + /* get next character */ + next_char = *src; + src++; + unpack_left--; + while (next_char == curr_char) + { + run_len++; + if (run_len == 128) + { + /* run is at max length */ + break; + } + next_char = *src; + src++; + unpack_left--; + } + + /* write out encoded run length and run symbol */ + g_string_append_c (dst_str, (gchar)(1 - (gint)(run_len))); + g_string_append_c (dst_str, curr_char); + IFDBG(2) g_debug ("(2) Number of chars: %d, run length tag: %d", + run_len, (1 - (gint)(run_len))); + + if (unpack_left > 0) + { + /* make run breaker start of next buffer */ + char_buff[0] = next_char; + run_len = 1; + } + else + { + /* file ends in a run */ + run_len = 0; + } + } + } + + if (run_len == 128) + { + /* write out buffer */ + g_string_append_c (dst_str, 127); + g_string_append_len (dst_str, char_buff, 128); + IFDBG(2) g_debug ("(3) Number of chars: 128, run length tag: 127"); + + /* start a new buffer */ + run_len = 0; + } + + curr_char = *src; + src++; + unpack_left--; + } + + /* write out last buffer */ + if (run_len != 0) + { + if (run_len <= 128) + { + /* write out entire copy buffer */ + g_string_append_c (dst_str, run_len - 1); + g_string_append_len (dst_str, char_buff, run_len); + IFDBG(2) g_debug ("(4) Number of chars: %d, run length tag: %d", + run_len, run_len - 1); + } + else + { + IFDBG(2) g_debug ("(5) Very bad - should not be here"); + } + } + + *packed_len = dst_str->len; + IFDBG(2) g_debug ("Packed len %d, unpacked %d", *packed_len, unpacked_len); + + return g_string_free (dst_str, FALSE); +} + +void +psd_to_gimp_blend_mode (PSDlayer *psd_layer, + LayerModeInfo *mode_info) +{ + gint i; + + mode_info->mode = GIMP_LAYER_MODE_NORMAL; + /* FIXME: use the image mode to select the correct color spaces. for now, + * we use rgb-perceptual blending/compositing unconditionally. + */ + mode_info->blend_space = GIMP_LAYER_COLOR_SPACE_RGB_PERCEPTUAL; + mode_info->composite_space = GIMP_LAYER_COLOR_SPACE_RGB_PERCEPTUAL; + if (psd_layer->clipping == 1) + mode_info->composite_mode = GIMP_LAYER_COMPOSITE_CLIP_TO_BACKDROP; + else + mode_info->composite_mode = GIMP_LAYER_COMPOSITE_UNION; + + for (i = 0; i < G_N_ELEMENTS (layer_mode_map); i++) + { + if (g_ascii_strncasecmp (psd_layer->blend_mode, layer_mode_map[i].psd_mode, 4) == 0) + { + if (! layer_mode_map[i].exact && CONVERSION_WARNINGS) + { + g_message ("GIMP uses a different equation than Photoshop for " + "blend mode: %s. Results will differ.", + layer_mode_map[i].name); + } + + mode_info->mode = layer_mode_map[i].gimp_mode; + + return; + } + } + + if (CONVERSION_WARNINGS) + { + gchar *mode_name = g_strndup (psd_layer->blend_mode, 4); + g_message ("Unsupported blend mode: %s. Mode reverts to normal", + mode_name); + g_free (mode_name); + } +} + +const gchar * +gimp_to_psd_blend_mode (const LayerModeInfo *mode_info) +{ + gint i; + + /* FIXME: select the image mode based on the layer mode color spaces. for + * now, we assume rgb-perceptual blending/compositing unconditionally. + */ + if (mode_info->blend_space != GIMP_LAYER_COLOR_SPACE_AUTO && + mode_info->blend_space != GIMP_LAYER_COLOR_SPACE_RGB_PERCEPTUAL) + { + if (CONVERSION_WARNINGS) + g_message ("Unsupported blend color space: %s. " + "Blend color space reverts to rgb-perceptual", + get_enum_value_nick (GIMP_TYPE_LAYER_COLOR_SPACE, + mode_info->blend_space)); + } + + if (mode_info->composite_space != GIMP_LAYER_COLOR_SPACE_AUTO && + mode_info->composite_space != GIMP_LAYER_COLOR_SPACE_RGB_PERCEPTUAL) + { + if (CONVERSION_WARNINGS) + g_message ("Unsupported composite color space: %s. " + "Composite color space reverts to rgb-perceptual", + get_enum_value_nick (GIMP_TYPE_LAYER_COLOR_SPACE, + mode_info->composite_space)); + } + + if (mode_info->composite_mode != GIMP_LAYER_COMPOSITE_AUTO && + mode_info->composite_mode != GIMP_LAYER_COMPOSITE_UNION && + mode_info->composite_mode != GIMP_LAYER_COMPOSITE_CLIP_TO_BACKDROP) + { + if (CONVERSION_WARNINGS) + g_message ("Unsupported composite mode: %s. " + "Composite mode reverts to union", + get_enum_value_nick (GIMP_TYPE_LAYER_COMPOSITE_MODE, + mode_info->composite_mode)); + } + + for (i = 0; i < G_N_ELEMENTS (layer_mode_map); i++) + { + if (layer_mode_map[i].gimp_mode == mode_info->mode) + { + if (! layer_mode_map[i].exact && CONVERSION_WARNINGS) + { + g_message ("GIMP uses a different equation than Photoshop for " + "blend mode: %s. Results may differ.", + get_enum_value_nick (GIMP_TYPE_LAYER_MODE, + mode_info->mode)); + } + + return layer_mode_map[i].psd_mode; + } + } + + if (CONVERSION_WARNINGS) + g_message ("Unsupported blend mode: %s. Mode reverts to normal", + get_enum_value_nick (GIMP_TYPE_LAYER_MODE, mode_info->mode)); + + return "norm"; +} + +GimpColorTag +psd_to_gimp_layer_color_tag (guint16 layer_color_tag) +{ + GimpColorTag colorTag; + + switch (layer_color_tag) + { + case 1: + colorTag = GIMP_COLOR_TAG_RED; + break; + + case 2: + colorTag = GIMP_COLOR_TAG_ORANGE; + break; + + case 3: + colorTag = GIMP_COLOR_TAG_YELLOW; + break; + + case 4: + colorTag = GIMP_COLOR_TAG_GREEN; + break; + + case 5: + colorTag = GIMP_COLOR_TAG_BLUE; + break; + + case 6: + colorTag = GIMP_COLOR_TAG_VIOLET; + break; + + case 7: + colorTag = GIMP_COLOR_TAG_GRAY; + break; + + default: + if (CONVERSION_WARNINGS) + g_message ("Unsupported Photoshop layer color tag: %i. GIMP layer color tag set to none.", + layer_color_tag); + colorTag = GIMP_COLOR_TAG_NONE; + } + + return colorTag; +} + +guint16 +gimp_to_psd_layer_color_tag (GimpColorTag layer_color_tag) +{ + guint16 color_tag; + + switch (layer_color_tag) + { + case GIMP_COLOR_TAG_RED: + color_tag = 1; + break; + + case GIMP_COLOR_TAG_ORANGE: + color_tag = 2; + break; + + case GIMP_COLOR_TAG_YELLOW: + color_tag = 3; + break; + + case GIMP_COLOR_TAG_GREEN: + color_tag = 4; + break; + + case GIMP_COLOR_TAG_BLUE: + color_tag = 5; + break; + + case GIMP_COLOR_TAG_VIOLET: + color_tag = 6; + break; + + case GIMP_COLOR_TAG_GRAY: + color_tag = 7; + break; + + default: + if (CONVERSION_WARNINGS) + g_message ("Photoshop doesn't support GIMP layer color tag: %i. Photoshop layer color tag set to none.", + layer_color_tag); + color_tag = 0; + } + + return color_tag; +} + +static const gchar * +get_enum_value_nick (GType type, + gint value) +{ + const gchar *nick; + + if (gimp_enum_get_value (type, value, NULL, &nick, NULL, NULL)) + { + return nick; + } + else + { + static gchar err_name[32]; + + snprintf (err_name, sizeof (err_name), "UNKNOWN (%d)", value); + + return err_name; + } +} |