@@ -275,6 +275,12 @@ OpenEXRInput::open(const std::string& name, ImageSpec& newspec,
275275 if (mc.size ())
276276 m_missingcolor = Strutil::extract_from_list_string<float >(mc);
277277 }
278+ // "openexr:missing_data_ok" asks to tolerate missing scanlines or tiles
279+ // -- for example, in a partially written file from a renderer that was
280+ // interrupted -- by filling the unreadable regions with black instead of
281+ // failing the read. Other readers will simply ignore this hint.
282+ if (const ParamValue* mdo = config.find_attribute (" openexr:missing_data_ok" ))
283+ m_missing_data_ok = mdo->get_int () != 0 ;
278284
279285 // Before engaging further with OpenEXR, make sure it is using the right
280286 // number of threads.
@@ -1253,7 +1259,7 @@ OpenEXRInput::read_native_scanline(int subimage, int miplevel, int y, int z,
12531259 void * data)
12541260{
12551261 return read_native_scanlines (subimage, miplevel, y, y + 1 , z, 0 ,
1256- m_spec.nchannels , data);
1262+ m_spec.nchannels , data, false );
12571263}
12581264
12591265
@@ -1263,7 +1269,7 @@ OpenEXRInput::read_native_scanlines(int subimage, int miplevel, int ybegin,
12631269 int yend, int z, void * data)
12641270{
12651271 return read_native_scanlines (subimage, miplevel, ybegin, yend, z, 0 ,
1266- m_spec.nchannels , data);
1272+ m_spec.nchannels , data, false );
12671273}
12681274
12691275
@@ -1280,11 +1286,15 @@ OpenEXRInput::read_cached_chunk(int subimage, int miplevel, int ybegin,
12801286 ybegin, yend, scanlinebytes, data))
12811287 return true ;
12821288
1283- // Cache miss. Decode the whole chunk. This recursive call asks for
1284- // exactly one full chunk, so it won't come back here.
1289+ // Cache miss. Decode the whole chunk. The bypass_chunk_cache flag of
1290+ // this recursive call asks for exactly one full chunk, so it won't come
1291+ // back here -- this matters when the decode fails and tolerance is on:
1292+ // without the bypass, the per-scanline retry would re-enter the chunk
1293+ // cache and recurse forever.
12851294 default_init_vector<uint8_t > chunk (scanlinebytes * size_t (cend - cbegin));
12861295 if (!read_native_scanlines (subimage, miplevel, cbegin, cend, 0 , chbegin,
1287- chend, chunk.data ()))
1296+ chend, chunk.data (),
1297+ /* bypass_chunk_cache=*/ true ))
12881298 return false ;
12891299 memcpy (data, chunk.data () + scanlinebytes * size_t (ybegin - cbegin),
12901300 scanlinebytes * size_t (yend - ybegin));
@@ -1298,6 +1308,15 @@ bool
12981308OpenEXRInput::read_native_scanlines (int subimage, int miplevel, int ybegin,
12991309 int yend, int z, int chbegin, int chend,
13001310 void * data)
1311+ {
1312+ return read_native_scanlines (subimage, miplevel, ybegin, yend, z, chbegin,
1313+ chend, data, /* bypass_chunk_cache=*/ false );
1314+ }
1315+
1316+ bool
1317+ OpenEXRInput::read_native_scanlines (int subimage, int miplevel, int ybegin,
1318+ int yend, int z, int chbegin, int chend,
1319+ void * data, bool bypass_chunk_cache)
13011320{
13021321 lock_guard lock (*this );
13031322 if (!seek_subimage (subimage, miplevel))
@@ -1341,7 +1360,7 @@ OpenEXRInput::read_native_scanlines(int subimage, int miplevel, int ybegin,
13411360 default_init_vector<uint8_t > scratch (fullscanbytes
13421361 * size_t (yend - ybegin));
13431362 if (!read_native_scanlines (subimage, miplevel, ybegin, yend, z, 0 ,
1344- m_spec.nchannels , scratch.data ()))
1363+ m_spec.nchannels , scratch.data (), false ))
13451364 return false ;
13461365 size_t choff = m_spec.pixel_bytes (0 , chbegin, true );
13471366 for (int y = ybegin; y < yend; ++y) {
@@ -1364,7 +1383,7 @@ OpenEXRInput::read_native_scanlines(int subimage, int miplevel, int ybegin,
13641383 // swath) at a time will ask for the rest of that chunk next. (The
13651384 // library has a stash of its own, but it drops it every time we set the
13661385 // frame buffer, which we must do on every read.)
1367- if (part.scansperchunk > 1 && !part.luminance_chroma
1386+ if (part.scansperchunk > 1 && !part.luminance_chroma && !bypass_chunk_cache
13681387 && ybegin >= m_spec.y ) {
13691388 int ychunkstart = m_spec.y
13701389 + round_down_to_multiple (ybegin - m_spec.y ,
@@ -1441,7 +1460,7 @@ OpenEXRInput::read_native_scanlines(int subimage, int miplevel, int ybegin,
14411460 }
14421461 } catch (const std::exception& e) {
14431462 std::string err = e.what ();
1444- if (m_missingcolor.size ()) {
1463+ if (m_missingcolor.size () || m_missing_data_ok ) {
14451464 // User said not to fail for bad or missing scanlines. If we
14461465 // failed reading a single scanline, use the fill pattern. If we
14471466 // failed reading many scanlines, we don't know which ones, so go
@@ -1456,10 +1475,10 @@ OpenEXRInput::read_native_scanlines(int subimage, int miplevel, int ybegin,
14561475 } else {
14571476 // Read of many tiles -- don't know which failed, so try
14581477 // again to read them all individually.
1459- return read_native_scanlines_individually (subimage, miplevel,
1460- ybegin, yend, z,
1461- chbegin, chend, data ,
1462- scanlinebytes );
1478+ return read_native_scanlines_individually (
1479+ subimage, miplevel, ybegin, yend, z, chbegin, chend, data ,
1480+ scanlinebytes ,
1481+ /* bypass_chunk_cache= */ true );
14631482 }
14641483 } else {
14651484 errorfmt (" Failed OpenEXR read: {}" , err);
@@ -1593,7 +1612,7 @@ OpenEXRInput::read_native_tiles(int subimage, int miplevel, int xbegin,
15931612 }
15941613 } catch (const std::exception& e) {
15951614 std::string err = e.what ();
1596- if (m_missingcolor.size ()) {
1615+ if (m_missingcolor.size () || m_missing_data_ok ) {
15971616 // User said not to fail for bad or missing tiles. If we failed
15981617 // reading a single tile, use the fill pattern. If we failed
15991618 // reading many tiles, we don't know which ones, so go back and
@@ -1631,15 +1650,16 @@ bool
16311650OpenEXRInput::read_native_scanlines_individually (int subimage, int miplevel,
16321651 int ybegin, int yend, int z,
16331652 int chbegin, int chend,
1634- void * data, stride_t ystride)
1653+ void * data, stride_t ystride,
1654+ bool bypass_chunk_cache)
16351655{
16361656 // Note: this is only called by read_native_scanlines, which still holds
16371657 // the mutex, so it's safe to directly access m_spec.
16381658 bool ok = true ;
16391659 for (int y = ybegin; y < yend; ++y) {
16401660 char * d = (char *)data + (y - ybegin) * ystride;
16411661 ok &= read_native_scanlines (subimage, miplevel, y, y + 1 , z, chbegin,
1642- chend, d);
1662+ chend, d, bypass_chunk_cache );
16431663 }
16441664 return ok;
16451665}
@@ -1679,7 +1699,13 @@ OpenEXRInput::fill_missing(int xbegin, int xend, int ybegin, int yend,
16791699 void * data, stride_t xstride, stride_t ystride)
16801700{
16811701 std::vector<float > missingcolor = m_missingcolor;
1682- missingcolor.resize (chend, m_missingcolor.back ());
1702+ if (missingcolor.empty ()) {
1703+ // No explicit missing color (the "openexr:missing_data_ok" case):
1704+ // fill the unreadable regions with black.
1705+ missingcolor.resize (chend, 0 .0f );
1706+ } else {
1707+ missingcolor.resize (chend, m_missingcolor.back ());
1708+ }
16831709 bool stripe = missingcolor[0 ] < 0 .0f ;
16841710 if (stripe)
16851711 missingcolor[0 ] = fabsf (missingcolor[0 ]);
0 commit comments