diff --git a/Changes.md b/Changes.md index 2d672d1589b..7a6e08b479d 100644 --- a/Changes.md +++ b/Changes.md @@ -1,6 +1,11 @@ 1.x.x.x (relative to 1.3.x.x) ======= +Features +-------- + +- GafferImage : Added DeepSlice node for clipping out part of an image based on depth. + Improvements ------------ diff --git a/include/GafferImage/DeepSlice.h b/include/GafferImage/DeepSlice.h index d6de2e7469c..abe1c25fb18 100644 --- a/include/GafferImage/DeepSlice.h +++ b/include/GafferImage/DeepSlice.h @@ -38,7 +38,7 @@ #include "GafferImage/ImageProcessor.h" -#include "Gaffer/CompoundNumericPlug.h" +#include "Gaffer/OptionalValuePlug.h" #include "Gaffer/NumericPlug.h" namespace Gaffer @@ -65,17 +65,11 @@ class GAFFERIMAGE_API DeepSlice : public ImageProcessor GAFFER_NODE_DECLARE_TYPE( GafferImage::DeepSlice, DeepSliceTypeId, ImageProcessor ); - Gaffer::BoolPlug *nearClipPlug(); - const Gaffer::BoolPlug *nearClipPlug() const; + Gaffer::OptionalValuePlug *nearClipPlug(); + const Gaffer::OptionalValuePlug *nearClipPlug() const; - Gaffer::FloatPlug *nearClipDepthPlug(); - const Gaffer::FloatPlug *nearClipDepthPlug() const; - - Gaffer::BoolPlug *farClipPlug(); - const Gaffer::BoolPlug *farClipPlug() const; - - Gaffer::FloatPlug *farClipDepthPlug(); - const Gaffer::FloatPlug *farClipDepthPlug() const; + Gaffer::OptionalValuePlug *farClipPlug(); + const Gaffer::OptionalValuePlug *farClipPlug() const; Gaffer::BoolPlug *flattenPlug(); const Gaffer::BoolPlug *flattenPlug() const; diff --git a/python/GafferImageTest/DeepSliceTest.py b/python/GafferImageTest/DeepSliceTest.py index 9c214da4027..59c0a431357 100644 --- a/python/GafferImageTest/DeepSliceTest.py +++ b/python/GafferImageTest/DeepSliceTest.py @@ -109,14 +109,14 @@ def testBasics( self ) : # We could compute expectedWeights based some simple computations, but I think it makes it more # obivous what we're testing to hardcode the expectedWeights for each test. def sliceTest( nearClip, nearClipDepth, farClip, farClipDepth, expectedWeights ): - deepSlice["nearClip"].setValue( nearClip ) - deepSlice["nearClipDepth"].setValue( nearClipDepth ) - deepSlice["farClip"].setValue( farClip ) - deepSlice["farClipDepth"].setValue( farClipDepth ) - deepSliceFlatten["nearClip"].setValue( nearClip ) - deepSliceFlatten["nearClipDepth"].setValue( nearClipDepth ) - deepSliceFlatten["farClip"].setValue( farClip ) - deepSliceFlatten["farClipDepth"].setValue( farClipDepth ) + deepSlice["nearClip"]["enabled"].setValue( nearClip ) + deepSlice["nearClip"]["value"].setValue( nearClipDepth ) + deepSlice["farClip"]["enabled"].setValue( farClip ) + deepSlice["farClip"]["value"].setValue( farClipDepth ) + deepSliceFlatten["nearClip"]["enabled"].setValue( nearClip ) + deepSliceFlatten["nearClip"]["value"].setValue( nearClipDepth ) + deepSliceFlatten["farClip"]["enabled"].setValue( farClip ) + deepSliceFlatten["farClip"]["value"].setValue( farClipDepth ) self.assertImagesEqual( deepSliceFlatten["out"], flatten["out"], maxDifference = 1e-7 ) @@ -270,8 +270,8 @@ def testBruteForce( self ) : sliceNear = GafferImage.DeepSlice() sliceNear["in"].setInput( testImage ) - sliceNear["nearClip"].setValue( False ) - sliceNear["farClip"].setValue( True ) + sliceNear["nearClip"]["enabled"].setValue( False ) + sliceNear["farClip"]["enabled"].setValue( True ) sliceNear["flatten"].setValue( False ) flattenedNear = GafferImage.DeepToFlat() @@ -280,15 +280,15 @@ def testBruteForce( self ) : flatSliceNear = GafferImage.DeepSlice() flatSliceNear["in"].setInput( testImage ) - flatSliceNear["nearClip"].setValue( False ) - flatSliceNear["farClip"].setValue( True ) - flatSliceNear["farClipDepth"].setInput( sliceNear["farClipDepth"] ) + flatSliceNear["nearClip"]["enabled"].setValue( False ) + flatSliceNear["farClip"]["enabled"].setValue( True ) + flatSliceNear["farClip"]["value"].setInput( sliceNear["farClip"]["value"] ) flatSliceNear["flatten"].setValue( True ) sliceFar = GafferImage.DeepSlice() sliceFar["in"].setInput( testImage ) - sliceFar["nearClip"].setValue( True ) - sliceFar["farClip"].setValue( False ) + sliceFar["nearClip"]["enabled"].setValue( True ) + sliceFar["farClip"]["enabled"].setValue( False ) sliceFar["flatten"].setValue( False ) flattenedFar = GafferImage.DeepToFlat() @@ -297,15 +297,15 @@ def testBruteForce( self ) : flatSliceFar = GafferImage.DeepSlice() flatSliceFar["in"].setInput( testImage ) - flatSliceFar["nearClip"].setValue( True ) - flatSliceFar["nearClipDepth"].setInput( sliceFar["nearClipDepth"] ) - flatSliceFar["farClip"].setValue( False ) + flatSliceFar["nearClip"]["enabled"].setValue( True ) + flatSliceFar["nearClip"]["value"].setInput( sliceFar["nearClip"]["value"] ) + flatSliceFar["farClip"]["enabled"].setValue( False ) flatSliceFar["flatten"].setValue( True ) sliceMiddle = GafferImage.DeepSlice() sliceMiddle["in"].setInput( testImage ) - sliceMiddle["nearClip"].setValue( True ) - sliceMiddle["farClip"].setValue( True ) + sliceMiddle["nearClip"]["enabled"].setValue( True ) + sliceMiddle["farClip"]["enabled"].setValue( True ) sliceMiddle["flatten"].setValue( False ) flattenedMiddle = GafferImage.DeepToFlat() @@ -314,10 +314,10 @@ def testBruteForce( self ) : flatSliceMiddle = GafferImage.DeepSlice() flatSliceMiddle["in"].setInput( testImage ) - flatSliceMiddle["nearClip"].setValue( True ) - flatSliceMiddle["nearClipDepth"].setInput( sliceMiddle["nearClipDepth"] ) - flatSliceMiddle["farClip"].setValue( True ) - flatSliceMiddle["farClipDepth"].setInput( sliceMiddle["farClipDepth"] ) + flatSliceMiddle["nearClip"]["enabled"].setValue( True ) + flatSliceMiddle["nearClip"]["value"].setInput( sliceMiddle["nearClip"]["value"] ) + flatSliceMiddle["farClip"]["enabled"].setValue( True ) + flatSliceMiddle["farClip"]["value"].setInput( sliceMiddle["farClip"]["value"] ) flatSliceMiddle["flatten"].setValue( True ) flattenedInput = GafferImage.DeepToFlat() @@ -413,8 +413,8 @@ def testBruteForce( self ) : [ random.uniform( zStart, zEnd ) for i in range( 20 ) ] ): with self.subTest( mode = "Near/Far", name = image.node().getName(), depth = depth ) : - sliceNear["farClipDepth"].setValue( depth ) - sliceFar["nearClipDepth"].setValue( depth ) + sliceNear["farClip"]["value"].setValue( depth ) + sliceFar["nearClip"]["value"].setValue( depth ) # The output from DeepSlice should always be tidy, which we can validate by making # sure tidying has no effect @@ -458,8 +458,7 @@ def testBruteForce( self ) : # the original sample counts, and no more than 1 greater ( since if a sample is split by # the depth, it will appear in both ) self.assertImagesEqual( - sampleCountsNearFar["out"], sampleCountsInput["out"], - maxDifferenceGreater = 1, maxDifferenceLess = 0 + sampleCountsInput["out"], sampleCountsNearFar["out"], maxDifference = (0,1) ) # Run a few more tests when we're taking a middle slice by clipping both near and far @@ -467,10 +466,10 @@ def testBruteForce( self ) : with self.subTest( mode = "Middle", name = image.node().getName(), depth = depth ) : nearDepth = min( a, b ) farDepth = max( a, b ) - sliceNear["farClipDepth"].setValue( nearDepth ) - sliceMiddle["nearClipDepth"].setValue( nearDepth ) - sliceMiddle["farClipDepth"].setValue( farDepth ) - sliceFar["nearClipDepth"].setValue( farDepth ) + sliceNear["farClip"]["value"].setValue( nearDepth ) + sliceMiddle["nearClip"]["value"].setValue( nearDepth ) + sliceMiddle["farClip"]["value"].setValue( farDepth ) + sliceFar["nearClip"]["value"].setValue( farDepth ) # Check that the flat output from DeepSlice matches with what we get by flattening # the deep output @@ -488,10 +487,8 @@ def testBruteForce( self ) : # the original sample counts, and no more than 2 greater ( since both clipping depths # could split a sample ) self.assertImagesEqual( - sampleCountsNearMiddleFar["out"], sampleCountsInput["out"], - maxDifferenceGreater = 2, maxDifferenceLess = 0 + sampleCountsInput["out"], sampleCountsNearMiddleFar["out"], maxDifference = (0,2) ) if __name__ == "__main__": unittest.main() - diff --git a/python/GafferImageUI/DeepSliceUI.py b/python/GafferImageUI/DeepSliceUI.py index a52750722c8..1d74fd0cc47 100644 --- a/python/GafferImageUI/DeepSliceUI.py +++ b/python/GafferImageUI/DeepSliceUI.py @@ -43,60 +43,39 @@ "description", """ - Takes a slice out of a deep image by discarding everything outside of a clipping range. - Optionally also flattens the image. The range is half open, including point samples exactly - at the near clip, but excluding point samples exactly at the far clip. This means that if you split - an image into a front and back with two DeepSlices, they will composite back together to match the - original. + Takes a slice out of an image with depth defined by Z ( and optionally ZBack ) channels by + discarding everything outside of a clipping range. The range is half open, including point samples + exactly at the near clip, but excluding point samples exactly at the far clip. This means that if + you split an image into a front and back with two DeepSlices, they will composite back together to + match the original. Optionally also flattens the image. """, - "layout:activator:nearClip", lambda node : node["nearClip"].getValue(), - "layout:activator:farClip", lambda node : node["farClip"].getValue(), - plugs = { "nearClip" : [ "description", """ - Remove everything with Z less than the near clip depth. - """, - - ], - "nearClipDepth" : [ - - "description", - """ - The depth for the near clip. + Removes everything with Z less than the near clip depth. """, - "label", "", - "layout:accessory", True, - "layout:activator", "nearClip", ], "farClip" : [ "description", """ - Remove everything with Z greater than or equal to the far clip depth. + Removes everything with Z greater than or equal to the far clip depth. """, ], - "farClipDepth" : [ - - "description", - """ - The depth for the far clip. - """, - "label", "", - "layout:accessory", True, - "layout:activator", "farClip", - ], "flatten" : [ "description", """ - Output a flat image, instead of output a deep image with any samples within the range. + Outputs a flat image, instead of output a deep image with any samples within the range. + Flattening as part of DeepSlice is up to 2X faster than flattening afterwards, and is + convenient if you're using a DeepSlice to preview the contents of a deep image by + scrubbing through depth. """, ], diff --git a/src/GafferImage/DeepSlice.cpp b/src/GafferImage/DeepSlice.cpp index adbd7c136f8..10b1cbd7992 100644 --- a/src/GafferImage/DeepSlice.cpp +++ b/src/GafferImage/DeepSlice.cpp @@ -94,7 +94,7 @@ float sampleMultiplier( float alpha, float fraction ) } } -const IECore::InternedString g_accumCountsName = "accumCounts"; +const IECore::InternedString g_sampleOffsetsName = "sampleOffsets"; const IECore::InternedString g_inputIndicesName = "inputIndices"; const IECore::InternedString g_firstWeightsName = "firstWeights"; const IECore::InternedString g_lastWeightsName = "lastWeights"; @@ -113,10 +113,8 @@ DeepSlice::DeepSlice( const std::string &name ) : ImageProcessor( name ) { storeIndexOfNextChild( g_firstPlugIndex ); - addChild( new BoolPlug( "nearClip", Plug::In, false ) ); - addChild( new FloatPlug( "nearClipDepth", Plug::In, 0.0f ) ); - addChild( new BoolPlug( "farClip", Plug::In, true ) ); - addChild( new FloatPlug( "farClipDepth", Plug::In, 1.0f ) ); + addChild( new OptionalValuePlug( "nearClip", new FloatPlug( "value", Gaffer::Plug::In, 0.0f, 0.0f ) ) ); + addChild( new OptionalValuePlug( "farClip", new FloatPlug( "value", Gaffer::Plug::In, 0.0f, 0.0f ) ) ); addChild( new BoolPlug( "flatten", Plug::In, true ) ); addChild( new ImagePlug( "__tidyIn", Plug::In, Plug::Default & ~Plug::Serialisable ) ); @@ -125,12 +123,6 @@ DeepSlice::DeepSlice( const std::string &name ) // See compute() for more description. addChild( new CompoundObjectPlug( "__sliceData", Gaffer::Plug::Out, new IECore::CompoundObject ) ); - // We don't ever want to change these, so we make pass-through connections. - outPlug()->viewNamesPlug()->setInput( inPlug()->viewNamesPlug() ); - outPlug()->formatPlug()->setInput( inPlug()->formatPlug() ); - outPlug()->metadataPlug()->setInput( inPlug()->metadataPlug() ); - outPlug()->channelNamesPlug()->setInput( inPlug()->channelNamesPlug() ); - // We tidy the input image before we process it, because this means we can just process each sample // in order ( and is quite cheap if the image is already tidy ). DeepStatePtr tidy = new DeepState( "__tidy" ); @@ -138,6 +130,7 @@ DeepSlice::DeepSlice( const std::string &name ) tidy->inPlug()->setInput( inPlug() ); tidyInPlug()->setInput( tidy->outPlug() ); + // We don't ever want to change these, so we make pass-through connections. outPlug()->viewNamesPlug()->setInput( inPlug()->viewNamesPlug() ); outPlug()->channelNamesPlug()->setInput( inPlug()->channelNamesPlug() ); outPlug()->dataWindowPlug()->setInput( inPlug()->dataWindowPlug() ); @@ -149,74 +142,54 @@ DeepSlice::~DeepSlice() { } -Gaffer::BoolPlug *DeepSlice::nearClipPlug() -{ - return getChild( g_firstPlugIndex ); -} - -const Gaffer::BoolPlug *DeepSlice::nearClipPlug() const +Gaffer::OptionalValuePlug *DeepSlice::nearClipPlug() { - return getChild( g_firstPlugIndex ); + return getChild( g_firstPlugIndex ); } -Gaffer::FloatPlug *DeepSlice::nearClipDepthPlug() +const Gaffer::OptionalValuePlug *DeepSlice::nearClipPlug() const { - return getChild( g_firstPlugIndex + 1 ); + return getChild( g_firstPlugIndex ); } -const Gaffer::FloatPlug *DeepSlice::nearClipDepthPlug() const +Gaffer::OptionalValuePlug *DeepSlice::farClipPlug() { - return getChild( g_firstPlugIndex + 1 ); + return getChild( g_firstPlugIndex + 1 ); } -Gaffer::BoolPlug *DeepSlice::farClipPlug() +const Gaffer::OptionalValuePlug *DeepSlice::farClipPlug() const { - return getChild( g_firstPlugIndex + 2 ); -} - -const Gaffer::BoolPlug *DeepSlice::farClipPlug() const -{ - return getChild( g_firstPlugIndex + 2 ); -} - -Gaffer::FloatPlug *DeepSlice::farClipDepthPlug() -{ - return getChild( g_firstPlugIndex + 3 ); -} - -const Gaffer::FloatPlug *DeepSlice::farClipDepthPlug() const -{ - return getChild( g_firstPlugIndex + 3 ); + return getChild( g_firstPlugIndex + 1 ); } Gaffer::BoolPlug *DeepSlice::flattenPlug() { - return getChild( g_firstPlugIndex + 4 ); + return getChild( g_firstPlugIndex + 2 ); } const Gaffer::BoolPlug *DeepSlice::flattenPlug() const { - return getChild( g_firstPlugIndex + 4 ); + return getChild( g_firstPlugIndex + 2 ); } GafferImage::ImagePlug *DeepSlice::tidyInPlug() { - return getChild( g_firstPlugIndex + 5 ); + return getChild( g_firstPlugIndex + 3 ); } const GafferImage::ImagePlug *DeepSlice::tidyInPlug() const { - return getChild( g_firstPlugIndex + 5 ); + return getChild( g_firstPlugIndex + 3 ); } CompoundObjectPlug *DeepSlice::sliceDataPlug() { - return getChild( g_firstPlugIndex + 6 ); + return getChild( g_firstPlugIndex + 4 ); } const CompoundObjectPlug *DeepSlice::sliceDataPlug() const { - return getChild( g_firstPlugIndex + 6 ); + return getChild( g_firstPlugIndex + 4 ); } void DeepSlice::affects( const Gaffer::Plug *input, AffectedPlugsContainer &outputs ) const @@ -232,11 +205,10 @@ void DeepSlice::affects( const Gaffer::Plug *input, AffectedPlugsContainer &outp } if( - input == nearClipPlug() || - input == nearClipDepthPlug() || - input == farClipPlug() || - input == farClipDepthPlug() || + nearClipPlug()->isAncestorOf( input ) || + farClipPlug()->isAncestorOf( input ) || input == inPlug()->deepPlug() || + input == inPlug()->channelNamesPlug() || input == tidyInPlug()->channelDataPlug() || input == tidyInPlug()->sampleOffsetsPlug() ) @@ -245,12 +217,11 @@ void DeepSlice::affects( const Gaffer::Plug *input, AffectedPlugsContainer &outp } if( - input == nearClipPlug() || - input == nearClipDepthPlug() || - input == farClipPlug() || - input == farClipDepthPlug() || + nearClipPlug()->isAncestorOf( input ) || + farClipPlug()->isAncestorOf( input ) || input == flattenPlug() || input == inPlug()->deepPlug() || + input == inPlug()->channelNamesPlug() || input == tidyInPlug()->channelDataPlug() || input == sliceDataPlug() ) @@ -259,6 +230,8 @@ void DeepSlice::affects( const Gaffer::Plug *input, AffectedPlugsContainer &outp } if( + nearClipPlug()->isAncestorOf( input ) || + farClipPlug()->isAncestorOf( input ) || input == sliceDataPlug() ) { @@ -281,22 +254,20 @@ void DeepSlice::hash( const Gaffer::ValuePlug *output, const Gaffer::Context *co ImagePlug::GlobalScope s( context ); inPlug()->deepPlug()->hash( h ); nearClipPlug()->hash( h ); - nearClipDepthPlug()->hash( h ); farClipPlug()->hash( h ); - farClipDepthPlug()->hash( h ); channelNamesData = inPlug()->channelNamesPlug()->getValue(); } const std::vector &channelNames = channelNamesData->readable(); - inPlug()->sampleOffsetsPlug()->hash( h ); + tidyInPlug()->sampleOffsetsPlug()->hash( h ); { ImagePlug::ChannelDataScope s( context ); if( ImageAlgo::channelExists( channelNames, ImageAlgo::channelNameA ) ) { s.setChannelName( &ImageAlgo::channelNameA ); - inPlug()->channelDataPlug()->hash( h ); + tidyInPlug()->channelDataPlug()->hash( h ); } else { @@ -306,7 +277,7 @@ void DeepSlice::hash( const Gaffer::ValuePlug *output, const Gaffer::Context *co if( ImageAlgo::channelExists( channelNames, ImageAlgo::channelNameZ ) ) { s.setChannelName( &ImageAlgo::channelNameZ ); - inPlug()->channelDataPlug()->hash( h ); + tidyInPlug()->channelDataPlug()->hash( h ); } else { @@ -316,7 +287,7 @@ void DeepSlice::hash( const Gaffer::ValuePlug *output, const Gaffer::Context *co if( ImageAlgo::channelExists( channelNames, ImageAlgo::channelNameZBack ) ) { s.setChannelName( &ImageAlgo::channelNameZBack ); - inPlug()->channelDataPlug()->hash( h ); + tidyInPlug()->channelDataPlug()->hash( h ); } else { @@ -336,14 +307,14 @@ void DeepSlice::compute( Gaffer::ValuePlug *output, const Gaffer::Context *conte // sliceData is a CompoundObject with up to 4 members, storing the following things // - // "accumCounts" : a running sum of the number of samples contributing to each pixel. - // When outputting a deep image, this will be the sampleOffsets of the output. - // When outputting a flat image, this is used to know which samples to sum. - // "inputIndices" : an int vector with the sample index where we start taking samples for each pixel - // "firstWeights" : a float for each pixel with a multiplier for the first sample for each pixel - // ( included when nearClip is on ) - // "lastWeights" : a float for each pixel with a multiplier for the last sample for each pixel - // ( included when farClip is on ) + // "sampleOffsets" : a running sum of the number of samples contributing to each pixel. + // When outputting a deep image, this will be the sampleOffsets of the output. + // When outputting a flat image, this is used to know which samples to sum. + // "inputIndices" : an int vector with the sample index where we start taking samples for each pixel + // "firstWeights" : a float for each pixel with a multiplier for the first sample for each pixel + // ( included when nearClip is on ) + // "lastWeights" : a float for each pixel with a multiplier for the last sample for each pixel + // ( included when farClip is on ) // // ( Note that any sample that is not first or last cannot intersect a clip plane, so we always take 100% ) @@ -360,10 +331,10 @@ void DeepSlice::compute( Gaffer::ValuePlug *output, const Gaffer::Context *conte { ImagePlug::GlobalScope s( context ); deep = inPlug()->deepPlug()->getValue(); - nearClip = nearClipPlug()->getValue(); - nearClipDepth = nearClipDepthPlug()->getValue(); - farClip = farClipPlug()->getValue(); - farClipDepth = farClipDepthPlug()->getValue(); + nearClip = nearClipPlug()->enabledPlug()->getValue(); + nearClipDepth = nearClipPlug()->valuePlug()->getValue(); + farClip = farClipPlug()->enabledPlug()->getValue(); + farClipDepth = farClipPlug()->valuePlug()->getValue(); channelNamesData = inPlug()->channelNamesPlug()->getValue(); } @@ -426,8 +397,8 @@ void DeepSlice::compute( Gaffer::ValuePlug *output, const Gaffer::Context *conte // Allocate outputs - IntVectorDataPtr accumCountsData; - int *accumCounts = nullptr; + IntVectorDataPtr outputSampleOffsetsData; + int *outputSampleOffsets = nullptr; IntVectorDataPtr inputIndicesData; int *inputIndices = nullptr; FloatVectorDataPtr firstWeightsData; @@ -435,9 +406,9 @@ void DeepSlice::compute( Gaffer::ValuePlug *output, const Gaffer::Context *conte FloatVectorDataPtr lastWeightsData; float *lastWeights = nullptr; - accumCountsData = new IntVectorData(); - accumCountsData->writable().resize( ImagePlug::tilePixels() ); - accumCounts = &accumCountsData->writable()[0]; + outputSampleOffsetsData = new IntVectorData(); + outputSampleOffsetsData->writable().resize( ImagePlug::tilePixels() ); + outputSampleOffsets = &outputSampleOffsetsData->writable()[0]; inputIndicesData = new IntVectorData(); inputIndicesData->writable().resize( ImagePlug::tilePixels() ); inputIndices = &inputIndicesData->writable()[0]; @@ -459,7 +430,7 @@ void DeepSlice::compute( Gaffer::ValuePlug *output, const Gaffer::Context *conte // Now we're ready to actually process all the samples int prevOffset = 0; - int accumCount = 0; + int outputSampleOffset = 0; for( int i = 0; i < ImagePlug::tilePixels(); i++ ) { @@ -516,8 +487,8 @@ void DeepSlice::compute( Gaffer::ValuePlug *output, const Gaffer::Context *conte } } - accumCount += end - start; - accumCounts[i] = accumCount; + outputSampleOffset += end - start; + outputSampleOffsets[i] = outputSampleOffset; inputIndices[i] = start; // Now set the weights for what fractions of the input samples to take. @@ -580,7 +551,7 @@ void DeepSlice::compute( Gaffer::ValuePlug *output, const Gaffer::Context *conte // Fill the result CompoundObject CompoundObjectPtr result = new CompoundObject; - result->members()[ g_accumCountsName ] = std::move( accumCountsData ); + result->members()[ g_sampleOffsetsName ] = std::move( outputSampleOffsetsData ); result->members()[ g_inputIndicesName ] = std::move( inputIndicesData ); if( firstWeightsData ) { @@ -601,15 +572,23 @@ void DeepSlice::hashChannelData( const GafferImage::ImagePlug *parent, const Gaf bool deep; bool flatten; + bool nearClip; + bool farClip; { ImagePlug::GlobalScope s( context ); flatten = flattenPlug()->getValue(); deep = inPlug()->deepPlug()->getValue(); + nearClip = nearClipPlug()->enabledPlug()->getValue(); + farClip = farClipPlug()->enabledPlug()->getValue(); nearClipPlug()->hash( h ); - nearClipDepthPlug()->hash( h ); farClipPlug()->hash( h ); - farClipDepthPlug()->hash( h ); + } + + if( !flatten && !nearClip && !farClip ) + { + h = tidyInPlug()->channelDataPlug()->hash(); + return; } h.append( deep ); @@ -661,10 +640,15 @@ IECore::ConstFloatVectorDataPtr DeepSlice::computeChannelData( const std::string ImagePlug::GlobalScope s( context ); deep = inPlug()->deepPlug()->getValue(); flatten = flattenPlug()->getValue(); - nearClip = nearClipPlug()->getValue(); - nearClipDepth = nearClipDepthPlug()->getValue(); - farClip = farClipPlug()->getValue(); - farClipDepth = farClipDepthPlug()->getValue(); + nearClip = nearClipPlug()->enabledPlug()->getValue(); + nearClipDepth = nearClipPlug()->valuePlug()->getValue(); + farClip = farClipPlug()->enabledPlug()->getValue(); + farClipDepth = farClipPlug()->valuePlug()->getValue(); + } + + if( !flatten && !nearClip && !farClip ) + { + return tidyInPlug()->channelDataPlug()->getValue(); } if( !deep ) @@ -701,14 +685,14 @@ IECore::ConstFloatVectorDataPtr DeepSlice::computeChannelData( const std::string } } - const int *accumCounts = nullptr; + const int *sliceDataSampleOffsets = nullptr; const int *inputIndices = nullptr; const float *firstWeights = nullptr; const float *lastWeights = nullptr; - if( const IntVectorData *accumCountsData = sliceData->member( g_accumCountsName ) ) + if( const IntVectorData *sliceDataSampleOffsetsData = sliceData->member( g_sampleOffsetsName ) ) { - accumCounts = &accumCountsData->readable()[0]; + sliceDataSampleOffsets = &sliceDataSampleOffsetsData->readable()[0]; } if( const IntVectorData *inputIndicesData = sliceData->member( g_inputIndicesName ) ) { @@ -731,7 +715,7 @@ IECore::ConstFloatVectorDataPtr DeepSlice::computeChannelData( const std::string } else { - result.reserve( accumCounts[ ImagePlug::tilePixels() - 1 ] ); + result.reserve( sliceDataSampleOffsets[ ImagePlug::tilePixels() - 1 ] ); } if( channelName == ImageAlgo::channelNameZ ) @@ -744,8 +728,8 @@ IECore::ConstFloatVectorDataPtr DeepSlice::computeChannelData( const std::string int count = 1; if( deep ) { - count = accumCounts[i] - prevAccumCount; - prevAccumCount = accumCounts[i]; + count = sliceDataSampleOffsets[i] - prevAccumCount; + prevAccumCount = sliceDataSampleOffsets[i]; if( count == 0 ) { @@ -790,8 +774,8 @@ IECore::ConstFloatVectorDataPtr DeepSlice::computeChannelData( const std::string if( deep ) { - count = accumCounts[i] - prevAccumCount; - prevAccumCount = accumCounts[i]; + count = sliceDataSampleOffsets[i] - prevAccumCount; + prevAccumCount = sliceDataSampleOffsets[i]; if( count == 0 ) { @@ -834,8 +818,11 @@ IECore::ConstFloatVectorDataPtr DeepSlice::computeChannelData( const std::string int inputIndex = inputIndices[i]; int curIndex = inputIndex; - int count = accumCounts[i] - prevAccumCount; - prevAccumCount = accumCounts[i]; + // When flattening, the slice data sample offsets are not used as our actual sample offsets + // ( which are just flat ), but we still use these sample offsets to find which samples + // to accumulate. + int count = sliceDataSampleOffsets[i] - prevAccumCount; + prevAccumCount = sliceDataSampleOffsets[i]; if( count == 0 ) { @@ -891,8 +878,11 @@ IECore::ConstFloatVectorDataPtr DeepSlice::computeChannelData( const std::string int inputIndex = inputIndices[i]; int curIndex = inputIndex; - int count = accumCounts[i] - prevAccumCount; - prevAccumCount = accumCounts[i]; + // When flattening, the slice data sample offsets are not used as our actual sample offsets + // ( which are just flat ), but we still use these sample offsets to find which samples + // to accumulate. + int count = sliceDataSampleOffsets[i] - prevAccumCount; + prevAccumCount = sliceDataSampleOffsets[i]; if( count == 0 ) { @@ -957,8 +947,8 @@ IECore::ConstFloatVectorDataPtr DeepSlice::computeChannelData( const std::string int curIndex = inputIndex; if( deep ) { - count = accumCounts[i] - prevAccumCount; - prevAccumCount = accumCounts[i]; + count = sliceDataSampleOffsets[i] - prevAccumCount; + prevAccumCount = sliceDataSampleOffsets[i]; if( count == 0 ) { continue; @@ -999,13 +989,28 @@ void DeepSlice::hashSampleOffsets( const GafferImage::ImagePlug *parent, const G { ImageProcessor::hashSampleOffsets( parent, context, h ); + bool passThrough = false; + { ImagePlug::GlobalScope s( context ); - if( flattenPlug()->getValue() || !inPlug()->deepPlug()->getValue() ) + bool flatten = flattenPlug()->getValue(); + if( flatten || !inPlug()->deepPlug()->getValue() ) { h = ImagePlug::flatTileSampleOffsets()->Object::hash(); return; } + bool nearClip = nearClipPlug()->enabledPlug()->getValue(); + bool farClip = farClipPlug()->enabledPlug()->getValue(); + if( !flatten && !nearClip && !farClip ) + { + passThrough = true; + } + } + + if( passThrough ) + { + h = tidyInPlug()->sampleOffsetsPlug()->hash(); + return; } sliceDataPlug()->hash( h ); @@ -1014,19 +1019,31 @@ void DeepSlice::hashSampleOffsets( const GafferImage::ImagePlug *parent, const G IECore::ConstIntVectorDataPtr DeepSlice::computeSampleOffsets( const Imath::V2i &tileOrigin, const Gaffer::Context *context, const ImagePlug *parent ) const { + bool passThrough = false; + { ImagePlug::GlobalScope s( context ); - if( flattenPlug()->getValue() || !inPlug()->deepPlug()->getValue() ) + bool flatten = flattenPlug()->getValue(); + if( flatten || !inPlug()->deepPlug()->getValue() ) { return ImagePlug::flatTileSampleOffsets(); } + bool nearClip = nearClipPlug()->enabledPlug()->getValue(); + bool farClip = farClipPlug()->enabledPlug()->getValue(); + if( !flatten && !nearClip && !farClip ) + { + passThrough = true; + } } - // The accumulated counts in the sliceData are the same thing as the sampleOffsets. The only reason the - // name is different is because it doesn't really make sense to call them "sampleOffsets" when the output - // is flat ( in that case, we use the same data for a different purpose ). + if( passThrough ) + { + return tidyInPlug()->sampleOffsetsPlug()->getValue(); + } + + // Just output the sample offsets computed in the the sliceData ConstCompoundObjectPtr sliceData = sliceDataPlug()->getValue(); - return sliceData->member( g_accumCountsName ); + return sliceData->member( g_sampleOffsetsName ); } void DeepSlice::hashDeep( const GafferImage::ImagePlug *parent, const Gaffer::Context *context, IECore::MurmurHash &h ) const @@ -1044,4 +1061,3 @@ bool DeepSlice::computeDeep( const Gaffer::Context *context, const ImagePlug *pa } return deep; } -