Skip to content
Snippets Groups Projects
Commit 266160b8 authored by Tobias Pietzsch's avatar Tobias Pietzsch
Browse files

WIP ScreenScales class manages resizing, interval requests, and scale suggestion

parent d5dadbf1
No related branches found
No related tags found
No related merge requests found
......@@ -31,9 +31,8 @@ package bdv.viewer.render;
import bdv.cache.CacheControl;
import bdv.util.MovingAverage;
import bdv.viewer.ViewerState;
import bdv.viewer.render.ScreenScales.ScreenScale;
import java.util.concurrent.ExecutorService;
import net.imglib2.Dimensions;
import net.imglib2.FinalDimensions;
import net.imglib2.Interval;
import net.imglib2.RandomAccessibleInterval;
import net.imglib2.Volatile;
......@@ -44,7 +43,6 @@ import net.imglib2.ui.PainterThread;
import net.imglib2.ui.RenderTarget;
import net.imglib2.util.Intervals;
/**
* A renderer that uses a coarse-to-fine rendering scheme. First, a small target
* image at a fraction of the canvas resolution is rendered. Then, increasingly
......@@ -115,84 +113,9 @@ public class MultiResolutionRenderer
private final long[] iobudget = new long[] { 100l * 1000000l, 10l * 1000000l };
/**
* Scale factors from the {@link #display viewer canvas} to the
* {@code screenImages}.
*
* A scale factor of 1 means 1 pixel in the screen image is displayed as 1
* pixel on the canvas, a scale factor of 0.5 means 1 pixel in the screen
* image is displayed as 2 pixel on the canvas, etc.
*/
private final double[] screenScaleFactors;
static class ScreenScale
{
/**
* The width of the target image at this ScreenScale.
*/
final int w;
/**
* The height of the target image at this ScreenScale.
* TODO javadoc
*/
final int h;
/**
* The transformation from viewer to target image coordinates at this ScreenScale.
*/
final AffineTransform3D scale;
private final double screenToViewerScale;
public ScreenScale( final int screenW, final int screenH, final double screenToViewerScale )
{
this.screenToViewerScale = screenToViewerScale;
w = ( int ) Math.ceil( screenToViewerScale * screenW );
h = ( int ) Math.ceil( screenToViewerScale * screenH );
scale = new AffineTransform3D();
scale.set( screenToViewerScale, 0, 0 );
scale.set( screenToViewerScale, 1, 1 );
scale.set( 0.5 * screenToViewerScale - 0.5, 0, 3 );
scale.set( 0.5 * screenToViewerScale - 0.5, 1, 3 );
}
Interval requestedScreenInterval = null;
public void requestInterval( final Interval screenInterval )
{
requestedScreenInterval = requestedScreenInterval == null ? screenInterval : Intervals.union( requestedScreenInterval, screenInterval );
}
public Interval pullScreenInterval()
{
final Interval interval = requestedScreenInterval;
requestedScreenInterval = null;
return interval;
}
public double estimateIntervalRenderNanos( final double renderNanosPerPixel )
{
return renderNanosPerPixel * Intervals.numElements( scaleScreenInterval( requestedScreenInterval ) );
}
public Interval scaleScreenInterval( final Interval requestedScreenInterval )
{
// This is equivalent to
// Intervals.intersect( new FinalInterval( w, h ), Intervals.smallestContainingInterval( Intervals.scale( requestedScreenInterval, screenToViewerScale ) ) );
return Intervals.createMinMax(
Math.max( 0, ( int ) Math.floor( requestedScreenInterval.min( 0 ) * screenToViewerScale ) ),
Math.max( 0, ( int ) Math.floor( requestedScreenInterval.min( 1 ) * screenToViewerScale ) ),
Math.min( w - 1, ( int ) Math.ceil( requestedScreenInterval.max( 0 ) * screenToViewerScale ) ),
Math.min( h - 1, ( int ) Math.ceil( requestedScreenInterval.max( 1 ) * screenToViewerScale ) )
);
}
}
private final ScreenScale[] screenScales;
private int screenW;
private int screenH;
private final ScreenScales screenScales;
/**
* Maintains arrays for intermediate per-source render images and masks.
......@@ -321,15 +244,14 @@ public class MultiResolutionRenderer
this.painterThread = painterThread;
projector = null;
currentScreenScaleIndex = -1;
this.screenScaleFactors = screenScaleFactors.clone();
this.screenScales = new ScreenScale[ screenScaleFactors.length ];
screenScales = new ScreenScales( screenScaleFactors );
renderStorage = new RenderStorage();
this.targetRenderNanos = targetRenderNanos;
renderNanosPerPixelAndSource = new MovingAverage( 3 );
renderNanosPerPixelAndSource.init( 500 );
requestedScreenScaleIndex = screenScales.length - 1;
requestedScreenScaleIndex = screenScales.size() - 1;
renderingMayBeCancelled = false;
this.cacheControl = cacheControl;
newFrameRequest = false;
......@@ -343,26 +265,6 @@ public class MultiResolutionRenderer
accumulateProjectorFactory );
}
/**
* Check whether the size of the display component was changed and
* recreate {@link #screenScales} accordingly.
*
* @return whether the size was changed.
*/
private boolean checkResize()
{
final int newScreenW = display.getWidth();
final int newScreenH = display.getHeight();
if ( newScreenW != screenW || newScreenH != screenH )
{
screenW = newScreenW;
screenH = newScreenH;
for ( int i = 0; i < screenScales.length; ++i )
screenScales[ i ] = new ScreenScale( screenW, screenH, screenScaleFactors[ i ] );
return true;
}
return false;
}
/**
* Render image at the {@link #requestedScreenScaleIndex requested screen
......@@ -370,10 +272,12 @@ public class MultiResolutionRenderer
*/
public boolean paint( final ViewerState viewerState )
{
if ( display.getWidth() <= 0 || display.getHeight() <= 0 )
int screenW = display.getWidth();
int screenH = display.getHeight();
if ( screenW <= 0 || screenH <= 0 )
return false;
final boolean resized = checkResize();
final boolean resized = screenScales.checkResize( screenW, screenH );
final boolean newFrame;
final boolean newInterval;
......@@ -405,7 +309,8 @@ public class MultiResolutionRenderer
cacheControl.prepareNextFrame();
currentViewerState = viewerState.snapshot();
currentNumVisibleSources = currentViewerState.getVisibleAndPresentSources().size();
requestedScreenScaleIndex = suggestScreenScale( new FinalDimensions( screenW, screenH ), currentNumVisibleSources );
final double renderNanosPerPixel = renderNanosPerPixelAndSource.getAverage() * currentNumVisibleSources;
requestedScreenScaleIndex = screenScales.suggestScreenScale( renderNanosPerPixel, targetRenderNanos );
}
final boolean createProjector = newFrame || ( requestedScreenScaleIndex != currentScreenScaleIndex );
......@@ -423,13 +328,13 @@ public class MultiResolutionRenderer
{
if ( createProjector )
{
final ScreenScale screenScale = screenScales[ requestedScreenScaleIndex ];
final ScreenScale screenScale = screenScales.get( requestedScreenScaleIndex );
renderResult = display.getReusableRenderResult();
renderResult.init( screenScale.w, screenScale.h );
renderResult.setScaleFactor( screenScaleFactors[ requestedScreenScaleIndex ] );
renderResult.init( screenScale.width(), screenScale.height() );
renderResult.setScaleFactor( screenScale.scale() );
renderStorage.checkRenewData( screenScales[ 0 ].w, screenScales[ 0 ].h, currentNumVisibleSources );
renderStorage.checkRenewData( screenScales.get( 0 ).width(), screenScales.get( 0 ).height(), currentNumVisibleSources );
projector = createProjector( currentViewerState, requestedScreenScaleIndex, renderResult.getScreenImage(), 0, 0 );
requestNewFrameIfIncomplete = projectorFactory.requestNewFrameIfIncomplete();
renderingMayBeCancelled = !newFrame;
......@@ -514,9 +419,7 @@ public class MultiResolutionRenderer
{
if ( renderingMayBeCancelled && projector != null )
projector.cancel();
for ( ScreenScale screenScale : screenScales )
if ( screenScale != null )
screenScale.pullScreenInterval();
screenScales.clearRequestedIntervals();
intervalMode = false;
newFrameRequest = true;
painterThread.requestRepaint();
......@@ -528,8 +431,7 @@ public class MultiResolutionRenderer
{
if ( projector != null )
projector.cancel();
for ( final ScreenScale screenScale : screenScales )
screenScale.requestInterval( screenInterval );
screenScales.requestInterval( screenInterval );
intervalMode = true;
newIntervalRequest = true;
painterThread.requestRepaint();
......@@ -538,20 +440,6 @@ public class MultiResolutionRenderer
requestRepaint();
}
private int suggestScreenScale( final Dimensions screenSize, final int numSources )
{
final double intervalRenderNanos = renderNanosPerPixelAndSource.getAverage() * Intervals.numElements( screenSize ) * numSources;
for ( int i = 0; i < screenScaleFactors.length - 1; i++ )
{
final double s = screenScaleFactors[ i ];
final double renderTime = intervalRenderNanos * s * s;
if ( renderTime <= targetRenderNanos )
return i;
}
return screenScaleFactors.length - 1;
}
/**
* DON'T USE THIS.
* <p>
......@@ -579,7 +467,7 @@ public class MultiResolutionRenderer
*/
// CacheIoTiming.getIoTimeBudget().clear(); // clear time budget such that prefetching doesn't wait for loading blocks.
final AffineTransform3D screenScaleTransform = screenScales[ screenScaleIndex ].scale;
final AffineTransform3D screenScaleTransform = screenScales.get( screenScaleIndex ).scaleTransform();
final AffineTransform3D screenTransform = viewerState.getViewerTransform();
screenTransform.preConcatenate( screenScaleTransform );
screenTransform.translate( -offsetX, -offsetY, 0 );
......@@ -593,12 +481,7 @@ public class MultiResolutionRenderer
return projector;
}
// =========== intervals =============
// =========== intervals ==========================================================================================
private RenderResult currentRenderResult;
......@@ -636,20 +519,12 @@ public class MultiResolutionRenderer
if ( newInterval )
{
cacheControl.prepareNextFrame();
final double renderNanosPerPixel = currentNumVisibleSources * renderNanosPerPixelAndSource.getAverage();
for ( int i = currentScreenScaleIndex; i < screenScales.length; i++ )
{
final double renderTime = screenScales[ i ].estimateIntervalRenderNanos( renderNanosPerPixel );
if ( renderTime <= targetRenderNanos )
{
requestedIntervalScaleIndex = i;
break;
}
}
final double renderNanosPerPixel = renderNanosPerPixelAndSource.getAverage() * currentNumVisibleSources;
requestedIntervalScaleIndex = screenScales.suggestIntervalScreenScale( renderNanosPerPixel, targetRenderNanos, currentScreenScaleIndex );
}
createProjector = newInterval || ( requestedIntervalScaleIndex != currentIntervalScaleIndex );
screenScale = screenScales[ requestedIntervalScaleIndex ];
screenScale = screenScales.get( requestedIntervalScaleIndex );
requestedScreenInterval = screenScale.pullScreenInterval();
if ( createProjector )
......@@ -658,14 +533,14 @@ public class MultiResolutionRenderer
intervalResult.init(
( int ) interval.dimension( 0 ),
( int ) interval.dimension( 1 ) );
final double intervalScale = screenScale.screenToViewerScale;
final double intervalScale = screenScale.scale();
intervalResult.setScaleFactor( intervalScale );
final double offsetX = interval.min( 0 );
final double offsetY = interval.min( 1 );
projector = createProjector( currentViewerState, requestedIntervalScaleIndex, intervalResult.getScreenImage(), offsetX, offsetY );
final Interval targetInterval = screenScales[ currentScreenScaleIndex ].scaleScreenInterval( requestedScreenInterval );
final double relativeScale = screenScaleFactors[ currentScreenScaleIndex ] / intervalScale;
final Interval targetInterval = screenScales.get( currentScreenScaleIndex ).scaleScreenInterval( requestedScreenInterval );
final double relativeScale = screenScales.get( currentScreenScaleIndex ).scale() / intervalScale;
final double tx = interval.min( 0 ) * relativeScale;
final double ty = interval.min( 1 ) * relativeScale;
intervalRenderData = new IntervalRenderData( targetInterval, tx, ty );
......@@ -679,6 +554,7 @@ public class MultiResolutionRenderer
synchronized ( this )
{
// if rendering was not cancelled...
if ( success )
{
if ( createProjector )
......@@ -729,19 +605,4 @@ public class MultiResolutionRenderer
requestedIntervalScaleIndex = intervalScaleIndex;
painterThread.requestRepaint();
}
private Interval scaleScreenInterval( final Interval requestedScreenInterval, final int intervalScaleIndex )
{
final int clipW = screenScales[ intervalScaleIndex ].w;
final int clipH = screenScales[ intervalScaleIndex ].h;
final double scale = screenScaleFactors[ intervalScaleIndex ];
// This is equivalent to
// Intervals.intersect( new FinalInterval( clipW, clipH ), Intervals.smallestContainingInterval( Intervals.scale( requestedScreenInterval, scale ) ) );
return Intervals.createMinMax(
Math.max( 0, ( int ) Math.floor( requestedScreenInterval.min( 0 ) * scale ) ),
Math.max( 0, ( int ) Math.floor( requestedScreenInterval.min( 1 ) * scale ) ),
Math.min( clipW - 1, ( int ) Math.ceil( requestedScreenInterval.max( 0 ) * scale ) ),
Math.min( clipH - 1, ( int ) Math.ceil( requestedScreenInterval.max( 1 ) * scale ) )
);
}
}
package bdv.viewer.render;
import java.util.ArrayList;
import java.util.List;
import net.imglib2.Interval;
import net.imglib2.realtransform.AffineTransform3D;
import net.imglib2.util.Intervals;
public class ScreenScales
{
private final List< ScreenScale > screenScales;
private int screenW = 0;
private int screenH = 0;
/**
* @param screenScaleFactors
* Scale factors from the viewer canvas to screen images of
* different resolutions. A scale factor of 1 means 1 pixel in
* the screen image is displayed as 1 pixel on the canvas, a
* scale factor of 0.5 means 1 pixel in the screen image is
* displayed as 2 pixel on the canvas, etc.
*/
public ScreenScales( final double[] screenScaleFactors )
{
screenScales = new ArrayList<>();
for ( double scale : screenScaleFactors )
screenScales.add( new ScreenScale( scale ) );
}
/**
* Check whether the screen size was changed and revise {@link #screenScales} accordingly.
*
* @return whether the size was changed.
*/
public boolean checkResize( final int newScreenW, final int newScreenH )
{
if ( newScreenW != screenW || newScreenH != screenH )
{
screenW = newScreenW;
screenH = newScreenH;
screenScales.forEach( s -> s.resize( screenW, screenH ) );
return true;
}
return false;
}
public ScreenScale get( final int index )
{
return screenScales.get( index );
}
public int size()
{
return screenScales.size();
}
public int suggestScreenScale( final double renderNanosPerPixel, final double targetRenderNanos )
{
for ( int i = 0; i < screenScales.size() - 1; i++ )
{
final double renderTime = screenScales.get( i ).estimateRenderNanos( renderNanosPerPixel );
if ( renderTime <= targetRenderNanos )
return i;
}
return screenScales.size() - 1;
}
public int suggestIntervalScreenScale( final double renderNanosPerPixel, final double targetRenderNanos, final int minScreenScaleIndex )
{
for ( int i = minScreenScaleIndex; i < screenScales.size() - 1; i++ )
{
final double renderTime = screenScales.get( i ).estimateIntervalRenderNanos( renderNanosPerPixel );
if ( renderTime <= targetRenderNanos )
return i;
}
return screenScales.size() - 1;
}
public void requestInterval( final Interval screenInterval )
{
screenScales.forEach( s -> s.requestInterval( screenInterval ) );
}
public void clearRequestedIntervals()
{
screenScales.forEach( ScreenScale::pullScreenInterval );
}
public static class ScreenScale
{
/**
* Scale factor from the viewer coordinates to target image of this screen scale.
*/
private final double scale;
/**
* The width of the target image at this ScreenScale.
*/
private int w = 0;
/**
* The height of the target image at this ScreenScale.
*/
private int h = 0;
/**
* The transformation from viewer to target image coordinates at this ScreenScale.
*/
private final AffineTransform3D scaleTransform = new AffineTransform3D();
/**
* Pending interval request.
* This is in viewer coordinates.
* To transform to target coordinates of this scale, use {@link #scaleScreenInterval}.
*/
private Interval requestedScreenInterval = null;
/**
* @param scale
* Scale factor from the viewer coordinates to target image of this screen scale/
*/
ScreenScale( final double scale )
{
this.scale = scale;
}
void resize( final int screenW, final int screenH )
{
w = ( int ) Math.ceil( scale * screenW );
h = ( int ) Math.ceil( scale * screenH );
scaleTransform.set( scale, 0, 0 );
scaleTransform.set( scale, 1, 1 );
scaleTransform.set( 0.5 * scale - 0.5, 0, 3 );
scaleTransform.set( 0.5 * scale - 0.5, 1, 3 );
requestedScreenInterval = null;
}
double estimateRenderNanos( final double renderNanosPerPixel )
{
return renderNanosPerPixel * w * h;
}
public void requestInterval( final Interval screenInterval )
{
requestedScreenInterval = requestedScreenInterval == null
? screenInterval
: Intervals.union( requestedScreenInterval, screenInterval );
}
public Interval pullScreenInterval()
{
final Interval interval = requestedScreenInterval;
requestedScreenInterval = null;
return interval;
}
public double estimateIntervalRenderNanos( final double renderNanosPerPixel )
{
return renderNanosPerPixel * Intervals.numElements( scaleScreenInterval( requestedScreenInterval ) );
}
public Interval scaleScreenInterval( final Interval requestedScreenInterval )
{
// This is equivalent to
// Intervals.intersect( new FinalInterval( w, h ), Intervals.smallestContainingInterval( Intervals.scale( requestedScreenInterval, screenToViewerScale ) ) );
return Intervals.createMinMax(
Math.max( 0, ( int ) Math.floor( requestedScreenInterval.min( 0 ) * scale ) ),
Math.max( 0, ( int ) Math.floor( requestedScreenInterval.min( 1 ) * scale ) ),
Math.min( w - 1, ( int ) Math.ceil( requestedScreenInterval.max( 0 ) * scale ) ),
Math.min( h - 1, ( int ) Math.ceil( requestedScreenInterval.max( 1 ) * scale ) )
);
}
public int width()
{
return w;
}
public int height()
{
return h;
}
public double scale()
{
return scale;
}
public AffineTransform3D scaleTransform()
{
return scaleTransform;
}
}
}
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment