Skip to content

Commit b7580f4

Browse files
authored
fix(IBA): Handle offset data windows in fillholes_pushpull (#5105)
Fixes #4942 IBA::fillholes_pushpull() produced incorrect results when the source image had a non-zero data window origin (such as exr files with overscan). Two interrelated bugs: 1. The paste() call double-applied the data window offset. paste() maps source pixel (0,0) to destination (xbegin,ybegin), but since the source data starts at (x,y), passing (x,y) as the offset shifted every pixel by an extra (x,y). Pixels in the negative-coordinate region landed outside the destination buffer and were silently lost. 2. The top pyramid level preserved the original display/full window while all smaller levels had full=data at (0,0). Since resize() uses the full window for coordinate mapping, it only sampled the display window portion of the top level, losing all overscan data. Fix by shifting the top pyramid level to origin (0,0) with full=data, making all levels coordinate-consistent. The initial paste translates source pixels into the origin-based pyramid, and the final paste (which was already using src origin offsets) correctly translates back. Add test cases in testsuite/oiiotool for fillholes with an offset data window, and an offset display window, to ensure that both cases work. Also, for the existing fillholes related test, move the source image from ref (never should have been there) to src. Assisted-by: Claude Code / claude-opus-4-6 Signed-off-by: Larry Gritz <lg@larrygritz.com>
1 parent 0b93c3e commit b7580f4

File tree

7 files changed

+22
-4
lines changed

7 files changed

+22
-4
lines changed

src/libOpenImageIO/imagebufalgo.cpp

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1614,10 +1614,20 @@ ImageBufAlgo::fillholes_pushpull(ImageBuf& dst, const ImageBuf& src, ROI roi,
16141614

16151615
// First, make a writable copy of the original image (converting
16161616
// to float as a convenience) as the top level of the pyramid.
1617+
// Shift everything to origin (0,0) with full=data so that resize
1618+
// correctly maps the entire data extent between pyramid levels,
1619+
// even when the data window is offset from the display window.
16171620
ImageSpec topspec = src.spec();
16181621
topspec.set_format(TypeDesc::FLOAT);
1619-
ImageBuf* top = new ImageBuf(topspec);
1620-
paste(*top, topspec.x, topspec.y, topspec.z, 0, src);
1622+
topspec.full_x = 0;
1623+
topspec.full_y = 0;
1624+
topspec.full_width = topspec.width;
1625+
topspec.full_height = topspec.height;
1626+
topspec.x = 0;
1627+
topspec.y = 0;
1628+
topspec.z = 0;
1629+
ImageBuf* top = new ImageBuf(topspec);
1630+
paste(*top, -src.spec().x, -src.spec().y, -src.spec().z, 0, src);
16211631
pyramid.emplace_back(top);
16221632

16231633
// Construct the rest of the pyramid by successive x/2 resizing and
9.21 KB
Binary file not shown.
9.21 KB
Binary file not shown.

testsuite/oiiotool/ref/out.txt

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -126,6 +126,10 @@ Comparing "tahoe-filled.tif" and "ref/tahoe-filled.tif"
126126
PASS
127127
Comparing "growholes.tif" and "ref/growholes.tif"
128128
PASS
129+
Comparing "data-offset-hole-filled.exr" and "ref/data-offset-hole-filled.exr"
130+
PASS
131+
Comparing "display-offset-hole-filled.exr" and "ref/display-offset-hole-filled.exr"
132+
PASS
129133
Comparing "rangecompress.tif" and "ref/rangecompress.tif"
130134
PASS
131135
Comparing "rangeexpand.tif" and "ref/rangeexpand.tif"

testsuite/oiiotool/run.py

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -121,9 +121,12 @@
121121
+ "--siappendall -trim -origin +0+0 -fullpixels -d uint8 -o trimemptysubimages.tif")
122122

123123
# test hole filling
124-
command += oiiotool ("ref/hole.tif --fillholes -o tahoe-filled.tif")
124+
command += oiiotool ("src/hole.tif --fillholes -o tahoe-filled.tif")
125125
# test hole filling for a cropped image
126126
command += oiiotool ("-pattern checker 64x64+32+32 3 -ch R,G,B,A=1.0 -fullsize 128x128+0+0 --croptofull -fillholes -d uint8 -o growholes.tif")
127+
# test hole filling with offset data and data windows (issue #4942)
128+
command += oiiotool("--create 64x64-16-16 4 --fullsize 32x32+0+0 --box:color=1,0,0,1 0,0,31,31 --box:color=0,1,0,1 1,1,30,30 -d half -o data-offset-hole.exr --fillholes -o data-offset-hole-filled.exr")
129+
command += oiiotool("--create 64x64 4 --fullsize 32x32+16+16 --box:color=1,0,0,1 16,16,47,47 --box:color=0,1,0,1 17,17,46,46 -d half -o display-offset-hole.exr --fillholes -o display-offset-hole-filled.exr")
127130

128131
# Test --min/--max
129132
command += oiiotool ("--pattern fill:top=0,0,0:bottom=1,1,1 64x64 3 "
@@ -285,6 +288,7 @@
285288
"abs.exr", "absdiff.exr", "absdiffc.exr",
286289
"chsum.tif",
287290
"tahoe-filled.tif", "growholes.tif",
291+
"data-offset-hole-filled.exr", "display-offset-hole-filled.exr",
288292
"rangecompress.tif", "rangeexpand.tif",
289293
"rangecompress-luma.tif", "rangeexpand-luma.tif",
290294
"min.exr", "cmin1.exr", "cmin2.exr",

testsuite/python-imagebufalgo/src/test_imagebufalgo.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -532,7 +532,7 @@ def test_iba (func: Callable[..., oiio.ImageBuf], *args, **kwargs) -> oiio.Image
532532
bad.clear()
533533

534534
# fillholes_pushpull
535-
b = test_iba (ImageBufAlgo.fillholes_pushpull, ImageBuf(OIIO_TESTSUITE_ROOT+"/oiiotool/ref/hole.tif"))
535+
b = test_iba (ImageBufAlgo.fillholes_pushpull, ImageBuf(OIIO_TESTSUITE_ROOT+"/oiiotool/src/hole.tif"))
536536
write (b, "tahoe-filled.tif", oiio.UINT8)
537537

538538
# over

0 commit comments

Comments
 (0)