Skip to content

Commit

Permalink
fix(psd): various PSD files fail to load correctly (#4302)
Browse files Browse the repository at this point in the history
The primary issue seemed to be that the ChannelInfo width and height
were not filled in for some of the layers, notably the merged image data
layer in most(all?) cases.

A second issue was that there was incorrect nesting of the switch
statement in `read_channel_row`. The last 3 case statements were all
nested under the `if (!bigendian()) {` conditional from the 1st case.

Fixes #4298

## Tests

Use `iconvert` on all PSD files contained in the attached .zip.
Converting to both .exr and .png is a good test to ensure the data is
flowing out to those formats correctly.
[psd.zip](https://github.com/user-attachments/files/15905814/psd.zip)

---------

Signed-off-by: Jesse Yurkovich <[email protected]>
  • Loading branch information
jessey-git authored and lgritz committed Jul 1, 2024
1 parent eeb1cfd commit c232aed
Show file tree
Hide file tree
Showing 8 changed files with 41 additions and 39 deletions.
2 changes: 1 addition & 1 deletion src/doc/builtinplugins.rst
Original file line number Diff line number Diff line change
Expand Up @@ -1876,7 +1876,7 @@ Lab or duotone modes.

**Custom I/O Overrides**

PSD output supports the "custom I/O" feature via the special ``"oiio:ioproxy"``
PSD input supports the "custom I/O" feature via the special ``"oiio:ioproxy"``
attributes (see Sections :ref:`sec-imageoutput-ioproxy` and
:ref:`sec-imageinput-ioproxy`) as well as the `set_ioproxy()` methods.

Expand Down
66 changes: 34 additions & 32 deletions src/psd.imageio/psdinput.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1892,6 +1892,8 @@ PSDInput::load_image_data()
// setup some generic properties and read any RLE lengths
// Image Data Section has RLE lengths for all channels stored first
for (ChannelInfo& channel_info : m_image_data.channel_info) {
channel_info.width = m_header.width;
channel_info.height = m_header.height;
channel_info.compression = compression;
channel_info.channel_id = id++;
channel_info.data_length = row_length * m_header.height;
Expand Down Expand Up @@ -2047,39 +2049,39 @@ PSDInput::read_channel_row(ChannelInfo& channel_info, uint32_t row, char* data)
case 16: swap_endian((uint16_t*)data, channel_info.width); break;
case 32: swap_endian((uint32_t*)data, channel_info.width); break;
}
break;
case Compression_RLE: {
if (!ioseek(channel_info.row_pos[row]))
return false;
uint32_t rle_length = channel_info.rle_lengths[row];
char* rle_buffer;
OIIO_ALLOCATE_STACK_OR_HEAP(rle_buffer, char, rle_length);
if (!ioread(rle_buffer, rle_length)
|| !decompress_packbits(rle_buffer, data, rle_length,
channel_info.row_length))
return false;
} break;
case Compression_ZIP: {
OIIO_ASSERT(channel_info.decompressed_data.size()
== static_cast<uint64_t>(channel_info.width)
* channel_info.height * (m_header.depth / 8));
// We simply copy over the row into destination
uint64_t row_index = static_cast<uint64_t>(row) * channel_info.width
* (m_header.depth / 8);
std::memcpy(data, channel_info.decompressed_data.data() + row_index,
channel_info.row_length);
} break;
case Compression_ZIP_Predict: {
OIIO_ASSERT(channel_info.decompressed_data.size()
== static_cast<uint64_t>(channel_info.width)
* channel_info.height * (m_header.depth / 8));
// We simply copy over the row into destination
uint64_t row_index = static_cast<uint64_t>(row) * channel_info.width
* (m_header.depth / 8);
std::memcpy(data, channel_info.decompressed_data.data() + row_index,
channel_info.row_length);
} break;
}
break;
case Compression_RLE: {
if (!ioseek(channel_info.row_pos[row]))
return false;
uint32_t rle_length = channel_info.rle_lengths[row];
char* rle_buffer;
OIIO_ALLOCATE_STACK_OR_HEAP(rle_buffer, char, rle_length);
if (!ioread(rle_buffer, rle_length)
|| !decompress_packbits(rle_buffer, data, rle_length,
channel_info.row_length))
return false;
} break;
case Compression_ZIP: {
OIIO_ASSERT(channel_info.decompressed_data.size()
== static_cast<uint64_t>(channel_info.width)
* channel_info.height * (m_header.depth / 8));
// We simply copy over the row into destination
uint64_t row_index = static_cast<uint64_t>(row) * channel_info.width
* (m_header.depth / 8);
std::memcpy(data, channel_info.decompressed_data.data() + row_index,
channel_info.row_length);
} break;
case Compression_ZIP_Predict: {
OIIO_ASSERT(channel_info.decompressed_data.size()
== static_cast<uint64_t>(channel_info.width)
* channel_info.height * (m_header.depth / 8));
// We simply copy over the row into destination
uint64_t row_index = static_cast<uint64_t>(row) * channel_info.width
* (m_header.depth / 8);
std::memcpy(data, channel_info.decompressed_data.data() + row_index,
channel_info.row_length);
} break;
}

