Skip to content

Commit 0bc50dc

Browse files
committed
fix(softimage): Various fixes for malformed channel packets
* Fix bugs when channel order isn't strictly R,G,B,A by using a `m_channel_map`. * Detect malformed file with too many channel packets. * Gracefully handle duplicate channel bits across packets by not thinking that there are duplicate channels. * Guard against channel packets that contain no channels. Assisted-by: Claude Code / Opus 4.6 (mostly for analysis/identification of the potential problems; touching the code only for the out-of-order channels fix) Signed-off-by: Larry Gritz <lg@larrygritz.com>
1 parent 6e8d62b commit 0bc50dc

1 file changed

Lines changed: 36 additions & 12 deletions

File tree

src/softimage.imageio/softimageinput.cpp

Lines changed: 36 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,9 @@ class SoftimageInput final : public ImageInput {
5050
std::vector<softimage_pvt::ChannelPacket> m_channel_packets;
5151
std::string m_filename;
5252
std::vector<fpos_t> m_scanline_markers;
53+
// Maps absolute channel index (0=R,1=G,2=B,3=A) to sequential output
54+
// offset within the scanline buffer. Initialized to -1 (unused).
55+
int m_channel_map[4];
5356
};
5457

5558

@@ -76,6 +79,7 @@ SoftimageInput::init()
7679
m_filename.clear();
7780
m_channel_packets.clear();
7881
m_scanline_markers.clear();
82+
std::fill(m_channel_map, m_channel_map + 4, -1);
7983
}
8084

8185

@@ -110,7 +114,6 @@ SoftimageInput::open(const std::string& name, ImageSpec& spec)
110114

111115
// Get the ChannelPackets
112116
ChannelPacket curPacket;
113-
int nchannels = 0;
114117
std::vector<std::string> encodings;
115118
do {
116119
// Read the next packet into curPacket and store it off
@@ -126,13 +129,31 @@ SoftimageInput::open(const std::string& name, ImageSpec& spec)
126129
close();
127130
return false;
128131
}
132+
if (curPacket.channelCode == 0) {
133+
errorfmt("Channel packet with no channels");
134+
close();
135+
return false;
136+
}
129137
m_channel_packets.push_back(curPacket);
130138

131-
// Add the number of channels in this packet to nchannels
132-
nchannels += curPacket.channels().size();
133139
encodings.push_back(encoding_name(m_channel_packets.back().type));
140+
141+
if (m_channel_packets.size() > 4) {
142+
errorfmt("Too many channel packets");
143+
close();
144+
return false;
145+
}
134146
} while (curPacket.chained);
135147

148+
// Build channel map: absolute RGBA index -> sequential output offset
149+
int nchannels = 0;
150+
{
151+
for (auto& cp : m_channel_packets)
152+
for (int ch : cp.channels())
153+
if (m_channel_map[ch] == -1)
154+
m_channel_map[ch] = nchannels++;
155+
}
156+
136157
// Get the depth per pixel per channel
137158
TypeDesc chanType = TypeDesc::UINT8;
138159
if (curPacket.size == 16)
@@ -307,7 +328,8 @@ SoftimageInput::read_pixels_uncompressed(
307328
//read the data into the correct place
308329
if (fread(&scanlineData[(pixelX * pixelChannelSize
309330
* m_spec.nchannels)
310-
+ (channel * pixelChannelSize)
331+
+ (m_channel_map[channel]
332+
* pixelChannelSize)
311333
+ curByte],
312334
1, 1, m_fd)
313335
!= 1)
@@ -370,7 +392,8 @@ SoftimageInput::read_pixels_pure_run_length(
370392
//put the data into the correct place
371393
scanlineData[(pixelX * pixelChannelSize
372394
* m_spec.nchannels)
373-
+ (channels[curChan] * pixelChannelSize)
395+
+ (m_channel_map[channels[curChan]]
396+
* pixelChannelSize)
374397
+ curByte]
375398
= pixelData[(curChan * pixelChannelSize) + curByte];
376399
}
@@ -432,12 +455,12 @@ SoftimageInput::read_pixels_mixed_run_length(
432455
curByte = ((pixelChannelSize)-1) - curByte;
433456

434457
//read the data into the correct place
435-
if (fread(
436-
&scanlineData[(pixelX * pixelChannelSize
437-
* m_spec.nchannels)
438-
+ (channel * pixelChannelSize)
439-
+ curByte],
440-
1, 1, m_fd)
458+
if (fread(&scanlineData[(pixelX * pixelChannelSize
459+
* m_spec.nchannels)
460+
+ (m_channel_map[channel]
461+
* pixelChannelSize)
462+
+ curByte],
463+
1, 1, m_fd)
441464
!= 1)
442465
return false;
443466
}
@@ -500,7 +523,8 @@ SoftimageInput::read_pixels_mixed_run_length(
500523
//put the data into the correct place
501524
scanlineData[(pixelX * pixelChannelSize
502525
* m_spec.nchannels)
503-
+ (channels[curChan] * pixelChannelSize)
526+
+ (m_channel_map[channels[curChan]]
527+
* pixelChannelSize)
504528
+ curByte]
505529
= pixelData[(curChan * pixelChannelSize)
506530
+ curByte];

0 commit comments

Comments
 (0)