plug-ins: add support for exporting 16-bit PSDs

In file-psd, add support for exporting high bit-depth images.  This
is currently limited to 16-bit images, since 32-bit images seem to
have a different structure (our loading code can successfully load
32-bit images exported by the plug-in, but not actual 32-bit PSD
files saved in Photoshop.)  Higher bit-depth images are saved as
16-bit for now.

Note also that when saving a linear image with a built-in linear
profile the result is wrong (the image is exported with a linear-
TRC profile, but the data is perceptual), but this is a general
problem we have to fix, not restricted to the PSD plug-in.

(cherry picked from commit 9099f317bc)
This commit is contained in:
Ell
2020-04-09 00:58:30 +03:00
parent 5fa235c192
commit 7e673f4e52

View File

@ -196,6 +196,7 @@ static void write_pixel_data (FILE *fd,
static gint32 create_merged_image (gint32 imageID);
static gint get_bpc (gint32 imageID);
static const Babl * get_pixel_format (gint32 drawableID);
static const Babl * get_channel_format (gint32 drawableID);
static const Babl * get_mask_format (gint32 drawableID);
@ -387,7 +388,6 @@ write_datablock_luni (FILE *fd,
static gint32
pack_pb_line (guchar *start,
gint32 length,
gint32 stride,
guchar *dest_ptr)
{
gint32 remaining = length;
@ -401,7 +401,7 @@ pack_pb_line (guchar *start,
i = 0;
while ((i < 128) &&
(remaining - i > 0) &&
(start[0] == start[i*stride]))
(start[0] == start[i]))
i++;
if (i > 1) /* Match found */
@ -410,7 +410,7 @@ pack_pb_line (guchar *start,
*dest_ptr++ = -(i - 1);
*dest_ptr++ = *start;
start += i*stride;
start += i;
remaining -= i;
length += 2;
}
@ -419,8 +419,8 @@ pack_pb_line (guchar *start,
i = 0;
while ((i < 128) &&
(remaining - (i + 1) > 0) &&
(start[i*stride] != start[(i + 1)*stride] ||
remaining - (i + 2) <= 0 || start[i*stride] != start[(i+2)*stride]))
(start[i] != start[i + 1] ||
remaining - (i + 2) <= 0 || start[i] != start[i+2]))
i++;
/* If there's only 1 remaining, the previous WHILE stmt doesn't
@ -436,9 +436,9 @@ pack_pb_line (guchar *start,
*dest_ptr++ = i - 1;
for (j = 0; j < i; j++)
{
*dest_ptr++ = start[j*stride];
*dest_ptr++ = start[j];
}
start += i*stride;
start += i;
remaining -= i;
length += i + 1;
}
@ -535,7 +535,7 @@ save_header (FILE *fd,
"channels");
write_gint32 (fd, PSDImageData.image_height, "rows");
write_gint32 (fd, PSDImageData.image_width, "columns");
write_gint16 (fd, 8, "depth"); /* Exporting can only be done in 8 bits at the moment. */
write_gint16 (fd, 8 * get_bpc (image_id), "depth");
write_gint16 (fd, gimpBaseTypeToPsdMode (PSDImageData.baseType), "mode");
}
@ -837,6 +837,7 @@ get_compress_channel_data (guchar *channel_data,
gint32 channel_cols,
gint32 channel_rows,
gint32 stride,
gint32 bpc,
gint16 *LengthsTable,
guchar *remdata)
{
@ -844,15 +845,72 @@ get_compress_channel_data (guchar *channel_data,
gint32 len; /* Length of compressed data */
guchar *start; /* Starting position of a row in channel_data */
stride /= bpc;
/* Pack channel data, and perform byte-order conversion */
switch (bpc)
{
case 1:
{
if (stride > 1)
{
const guint8 *src = (const guint8 *) channel_data;
guint8 *dest = (guint8 *) channel_data;
for (i = 0; i < channel_rows * channel_cols; i++)
{
*dest = *src;
dest++;
src += stride;
}
}
}
break;
case 2:
{
const guint16 *src = (const guint16 *) channel_data;
guint16 *dest = (guint16 *) channel_data;
for (i = 0; i < channel_rows * channel_cols; i++)
{
*dest = GUINT16_TO_BE (*src);
dest++;
src += stride;
}
}
break;
case 4:
{
const guint32 *src = (const guint32 *) channel_data;
guint32 *dest = (guint32 *) channel_data;
for (i = 0; i < channel_rows * channel_cols; i++)
{
*dest = GUINT32_TO_BE (*src);
dest++;
src += stride;
}
}
break;
default:
g_return_val_if_reached (0);
}
/* For every row in the channel */
len = 0;
for (i = 0; i < channel_rows; i++)
{
start = channel_data + (i * channel_cols * stride);
start = channel_data + i * channel_cols * bpc;
/* Create packed data for this row */
LengthsTable[i] = pack_pb_line (start, channel_cols, stride,
LengthsTable[i] = pack_pb_line (start, channel_cols * bpc,
&remdata[len]);
len += LengthsTable[i];
}
@ -879,6 +937,7 @@ save_layer_and_mask (FILE *fd,
gchar *layerName; /* Layer name */
gint mask; /* Layer mask */
gint depth; /* Layer group nesting depth */
gint bpc; /* Image BPC */
glong eof_pos; /* Position: End of file */
glong ExtraDataPos; /* Position: Extra data length */
@ -912,6 +971,8 @@ save_layer_and_mask (FILE *fd,
depth = 0;
bpc = get_bpc (image_id);
/* Layer records section */
/* GIMP layers must be written in reverse order */
@ -992,7 +1053,7 @@ save_layer_and_mask (FILE *fd,
will modify it later when writing data. */
ChannelLengthPos[i][j] = ftell (fd);
ChanSize = sizeof (gint16) + (layerWidth * layerHeight);
ChanSize = sizeof (gint16) + (layerWidth * layerHeight * bpc);
write_gint32 (fd, ChanSize, "Channel Size");
IFDBG printf ("\t\t\tLength: %d\n", ChanSize);
@ -1192,6 +1253,8 @@ write_pixel_data (FILE *fd,
gint32 height = gegl_buffer_get_height (buffer);
gint32 width = gegl_buffer_get_width (buffer);
gint32 bytes;
gint32 components;
gint32 bpc;
gint32 colors;
gint32 y;
gint32 len; /* Length of compressed data */
@ -1222,8 +1285,10 @@ write_pixel_data (FILE *fd,
format = get_pixel_format (drawableID);
bytes = babl_format_get_bytes_per_pixel (format);
components = babl_format_get_n_components (format);
bpc = bytes / components;
colors = bytes;
colors = components;
if (gimp_drawable_has_alpha (drawableID) &&
! gimp_drawable_is_indexed (drawableID))
@ -1231,7 +1296,7 @@ write_pixel_data (FILE *fd,
LengthsTable = g_new (gint16, height);
rledata = g_new (guchar, (MIN (height, tile_height) *
(width + 10 + (width / 100))));
(width + 10 + (width / 100))) * bpc);
data = g_new (guchar, MIN (height, tile_height) * width * bytes);
@ -1242,17 +1307,17 @@ write_pixel_data (FILE *fd,
height = 0;
}
for (i = 0; i < bytes; i++)
for (i = 0; i < components; i++)
{
gint chan;
len = 0;
if (bytes != colors && ltable_offset == 0) /* Need to write alpha channel first, except in image data section */
if (components != colors && ltable_offset == 0) /* Need to write alpha channel first, except in image data section */
{
if (i == 0)
{
chan = bytes - 1;
chan = components - 1;
}
else
{
@ -1293,10 +1358,10 @@ write_pixel_data (FILE *fd,
MIN (height - y, tile_height)),
1.0, format, data,
GEGL_AUTO_ROWSTRIDE, GEGL_ABYSS_NONE);
tlen = get_compress_channel_data (&data[chan],
tlen = get_compress_channel_data (&data[chan * bpc],
width,
MIN(height - y, tile_height),
bytes,
bytes, bpc,
&LengthsTable[y],
rledata);
len += tlen;
@ -1339,7 +1404,7 @@ write_pixel_data (FILE *fd,
if (ltable_offset > 0)
{
length_table_pos = ltable_offset + 2 * (bytes+1) * height;
length_table_pos = ltable_offset + 2 * (components+1) * height;
IF_DEEP_DBG printf ("\t\t\t\t. ltable, pos %ld\n",
length_table_pos);
}
@ -1366,7 +1431,7 @@ write_pixel_data (FILE *fd,
tlen = get_compress_channel_data (&data[0],
width,
MIN(height - y, tile_height),
1,
bpc, bpc,
&LengthsTable[y],
rledata);
len += tlen;
@ -1635,64 +1700,96 @@ save_image (const gchar *filename,
return TRUE;
}
static gint
get_bpc (gint32 image_id)
{
switch (gimp_image_get_precision (image_id))
{
case GIMP_PRECISION_U8_LINEAR:
case GIMP_PRECISION_U8_GAMMA:
return 1;
case GIMP_PRECISION_U16_LINEAR:
case GIMP_PRECISION_U16_GAMMA:
case GIMP_PRECISION_HALF_LINEAR:
case GIMP_PRECISION_HALF_GAMMA:
return 2;
case GIMP_PRECISION_U32_LINEAR:
case GIMP_PRECISION_U32_GAMMA:
case GIMP_PRECISION_FLOAT_LINEAR:
case GIMP_PRECISION_FLOAT_GAMMA:
default:
/* FIXME: we *should* encode the image as u32 in this case, but simply
* using the same code as for the other cases produces invalid psd files
* (they're rejected by photoshop, although they can be read by the
* corresponding psd-load.c code, which in turn can't actually read
* photoshop-generated u32 files.)
*
* simply encode the image as u16 for now.
*/
/* return 4; */
return 2;
}
}
static const Babl *
get_pixel_format (gint32 drawableID)
{
const Babl *format;
const gchar *model;
gint bpc;
gchar format[32];
switch (gimp_drawable_type (drawableID))
{
case GIMP_GRAY_IMAGE:
format = babl_format ("Y' u8");
model = "Y'";
break;
case GIMP_GRAYA_IMAGE:
format = babl_format ("Y'A u8");
model = "Y'A";
break;
case GIMP_RGB_IMAGE:
format = babl_format ("R'G'B' u8");
model = "R'G'B'";
break;
case GIMP_RGBA_IMAGE:
format = babl_format ("R'G'B'A u8");
model = "R'G'B'A";
break;
case GIMP_INDEXED_IMAGE:
case GIMP_INDEXEDA_IMAGE:
format = gimp_drawable_get_format(drawableID);
break;
return gimp_drawable_get_format (drawableID);
default:
return NULL;
break;
g_return_val_if_reached (NULL);
}
return format;
bpc = get_bpc (gimp_item_get_image (drawableID));
sprintf (format, "%s u%d", model, 8 * bpc);
return babl_format (format);
}
static const Babl *
get_channel_format (gint32 drawableID)
{
const Babl *format;
gint bpc;
gchar format[32];
/* eventually we'll put a switch statement for bit depth here to
* support higher depth exports */
format = babl_format ("Y u8");
bpc = get_bpc (gimp_item_get_image (drawableID));
return format;
sprintf (format, "Y u%d", 8 * bpc);
return babl_format (format);
}
static const Babl *
get_mask_format (gint32 drawableID)
{
const Babl *format;
/* eventually we'll put a switch statement for bit depth here to
* support higher depth exports */
format = babl_format ("Y u8");
return format;
return get_channel_format (drawableID);
}
static void