plug-ins: Export PSD with paths

Ports PSD path export from file-tiff-save.c so that paths are carried
over in PSD project files as well.
This commit is contained in:
Alx Sa 2022-09-30 18:27:17 +00:00
parent b3332bb900
commit 697b41dc45
1 changed files with 172 additions and 0 deletions

View File

@ -145,6 +145,9 @@ static void save_resources (GOutputStream *output,
gboolean export_cmyk,
gboolean export_duotone);
static void save_paths (GOutputStream *output,
GimpImage *image);
static void save_layer_and_mask (GOutputStream *output,
GimpImage *image,
gboolean export_cmyk);
@ -153,6 +156,9 @@ static void save_data (GOutputStream *output,
GimpImage *image,
gboolean export_cmyk);
static void double_to_psd_fixed (gdouble value,
gchar *target);
static void xfwrite (GOutputStream *output,
gconstpointer buf,
gsize len,
@ -844,6 +850,9 @@ save_resources (GOutputStream *output,
(int) sizeof (gint16));
}
/* --------------- Write paths ------------------- */
save_paths (output, image);
/* --------------- Write resolution data ------------------- */
{
gdouble xres = 0, yres = 0;
@ -1066,6 +1075,169 @@ get_compress_channel_data (guchar *channel_data,
return len;
}
/* Ported /from plug-ins/file-tiff/file-tiff-save.c */
static void
double_to_psd_fixed (gdouble value,
gchar *target)
{
gdouble in, frac;
gint i, f;
frac = modf (value, &in);
if (frac < 0)
{
in -= 1;
frac += 1;
}
i = (gint) CLAMP (in, -16, 15);
f = CLAMP ((gint) (frac * 0xFFFFFF), 0, 0xFFFFFF);
target[0] = i & 0xFF;
target[1] = (f >> 16) & 0xFF;
target[2] = (f >> 8) & 0xFF;
target[3] = f & 0xFF;
}
/* Ported from /plug-ins/file-tiff/file-tiff-save.c */
static void
save_paths (GOutputStream *output,
GimpImage *image)
{
gshort id = 0x07D0; /* Photoshop paths have IDs >= 2000 */
gdouble width = gimp_image_get_width (image);
gdouble height = gimp_image_get_height (image);
GList *vectors;
GList *iter;
gint v;
gint num_strokes;
gint *strokes;
gint s;
vectors = gimp_image_list_vectors (image);
if (! vectors)
return;
/* Only up to 997 paths supported */
for (iter = vectors, v = 0;
iter && v <= 997;
iter = g_list_next (iter), v++)
{
GString *data;
gchar *name, *nameend;
gsize len;
gint lenpos;
gchar pointrecord[26] = { 0, };
gchar *tmpname;
GError *err = NULL;
data = g_string_new ("8BIM");
g_string_append_c (data, id / 256);
g_string_append_c (data, id % 256);
/*
* - use iso8859-1 if possible
* - otherwise use UTF-8, prepended with \xef\xbb\xbf (Byte-Order-Mark)
*/
name = gimp_item_get_name (iter->data);
tmpname = g_convert (name, -1, "iso8859-1", "utf-8", NULL, &len, &err);
if (tmpname && err == NULL)
{
g_string_append_c (data, MIN (len, 255));
g_string_append_len (data, tmpname, MIN (len, 255));
g_free (tmpname);
}
else
{
/* conversion failed, we fall back to UTF-8 */
len = g_utf8_strlen (name, 255 - 3); /* need three marker-bytes */
nameend = g_utf8_offset_to_pointer (name, len);
len = nameend - name; /* in bytes */
g_assert (len + 3 <= 255);
g_string_append_c (data, len + 3);
g_string_append_len (data, "\xEF\xBB\xBF", 3); /* Unicode 0xfeff */
g_string_append_len (data, name, len);
if (tmpname)
g_free (tmpname);
}
if (data->len % 2) /* padding to even size */
g_string_append_c (data, 0);
g_free (name);
lenpos = data->len;
g_string_append_len (data, "\0\0\0\0", 4); /* will be filled in later */
len = data->len; /* to calculate the data size later */
pointrecord[1] = 6; /* fill rule record */
g_string_append_len (data, pointrecord, 26);
strokes = gimp_vectors_get_strokes (iter->data, &num_strokes);
for (s = 0; s < num_strokes; s++)
{
GimpVectorsStrokeType type;
gdouble *points;
gint num_points;
gboolean closed;
gint p = 0;
type = gimp_vectors_stroke_get_points (iter->data, strokes[s],
&num_points, &points, &closed);
if (type != GIMP_VECTORS_STROKE_TYPE_BEZIER ||
num_points > 65535 ||
num_points % 6)
{
g_printerr ("psd-save: unsupported stroke type: "
"%d (%d points)\n", type, num_points);
continue;
}
memset (pointrecord, 0, 26);
pointrecord[1] = closed ? 0 : 3;
pointrecord[2] = (num_points / 6) / 256;
pointrecord[3] = (num_points / 6) % 256;
g_string_append_len (data, pointrecord, 26);
for (p = 0; p < num_points; p += 6)
{
pointrecord[1] = closed ? 2 : 5;
double_to_psd_fixed (points[p+1] / height, pointrecord + 2);
double_to_psd_fixed (points[p+0] / width, pointrecord + 6);
double_to_psd_fixed (points[p+3] / height, pointrecord + 10);
double_to_psd_fixed (points[p+2] / width, pointrecord + 14);
double_to_psd_fixed (points[p+5] / height, pointrecord + 18);
double_to_psd_fixed (points[p+4] / width, pointrecord + 22);
g_string_append_len (data, pointrecord, 26);
}
}
g_free (strokes);
/* fix up the length */
len = data->len - len;
data->str[lenpos + 0] = (len & 0xFF000000) >> 24;
data->str[lenpos + 1] = (len & 0x00FF0000) >> 16;
data->str[lenpos + 2] = (len & 0x0000FF00) >> 8;
data->str[lenpos + 3] = (len & 0x000000FF) >> 0;
xfwrite (output, data->str, data->len, "path resources data");
g_string_free (data, TRUE);
id += 0x01;
}
g_list_free (vectors);
}
static void
save_layer_and_mask (GOutputStream *output,
GimpImage *image,