From 8441d1b7306dca8e5662367076684a0f6515bb0c Mon Sep 17 00:00:00 2001 From: Matthias Arzt Date: Fri, 26 Jul 2019 10:44:00 +0200 Subject: [PATCH 1/5] Simplify MultiResolutionRendererGeneric by having an interface for the output image --- .../render/MultiResolutionRendererFX.java | 58 +++++++++++++---- .../MultiResolutionRendererGeneric.java | 64 ++++++------------- .../fx/viewer/render/RenderOutputImage.java | 25 ++++++++ 3 files changed, 90 insertions(+), 57 deletions(-) create mode 100644 src/main/java/bdv/fx/viewer/render/RenderOutputImage.java diff --git a/src/main/java/bdv/fx/viewer/render/MultiResolutionRendererFX.java b/src/main/java/bdv/fx/viewer/render/MultiResolutionRendererFX.java index 590b555d2..7e4f4e0f2 100644 --- a/src/main/java/bdv/fx/viewer/render/MultiResolutionRendererFX.java +++ b/src/main/java/bdv/fx/viewer/render/MultiResolutionRendererFX.java @@ -36,9 +36,12 @@ import net.imglib2.Interval; import net.imglib2.Volatile; import net.imglib2.img.array.ArrayImg; +import net.imglib2.img.basictypeaccess.IntAccess; +import net.imglib2.realtransform.AffineTransform3D; import net.imglib2.type.numeric.ARGBType; import net.imglib2.ui.RenderTarget; import net.imglib2.ui.Renderer; +import net.imglib2.ui.TransformListener; /** * A {@link Renderer} that uses a coarse-to-fine rendering scheme. First, a small {@link ArrayImg} at a fraction of @@ -88,15 +91,15 @@ public class MultiResolutionRendererFX extends MultiResolutionRendererGeneric + implements RenderOutputImage.Factory { @Override - public BufferExposingWritableImage create(final int width, final int height) + public RenderOutputImage create(final int width, final int height) { try { - return new BufferExposingWritableImage(width, height); + return new MyRenderOutputImage(new BufferExposingWritableImage(width, height)); } catch (final Exception e) { throw e instanceof RuntimeException ? (RuntimeException) e : new RuntimeException(e); @@ -104,13 +107,48 @@ public BufferExposingWritableImage create(final int width, final int height) } @Override - public BufferExposingWritableImage create(final int width, final int height, final BufferExposingWritableImage - other) - { - // TODO can we somehow re-use smaller image? + public RenderOutputImage create(int width, int height, RenderOutputImage other) { return create(width, height); } + @Override + public RenderOutputImage wrap(BufferExposingWritableImage image) { + return new MyRenderOutputImage(image); + } + + @Override + public BufferExposingWritableImage unwrap(RenderOutputImage image) { + return ((MyRenderOutputImage) image).image; + } + + } + + public static class MyRenderOutputImage implements RenderOutputImage { + + private final BufferExposingWritableImage image; + + public MyRenderOutputImage(BufferExposingWritableImage image) { + this.image = image; + } + + @Override + public int width() { + return (int) image.getWidth(); + } + + @Override + public int height() { + return (int) image.getHeight(); + } + + @Override + public ArrayImg asArrayImg() { + return image.asArrayImg(); + } + + public BufferExposingWritableImage image() { + return image(); + } } public MultiResolutionRendererFX( @@ -136,11 +174,7 @@ public MultiResolutionRendererFX( useVolatileIfAvailable, accumulateProjectorFactory, cacheControl, - BufferExposingWritableImage::asArrayImg, - new MakeWritableImage(), - img -> (int) img.getWidth(), - img -> (int) img.getHeight() + new MakeWritableImage() ); } - } diff --git a/src/main/java/bdv/fx/viewer/render/MultiResolutionRendererGeneric.java b/src/main/java/bdv/fx/viewer/render/MultiResolutionRendererGeneric.java index 033b27bda..d74867971 100644 --- a/src/main/java/bdv/fx/viewer/render/MultiResolutionRendererGeneric.java +++ b/src/main/java/bdv/fx/viewer/render/MultiResolutionRendererGeneric.java @@ -61,7 +61,6 @@ import net.imglib2.converter.Converter; import net.imglib2.img.array.ArrayImg; import net.imglib2.img.array.ArrayImgs; -import net.imglib2.img.basictypeaccess.IntAccess; import net.imglib2.img.basictypeaccess.array.IntArray; import net.imglib2.realtransform.AffineTransform3D; import net.imglib2.realtransform.RealViews; @@ -85,7 +84,6 @@ import java.util.Optional; import java.util.concurrent.ExecutorService; import java.util.function.Function; -import java.util.function.ToIntFunction; /** * @@ -98,15 +96,6 @@ public class MultiResolutionRendererGeneric private static final Logger LOG = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass()); - public interface ImageGenerator - { - - T create(int width, int height); - - T create(int width, int height, T other); - - } - /** * Receiver for the data store that we render. */ @@ -140,7 +129,7 @@ public interface ImageGenerator /** * Maps from data store to double-buffer index. Needed for double-buffering. */ - private final HashMap bufferedImageToRenderId; + private final HashMap bufferedImageToRenderId; /** * Used to render an individual source. One image per screen resolution and visible source. First index is screen @@ -158,13 +147,13 @@ public interface ImageGenerator * Used to render the image for display. Three images per screen resolution if double buffering is enabled. First * index is screen scale, second index is double-buffer. */ - private List> screenImages; + private List> screenImages; /** * data store wrapping the data in the {@link #screenImages}. First index is screen scale, second index is * double-buffer. */ - private List> bufferedImages; + private List> bufferedImages; /** * Scale factors from the {@link #display viewer canvas} to the {@link #screenImages}. @@ -262,13 +251,7 @@ public interface ImageGenerator private boolean prefetchCells = true; - private final Function> wrapAsArrayImg; - - private final ToIntFunction width; - - private final ToIntFunction height; - - private final ImageGenerator makeImage; + private final RenderOutputImage.Factory makeImage; private final AffineTransform3D currentProjectorTransform = new AffineTransform3D(); @@ -310,10 +293,7 @@ public interface ImageGenerator final boolean useVolatileIfAvailable, final AccumulateProjectorFactory accumulateProjectorFactory, final CacheControl cacheControl, - final Function> wrapAsArrayImg, - final ImageGenerator makeImage, - final ToIntFunction width, - final ToIntFunction height) + final RenderOutputImage.Factory makeImage) { this.display = display; this.painterThread = painterThread; @@ -327,12 +307,6 @@ public interface ImageGenerator this.makeImage = makeImage; - this.width = width; - - this.height = height; - - this.wrapAsArrayImg = wrapAsArrayImg; - this.targetRenderNanos = targetRenderNanos; renderingMayBeCancelled = true; @@ -356,8 +330,8 @@ private synchronized boolean checkResize() final int componentW = display.getWidth(); final int componentH = display.getHeight(); if (screenImages.get(0).get(0) == null - || width .applyAsInt(screenImages.get(0).get(0)) != (int) Math.ceil(componentW * screenScales[0]) - || height.applyAsInt(screenImages.get(0).get(0)) != (int) Math.ceil(componentH * screenScales[0])) + || screenImages.get(0).get(0).width() != (int) Math.ceil(componentW * screenScales[0]) + || screenImages.get(0).get(0).height() != (int) Math.ceil(componentH * screenScales[0])) { renderIdQueue.clear(); renderIdQueue.addAll(Arrays.asList(0, 1, 2)); @@ -375,7 +349,7 @@ private synchronized boolean checkResize() screenImages.get(i).set(b, i == 0 ? makeImage.create(w, h) : makeImage.create(w, h, screenImages.get(0).get(b))); - final T bi = screenImages.get(i).get(b); + final RenderOutputImage bi = screenImages.get(i).get(b); // getBufferedImage.apply( screenImages[ i ][ b ] ); bufferedImages.get(i).set(b, bi); bufferedImageToRenderId.put(bi, b); @@ -408,14 +382,14 @@ private boolean checkRenewRenderImages(final int numVisibleSources) final int n = numVisibleSources > 1 ? numVisibleSources : 0; if (n != renderImages[0].length || n != 0 && - (renderImages[0][0].dimension(0) != width.applyAsInt(screenImages.get(0).get(0)) || - renderImages[0][0].dimension(1) != height.applyAsInt(screenImages.get(0).get(0)))) + (renderImages[0][0].dimension(0) != screenImages.get(0).get(0).width() || + renderImages[0][0].dimension(1) != screenImages.get(0).get(0).height())) { renderImages = new ArrayImg[screenScales.length][n]; for (int i = 0; i < screenScales.length; ++i) { - final int w = width.applyAsInt(screenImages.get(i).get(0)); - final int h = height.applyAsInt(screenImages.get(i).get(0)); + final int w = screenImages.get(i).get(0).width(); + final int h = screenImages.get(i).get(0).height(); for (int j = 0; j < n; ++j) renderImages[i][j] = i == 0 ? ArrayImgs.argbs(w, h) @@ -428,7 +402,7 @@ private boolean checkRenewRenderImages(final int numVisibleSources) private boolean checkRenewMaskArrays(final int numVisibleSources) { - final int size = width.applyAsInt(screenImages.get(0).get(0)) * height.applyAsInt(screenImages.get(0).get(0)); + final int size = screenImages.get(0).get(0).width() * screenImages.get(0).get(0).height(); if (numVisibleSources != renderMaskArrays.length || numVisibleSources != 0 && renderMaskArrays[0].length < size) { @@ -440,9 +414,9 @@ private boolean checkRenewMaskArrays(final int numVisibleSources) return false; } - private int[] getImageSize(final T image) + private int[] getImageSize(final RenderOutputImage image) { - return new int[] {this.width.applyAsInt(image), this.height.applyAsInt(image)}; + return new int[] {image.width(), image.height()}; } private static Interval padInterval(final Interval interval, final int[] padding, final int[] imageSize) @@ -472,7 +446,7 @@ public int paint( final boolean resized = checkResize(); // the BufferedImage that is rendered to (to paint to the canvas) - final T bufferedImage; + final RenderOutputImage bufferedImage; // the projector that paints to the screenImage. final VolatileProjector p; @@ -516,7 +490,7 @@ public int paint( final int renderId = renderIdQueue.peek(); currentScreenScaleIndex = requestedScreenScaleIndex; bufferedImage = bufferedImages.get(currentScreenScaleIndex).get(renderId); - final T renderTarget = screenImages.get(currentScreenScaleIndex).get(renderId); + final RenderOutputImage renderTarget = screenImages.get(currentScreenScaleIndex).get(renderId); synchronized (Optional.ofNullable(synchronizationLock).orElse(this)) { final int numSources = sacs.size(); @@ -546,7 +520,7 @@ public int paint( 0 ); - final RandomAccessibleInterval renderTargetRoi = Views.interval(wrapAsArrayImg.apply(renderTarget), renderTargetPaddedInterval); + final RandomAccessibleInterval renderTargetRoi = Views.interval(renderTarget.asArrayImg(), renderTargetPaddedInterval); p = createProjector( sacs, @@ -583,7 +557,7 @@ public int paint( { if (createProjector) { - final T bi = display.setBufferedImageAndTransform(bufferedImage, currentProjectorTransform); + final RenderOutputImage bi = makeImage.wrap(display.setBufferedImageAndTransform(makeImage.unwrap(bufferedImage), currentProjectorTransform)); if (doubleBuffered) { renderIdQueue.pop(); diff --git a/src/main/java/bdv/fx/viewer/render/RenderOutputImage.java b/src/main/java/bdv/fx/viewer/render/RenderOutputImage.java new file mode 100644 index 000000000..ef0f4607d --- /dev/null +++ b/src/main/java/bdv/fx/viewer/render/RenderOutputImage.java @@ -0,0 +1,25 @@ +package bdv.fx.viewer.render; + +import net.imglib2.img.array.ArrayImg; +import net.imglib2.img.basictypeaccess.IntAccess; +import net.imglib2.type.numeric.ARGBType; + +public interface RenderOutputImage { + + int width(); + + int height(); + + ArrayImg asArrayImg(); + + interface Factory { + + RenderOutputImage create( int width, int height ); + + RenderOutputImage create( int width, int heihgt, RenderOutputImage other); + + RenderOutputImage wrap(T image); + + T unwrap(RenderOutputImage image); + } +} From 87503fc84b13ca9fadfcb843729882ef0413eec8 Mon Sep 17 00:00:00 2001 From: Matthias Arzt Date: Fri, 26 Jul 2019 13:54:47 +0200 Subject: [PATCH 2/5] MultiResolutionRendererGeneric: refactoring use subclass ScreenScale --- .../MultiResolutionRendererGeneric.java | 204 ++++++++++-------- 1 file changed, 111 insertions(+), 93 deletions(-) diff --git a/src/main/java/bdv/fx/viewer/render/MultiResolutionRendererGeneric.java b/src/main/java/bdv/fx/viewer/render/MultiResolutionRendererGeneric.java index d74867971..bc87fd70e 100644 --- a/src/main/java/bdv/fx/viewer/render/MultiResolutionRendererGeneric.java +++ b/src/main/java/bdv/fx/viewer/render/MultiResolutionRendererGeneric.java @@ -84,6 +84,7 @@ import java.util.Optional; import java.util.concurrent.ExecutorService; import java.util.function.Function; +import java.util.stream.DoubleStream; /** * @@ -107,7 +108,7 @@ public class MultiResolutionRendererGeneric private final PainterThread painterThread; /** - * Currently active projector, used to re-paint the display. It maps the source data to {@link #screenImages}. + * Currently active projector, used to re-paint the display. It maps the source data to {@link ScreenScale#screenImages}. */ private VolatileProjector projector; @@ -122,7 +123,7 @@ public class MultiResolutionRendererGeneric private final boolean doubleBuffered; /** - * Double-buffer index of next {@link #screenImages image} to render. + * Double-buffer index of next {@link ScreenScale#screenImages image} to render. */ private final ArrayDeque renderIdQueue; @@ -132,47 +133,70 @@ public class MultiResolutionRendererGeneric private final HashMap bufferedImageToRenderId; /** - * Used to render an individual source. One image per screen resolution and visible source. First index is screen - * scale, second index is index in list of visible sources. - */ - ArrayImg[][] renderImages; - - /** - * Storage for mask images of {@link VolatileHierarchyProjector}. One array per visible source. (First) index is - * index in list of visible sources. + * Storage for mask images of {@link VolatileHierarchyProjector}. + * One array per visible source. (First) index is index in list of visible sources. */ private byte[][] renderMaskArrays; /** - * Used to render the image for display. Three images per screen resolution if double buffering is enabled. First - * index is screen scale, second index is double-buffer. + * List of scale factors and associate image buffers */ - private List> screenImages; + private ScreenScale[] screenScales; /** - * data store wrapping the data in the {@link #screenImages}. First index is screen scale, second index is - * double-buffer. + * Scale factor and associated image buffers and transformation. */ - private List> bufferedImages; + private static class ScreenScale { + + /** + * Scale factors from the {@link #display viewer canvas} to the + * {@link #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 double scaleFactor; - /** - * Scale factors from the {@link #display viewer canvas} to the {@link #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 double[] screenScales; + /** + * Used to render an individual source. One image per visible source + * Index is index in list of visible sources. + */ + // this + private List< ArrayImg > renderImages = new ArrayList<>(); + + /** + * Used to render the image for display. Three images if double buffering is + * enabled. + */ + // this + private List< RenderOutputImage > screenImages = new ArrayList<>( Collections.nCopies( 3, null ) ); + + /** + * {@link RenderOutputImage}s wrapping the data in the {@link #screenImages}. + * Three images if double buffering is enabled. + */ + // this + private List< RenderOutputImage > bufferedImages = new ArrayList<>( Collections.nCopies( 3, null ) ); + + /** + * The scale transformation from viewer to {@link #screenImages screen + * image}. + */ + // this + private AffineTransform3D screenScaleTransforms = new AffineTransform3D(); + + /** + * Pending repaint requests. + */ + private Interval pendingRepaintRequests; + + private ScreenScale(double scaleFactor) { + this.scaleFactor = scaleFactor; + } + } - /** - * The scale transformation from viewer to {@link #screenImages screen image}. Each transformation corresponds - * to a {@link #screenScales screen scale}. - */ - private AffineTransform3D[] screenScaleTransforms; - /** - * Pending repaint requests for each {@link #screenScales screen scale}. - */ - private Interval[] pendingRepaintRequests; /** * The last rendered interval in screen space. @@ -299,11 +323,11 @@ public class MultiResolutionRendererGeneric this.painterThread = painterThread; projector = null; currentScreenScaleIndex = -1; - this.screenScales = screenScales.clone(); + this.screenScales = DoubleStream.of(screenScales).mapToObj(ScreenScale::new).toArray(ScreenScale[]::new); this.doubleBuffered = doubleBuffered; renderIdQueue = new ArrayDeque<>(); bufferedImageToRenderId = new HashMap<>(); - createVariables(); + createVariables(screenScales); this.makeImage = makeImage; @@ -320,8 +344,8 @@ public class MultiResolutionRendererGeneric } /** - * Check whether the size of the display component was changed and recreate {@link #screenImages} and {@link - * #screenScaleTransforms} accordingly. + * Check whether the size of the display component was changed and + * recreate {@link ScreenScale#screenImages} and {@link ScreenScale#screenScaleTransforms} accordingly. * * @return whether the size was changed. */ @@ -329,16 +353,16 @@ private synchronized boolean checkResize() { final int componentW = display.getWidth(); final int componentH = display.getHeight(); - if (screenImages.get(0).get(0) == null - || screenImages.get(0).get(0).width() != (int) Math.ceil(componentW * screenScales[0]) - || screenImages.get(0).get(0).height() != (int) Math.ceil(componentH * screenScales[0])) + if (screenScales[ 0 ].screenImages.get(0) == null + || screenScales[ 0 ].screenImages.get(0).width() != (int) Math.ceil(componentW * screenScales[0].scaleFactor) + || screenScales[ 0 ].screenImages.get(0).height() != (int) Math.ceil(componentH * screenScales[0].scaleFactor)) { renderIdQueue.clear(); renderIdQueue.addAll(Arrays.asList(0, 1, 2)); bufferedImageToRenderId.clear(); for (int i = 0; i < screenScales.length; ++i) { - final double screenToViewerScale = screenScales[i]; + final double screenToViewerScale = screenScales[i].scaleFactor; final int w = (int) Math.ceil(screenToViewerScale * componentW); final int h = (int) Math.ceil(screenToViewerScale * componentH); if (doubleBuffered) @@ -346,19 +370,20 @@ private synchronized boolean checkResize() for (int b = 0; b < 3; ++b) { // reuse storage arrays of level 0 (highest resolution) - screenImages.get(i).set(b, i == 0 - ? makeImage.create(w, h) - : makeImage.create(w, h, screenImages.get(0).get(b))); - final RenderOutputImage bi = screenImages.get(i).get(b); + final RenderOutputImage screenImage = ( i == 0 ) ? + makeImage.create( w, h ) : + makeImage.create( w, h, screenScales[ 0 ].screenImages.get( b ) ); + screenScales[ i ].screenImages.set( b, screenImage ); + final RenderOutputImage bi = screenScales[ i ].screenImages.get(b); // getBufferedImage.apply( screenImages[ i ][ b ] ); - bufferedImages.get(i).set(b, bi); + screenScales[ i ].bufferedImages.set(b, bi); bufferedImageToRenderId.put(bi, b); } } else { - screenImages.get(i).set(0, makeImage.create(w, h)); - bufferedImages.get(i).set(0, screenImages.get(i).get(0)); + screenScales[ i ].screenImages.set(0, makeImage.create(w, h)); + screenScales[ i ].bufferedImages.set(0, screenScales[ i ].screenImages.get(0)); // getBufferedImage.apply( screenImages[ i ][ 0 ] ); } final AffineTransform3D scale = new AffineTransform3D(); @@ -368,7 +393,7 @@ private synchronized boolean checkResize() scale.set(yScale, 1, 1); scale.set(0.5 * (xScale - 1), 0, 3); scale.set(0.5 * (yScale - 1), 1, 3); - screenScaleTransforms[i] = scale; + screenScales[ i ].screenScaleTransforms = scale; } return true; @@ -380,20 +405,23 @@ private synchronized boolean checkResize() private boolean checkRenewRenderImages(final int numVisibleSources) { final int n = numVisibleSources > 1 ? numVisibleSources : 0; - if (n != renderImages[0].length || + if (n != screenScales[0].renderImages.size() || n != 0 && - (renderImages[0][0].dimension(0) != screenImages.get(0).get(0).width() || - renderImages[0][0].dimension(1) != screenImages.get(0).get(0).height())) + (screenScales[0].renderImages.get( 0 ).dimension(0) != screenScales[ 0 ].screenImages.get(0).width() || + screenScales[0].renderImages.get( 0 ).dimension(1) != screenScales[ 0 ].screenImages.get(0).height())) { - renderImages = new ArrayImg[screenScales.length][n]; for (int i = 0; i < screenScales.length; ++i) { - final int w = screenImages.get(i).get(0).width(); - final int h = screenImages.get(i).get(0).height(); + screenScales[ i ].renderImages = new ArrayList<>( Collections.nCopies( n, null) ); + final int w = ( int ) screenScales[ i ].screenImages.get( 0 ).width(); + final int h = ( int ) screenScales[ i ].screenImages.get( 0 ).height(); for (int j = 0; j < n; ++j) - renderImages[i][j] = i == 0 - ? ArrayImgs.argbs(w, h) - : ArrayImgs.argbs(renderImages[0][j].update(null), w, h); + { + final ArrayImg renderImage = (i == 0) + ? ArrayImgs.argbs(w, h) + : ArrayImgs.argbs(screenScales[0].renderImages.get(j).update(null), w, h); + screenScales[ i ].renderImages.set( j, renderImage ); + } } return true; } @@ -402,7 +430,7 @@ private boolean checkRenewRenderImages(final int numVisibleSources) private boolean checkRenewMaskArrays(final int numVisibleSources) { - final int size = screenImages.get(0).get(0).width() * screenImages.get(0).get(0).height(); + final int size = screenScales[ 0 ].screenImages.get(0).width() * screenScales[ 0 ].screenImages.get(0).height(); if (numVisibleSources != renderMaskArrays.length || numVisibleSources != 0 && renderMaskArrays[0].length < size) { @@ -463,11 +491,11 @@ public int paint( // Screen scales are first initialized with the default setting (see RenderUnit), // then the project metadata is loaded, and the screen scales are changed to the saved configuration. // If the project screen scales are [1.0], sometimes the renderer receives a request to re-render the screen at screen scale 1, which results in the exception. - if (requestedScreenScaleIndex >= pendingRepaintRequests.length) + if (requestedScreenScaleIndex >= screenScales.length) return -1; - repaintScreenInterval = pendingRepaintRequests[requestedScreenScaleIndex]; - pendingRepaintRequests[requestedScreenScaleIndex] = null; + repaintScreenInterval = screenScales[requestedScreenScaleIndex].pendingRepaintRequests; + screenScales[requestedScreenScaleIndex].pendingRepaintRequests = null; if (repaintScreenInterval == null) return -1; @@ -489,8 +517,8 @@ public int paint( { final int renderId = renderIdQueue.peek(); currentScreenScaleIndex = requestedScreenScaleIndex; - bufferedImage = bufferedImages.get(currentScreenScaleIndex).get(renderId); - final RenderOutputImage renderTarget = screenImages.get(currentScreenScaleIndex).get(renderId); + bufferedImage = screenScales[currentScreenScaleIndex].bufferedImages.get(renderId); + final RenderOutputImage renderTarget = screenScales[currentScreenScaleIndex].screenImages.get(renderId); synchronized (Optional.ofNullable(synchronizationLock).orElse(this)) { final int numSources = sacs.size(); @@ -499,7 +527,7 @@ public int paint( // find the scaling ratio between render target pixels and screen pixels final double[] renderTargetToScreenPixelRatio = new double[2]; - Arrays.setAll(renderTargetToScreenPixelRatio, d -> screenScaleTransforms[currentScreenScaleIndex].get(d, d)); + Arrays.setAll(renderTargetToScreenPixelRatio, d -> screenScales[currentScreenScaleIndex].screenScaleTransforms.get(d, d)); // scale the screen repaint request interval into render target coordinates final double[] renderTargetRealIntervalMin = new double[2], renderTargetRealIntervalMax = new double[2]; @@ -612,10 +640,10 @@ else if (!p.isValid()) else { // Add the requested interval back into the queue if it was not rendered - if (pendingRepaintRequests[currentScreenScaleIndex] == null) - pendingRepaintRequests[currentScreenScaleIndex] = repaintScreenInterval; + if (screenScales[currentScreenScaleIndex].pendingRepaintRequests == null) + screenScales[currentScreenScaleIndex].pendingRepaintRequests = repaintScreenInterval; else - pendingRepaintRequests[currentScreenScaleIndex] = Intervals.union(pendingRepaintRequests[currentScreenScaleIndex], repaintScreenInterval); + screenScales[currentScreenScaleIndex].pendingRepaintRequests = Intervals.union(screenScales[currentScreenScaleIndex].pendingRepaintRequests, repaintScreenInterval); } return success ? currentScreenScaleIndex : -1; @@ -660,13 +688,13 @@ public synchronized void requestRepaint(final Interval interval, final int scree // Screen scales are first initialized with the default setting (see RenderUnit), // then the project metadata is loaded, and the screen scales are changed to the saved configuration. // If the project screen scales are [1.0], sometimes the renderer receives a request to re-render the screen at screen scale 1, which results in the exception. - if (requestedScreenScaleIndex >= pendingRepaintRequests.length) + if (requestedScreenScaleIndex >= screenScales.length) return; - if (pendingRepaintRequests[requestedScreenScaleIndex] == null) - pendingRepaintRequests[requestedScreenScaleIndex] = interval; + if (screenScales[requestedScreenScaleIndex].pendingRepaintRequests == null) + screenScales[requestedScreenScaleIndex].pendingRepaintRequests = interval; else - pendingRepaintRequests[requestedScreenScaleIndex] = Intervals.union(pendingRepaintRequests[requestedScreenScaleIndex], interval); + screenScales[requestedScreenScaleIndex].pendingRepaintRequests = Intervals.union(screenScales[requestedScreenScaleIndex].pendingRepaintRequests, interval); painterThread.requestRepaint(); } @@ -694,7 +722,7 @@ else if (sacs.size() == 1) LOG.debug("Got only one source, creating pre-multiplying single source projector"); final SourceAndConverter sac = sacs.get(0); final Interpolation interpolation = interpolationForSource.apply(sac.getSpimSource()); - final int[] renderTargetSize = getImageSize(this.screenImages.get(currentScreenScaleIndex).get(0)); + final int[] renderTargetSize = getImageSize(screenScales[currentScreenScaleIndex].screenImages.get(0)); projector = createSingleSourceProjector( sac, axisOrders.apply(sac.getSpimSource()), @@ -716,12 +744,12 @@ else if (sacs.size() == 1) int j = 0; for (final SourceAndConverter sac : sacs) { - final RandomAccessibleInterval renderImage = Views.interval(renderImages[currentScreenScaleIndex][j], screenImage); + final RandomAccessibleInterval renderImage = Views.interval(screenScales[currentScreenScaleIndex].renderImages.get(j), screenImage); final byte[] maskArray = renderMaskArrays[j]; final AxisOrder axisOrder = axisOrders.apply(sac.getSpimSource()); ++j; final Interpolation interpolation = interpolationForSource.apply(sac.getSpimSource()); - final int[] renderTargetSize = getImageSize(this.screenImages.get(currentScreenScaleIndex).get(0)); + final int[] renderTargetSize = getImageSize(screenScales[currentScreenScaleIndex].screenImages.get(0)); final VolatileProjector p = createSingleSourceProjector( sac, axisOrder, @@ -835,7 +863,7 @@ else if (source.getSpimSource().getType() instanceof Volatile) ); } - final AffineTransform3D screenScaleTransform = screenScaleTransforms[currentScreenScaleIndex]; + final AffineTransform3D screenScaleTransform = screenScales[currentScreenScaleIndex].screenScaleTransforms; final AffineTransform3D screenTransform = viewerTransform.copy(); screenTransform.preConcatenate(screenScaleTransform); final int bestLevel = MipmapTransforms.getBestMipMapLevel(screenTransform, source.getSpimSource(), timepoint); @@ -871,7 +899,7 @@ private > VolatileProjector createSingleSourceVolatileProj source.getSpimSource(), source.getSpimSource().getName() ); - final AffineTransform3D screenScaleTransform = screenScaleTransforms[currentScreenScaleIndex]; + final AffineTransform3D screenScaleTransform = screenScales[currentScreenScaleIndex].screenScaleTransforms; final ArrayList> renderList = new ArrayList<>(); final Source spimSource = source.getSpimSource(); LOG.debug("Creating single source volatile projector for type={}", spimSource.getType()); @@ -1030,8 +1058,7 @@ private static void prefetch( public synchronized void setScreenScales(final double[] screenScales) { - this.screenScales = screenScales.clone(); - createVariables(); + createVariables(screenScales); } /** @@ -1042,27 +1069,18 @@ public synchronized void setScreenScales(final double[] screenScales) */ public synchronized void getScreenScaleTransform(final int screenScaleIndex, final AffineTransform3D screenScaleTransform) { - if (screenScaleIndex < this.screenScaleTransforms.length && this.screenScaleTransforms[screenScaleIndex] != null) - screenScaleTransform.set(this.screenScaleTransforms[screenScaleIndex]); + if (screenScaleIndex < screenScales.length && screenScales[screenScaleIndex].screenScaleTransforms != null) + screenScaleTransform.set(this.screenScales[screenScaleIndex].screenScaleTransforms); } - private synchronized void createVariables() + private synchronized void createVariables(double[] screenScales) { - LOG.debug("Updating images for screen scales {}", screenScales); + LOG.debug("Updating images for screen scales {}", this.screenScales); if (renderingMayBeCancelled && projector != null) projector.cancel(); - renderImages = new ArrayImg[screenScales.length][0]; + this.screenScales = DoubleStream.of(screenScales).mapToObj(ScreenScale::new).toArray(ScreenScale[]::new); renderMaskArrays = new byte[0][]; - screenImages = new ArrayList<>(); - bufferedImages = new ArrayList<>(); - for (int i = 0; i < screenScales.length; ++i) - { - screenImages.add(Arrays.asList(null, null, null)); - bufferedImages.add(Arrays.asList(null, null, null)); - } - screenScaleTransforms = new AffineTransform3D[screenScales.length]; - pendingRepaintRequests = new Interval[screenScales.length]; - maxScreenScaleIndex = screenScales.length - 1; + maxScreenScaleIndex = this.screenScales.length - 1; requestedScreenScaleIndex = maxScreenScaleIndex; } From fa2736519af985198c653a11d6ee55bb605ba83f Mon Sep 17 00:00:00 2001 From: Matthias Arzt Date: Fri, 26 Jul 2019 15:47:45 +0200 Subject: [PATCH 3/5] RenderOutputImage: remove wrap method from Factory This makes it easier to implement an RenderOutputImage.Factory for BigDataViewer AWT renderer. --- .../render/MultiResolutionRendererFX.java | 18 +++--------- .../MultiResolutionRendererGeneric.java | 28 +++++++++---------- .../fx/viewer/render/RenderOutputImage.java | 12 ++++---- 3 files changed, 23 insertions(+), 35 deletions(-) diff --git a/src/main/java/bdv/fx/viewer/render/MultiResolutionRendererFX.java b/src/main/java/bdv/fx/viewer/render/MultiResolutionRendererFX.java index 7e4f4e0f2..15b44bb97 100644 --- a/src/main/java/bdv/fx/viewer/render/MultiResolutionRendererFX.java +++ b/src/main/java/bdv/fx/viewer/render/MultiResolutionRendererFX.java @@ -110,20 +110,9 @@ public RenderOutputImage create(final int width, final int height) public RenderOutputImage create(int width, int height, RenderOutputImage other) { return create(width, height); } - - @Override - public RenderOutputImage wrap(BufferExposingWritableImage image) { - return new MyRenderOutputImage(image); - } - - @Override - public BufferExposingWritableImage unwrap(RenderOutputImage image) { - return ((MyRenderOutputImage) image).image; - } - } - public static class MyRenderOutputImage implements RenderOutputImage { + public static class MyRenderOutputImage implements RenderOutputImage { private final BufferExposingWritableImage image; @@ -146,8 +135,9 @@ public ArrayImg asArrayImg() { return image.asArrayImg(); } - public BufferExposingWritableImage image() { - return image(); + @Override + public BufferExposingWritableImage unwrap() { + return image; } } diff --git a/src/main/java/bdv/fx/viewer/render/MultiResolutionRendererGeneric.java b/src/main/java/bdv/fx/viewer/render/MultiResolutionRendererGeneric.java index bc87fd70e..e1b3f56e6 100644 --- a/src/main/java/bdv/fx/viewer/render/MultiResolutionRendererGeneric.java +++ b/src/main/java/bdv/fx/viewer/render/MultiResolutionRendererGeneric.java @@ -130,7 +130,7 @@ public class MultiResolutionRendererGeneric /** * Maps from data store to double-buffer index. Needed for double-buffering. */ - private final HashMap bufferedImageToRenderId; + private final HashMap bufferedImageToRenderId; /** * Storage for mask images of {@link VolatileHierarchyProjector}. @@ -141,12 +141,12 @@ public class MultiResolutionRendererGeneric /** * List of scale factors and associate image buffers */ - private ScreenScale[] screenScales; + private ScreenScale< T >[] screenScales; /** * Scale factor and associated image buffers and transformation. */ - private static class ScreenScale { + private static class ScreenScale< T > { /** * Scale factors from the {@link #display viewer canvas} to the @@ -170,14 +170,14 @@ private static class ScreenScale { * enabled. */ // this - private List< RenderOutputImage > screenImages = new ArrayList<>( Collections.nCopies( 3, null ) ); + private List< RenderOutputImage< T > > screenImages = new ArrayList<>( Collections.nCopies( 3, null ) ); /** - * {@link RenderOutputImage}s wrapping the data in the {@link #screenImages}. + * {@link RenderOutputImage< T >}s wrapping the data in the {@link #screenImages}. * Three images if double buffering is enabled. */ // this - private List< RenderOutputImage > bufferedImages = new ArrayList<>( Collections.nCopies( 3, null ) ); + private List< RenderOutputImage< T > > bufferedImages = new ArrayList<>( Collections.nCopies( 3, null ) ); /** * The scale transformation from viewer to {@link #screenImages screen @@ -317,7 +317,7 @@ private ScreenScale(double scaleFactor) { final boolean useVolatileIfAvailable, final AccumulateProjectorFactory accumulateProjectorFactory, final CacheControl cacheControl, - final RenderOutputImage.Factory makeImage) + final RenderOutputImage.Factory< T > makeImage) { this.display = display; this.painterThread = painterThread; @@ -370,14 +370,14 @@ private synchronized boolean checkResize() for (int b = 0; b < 3; ++b) { // reuse storage arrays of level 0 (highest resolution) - final RenderOutputImage screenImage = ( i == 0 ) ? + final RenderOutputImage< T > screenImage = ( i == 0 ) ? makeImage.create( w, h ) : makeImage.create( w, h, screenScales[ 0 ].screenImages.get( b ) ); screenScales[ i ].screenImages.set( b, screenImage ); - final RenderOutputImage bi = screenScales[ i ].screenImages.get(b); + final RenderOutputImage< T > bi = screenScales[ i ].screenImages.get(b); // getBufferedImage.apply( screenImages[ i ][ b ] ); screenScales[ i ].bufferedImages.set(b, bi); - bufferedImageToRenderId.put(bi, b); + bufferedImageToRenderId.put(bi.unwrap(), b); } } else @@ -442,7 +442,7 @@ private boolean checkRenewMaskArrays(final int numVisibleSources) return false; } - private int[] getImageSize(final RenderOutputImage image) + private int[] getImageSize(final RenderOutputImage< T > image) { return new int[] {image.width(), image.height()}; } @@ -474,7 +474,7 @@ public int paint( final boolean resized = checkResize(); // the BufferedImage that is rendered to (to paint to the canvas) - final RenderOutputImage bufferedImage; + final RenderOutputImage< T > bufferedImage; // the projector that paints to the screenImage. final VolatileProjector p; @@ -518,7 +518,7 @@ public int paint( final int renderId = renderIdQueue.peek(); currentScreenScaleIndex = requestedScreenScaleIndex; bufferedImage = screenScales[currentScreenScaleIndex].bufferedImages.get(renderId); - final RenderOutputImage renderTarget = screenScales[currentScreenScaleIndex].screenImages.get(renderId); + final RenderOutputImage< T > renderTarget = screenScales[currentScreenScaleIndex].screenImages.get(renderId); synchronized (Optional.ofNullable(synchronizationLock).orElse(this)) { final int numSources = sacs.size(); @@ -585,7 +585,7 @@ public int paint( { if (createProjector) { - final RenderOutputImage bi = makeImage.wrap(display.setBufferedImageAndTransform(makeImage.unwrap(bufferedImage), currentProjectorTransform)); + final T bi = display.setBufferedImageAndTransform(bufferedImage.unwrap(), currentProjectorTransform); if (doubleBuffered) { renderIdQueue.pop(); diff --git a/src/main/java/bdv/fx/viewer/render/RenderOutputImage.java b/src/main/java/bdv/fx/viewer/render/RenderOutputImage.java index ef0f4607d..5b0f29837 100644 --- a/src/main/java/bdv/fx/viewer/render/RenderOutputImage.java +++ b/src/main/java/bdv/fx/viewer/render/RenderOutputImage.java @@ -4,7 +4,7 @@ import net.imglib2.img.basictypeaccess.IntAccess; import net.imglib2.type.numeric.ARGBType; -public interface RenderOutputImage { +public interface RenderOutputImage { int width(); @@ -12,14 +12,12 @@ public interface RenderOutputImage { ArrayImg asArrayImg(); - interface Factory { - - RenderOutputImage create( int width, int height ); + T unwrap(); - RenderOutputImage create( int width, int heihgt, RenderOutputImage other); + interface Factory { - RenderOutputImage wrap(T image); + RenderOutputImage< T > create( int width, int height ); - T unwrap(RenderOutputImage image); + RenderOutputImage< T > create( int width, int heihgt, RenderOutputImage< T > other); } } From 7d47628b45e2f848b4b61a358bfc6120ccbbbc7b Mon Sep 17 00:00:00 2001 From: Matthias Arzt Date: Fri, 26 Jul 2019 16:18:37 +0200 Subject: [PATCH 4/5] MultiResolutionRendererGeneric: format java doc the same as in BDV core --- .../MultiResolutionRendererGeneric.java | 32 +++++++++++-------- 1 file changed, 19 insertions(+), 13 deletions(-) diff --git a/src/main/java/bdv/fx/viewer/render/MultiResolutionRendererGeneric.java b/src/main/java/bdv/fx/viewer/render/MultiResolutionRendererGeneric.java index e1b3f56e6..46470b517 100644 --- a/src/main/java/bdv/fx/viewer/render/MultiResolutionRendererGeneric.java +++ b/src/main/java/bdv/fx/viewer/render/MultiResolutionRendererGeneric.java @@ -261,13 +261,14 @@ private ScreenScale(double scaleFactor) { private final boolean useVolatileIfAvailable; /** - * Whether a repaint was {@link #requestRepaint(Interval) requested}. This will cause {@link - * CacheControl#prepareNextFrame()}. + * Whether a repaint was {@link #requestRepaint(Interval) requested}. This will + * cause {@link CacheControl#prepareNextFrame()}. */ private boolean newFrameRequest; /** - * The timepoint for which last a projector was {@link #createProjector created}. + * The timepoint for which last a projector was + * {@link #createProjector created}. */ private int previousTimepoint; @@ -283,24 +284,29 @@ private ScreenScale(double scaleFactor) { * @param display * The canvas that will display the images we render. * @param painterThread - * Thread that triggers repainting of the display. Requests for repainting are send there. + * Thread that triggers repainting of the display. Requests for + * repainting are send there. * @param screenScales - * 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. + * 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. * @param targetRenderNanos - * Target rendering time in nanoseconds. The rendering time for the coarsest rendered scale should be below - * this - * threshold. + * Target rendering time in nanoseconds. The rendering time for + * the coarsest rendered scale should be below this threshold. * @param doubleBuffered * Whether to use double buffered rendering. * @param numRenderingThreads * How many threads to use for rendering. * @param renderingExecutorService - * if non-null, this is used for rendering. Note, that it is still important to supply the numRenderingThreads - * parameter, because that is used to determine into how many sub-tasks rendering is split. + * if non-null, this is used for rendering. Note, that it is + * still important to supply the numRenderingThreads parameter, + * because that is used to determine into how many sub-tasks + * rendering is split. * @param useVolatileIfAvailable - * whether volatile versions of sources should be used if available. + * whether volatile versions of sources should be used if + * available. * @param accumulateProjectorFactory * can be used to customize how sources are combined. * @param cacheControl From 87182af29f91e45ac09a5fbf4ed59dbf6184f9b4 Mon Sep 17 00:00:00 2001 From: Matthias Arzt Date: Fri, 26 Jul 2019 16:25:02 +0200 Subject: [PATCH 5/5] MultiResolutionRendererGeneric: use List for screenScales A List is better as it handles the generics correctly. --- .../MultiResolutionRendererGeneric.java | 94 +++++++++---------- 1 file changed, 47 insertions(+), 47 deletions(-) diff --git a/src/main/java/bdv/fx/viewer/render/MultiResolutionRendererGeneric.java b/src/main/java/bdv/fx/viewer/render/MultiResolutionRendererGeneric.java index 46470b517..b7f2a61d9 100644 --- a/src/main/java/bdv/fx/viewer/render/MultiResolutionRendererGeneric.java +++ b/src/main/java/bdv/fx/viewer/render/MultiResolutionRendererGeneric.java @@ -84,6 +84,7 @@ import java.util.Optional; import java.util.concurrent.ExecutorService; import java.util.function.Function; +import java.util.stream.Collectors; import java.util.stream.DoubleStream; /** @@ -141,7 +142,7 @@ public class MultiResolutionRendererGeneric /** * List of scale factors and associate image buffers */ - private ScreenScale< T >[] screenScales; + private List< ScreenScale< T > > screenScales; /** * Scale factor and associated image buffers and transformation. @@ -329,7 +330,6 @@ private ScreenScale(double scaleFactor) { this.painterThread = painterThread; projector = null; currentScreenScaleIndex = -1; - this.screenScales = DoubleStream.of(screenScales).mapToObj(ScreenScale::new).toArray(ScreenScale[]::new); this.doubleBuffered = doubleBuffered; renderIdQueue = new ArrayDeque<>(); bufferedImageToRenderId = new HashMap<>(); @@ -359,16 +359,16 @@ private synchronized boolean checkResize() { final int componentW = display.getWidth(); final int componentH = display.getHeight(); - if (screenScales[ 0 ].screenImages.get(0) == null - || screenScales[ 0 ].screenImages.get(0).width() != (int) Math.ceil(componentW * screenScales[0].scaleFactor) - || screenScales[ 0 ].screenImages.get(0).height() != (int) Math.ceil(componentH * screenScales[0].scaleFactor)) + if (screenScales.get( 0 ).screenImages.get(0) == null + || screenScales.get( 0 ).screenImages.get(0).width() != (int) Math.ceil(componentW * screenScales.get(0).scaleFactor) + || screenScales.get( 0 ).screenImages.get(0).height() != (int) Math.ceil(componentH * screenScales.get(0).scaleFactor)) { renderIdQueue.clear(); renderIdQueue.addAll(Arrays.asList(0, 1, 2)); bufferedImageToRenderId.clear(); - for (int i = 0; i < screenScales.length; ++i) + for (int i = 0; i < screenScales.size(); ++i) { - final double screenToViewerScale = screenScales[i].scaleFactor; + final double screenToViewerScale = screenScales.get(i).scaleFactor; final int w = (int) Math.ceil(screenToViewerScale * componentW); final int h = (int) Math.ceil(screenToViewerScale * componentH); if (doubleBuffered) @@ -378,18 +378,18 @@ private synchronized boolean checkResize() // reuse storage arrays of level 0 (highest resolution) final RenderOutputImage< T > screenImage = ( i == 0 ) ? makeImage.create( w, h ) : - makeImage.create( w, h, screenScales[ 0 ].screenImages.get( b ) ); - screenScales[ i ].screenImages.set( b, screenImage ); - final RenderOutputImage< T > bi = screenScales[ i ].screenImages.get(b); + makeImage.create( w, h, screenScales.get( 0 ).screenImages.get( b ) ); + screenScales.get( i ).screenImages.set( b, screenImage ); + final RenderOutputImage< T > bi = screenScales.get( i ).screenImages.get(b); // getBufferedImage.apply( screenImages[ i ][ b ] ); - screenScales[ i ].bufferedImages.set(b, bi); + screenScales.get( i ).bufferedImages.set(b, bi); bufferedImageToRenderId.put(bi.unwrap(), b); } } else { - screenScales[ i ].screenImages.set(0, makeImage.create(w, h)); - screenScales[ i ].bufferedImages.set(0, screenScales[ i ].screenImages.get(0)); + screenScales.get( i ).screenImages.set(0, makeImage.create(w, h)); + screenScales.get( i ).bufferedImages.set(0, screenScales.get( i ).screenImages.get(0)); // getBufferedImage.apply( screenImages[ i ][ 0 ] ); } final AffineTransform3D scale = new AffineTransform3D(); @@ -399,7 +399,7 @@ private synchronized boolean checkResize() scale.set(yScale, 1, 1); scale.set(0.5 * (xScale - 1), 0, 3); scale.set(0.5 * (yScale - 1), 1, 3); - screenScales[ i ].screenScaleTransforms = scale; + screenScales.get( i ).screenScaleTransforms = scale; } return true; @@ -411,22 +411,22 @@ private synchronized boolean checkResize() private boolean checkRenewRenderImages(final int numVisibleSources) { final int n = numVisibleSources > 1 ? numVisibleSources : 0; - if (n != screenScales[0].renderImages.size() || + if (n != screenScales.get(0).renderImages.size() || n != 0 && - (screenScales[0].renderImages.get( 0 ).dimension(0) != screenScales[ 0 ].screenImages.get(0).width() || - screenScales[0].renderImages.get( 0 ).dimension(1) != screenScales[ 0 ].screenImages.get(0).height())) + (screenScales.get(0).renderImages.get( 0 ).dimension(0) != screenScales.get( 0 ).screenImages.get(0).width() || + screenScales.get(0).renderImages.get( 0 ).dimension(1) != screenScales.get( 0 ).screenImages.get(0).height())) { - for (int i = 0; i < screenScales.length; ++i) + for (int i = 0; i < screenScales.size(); ++i) { - screenScales[ i ].renderImages = new ArrayList<>( Collections.nCopies( n, null) ); - final int w = ( int ) screenScales[ i ].screenImages.get( 0 ).width(); - final int h = ( int ) screenScales[ i ].screenImages.get( 0 ).height(); + screenScales.get( i ).renderImages = new ArrayList<>( Collections.nCopies( n, null) ); + final int w = ( int ) screenScales.get( i ).screenImages.get( 0 ).width(); + final int h = ( int ) screenScales.get( i ).screenImages.get( 0 ).height(); for (int j = 0; j < n; ++j) { final ArrayImg renderImage = (i == 0) ? ArrayImgs.argbs(w, h) - : ArrayImgs.argbs(screenScales[0].renderImages.get(j).update(null), w, h); - screenScales[ i ].renderImages.set( j, renderImage ); + : ArrayImgs.argbs(screenScales.get(0).renderImages.get(j).update(null), w, h); + screenScales.get( i ).renderImages.set( j, renderImage ); } } return true; @@ -436,7 +436,7 @@ private boolean checkRenewRenderImages(final int numVisibleSources) private boolean checkRenewMaskArrays(final int numVisibleSources) { - final int size = screenScales[ 0 ].screenImages.get(0).width() * screenScales[ 0 ].screenImages.get(0).height(); + final int size = screenScales.get( 0 ).screenImages.get(0).width() * screenScales.get( 0 ).screenImages.get(0).height(); if (numVisibleSources != renderMaskArrays.length || numVisibleSources != 0 && renderMaskArrays[0].length < size) { @@ -497,11 +497,11 @@ public int paint( // Screen scales are first initialized with the default setting (see RenderUnit), // then the project metadata is loaded, and the screen scales are changed to the saved configuration. // If the project screen scales are [1.0], sometimes the renderer receives a request to re-render the screen at screen scale 1, which results in the exception. - if (requestedScreenScaleIndex >= screenScales.length) + if (requestedScreenScaleIndex >= screenScales.size()) return -1; - repaintScreenInterval = screenScales[requestedScreenScaleIndex].pendingRepaintRequests; - screenScales[requestedScreenScaleIndex].pendingRepaintRequests = null; + repaintScreenInterval = screenScales.get(requestedScreenScaleIndex).pendingRepaintRequests; + screenScales.get(requestedScreenScaleIndex).pendingRepaintRequests = null; if (repaintScreenInterval == null) return -1; @@ -523,8 +523,8 @@ public int paint( { final int renderId = renderIdQueue.peek(); currentScreenScaleIndex = requestedScreenScaleIndex; - bufferedImage = screenScales[currentScreenScaleIndex].bufferedImages.get(renderId); - final RenderOutputImage< T > renderTarget = screenScales[currentScreenScaleIndex].screenImages.get(renderId); + bufferedImage = screenScales.get(currentScreenScaleIndex).bufferedImages.get(renderId); + final RenderOutputImage< T > renderTarget = screenScales.get(currentScreenScaleIndex).screenImages.get(renderId); synchronized (Optional.ofNullable(synchronizationLock).orElse(this)) { final int numSources = sacs.size(); @@ -533,7 +533,7 @@ public int paint( // find the scaling ratio between render target pixels and screen pixels final double[] renderTargetToScreenPixelRatio = new double[2]; - Arrays.setAll(renderTargetToScreenPixelRatio, d -> screenScales[currentScreenScaleIndex].screenScaleTransforms.get(d, d)); + Arrays.setAll(renderTargetToScreenPixelRatio, d -> screenScales.get(currentScreenScaleIndex).screenScaleTransforms.get(d, d)); // scale the screen repaint request interval into render target coordinates final double[] renderTargetRealIntervalMin = new double[2], renderTargetRealIntervalMax = new double[2]; @@ -618,7 +618,7 @@ public int paint( */ // if (currentScreenScaleIndex == maxScreenScaleIndex) // { -// if (rendertime > targetRenderNanos && maxScreenScaleIndex < screenScales.length - 1) +// if (rendertime > targetRenderNanos && maxScreenScaleIndex < screenScales.size() - 1) // maxScreenScaleIndex++; // else if (rendertime < targetRenderNanos / 3 && maxScreenScaleIndex > 0) // maxScreenScaleIndex--; @@ -646,10 +646,10 @@ else if (!p.isValid()) else { // Add the requested interval back into the queue if it was not rendered - if (screenScales[currentScreenScaleIndex].pendingRepaintRequests == null) - screenScales[currentScreenScaleIndex].pendingRepaintRequests = repaintScreenInterval; + if (screenScales.get(currentScreenScaleIndex).pendingRepaintRequests == null) + screenScales.get(currentScreenScaleIndex).pendingRepaintRequests = repaintScreenInterval; else - screenScales[currentScreenScaleIndex].pendingRepaintRequests = Intervals.union(screenScales[currentScreenScaleIndex].pendingRepaintRequests, repaintScreenInterval); + screenScales.get(currentScreenScaleIndex).pendingRepaintRequests = Intervals.union(screenScales.get(currentScreenScaleIndex).pendingRepaintRequests, repaintScreenInterval); } return success ? currentScreenScaleIndex : -1; @@ -694,13 +694,13 @@ public synchronized void requestRepaint(final Interval interval, final int scree // Screen scales are first initialized with the default setting (see RenderUnit), // then the project metadata is loaded, and the screen scales are changed to the saved configuration. // If the project screen scales are [1.0], sometimes the renderer receives a request to re-render the screen at screen scale 1, which results in the exception. - if (requestedScreenScaleIndex >= screenScales.length) + if (requestedScreenScaleIndex >= screenScales.size()) return; - if (screenScales[requestedScreenScaleIndex].pendingRepaintRequests == null) - screenScales[requestedScreenScaleIndex].pendingRepaintRequests = interval; + if (screenScales.get(requestedScreenScaleIndex).pendingRepaintRequests == null) + screenScales.get(requestedScreenScaleIndex).pendingRepaintRequests = interval; else - screenScales[requestedScreenScaleIndex].pendingRepaintRequests = Intervals.union(screenScales[requestedScreenScaleIndex].pendingRepaintRequests, interval); + screenScales.get(requestedScreenScaleIndex).pendingRepaintRequests = Intervals.union(screenScales.get(requestedScreenScaleIndex).pendingRepaintRequests, interval); painterThread.requestRepaint(); } @@ -728,7 +728,7 @@ else if (sacs.size() == 1) LOG.debug("Got only one source, creating pre-multiplying single source projector"); final SourceAndConverter sac = sacs.get(0); final Interpolation interpolation = interpolationForSource.apply(sac.getSpimSource()); - final int[] renderTargetSize = getImageSize(screenScales[currentScreenScaleIndex].screenImages.get(0)); + final int[] renderTargetSize = getImageSize(screenScales.get(currentScreenScaleIndex).screenImages.get(0)); projector = createSingleSourceProjector( sac, axisOrders.apply(sac.getSpimSource()), @@ -750,12 +750,12 @@ else if (sacs.size() == 1) int j = 0; for (final SourceAndConverter sac : sacs) { - final RandomAccessibleInterval renderImage = Views.interval(screenScales[currentScreenScaleIndex].renderImages.get(j), screenImage); + final RandomAccessibleInterval renderImage = Views.interval(screenScales.get(currentScreenScaleIndex).renderImages.get(j), screenImage); final byte[] maskArray = renderMaskArrays[j]; final AxisOrder axisOrder = axisOrders.apply(sac.getSpimSource()); ++j; final Interpolation interpolation = interpolationForSource.apply(sac.getSpimSource()); - final int[] renderTargetSize = getImageSize(screenScales[currentScreenScaleIndex].screenImages.get(0)); + final int[] renderTargetSize = getImageSize(screenScales.get(currentScreenScaleIndex).screenImages.get(0)); final VolatileProjector p = createSingleSourceProjector( sac, axisOrder, @@ -869,7 +869,7 @@ else if (source.getSpimSource().getType() instanceof Volatile) ); } - final AffineTransform3D screenScaleTransform = screenScales[currentScreenScaleIndex].screenScaleTransforms; + final AffineTransform3D screenScaleTransform = screenScales.get(currentScreenScaleIndex).screenScaleTransforms; final AffineTransform3D screenTransform = viewerTransform.copy(); screenTransform.preConcatenate(screenScaleTransform); final int bestLevel = MipmapTransforms.getBestMipMapLevel(screenTransform, source.getSpimSource(), timepoint); @@ -905,7 +905,7 @@ private > VolatileProjector createSingleSourceVolatileProj source.getSpimSource(), source.getSpimSource().getName() ); - final AffineTransform3D screenScaleTransform = screenScales[currentScreenScaleIndex].screenScaleTransforms; + final AffineTransform3D screenScaleTransform = screenScales.get(currentScreenScaleIndex).screenScaleTransforms; final ArrayList> renderList = new ArrayList<>(); final Source spimSource = source.getSpimSource(); LOG.debug("Creating single source volatile projector for type={}", spimSource.getType()); @@ -1075,8 +1075,8 @@ public synchronized void setScreenScales(final double[] screenScales) */ public synchronized void getScreenScaleTransform(final int screenScaleIndex, final AffineTransform3D screenScaleTransform) { - if (screenScaleIndex < screenScales.length && screenScales[screenScaleIndex].screenScaleTransforms != null) - screenScaleTransform.set(this.screenScales[screenScaleIndex].screenScaleTransforms); + if (screenScaleIndex < screenScales.size() && screenScales.get(screenScaleIndex).screenScaleTransforms != null) + screenScaleTransform.set(this.screenScales.get(screenScaleIndex).screenScaleTransforms); } private synchronized void createVariables(double[] screenScales) @@ -1084,9 +1084,9 @@ private synchronized void createVariables(double[] screenScales) LOG.debug("Updating images for screen scales {}", this.screenScales); if (renderingMayBeCancelled && projector != null) projector.cancel(); - this.screenScales = DoubleStream.of(screenScales).mapToObj(ScreenScale::new).toArray(ScreenScale[]::new); + this.screenScales = DoubleStream.of(screenScales).mapToObj(ScreenScale< T >::new).collect(Collectors.toList()); renderMaskArrays = new byte[0][]; - maxScreenScaleIndex = this.screenScales.length - 1; + maxScreenScaleIndex = this.screenScales.size() - 1; requestedScreenScaleIndex = maxScreenScaleIndex; }