Skip to content

Commit 937633d

Browse files
committed
fix(psd): handle indexed transparency index zero
Signed-off-by: Vlad (Kuzmin) Erium <libalias@gmail.com>
1 parent 165f18b commit 937633d

4 files changed

Lines changed: 105 additions & 4 deletions

File tree

src/psd.imageio/psdinput.cpp

Lines changed: 20 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -214,6 +214,7 @@ class PSDInput final : public ImageInput {
214214
std::vector<std::string> m_alpha_names;
215215
//Index of the transparent color, if any (for Indexed color mode only)
216216
int16_t m_transparency_index;
217+
bool m_has_transparency_index;
217218
//Background color
218219
float m_background_color[4];
219220
///< Do not convert unassociated alpha
@@ -912,6 +913,7 @@ PSDInput::init()
912913
m_channels.clear();
913914
m_alpha_names.clear();
914915
m_transparency_index = -1;
916+
m_has_transparency_index = false;
915917
m_keep_unassociated_alpha = false;
916918
m_background_color[0] = 1.0;
917919
m_background_color[1] = 1.0;
@@ -1262,15 +1264,23 @@ PSDInput::load_resource_1039(uint32_t length)
12621264

12631265

12641266
bool
1265-
PSDInput::load_resource_1047(uint32_t /*length*/)
1267+
PSDInput::load_resource_1047(uint32_t length)
12661268
{
1269+
if (length != sizeof(m_transparency_index)) {
1270+
errorfmt("[Image Resource] Transparency index length {} is invalid",
1271+
length);
1272+
return false;
1273+
}
1274+
12671275
if (!read_bige<int16_t>(m_transparency_index))
12681276
return false;
1269-
if (m_transparency_index < 0 || m_transparency_index >= 768) {
1277+
if (m_transparency_index < 0 || m_transparency_index >= 256) {
12701278
errorfmt("[Image Resource] Transparency index {} is out of range",
12711279
m_transparency_index);
12721280
return false;
12731281
}
1282+
1283+
m_has_transparency_index = true;
12741284
return true;
12751285
}
12761286

@@ -1980,7 +1990,7 @@ PSDInput::setup()
19801990
spec_channel_count++;
19811991
raw_channel_count++;
19821992
} else if (m_header.color_mode == ColorMode_Indexed
1983-
&& m_transparency_index) {
1993+
&& m_has_transparency_index) {
19841994
spec_channel_count++;
19851995
}
19861996
}
@@ -2144,7 +2154,11 @@ PSDInput::indexed_to_rgb(span<unsigned char> dst, cspan<unsigned char> src,
21442154
OIIO_ASSERT(src.size() && dst.size());
21452155
// The color table is 768 bytes which is 256 * 3 channels (always RGB)
21462156
const auto& table(m_color_data.data);
2147-
if (m_transparency_index >= 0) {
2157+
if (src.size() < span_size_t(width))
2158+
return false;
2159+
if (m_has_transparency_index) {
2160+
if (dst.size() < span_size_t(width) * 4)
2161+
return false;
21482162
for (int i = 0; i < width; ++i) {
21492163
int index = src[i];
21502164
if (index == m_transparency_index) {
@@ -2160,6 +2174,8 @@ PSDInput::indexed_to_rgb(span<unsigned char> dst, cspan<unsigned char> src,
21602174
}
21612175
}
21622176
} else {
2177+
if (dst.size() < span_size_t(width) * 3)
2178+
return false;
21632179
for (int i = 0; i < width; ++i) {
21642180
int index = src[i];
21652181
dst[3 * i + 0] = table[index]; // R

testsuite/psd-colormodes/ref/out.txt

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,11 @@ Comparing "src/pattern2-8-indexed.psd" and "pattern2-8-indexed.psd.tif"
1818
PASS
1919
Comparing "src/cmyk-with-alpha.psd" and "cmyk-with-alpha.psd.tif"
2020
PASS
21+
indexed-transparency-0.psd : 200 x 150, 4 channel, uint8 psd
22+
SHA-1: 670F7DE109D3A8D1E47A90141F555BAE4513A177
23+
indexed-transparency-255.psd : 200 x 150, 4 channel, uint8 psd
24+
SHA-1: BEF32B02F44B369E39670DC2E2D2D1578B4998AD
25+
indexed-transparency-256-rejected
2126
Comparing "pattern2-8-rgb.psd.tif" and "ref/pattern2.tif"
2227
PASS
2328
Comparing "pattern2-16-rgb.psd.tif" and "ref/pattern2.tif"

testsuite/psd-colormodes/run.py

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,4 +24,15 @@
2424
printinfo=False, output_filename=outfile)
2525
outputs += [ outfile ]
2626

27+
command += run_app (pythonbin + " src/make-indexed-transparency-psds.py",
28+
silent=True)
29+
command += info_command ("indexed-transparency-0.psd", verbose=False,
30+
safematch=True)
31+
command += info_command ("indexed-transparency-255.psd", verbose=False,
32+
safematch=True)
33+
command += run_app ("(" + oiio_app("iconvert")
34+
+ " indexed-transparency-256.psd "
35+
+ "out.null > /dev/null 2>&1 "
36+
+ "|| echo indexed-transparency-256-rejected)")
37+
2738
outputs += [ "out.txt" ]
Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
#!/usr/bin/env python
2+
3+
# Copyright Contributors to the OpenImageIO project.
4+
# SPDX-License-Identifier: Apache-2.0
5+
# https://github.com/AcademySoftwareFoundation/OpenImageIO
6+
7+
import struct
8+
9+
10+
BASE = "src/pattern2-8-indexed.psd"
11+
RESOURCE_TRANSPARENCY_INDEX = 1047
12+
13+
CASES = [
14+
("indexed-transparency-0.psd", 0),
15+
("indexed-transparency-255.psd", 255),
16+
("indexed-transparency-256.psd", 256),
17+
]
18+
19+
20+
def resource_section_bounds(data):
21+
offset = 26
22+
color_data_length = struct.unpack_from(">I", data, offset)[0]
23+
offset += 4 + color_data_length
24+
resource_length = struct.unpack_from(">I", data, offset)[0]
25+
offset += 4
26+
return offset, offset + resource_length
27+
28+
29+
def find_transparency_index_resource(data):
30+
offset, end = resource_section_bounds(data)
31+
while offset < end:
32+
if offset + 12 > end:
33+
raise RuntimeError("truncated image resource")
34+
if data[offset : offset + 4] != b"8BIM":
35+
raise RuntimeError("unexpected image resource signature")
36+
37+
resource_id = struct.unpack_from(">H", data, offset + 4)[0]
38+
name_length = data[offset + 6]
39+
name_size = 1 + name_length
40+
if name_size & 1:
41+
name_size += 1
42+
data_length_offset = offset + 6 + name_size
43+
data_length = struct.unpack_from(">I", data, data_length_offset)[0]
44+
data_offset = data_length_offset + 4
45+
46+
if data_offset + data_length > end:
47+
raise RuntimeError("truncated image resource payload")
48+
if resource_id == RESOURCE_TRANSPARENCY_INDEX:
49+
if data_length != 2:
50+
raise RuntimeError("unexpected transparency-index length")
51+
return data_offset
52+
53+
offset = data_offset + data_length
54+
if data_length & 1:
55+
offset += 1
56+
57+
raise RuntimeError("transparency-index resource not found")
58+
59+
60+
with open(BASE, "rb") as input_file:
61+
base_data = input_file.read()
62+
63+
index_offset = find_transparency_index_resource(base_data)
64+
65+
for filename, transparency_index in CASES:
66+
data = bytearray(base_data)
67+
struct.pack_into(">H", data, index_offset, transparency_index)
68+
with open(filename, "wb") as output_file:
69+
output_file.write(data)

0 commit comments

Comments
 (0)