Skip to content

Commit f862c06

Browse files
authored
fix: several bug fixes related to internal use of image_span (AcademySoftwareFoundation#5004)
* ImageBuf internal buffer span lacked correct chansize. The internal `m_bufspan` is an `image_span<byte>`, and as such, it needs to remember the size of the original data type. Otherwise, there's a cascade of potential errors when it thinks that the individual values are byte sized. * In both ImageInput and ImageOutput, several sanity checks of image_span size versus expectations were incorrect. They were only checking if the total byte sizes matched expectations, but they are allowed to disagree when you consider type conversions (in which case, it's the total number of values that need to match, not the total byte sizes. Signed-off-by: Larry Gritz <lg@larrygritz.com>
1 parent 550264b commit f862c06

File tree

3 files changed

+90
-75
lines changed

3 files changed

+90
-75
lines changed

src/libOpenImageIO/imagebuf.cpp

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -150,10 +150,11 @@ class ImageBufImpl {
150150
stride_t ystride = AutoStride,
151151
stride_t zstride = AutoStride)
152152
{
153-
m_bufspan = image_span(reinterpret_cast<std::byte*>(data),
154-
m_spec.nchannels, m_spec.width, m_spec.height,
155-
m_spec.depth, m_spec.format.size(), xstride,
156-
ystride, zstride);
153+
auto formatsize = m_spec.format.size();
154+
m_bufspan = image_span(reinterpret_cast<std::byte*>(data),
155+
m_spec.nchannels, m_spec.width, m_spec.height,
156+
m_spec.depth, formatsize, xstride, ystride,
157+
zstride, formatsize);
157158
}
158159

159160
bool init_spec(string_view filename, int subimage, int miplevel,

src/libOpenImageIO/imageinput.cpp

Lines changed: 43 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -211,6 +211,40 @@ ImageInput::spec_dimensions(int subimage, int miplevel)
211211

212212

213213

214+
// Utility: Make sure the provided data span is the right size for the
215+
// image described by spec and datatype. If they don't match, issue an
216+
// error and return false.
217+
static bool
218+
check_span_size(ImageInput* in, string_view caller, const ImageSpec& spec,
219+
TypeDesc datatype, imagesize_t npixels, int chbegin, int chend,
220+
const image_span<std::byte>& data)
221+
{
222+
// One of two things must be correct: Either format is Unknown and the
223+
// total byte size needs to match the "native" size, or the format is
224+
// concrete and the number of value must match (it's ok if the size
225+
// doesn't match, since a data type conversion will occur).
226+
if (datatype.is_unknown()) { // Unknown assumes native chan types
227+
size_t sz = npixels * spec.pixel_bytes(chbegin, chend, true);
228+
if (sz != data.size_bytes()) {
229+
in->errorfmt(
230+
"{}: image_span size is incorrect ({} bytes vs {} needed)",
231+
caller, data.size_bytes(), sz);
232+
return false;
233+
}
234+
} else { // single concrete type
235+
size_t nvals = npixels * size_t(chend - chbegin);
236+
if (nvals != data.nvalues()) {
237+
in->errorfmt(
238+
"{}: image_span size is incorrect ({} values vs {} needed)",
239+
caller, data.nvalues(), nvals);
240+
return false;
241+
}
242+
}
243+
return true;
244+
}
245+
246+
247+
214248
bool
215249
ImageInput::read_scanline(int y, int z, TypeDesc format, void* data,
216250
stride_t xstride)
@@ -300,16 +334,10 @@ ImageInput::read_scanlines(int subimage, int miplevel, int ybegin, int yend,
300334
chend);
301335
return false;
302336
}
303-
size_t isize = (format == TypeUnknown
304-
? spec.pixel_bytes(chbegin, chend, true /*native*/)
305-
: format.size() * (chend - chbegin))
306-
* size_t(spec.width);
307-
if (isize != data.size_bytes()) {
308-
errorfmt(
309-
"read_scanlines: Buffer size is incorrect ({} bytes vs {} needed)",
310-
isize, data.size_bytes());
337+
if (!check_span_size(this, "read_scanlines", m_spec, format,
338+
m_spec.width * size_t(yend - ybegin), chbegin, chend,
339+
data))
311340
return false;
312-
}
313341

314342
// Default implementation (for now): call the old pointer+stride
315343
return read_scanlines(subimage, miplevel, ybegin, yend, 0, chbegin, chend,
@@ -656,16 +684,11 @@ ImageInput::read_tiles(int subimage, int miplevel, int xbegin, int xend,
656684
errorfmt("read_tiles: invalid channel range [{},{})", chbegin, chend);
657685
return false;
658686
}
659-
size_t isize = (format == TypeUnknown
660-
? spec.pixel_bytes(chbegin, chend, true /*native*/)
661-
: format.size() * (chend - chbegin))
662-
* size_t(xend - xbegin) * size_t(yend - ybegin)
663-
* size_t(zend - zbegin);
664-
if (isize != data.size_bytes()) {
665-
errorfmt("read_tiles: Buffer size is incorrect ({} bytes vs {} needed)",
666-
isize, data.size_bytes());
687+
if (!check_span_size(this, "read_tiles", m_spec, format,
688+
size_t(xend - xbegin) * size_t(yend - ybegin)
689+
* size_t(zend - zbegin),
690+
chbegin, chend, data))
667691
return false;
668-
}
669692

670693
// Default implementation (for now): call the old pointer+stride
671694
return read_tiles(subimage, miplevel, ybegin, yend, xbegin, xend, zbegin,
@@ -1164,24 +1187,6 @@ bool
11641187
ImageInput::read_image(int subimage, int miplevel, int chbegin, int chend,
11651188
TypeDesc format, const image_span<std::byte>& data)
11661189
{
1167-
#if 0
1168-
ImageSpec spec = spec_dimensions(subimage, miplevel);
1169-
if (chend < 0 || chend > spec.nchannels)
1170-
chend = spec.nchannels;
1171-
size_t isize = (format == TypeUnknown
1172-
? spec.pixel_bytes(chbegin, chend, true /*native*/)
1173-
: format.size() * (chend - chbegin))
1174-
* spec.image_pixels();
1175-
if (isize != data.size_bytes()) {
1176-
errorfmt("read_image: Buffer size is incorrect ({} bytes vs {} needed)",
1177-
sz, data.size_bytes());
1178-
return false;
1179-
}
1180-
1181-
// Default implementation (for now): call the old pointer+stride
1182-
return read_image(subimage, miplevel, chbegin, chend, format, data.data(),
1183-
data.xstride(), data.ystride(), data.zstride());
1184-
#else
11851190
OIIO::pvt::LoggedTimer logtime("II::read_image");
11861191
ImageSpec spec;
11871192
int rps = 0;
@@ -1210,16 +1215,9 @@ ImageInput::read_image(int subimage, int miplevel, int chbegin, int chend,
12101215
errorfmt("read_image: invalid channel range [{},{})", chbegin, chend);
12111216
return false;
12121217
}
1213-
int nchans = chend - chbegin;
1214-
bool native = (format == TypeUnknown);
1215-
size_t pixel_bytes = native ? spec.pixel_bytes(chbegin, chend, native)
1216-
: (format.size() * nchans);
1217-
size_t isize = pixel_bytes * spec.image_pixels();
1218-
if (isize != data.size_bytes()) {
1219-
errorfmt("read_image: Buffer size is incorrect ({} bytes vs {} needed)",
1220-
isize, data.size_bytes());
1218+
if (!check_span_size(this, "read_image", m_spec, format,
1219+
spec.image_pixels(), chbegin, chend, data))
12211220
return false;
1222-
}
12231221

12241222
bool ok = true;
12251223
if (spec.tile_width) { // Tiled image -- rely on read_tiles
@@ -1259,7 +1257,6 @@ ImageInput::read_image(int subimage, int miplevel, int chbegin, int chend,
12591257
}
12601258
}
12611259
return ok;
1262-
#endif
12631260
}
12641261

12651262

src/libOpenImageIO/imageoutput.cpp

Lines changed: 42 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -102,6 +102,40 @@ ImageOutput::~ImageOutput()
102102

103103

104104

105+
// Utility: Make sure the provided data span is the right size for the
106+
// image described by spec and datatype. If they don't match, issue an
107+
// error and return false.
108+
static bool
109+
check_span_size(ImageOutput* out, string_view caller, const ImageSpec& spec,
110+
TypeDesc datatype, imagesize_t npixels,
111+
const image_span<const std::byte>& data)
112+
{
113+
// One of two things must be correct: Either format is Unknown and the
114+
// total byte size needs to match the "native" size, or the format is
115+
// concrete and the number of value must match (it's ok if the size
116+
// doesn't match, since a data type conversion will occur).
117+
if (datatype.is_unknown()) { // Unknown assumes native chan types
118+
size_t sz = npixels * spec.pixel_bytes(true);
119+
if (sz != data.size_bytes()) {
120+
out->errorfmt(
121+
"{}: image_span size is incorrect ({} bytes vs {} needed)",
122+
caller, data.size_bytes(), sz);
123+
return false;
124+
}
125+
} else { // single concrete type
126+
size_t nvals = npixels * size_t(spec.nchannels);
127+
if (nvals != data.nvalues()) {
128+
out->errorfmt(
129+
"{}: image_span size is incorrect ({} values vs {} needed)",
130+
caller, data.nvalues(), nvals);
131+
return false;
132+
}
133+
}
134+
return true;
135+
}
136+
137+
138+
105139
bool
106140
ImageOutput::write_scanline(int /*y*/, int /*z*/, TypeDesc /*format*/,
107141
const void* /*data*/, stride_t /*xstride*/)
@@ -120,13 +154,9 @@ ImageOutput::write_scanline(int y, TypeDesc format,
120154
errorfmt("write_scanlines: Invalid scanline index {}", y);
121155
return false;
122156
}
123-
size_t sz = m_spec.scanline_bytes(format);
124-
if (sz != data.size_bytes()) {
125-
errorfmt(
126-
"write_scanline: Buffer size is incorrect ({} bytes vs {} needed)",
127-
sz, data.size_bytes());
157+
if (!check_span_size(this, "write_scanline", m_spec, format, m_spec.width,
158+
data))
128159
return false;
129-
}
130160

131161
// Default implementation (for now): call the old pointer+stride
132162
return write_scanline(y, 0, format, data.data(), data.xstride());
@@ -164,13 +194,9 @@ ImageOutput::write_scanlines(int ybegin, int yend, TypeDesc format,
164194
errorfmt("write_scanlines: Invalid scanline range {}-{}", ybegin, yend);
165195
return false;
166196
}
167-
size_t sz = m_spec.scanline_bytes(format) * size_t(yend - ybegin);
168-
if (sz != data.size_bytes()) {
169-
errorfmt(
170-
"write_scanlines: Buffer size is incorrect ({} bytes vs {} needed)",
171-
sz, data.size_bytes());
197+
if (!check_span_size(this, "write_scanlines", m_spec, format,
198+
m_spec.width * size_t(yend - ybegin), data))
172199
return false;
173-
}
174200

175201
// Default implementation (for now): call the old pointer+stride
176202
return write_scanlines(ybegin, yend, 0, format, data.data(), data.xstride(),
@@ -194,15 +220,9 @@ bool
194220
ImageOutput::write_tile(int x, int y, int z, TypeDesc format,
195221
const image_span<const std::byte>& data)
196222
{
197-
size_t sz = format == TypeUnknown
198-
? m_spec.pixel_bytes(true /*native*/)
199-
: m_spec.tile_pixels() * size_t(m_spec.nchannels)
200-
* format.size();
201-
if (sz != data.size_bytes()) {
202-
errorfmt("write_tile: Buffer size is incorrect ({} bytes vs {} needed)",
203-
sz, data.size_bytes());
223+
if (!check_span_size(this, "write_tile", m_spec, format,
224+
m_spec.tile_pixels(), data))
204225
return false;
205-
}
206226

207227
// Default implementation (for now): call the old pointer+stride
208228
return write_tile(x, y, z, format, data.data(), data.xstride(),
@@ -691,12 +711,9 @@ bool
691711
ImageOutput::write_image(TypeDesc format,
692712
const image_span<const std::byte>& data)
693713
{
694-
size_t sz = m_spec.image_bytes(/*native=*/format == TypeUnknown);
695-
if (sz != data.size_bytes()) {
696-
errorfmt("write_image: Buffer size is incorrect ({} bytes vs {} needed)",
697-
sz, data.size_bytes());
714+
if (!check_span_size(this, "write_image", m_spec, format,
715+
m_spec.image_pixels(), data))
698716
return false;
699-
}
700717

701718
// Default implementation (for now): call the old pointer+stride
702719
return write_image(format, data.data(), data.xstride(), data.ystride(),

0 commit comments

Comments
 (0)