/* * #%L * ImgLib2: a general-purpose, multidimensional image processing library. * %% * Copyright (C) 2009 - 2016 Tobias Pietzsch, Stephan Preibisch, Stephan Saalfeld, * John Bogovic, Albert Cardona, Barry DeZonia, Christian Dietz, Jan Funke, * Aivar Grislis, Jonathan Hale, Grant Harris, Stefan Helfrich, Mark Hiner, * Martin Horn, Steffen Jaensch, Lee Kamentsky, Larry Lindsey, Melissa Linkert, * Mark Longair, Brian Northan, Nick Perry, Curtis Rueden, Johannes Schindelin, * Jean-Yves Tinevez and Michael Zinsmaier. * %% * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. * #L% */ package bdv.fx.viewer; import bdv.fx.viewer.render.OverlayRendererGeneric; import javafx.application.Platform; import javafx.beans.value.ChangeListener; import javafx.collections.FXCollections; import javafx.collections.ObservableList; import javafx.geometry.Insets; import javafx.scene.Node; import javafx.scene.canvas.Canvas; import javafx.scene.canvas.GraphicsContext; import javafx.scene.layout.Background; import javafx.scene.layout.BackgroundFill; import javafx.scene.layout.CornerRadii; import javafx.scene.layout.StackPane; import javafx.scene.paint.Color; import org.janelia.saalfeldlab.fx.event.InstallAndRemove; import org.janelia.saalfeldlab.fx.util.InvokeOnJavaFXApplicationThread; import java.util.Collection; import java.util.concurrent.CopyOnWriteArrayList; /** * @param <A> * transform type * * @author Tobias Pietzsch * @author Philipp Hanslovsky */ public class OverlayPane<A> extends StackPane { /** * The {@link OverlayRendererGeneric} that draws on top of the current buffered image. */ final protected CopyOnWriteArrayList<OverlayRendererGeneric<GraphicsContext>> overlayRenderers; private final CanvasPane canvasPane = new CanvasPane(1, 1); private final ObservableList<Node> children = FXCollections.unmodifiableObservableList(super.getChildren()); // private final Canvas canvas; /** */ public OverlayPane() { super(); super.getChildren().add(canvasPane); setBackground(new Background(new BackgroundFill(Color.BLACK.deriveColor(0.0, 1.0, 1.0, 0.0), CornerRadii.EMPTY, Insets.EMPTY))); this.overlayRenderers = new CopyOnWriteArrayList<>(); final ChangeListener<Number> sizeChangeListener = (observable, oldValue, newValue) -> { final double wd = widthProperty().get(); final double hd = heightProperty().get(); final int w = (int) wd; final int h = (int) hd; if (w <= 0 || h <= 0) return; overlayRenderers.forEach(or -> or.setCanvasSize(w, h)); layout(); drawOverlays(); }; widthProperty().addListener(sizeChangeListener); heightProperty().addListener(sizeChangeListener); } public void drawOverlays() { final Runnable r = () -> { final Canvas canvas = canvasPane.getCanvas(); final GraphicsContext gc = canvas.getGraphicsContext2D(); gc.clearRect(0, 0, canvas.getWidth(), canvas.getHeight()); overlayRenderers.forEach(or -> or.drawOverlays(gc)); }; InvokeOnJavaFXApplicationThread.invoke(r); } /** * Add an {@link OverlayRendererGeneric} that draws on top of the current buffered image. * * @param renderer * overlay renderer to add. */ public void addOverlayRenderer(final OverlayRendererGeneric<GraphicsContext> renderer) { overlayRenderers.add(renderer); renderer.setCanvasSize((int) getWidth(), (int) getHeight()); } /** * Remove an {@link OverlayRendererGeneric}. * * @param renderer * overlay renderer to remove. */ public void removeOverlayRenderer(final OverlayRendererGeneric<GraphicsContext> renderer) { overlayRenderers.remove(renderer); } /** * Add handler that installs itself into the pane. * * @param h * handler to remove */ public void addHandler(final Collection<InstallAndRemove<Node>> h) { h.forEach(i -> i.installInto(this)); } /** * Add handler that removes itself from the pane. * * @param h * handler to remove */ public void removeHandler(final Collection<InstallAndRemove<Node>> h) { h.forEach(i -> i.removeFrom(this)); } @Override public ObservableList<Node> getChildren() { return this.children; } }