551 lines
20 KiB
Diff
551 lines
20 KiB
Diff
From d80f11f4f55100d007ae80a162bf257ec291612c Mon Sep 17 00:00:00 2001
|
|
From: peterhillman <peterh@wetafx.co.nz>
|
|
Date: Fri, 11 Sep 2020 11:02:20 +1200
|
|
Subject: [PATCH] More efficient handling of filled channels reading tiles with
|
|
scanline API (#830)
|
|
|
|
* refactor channel filling in InputFile API with tiled source
|
|
|
|
Signed-off-by: Peter Hillman <peterh@wetafx.co.nz>
|
|
|
|
* handle edge-case of empty framebuffer
|
|
|
|
Signed-off-by: Peter Hillman <peterh@wetafx.co.nz>
|
|
---
|
|
IlmImf/ImfInputFile.cpp | 271 ++++++++++++++++---------
|
|
IlmImfTest/testScanLineApi.cpp | 134 +++++++++++-
|
|
2 files changed, 310 insertions(+), 95 deletions(-)
|
|
|
|
diff --git a/IlmImf/ImfInputFile.cpp b/IlmImf/ImfInputFile.cpp
|
|
index 6bcb451ec..2ca45a738 100644
|
|
--- a/IlmImf/ImfInputFile.cpp
|
|
+++ b/IlmImf/ImfInputFile.cpp
|
|
@@ -278,9 +278,14 @@ bufferedReadPixels (InputFile::Data* ifd, int scanLine1, int scanLine2)
|
|
//
|
|
// We don't have any valid buffered info, so we need to read in
|
|
// from the file.
|
|
+ // if no channels are being read that are present in file, cachedBuffer will be empty
|
|
//
|
|
|
|
- ifd->tFile->readTiles (0, ifd->tFile->numXTiles (0) - 1, j, j);
|
|
+ if (ifd->cachedBuffer->begin() != ifd->cachedBuffer->end())
|
|
+ {
|
|
+ ifd->tFile->readTiles (0, ifd->tFile->numXTiles (0) - 1, j, j);
|
|
+ }
|
|
+
|
|
ifd->cachedTileY = j;
|
|
}
|
|
|
|
@@ -289,58 +294,135 @@ bufferedReadPixels (InputFile::Data* ifd, int scanLine1, int scanLine2)
|
|
// framebuffer.
|
|
//
|
|
|
|
- for (FrameBuffer::ConstIterator k = ifd->cachedBuffer->begin();
|
|
- k != ifd->cachedBuffer->end();
|
|
+ for (FrameBuffer::ConstIterator k = ifd->tFileBuffer.begin();
|
|
+ k != ifd->tFileBuffer.end();
|
|
++k)
|
|
{
|
|
- Slice fromSlice = k.slice(); // slice to write from
|
|
- Slice toSlice = ifd->tFileBuffer[k.name()]; // slice to write to
|
|
|
|
- char *fromPtr, *toPtr;
|
|
- int size = pixelTypeSize (toSlice.type);
|
|
|
|
- int xStart = levelRange.min.x;
|
|
- int yStart = minYThisRow;
|
|
+ Slice toSlice = k.slice(); // slice to read from
|
|
+ char* toPtr;
|
|
|
|
- while (modp (xStart, toSlice.xSampling) != 0)
|
|
- ++xStart;
|
|
+ int xStart = levelRange.min.x;
|
|
+ int yStart = minYThisRow;
|
|
|
|
- while (modp (yStart, toSlice.ySampling) != 0)
|
|
- ++yStart;
|
|
+ while (modp (xStart, toSlice.xSampling) != 0)
|
|
+ ++xStart;
|
|
|
|
+ while (modp (yStart, toSlice.ySampling) != 0)
|
|
+ ++yStart;
|
|
|
|
- intptr_t fromBase = reinterpret_cast<intptr_t>(fromSlice.base);
|
|
+ FrameBuffer::ConstIterator c = ifd->cachedBuffer->find(k.name());
|
|
intptr_t toBase = reinterpret_cast<intptr_t>(toSlice.base);
|
|
|
|
- for (int y = yStart;
|
|
- y <= maxYThisRow;
|
|
- y += toSlice.ySampling)
|
|
+
|
|
+ if( c!=ifd->cachedBuffer->end())
|
|
+ {
|
|
+ //
|
|
+ // output channel was read from source image: copy to output slice
|
|
+ //
|
|
+ Slice fromSlice = c.slice(); // slice to write to
|
|
+ intptr_t fromBase = reinterpret_cast<intptr_t>(fromSlice.base);
|
|
+
|
|
+ int size = pixelTypeSize (toSlice.type);
|
|
+ char* fromPtr;
|
|
+
|
|
+ for (int y = yStart;
|
|
+ y <= maxYThisRow;
|
|
+ y += toSlice.ySampling)
|
|
+ {
|
|
+ //
|
|
+ // Set the pointers to the start of the y scanline in
|
|
+ // this row of tiles
|
|
+ //
|
|
+
|
|
+ fromPtr = reinterpret_cast<char*> (fromBase +
|
|
+ (y - tileRange.min.y) * fromSlice.yStride +
|
|
+ xStart * fromSlice.xStride);
|
|
+
|
|
+ toPtr = reinterpret_cast<char*> (toBase +
|
|
+ divp (y, toSlice.ySampling) * toSlice.yStride +
|
|
+ divp (xStart, toSlice.xSampling) * toSlice.xStride);
|
|
+
|
|
+ //
|
|
+ // Copy all pixels for the scanline in this row of tiles
|
|
+ //
|
|
+
|
|
+ for (int x = xStart;
|
|
+ x <= levelRange.max.x;
|
|
+ x += toSlice.xSampling)
|
|
+ {
|
|
+ for (int i = 0; i < size; ++i)
|
|
+ toPtr[i] = fromPtr[i];
|
|
+
|
|
+ fromPtr += fromSlice.xStride * toSlice.xSampling;
|
|
+ toPtr += toSlice.xStride;
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+ else
|
|
{
|
|
- //
|
|
- // Set the pointers to the start of the y scanline in
|
|
- // this row of tiles
|
|
- //
|
|
- fromPtr = reinterpret_cast<char*> (fromBase +
|
|
- (y - tileRange.min.y) * fromSlice.yStride +
|
|
- xStart * fromSlice.xStride);
|
|
-
|
|
- toPtr = reinterpret_cast<char*> (toBase +
|
|
- divp (y, toSlice.ySampling) * toSlice.yStride +
|
|
- divp (xStart, toSlice.xSampling) * toSlice.xStride);
|
|
-
|
|
- //
|
|
- // Copy all pixels for the scanline in this row of tiles
|
|
- //
|
|
-
|
|
- for (int x = xStart;
|
|
- x <= levelRange.max.x;
|
|
- x += toSlice.xSampling)
|
|
+
|
|
+ //
|
|
+ // channel wasn't present in source file: fill output slice
|
|
+ //
|
|
+ for (int y = yStart;
|
|
+ y <= maxYThisRow;
|
|
+ y += toSlice.ySampling)
|
|
{
|
|
- for (int i = 0; i < size; ++i)
|
|
- toPtr[i] = fromPtr[i];
|
|
|
|
- fromPtr += fromSlice.xStride * toSlice.xSampling;
|
|
- toPtr += toSlice.xStride;
|
|
+ toPtr = reinterpret_cast<char*> (toBase+
|
|
+ divp (y, toSlice.ySampling) * toSlice.yStride +
|
|
+ divp (xStart, toSlice.xSampling) * toSlice.xStride);
|
|
+
|
|
+ //
|
|
+ // Copy all pixels for the scanline in this row of tiles
|
|
+ //
|
|
+
|
|
+ switch ( toSlice.type)
|
|
+ {
|
|
+ case UINT:
|
|
+ {
|
|
+ unsigned int fill = toSlice.fillValue;
|
|
+ for (int x = xStart;
|
|
+ x <= levelRange.max.x;
|
|
+ x += toSlice.xSampling)
|
|
+ {
|
|
+ * reinterpret_cast<unsigned int*>(toPtr) = fill;
|
|
+ toPtr += toSlice.xStride;
|
|
+ }
|
|
+ break;
|
|
+ }
|
|
+ case HALF :
|
|
+ {
|
|
+ half fill = toSlice.fillValue;
|
|
+ for (int x = xStart;
|
|
+ x <= levelRange.max.x;
|
|
+ x += toSlice.xSampling)
|
|
+ {
|
|
+ * reinterpret_cast<half*>(toPtr) = fill;
|
|
+ toPtr += toSlice.xStride;
|
|
+ }
|
|
+ break;
|
|
+ }
|
|
+ case FLOAT :
|
|
+ {
|
|
+ float fill = toSlice.fillValue;
|
|
+ for (int x = xStart;
|
|
+ x <= levelRange.max.x;
|
|
+ x += toSlice.xSampling)
|
|
+ {
|
|
+ * reinterpret_cast<float*>(toPtr) = fill;
|
|
+ toPtr += toSlice.xStride;
|
|
+ }
|
|
+ break;
|
|
+ }
|
|
+ case NUM_PIXELTYPES :
|
|
+ {
|
|
+ break;
|
|
+ }
|
|
+
|
|
+ }
|
|
}
|
|
}
|
|
}
|
|
@@ -706,60 +788,67 @@ InputFile::setFrameBuffer (const FrameBuffer &frameBuffer)
|
|
{
|
|
Slice s = k.slice();
|
|
|
|
- switch (s.type)
|
|
- {
|
|
- case OPENEXR_IMF_INTERNAL_NAMESPACE::UINT:
|
|
-
|
|
- _data->cachedBuffer->insert
|
|
- (k.name(),
|
|
- Slice (UINT,
|
|
- (char *)(new unsigned int[tileRowSize] -
|
|
- _data->offset),
|
|
- sizeof (unsigned int),
|
|
- sizeof (unsigned int) *
|
|
- _data->tFile->levelWidth(0),
|
|
- 1, 1,
|
|
- s.fillValue,
|
|
- false, true));
|
|
- break;
|
|
-
|
|
- case OPENEXR_IMF_INTERNAL_NAMESPACE::HALF:
|
|
-
|
|
- _data->cachedBuffer->insert
|
|
- (k.name(),
|
|
- Slice (HALF,
|
|
- (char *)(new half[tileRowSize] -
|
|
- _data->offset),
|
|
- sizeof (half),
|
|
- sizeof (half) *
|
|
- _data->tFile->levelWidth(0),
|
|
- 1, 1,
|
|
- s.fillValue,
|
|
- false, true));
|
|
- break;
|
|
-
|
|
- case OPENEXR_IMF_INTERNAL_NAMESPACE::FLOAT:
|
|
-
|
|
- _data->cachedBuffer->insert
|
|
- (k.name(),
|
|
- Slice (OPENEXR_IMF_INTERNAL_NAMESPACE::FLOAT,
|
|
- (char *)(new float[tileRowSize] -
|
|
- _data->offset),
|
|
- sizeof(float),
|
|
- sizeof(float) *
|
|
- _data->tFile->levelWidth(0),
|
|
- 1, 1,
|
|
- s.fillValue,
|
|
- false, true));
|
|
- break;
|
|
-
|
|
- default:
|
|
-
|
|
- throw IEX_NAMESPACE::ArgExc ("Unknown pixel data type.");
|
|
- }
|
|
+ //
|
|
+ // omit adding channels that are not listed - 'fill' channels are added later
|
|
+ //
|
|
+ if ( _data->header.channels().find(k.name()) != _data->header.channels().end() )
|
|
+ {
|
|
+ switch (s.type)
|
|
+ {
|
|
+ case OPENEXR_IMF_INTERNAL_NAMESPACE::UINT:
|
|
+
|
|
+ _data->cachedBuffer->insert
|
|
+ (k.name(),
|
|
+ Slice (UINT,
|
|
+ (char *)(new unsigned int[tileRowSize] -
|
|
+ _data->offset),
|
|
+ sizeof (unsigned int),
|
|
+ sizeof (unsigned int) *
|
|
+ _data->tFile->levelWidth(0),
|
|
+ 1, 1,
|
|
+ s.fillValue,
|
|
+ false, true));
|
|
+ break;
|
|
+
|
|
+ case OPENEXR_IMF_INTERNAL_NAMESPACE::HALF:
|
|
+
|
|
+ _data->cachedBuffer->insert
|
|
+ (k.name(),
|
|
+ Slice (HALF,
|
|
+ (char *)(new half[tileRowSize] -
|
|
+ _data->offset),
|
|
+ sizeof (half),
|
|
+ sizeof (half) *
|
|
+ _data->tFile->levelWidth(0),
|
|
+ 1, 1,
|
|
+ s.fillValue,
|
|
+ false, true));
|
|
+ break;
|
|
+
|
|
+ case OPENEXR_IMF_INTERNAL_NAMESPACE::FLOAT:
|
|
+
|
|
+ _data->cachedBuffer->insert
|
|
+ (k.name(),
|
|
+ Slice (OPENEXR_IMF_INTERNAL_NAMESPACE::FLOAT,
|
|
+ (char *)(new float[tileRowSize] -
|
|
+ _data->offset),
|
|
+ sizeof(float),
|
|
+ sizeof(float) *
|
|
+ _data->tFile->levelWidth(0),
|
|
+ 1, 1,
|
|
+ s.fillValue,
|
|
+ false, true));
|
|
+ break;
|
|
+
|
|
+ default:
|
|
+
|
|
+ throw IEX_NAMESPACE::ArgExc ("Unknown pixel data type.");
|
|
+ }
|
|
+ }
|
|
}
|
|
|
|
_data->tFile->setFrameBuffer (*_data->cachedBuffer);
|
|
+
|
|
}
|
|
|
|
_data->tFileBuffer = frameBuffer;
|
|
diff --git a/IlmImfTest/testScanLineApi.cpp b/IlmImfTest/testScanLineApi.cpp
|
|
index 354d2dccf..720351ea5 100644
|
|
--- a/IlmImfTest/testScanLineApi.cpp
|
|
+++ b/IlmImfTest/testScanLineApi.cpp
|
|
@@ -93,7 +93,9 @@ writeRead (const Array2D<unsigned int> &pi1,
|
|
int yOffset,
|
|
Compression comp,
|
|
LevelMode mode,
|
|
- LevelRoundingMode rmode)
|
|
+ LevelRoundingMode rmode,
|
|
+ bool fillChannel
|
|
+ )
|
|
{
|
|
//
|
|
// Write the pixel data in pi1, ph1 and ph2 to a tiled
|
|
@@ -263,6 +265,16 @@ writeRead (const Array2D<unsigned int> &pi1,
|
|
Array2D<half> ph2 (h, w);
|
|
Array2D<float> pf2 (h, w);
|
|
|
|
+ Array2D<unsigned int> fi2 (fillChannel ? h : 1 , fillChannel ? w : 1);
|
|
+ Array2D<half> fh2 (fillChannel ? h : 1 , fillChannel ? w : 1);
|
|
+ Array2D<float> ff2 (fillChannel ? h : 1 , fillChannel ? w : 1);
|
|
+
|
|
+
|
|
+ const unsigned int fillInt = 12;
|
|
+ const half fillHalf = 4.5;
|
|
+ const float fillFloat = M_PI;
|
|
+
|
|
+
|
|
FrameBuffer fb;
|
|
|
|
fb.insert ("I", // name
|
|
@@ -286,6 +298,30 @@ writeRead (const Array2D<unsigned int> &pi1,
|
|
sizeof (pf2[0][0]) * w) // yStride
|
|
);
|
|
|
|
+ if(fillChannel)
|
|
+ {
|
|
+ fb.insert ("FI", // name
|
|
+ Slice (IMF::UINT, // type
|
|
+ (char *) &fi2[-dwy][-dwx],// base
|
|
+ sizeof (fi2[0][0]), // xStride
|
|
+ sizeof (fi2[0][0]) * w,1,1,fillInt) // yStride
|
|
+ );
|
|
+
|
|
+ fb.insert ("FH", // name
|
|
+ Slice (IMF::HALF, // type
|
|
+ (char *) &fh2[-dwy][-dwx],// base
|
|
+ sizeof (fh2[0][0]), // xStride
|
|
+ sizeof (fh2[0][0]) * w,1,1,fillHalf) // yStride
|
|
+ );
|
|
+
|
|
+ fb.insert ("FF", // name
|
|
+ Slice (IMF::FLOAT, // type
|
|
+ (char *) &ff2[-dwy][-dwx],// base
|
|
+ sizeof (ff2[0][0]), // xStride
|
|
+ sizeof (ff2[0][0]) * w,1,1,fillFloat) // yStride
|
|
+ );
|
|
+ }
|
|
+
|
|
in.setFrameBuffer (fb);
|
|
for (int y = dw.min.y; y <= dw.max.y; ++y)
|
|
in.readPixels (y);
|
|
@@ -323,6 +359,13 @@ writeRead (const Array2D<unsigned int> &pi1,
|
|
assert (pi1[y][x] == pi2[y][x]);
|
|
assert (ph1[y][x] == ph2[y][x]);
|
|
assert (pf1[y][x] == pf2[y][x]);
|
|
+
|
|
+ if (fillChannel)
|
|
+ {
|
|
+ assert(fi2[y][x] == fillInt);
|
|
+ assert(fh2[y][x] == fillHalf);
|
|
+ assert(ff2[y][x] == fillFloat);
|
|
+ }
|
|
}
|
|
}
|
|
}
|
|
@@ -342,6 +385,10 @@ writeRead (const Array2D<unsigned int> &pi1,
|
|
Array2D<half> ph2 (h, w);
|
|
Array2D<float> pf2 (h, w);
|
|
|
|
+ Array2D<unsigned int> fi2 (fillChannel ? h : 1 , fillChannel ? w : 1);
|
|
+ Array2D<half> fh2 (fillChannel ? h : 1 , fillChannel ? w : 1);
|
|
+ Array2D<float> ff2 (fillChannel ? h : 1 , fillChannel ? w : 1);
|
|
+
|
|
FrameBuffer fb;
|
|
|
|
fb.insert ("I", // name
|
|
@@ -364,6 +411,34 @@ writeRead (const Array2D<unsigned int> &pi1,
|
|
sizeof (pf2[0][0]), // xStride
|
|
sizeof (pf2[0][0]) * w) // yStride
|
|
);
|
|
+ const unsigned int fillInt = 21;
|
|
+ const half fillHalf = 42;
|
|
+ const float fillFloat = 2.8;
|
|
+
|
|
+ if (fillChannel)
|
|
+ {
|
|
+ fb.insert ("FI", // name
|
|
+ Slice (IMF::UINT, // type
|
|
+ (char *) &fi2[-dwy][-dwx],// base
|
|
+ sizeof (fi2[0][0]), // xStride
|
|
+ sizeof (fi2[0][0]) * w,1,1,fillInt) // yStride
|
|
+ );
|
|
+
|
|
+ fb.insert ("FH", // name
|
|
+ Slice (IMF::HALF, // type
|
|
+ (char *) &fh2[-dwy][-dwx],// base
|
|
+ sizeof (fh2[0][0]), // xStride
|
|
+ sizeof (fh2[0][0]) * w,1,1,fillHalf) // yStride
|
|
+ );
|
|
+
|
|
+ fb.insert ("FF", // name
|
|
+ Slice (IMF::FLOAT, // type
|
|
+ (char *) &ff2[-dwy][-dwx],// base
|
|
+ sizeof (ff2[0][0]), // xStride
|
|
+ sizeof (ff2[0][0]) * w,1,1,fillFloat) // yStride
|
|
+ );
|
|
+
|
|
+ }
|
|
|
|
in.setFrameBuffer (fb);
|
|
for (int y = dw.max.y; y >= dw.min.y; --y)
|
|
@@ -402,6 +477,12 @@ writeRead (const Array2D<unsigned int> &pi1,
|
|
assert (pi1[y][x] == pi2[y][x]);
|
|
assert (ph1[y][x] == ph2[y][x]);
|
|
assert (pf1[y][x] == pf2[y][x]);
|
|
+ if (fillChannel)
|
|
+ {
|
|
+ assert(fi2[y][x] == fillInt);
|
|
+ assert(fh2[y][x] == fillHalf);
|
|
+ assert(ff2[y][x] == fillFloat);
|
|
+ }
|
|
}
|
|
}
|
|
}
|
|
@@ -422,6 +503,17 @@ writeRead (const Array2D<unsigned int> &pi1,
|
|
Array2D<half> ph2 (h, w);
|
|
Array2D<float> pf2 (h, w);
|
|
|
|
+
|
|
+ Array2D<unsigned int> fi2 (fillChannel ? h : 1 , fillChannel ? w : 1);
|
|
+ Array2D<half> fh2 (fillChannel ? h : 1 , fillChannel ? w : 1);
|
|
+ Array2D<float> ff2 (fillChannel ? h : 1 , fillChannel ? w : 1);
|
|
+
|
|
+
|
|
+ const unsigned int fillInt = 81;
|
|
+ const half fillHalf = 0.5;
|
|
+ const float fillFloat = 7.8;
|
|
+
|
|
+
|
|
for (int y = dw.min.y; y <= dw.max.y; ++y)
|
|
{
|
|
FrameBuffer fb;
|
|
@@ -447,6 +539,31 @@ writeRead (const Array2D<unsigned int> &pi1,
|
|
0) // yStride
|
|
);
|
|
|
|
+ if (fillChannel)
|
|
+ {
|
|
+ fb.insert ("FI", // name
|
|
+ Slice (IMF::UINT, // type
|
|
+ (char *) &fi2[y - dwy][-dwx], // base
|
|
+ sizeof (fi2[0][0]), // xStride
|
|
+ 0,1,1,fillInt) // yStride
|
|
+ );
|
|
+
|
|
+ fb.insert ("FH", // name
|
|
+ Slice (IMF::HALF, // type
|
|
+ (char *) &fh2[y - dwy][-dwx], // base
|
|
+ sizeof (fh2[0][0]), // xStride
|
|
+ 0,1,1,fillHalf) // yStride
|
|
+ );
|
|
+
|
|
+ fb.insert ("FF", // name
|
|
+ Slice (IMF::FLOAT, // type
|
|
+ (char *) &ff2[y - dwy][-dwx], // base
|
|
+ sizeof (ff2[0][0]), // xStride
|
|
+ 0,1,1,fillFloat) // yStride
|
|
+ );
|
|
+
|
|
+ }
|
|
+
|
|
in.setFrameBuffer (fb);
|
|
in.readPixels (y);
|
|
}
|
|
@@ -484,7 +601,14 @@ writeRead (const Array2D<unsigned int> &pi1,
|
|
assert (pi1[y][x] == pi2[y][x]);
|
|
assert (ph1[y][x] == ph2[y][x]);
|
|
assert (pf1[y][x] == pf2[y][x]);
|
|
+ if (fillChannel)
|
|
+ {
|
|
+ assert (fi2[y][x] == fillInt);
|
|
+ assert (fh2[y][x] == fillHalf);
|
|
+ assert (ff2[y][x] == fillFloat);
|
|
+ }
|
|
}
|
|
+
|
|
}
|
|
}
|
|
|
|
@@ -509,11 +633,13 @@ writeRead (const std::string &tempDir,
|
|
std::string filename = tempDir + "imf_test_scanline_api.exr";
|
|
|
|
writeRead (pi, ph, pf, filename.c_str(), lorder, W, H,
|
|
- xSize, ySize, dx, dy, comp, ONE_LEVEL, rmode);
|
|
+ xSize, ySize, dx, dy, comp, ONE_LEVEL, rmode , false);
|
|
+ writeRead (pi, ph, pf, filename.c_str(), lorder, W, H,
|
|
+ xSize, ySize, dx, dy, comp, MIPMAP_LEVELS, rmode , false );
|
|
writeRead (pi, ph, pf, filename.c_str(), lorder, W, H,
|
|
- xSize, ySize, dx, dy, comp, MIPMAP_LEVELS, rmode);
|
|
+ xSize, ySize, dx, dy, comp, RIPMAP_LEVELS, rmode , false);
|
|
writeRead (pi, ph, pf, filename.c_str(), lorder, W, H,
|
|
- xSize, ySize, dx, dy, comp, RIPMAP_LEVELS, rmode);
|
|
+ xSize, ySize, dx, dy, comp, ONE_LEVEL, rmode , true);
|
|
}
|
|
|
|
} // namespace
|