return true;
Expand Down
4 changes: 2 additions & 2 deletions testsuite/psd-colormodes/ref/out.txt
Original file line number Diff line number Diff line change
Expand Up @@ -22,15 +22,15 @@ Comparing "pattern2-8-rgb.psd.tif" and "ref/pattern2.tif"
PASS
Comparing "pattern2-16-rgb.psd.tif" and "ref/pattern2.tif"
PASS
Comparing "pattern2-8-cmyk.psd.tif" and "ref/pattern2-cmyk.tif"
Comparing "pattern2-8-cmyk.psd.tif" and "ref/pattern2-8-cmyk.psd.tif"
PASS
Comparing "pattern2-16-cmyk.psd.tif" and "ref/pattern2-16-cmyk.psd.tif"
PASS
Comparing "pattern2-8-multichannel.psd.tif" and "ref/pattern2.tif"
PASS
Comparing "pattern2-16-multichannel.psd.tif" and "ref/pattern2.tif"
PASS
Comparing "pattern2-8-grayscale.psd.tif" and "ref/pattern2-gray.tif"
Comparing "pattern2-8-grayscale.psd.tif" and "ref/pattern2-8-grayscale.psd.tif"
PASS
Comparing "pattern2-16-grayscale.psd.tif" and "ref/pattern2-16-grayscale.psd.tif"
PASS
Expand Down
Binary file modified testsuite/psd-colormodes/ref/pattern2-16-cmyk.psd.tif
Binary file not shown.
Binary file modified testsuite/psd-colormodes/ref/pattern2-16-grayscale.psd.tif
Binary file not shown.
8 changes: 4 additions & 4 deletions testsuite/psd/ref/out.txt
Original file line number Diff line number Diff line change
Expand Up @@ -584,7 +584,7 @@ Reading ../oiio-images/psd_rgb_8.psd
stRef:originalDocumentID: "E146B3E37A92795EE3EA6577040DE6D5"
Reading ../oiio-images/psd_rgb_16.psd
../oiio-images/psd_rgb_16.psd : 320 x 240, 3 channel, uint16 psd
SHA-1: 591F6850EB0B548DF2CE7177661440CB812E5C31
SHA-1: E42334B0F0684E3C3BF9125F2920B07C44C17B11
channel list: R, G, B
Artist: "Daniel Wyatt"
DateTime: "2007-01-18T15:49:21"
Expand Down Expand Up @@ -655,7 +655,7 @@ Reading ../oiio-images/psd_rgb_16.psd
stRef:originalDocumentID: "E146B3E37A92795EE3EA6577040DE6D5"
Reading ../oiio-images/psd_rgb_32.psd
../oiio-images/psd_rgb_32.psd : 320 x 240, 3 channel, float psd
SHA-1: B7F8CE4B4259A92AA13333EA8112C3E37C6BAB39
SHA-1: 63CF8F7B010D24EFD3C41F51C16D8D285FE07313
channel list: R, G, B
Artist: "Daniel Wyatt"
DateTime: "2007-01-18T15:49:21"
Expand Down Expand Up @@ -1459,7 +1459,7 @@ Reading src/Layers_16bit_RGB.psd
src/Layers_16bit_RGB.psd : 48 x 27, 3 channel, uint16 psd
4 subimages: 48x27 [u16,u16,u16], 48x27 [u16,u16,u16,u16], 48x27 [u16,u16,u16,u16], 48x27 [u16,u16,u16,u16]
subimage 0: 48 x 27, 3 channel, uint16 psd
SHA-1: 22EAC1FF517BCCEA8EBA31AB240C11518EE42424
SHA-1: 0228B2F3AA493695E9653E1C32D303022DDEFAE4
channel list: R, G, B
DateTime: "2024-03-06T15:22:40+01:00"
ICCProfile: 0, 0, 12, 72, 76, 105, 110, 111, 2, 16, 0, 0, 109, 110, 116, 114, ... [3144 x uint8]
Expand Down Expand Up @@ -1653,7 +1653,7 @@ Reading src/Layers_32bit_RGB.psd
src/Layers_32bit_RGB.psd : 48 x 27, 3 channel, float psd
4 subimages: 48x27 [f,f,f], 48x27 [f,f,f,f], 48x27 [f,f,f,f], 48x27 [f,f,f,f]
subimage 0: 48 x 27, 3 channel, float psd
SHA-1: 90D05324071E32026D22E44FA9EF027C80D97308
SHA-1: C9C84C45C64884BD4D6F1B1E91CCA6744EA3C06C
channel list: R, G, B
DateTime: "2024-03-06T15:22:40+01:00"
ICCProfile: 0, 0, 2, 56, 108, 99, 109, 115, 4, 48, 0, 0, 109, 110, 116, 114, ... [568 x uint8]
Expand Down

0 comments on commit c232aed

Please sign in to comment.