Skip to content

Commit befbba8

Browse files
authored
fix(exr): not honoring 'missingcolor' for scanline files (#4757)
As a reminder, the global OIIO attribute "missingcolor", or the file-specific ImageInput open hint "oiio:missingcolor" is designed to treat missing scanlines or tiles as not a true error, but instead just pass over them and substitute the missingcolor value for those missing areas on the file. The nature of this PR is that while we had implemented this behavior fully for tile-based exr files, we never did so for scanline-based files. So here we do. Now then, unfortunately, it does not work. I think it's a bug in the OpenEXR library itself, but we're still trying to figure that out. But nonetheless, I believe this change is correct. Signed-off-by: Larry Gritz <lg@larrygritz.com>
1 parent f243897 commit befbba8

3 files changed

Lines changed: 64 additions & 6 deletions

File tree

src/openexr.imageio/exr_pvt.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -220,6 +220,10 @@ class OpenEXRInput final : public ImageInput {
220220
m_missingcolor.clear();
221221
}
222222

223+
bool read_native_scanlines_individually(int subimage, int miplevel,
224+
int ybegin, int yend, int z,
225+
int chbegin, int chend, void* data,
226+
stride_t ystride);
223227
bool read_native_tiles_individually(int subimage, int miplevel, int xbegin,
224228
int xend, int ybegin, int yend,
225229
int zbegin, int zend, int chbegin,

src/openexr.imageio/exrinput.cpp

Lines changed: 45 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1186,7 +1186,7 @@ OpenEXRInput::read_native_scanlines(int subimage, int miplevel, int ybegin,
11861186

11871187
bool
11881188
OpenEXRInput::read_native_scanlines(int subimage, int miplevel, int ybegin,
1189-
int yend, int /*z*/, int chbegin, int chend,
1189+
int yend, int z, int chbegin, int chend,
11901190
void* data)
11911191
{
11921192
lock_guard lock(*this);
@@ -1271,8 +1271,31 @@ OpenEXRInput::read_native_scanlines(int subimage, int miplevel, int ybegin,
12711271
return false;
12721272
}
12731273
} catch (const std::exception& e) {
1274-
errorfmt("Failed OpenEXR read: {}", e.what());
1275-
return false;
1274+
std::string err = e.what();
1275+
if (m_missingcolor.size()) {
1276+
// User said not to fail for bad or missing scanlines. If we
1277+
// failed reading a single scanline, use the fill pattern. If we
1278+
// failed reading many scanlines, we don't know which ones, so go
1279+
// back and read them individually for a second chance.
1280+
DBGEXR("Handling missingcolor case for {}-{}: {}\n", ybegin, yend,
1281+
err);
1282+
if (yend - ybegin == 1) {
1283+
// Read of one tile -- use the fill pattern
1284+
fill_missing(m_spec.x, m_spec.x + m_spec.width, ybegin, yend, 0,
1285+
1, chbegin, chend, data, pixelbytes,
1286+
scanlinebytes);
1287+
} else {
1288+
// Read of many tiles -- don't know which failed, so try
1289+
// again to read them all individually.
1290+
return read_native_scanlines_individually(subimage, miplevel,
1291+
ybegin, yend, z,
1292+
chbegin, chend, data,
1293+
scanlinebytes);
1294+
}
1295+
} else {
1296+
errorfmt("Failed OpenEXR read: {}", err);
1297+
return false;
1298+
}
12761299
} catch (...) { // catch-all for edge cases or compiler bugs
12771300
errorfmt("Failed OpenEXR read: unknown exception");
12781301
return false;
@@ -1427,6 +1450,25 @@ OpenEXRInput::read_native_tiles(int subimage, int miplevel, int xbegin,
14271450

14281451

14291452

1453+
bool
1454+
OpenEXRInput::read_native_scanlines_individually(int subimage, int miplevel,
1455+
int ybegin, int yend, int z,
1456+
int chbegin, int chend,
1457+
void* data, stride_t ystride)
1458+
{
1459+
// Note: this is only called by read_native_scanlines, which still holds
1460+
// the mutex, so it's safe to directly access m_spec.
1461+
bool ok = true;
1462+
for (int y = ybegin; y < yend; ++y) {
1463+
char* d = (char*)data + (y - ybegin) * ystride;
1464+
ok &= read_native_scanlines(subimage, miplevel, y, y + 1, z, chbegin,
1465+
chend, d);
1466+
}
1467+
return ok;
1468+
}
1469+
1470+
1471+
14301472
bool
14311473
OpenEXRInput::read_native_tiles_individually(int subimage, int miplevel,
14321474
int xbegin, int xend, int ybegin,

src/openexr.imageio/exrinput_c.cpp

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1217,7 +1217,8 @@ OpenEXRCoreInput::read_native_scanlines(int subimage, int miplevel, int ybegin,
12171217
parallel_for_chunked(
12181218
ychunkstart, yend, scansperchunk,
12191219
[&](int64_t yb, int64_t ye) {
1220-
int y = std::max(int(yb), ybegin);
1220+
int y = std::max(int(yb), ybegin);
1221+
DBGEXR("reading y={}\n", y);
12211222
uint8_t* linedata = static_cast<uint8_t*>(data)
12221223
+ scanlinebytes * (y - ybegin);
12231224
default_init_vector<uint8_t> fullchunk;
@@ -1276,8 +1277,19 @@ OpenEXRCoreInput::read_native_scanlines(int subimage, int miplevel, int ybegin,
12761277
if (rv == EXR_ERR_SUCCESS)
12771278
rv = exr_decoding_run(m_exr_context, subimage, &decoder);
12781279
if (rv != EXR_ERR_SUCCESS) {
1279-
ok = false;
1280-
} else if (cdata != linedata) {
1280+
if (check_fill_missing(spec.x, spec.x + spec.width, y,
1281+
y + nlines, 0, 1, chbegin, chend,
1282+
cdata + invalid * scanlinebytes,
1283+
pixelbytes, scanlinebytes)) {
1284+
// clear the error
1285+
DBGEXR("cfm true y={} {}-{}\n", y, yb, ye);
1286+
rv = EXR_ERR_SUCCESS;
1287+
} else {
1288+
DBGEXR("cfm false {}-{}\n", yb, ye);
1289+
ok = false;
1290+
}
1291+
}
1292+
if (rv == EXR_ERR_SUCCESS && cdata != linedata) {
12811293
y += invalid;
12821294
nlines = std::min(nlines, yend - y);
12831295
memcpy(linedata, cdata + invalid * scanlinebytes,

0 commit comments

Comments
 (0)