Skip to content

Commit

Permalink
Sampler : Add visitPixels
Browse files Browse the repository at this point in the history
  • Loading branch information
danieldresser-ie committed Jan 23, 2023
1 parent 67000ea commit 8efe839
Show file tree
Hide file tree
Showing 2 changed files with 104 additions and 0 deletions.
7 changes: 7 additions & 0 deletions include/GafferImage/Sampler.h
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,13 @@ class GAFFERIMAGE_API Sampler
/// 0.5, 0.5.
float sample( float x, float y );

/// Call a functor for all pixels in the region.
/// The signature of the functor must be `F( float value, int x, int y )`.
/// The order is not necessarily scanline order, but it is guaranteed to be
/// deterministic and visit every pixel exactly once.
template<typename F>
void visitPixels( Imath::Box2i region, F &&lambda );

/// Appends a hash that represent all the pixel
/// values within the requested sample area.
void hash( IECore::MurmurHash &h ) const;
Expand Down
97 changes: 97 additions & 0 deletions include/GafferImage/Sampler.inl
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,103 @@ inline float Sampler::sample( float x, float y )
return OIIO::bilerp( x0y0, x1y0, x0y1, x1y1, xf, yf );
}

template<typename F>
void Sampler::visitPixels( Imath::Box2i region, F &&visitor )
{
if( !BufferAlgo::contains( m_dataWindow, region ) )
{
if( m_boundingMode == Black )
{
for( int y = region.min.y; y < region.max.y; y++ )
{
if( y < m_dataWindow.min.y || y >= m_dataWindow.max.y )
{
for( int x = region.min.x; x < region.max.x; x++ )
{
visitor( 0.0f, x, y );
}
}
else
{
for( int x = region.min.x; x < m_dataWindow.min.x; x++ )
{
visitor( 0.0f, x, y );
}

for( int x = m_dataWindow.max.x + 1; x < region.max.x; x++ )
{
visitor( 0.0f, x, y );
}
}
}
}
else
{
// If we wanted to optimize this case, we could break the area within
// `region` but outside `m_dataWindow` into 8 octants, based on where we reuse
// the same border values for multiple outside pixels. Would be a potentially
// huge win for large regions with small data windows, but that's a rare case,
// and it would take about 90 lines of fairly fiddly code, it's simpler to do
// something slow instead.
for( int y = region.min.y; y < region.max.y; y++ )
{
if( y < m_dataWindow.min.y || y >= m_dataWindow.max.y )
{
for( int x = region.min.x; x < region.max.x; x++ )
{
visitor( sample( x, y ), x, y );
}
}
else
{
for( int x = region.min.x; x < m_dataWindow.min.x; x++ )
{
visitor( sample( x, y ), x, y );
}

for( int x = m_dataWindow.max.x + 1; x < region.max.x; x++ )
{
visitor( sample( x, y ), x, y );
}
}
}
}

region = BufferAlgo::intersection( m_dataWindow, region );
}

Imath::V2i minTile = ImagePlug::tileIndex( region.min );
Imath::V2i maxTile = ImagePlug::tileIndex( region.max - Imath::V2i( 1 ) );
for( int tx = minTile.x; tx <= maxTile.x; tx++ )
{
for( int ty = minTile.y; ty <= maxTile.y; ty++ )
{
Imath::V2i tileOrigin = Imath::V2i( tx * ImagePlug::tileSize(), ty * ImagePlug::tileSize() );

const float *tileData;
Imath::V2i tileIndex;
cachedData( tileOrigin, tileData, tileIndex );
for(
int y = std::max( tileOrigin.y, region.min.y );
y < std::min( tileOrigin.y + ImagePlug::tileSize(), region.max.y );
y++
)
{
for(
int x = std::max( tileOrigin.x, region.min.x );
x < std::min( tileOrigin.x + ImagePlug::tileSize(), region.max.x );
x++
)
{
visitor( *(tileData + ( y - tileOrigin.y ) * ImagePlug::tileSize() + x - tileOrigin.x), x, y );
}
}
}
}
}



inline void Sampler::cachedData( Imath::V2i p, const float *& tileData, Imath::V2i &tilePixelIndex )
{
// Get the smart pointer to the tile we want.
Expand Down

0 comments on commit 8efe839

Please sign in to comment.