Search in sources :

Example 1 with AbstractOverlay

use of qupath.lib.gui.viewer.overlays.AbstractOverlay in project qupath by qupath.

the class QuPathViewer method paintViewer.

protected void paintViewer(Graphics g, int w, int h) {
    ImageServer<BufferedImage> server = getServer();
    if (server == null) {
        g.setColor(background);
        g.fillRect(0, 0, w, h);
        updateRepaintTimestamp();
        return;
    }
    // // Get dimensions
    // int w = getWidth();
    // int h = getHeight();
    Rectangle clip = g.getClipBounds();
    boolean clipFull;
    if (clip == null) {
        clip = new Rectangle(0, 0, w, h);
        g.setClip(0, 0, w, h);
        clipFull = true;
    } else
        clipFull = clip.x == 0 && clip.y == 0 && clip.width == w && clip.height == h;
    // Ensure we have a sufficiently-large buffer
    if (imgBuffer == null || imgBuffer.getWidth() != w || imgBuffer.getHeight() != h) {
        // Create buffered images & buffers for RGB pixel values
        imgBuffer = createBufferedImage(w, h);
        imgBuffer.setAccelerationPriority(1f);
        logger.trace("New buffered image created: {}", imgBuffer);
        // imgVolatile = createVolatileImage(w, h);
        imageUpdated = true;
        // If the size changed, ensure the AffineTransform is up-to-date
        updateAffineTransform();
    }
    // Get the displayed region
    Shape shapeRegion = getDisplayedRegionShape();
    // The visible shape must have changed if there wasn't one previously...
    // Otherwise check if it has changed & update accordingly
    // This will be used to notify listeners soon
    boolean shapeChanged = lastVisibleShape == null || !lastVisibleShape.equals(shapeRegion);
    long t1 = System.currentTimeMillis();
    // Only repaint the image if this is requested, otherwise only overlays need to be repainted
    if (imageUpdated || locationUpdated) {
        // || imgVolatile.contentsLost()) {
        // Set flags that image no longer requiring an update
        // By setting them early, they might still be reset during this run... in which case we don't want to thwart the re-run
        imageUpdated = false;
        locationUpdated = false;
        // updateBufferedImage(imgVolatile, shapeRegion, w, h);
        updateBufferedImage(imgBuffer, shapeRegion, w, h);
    }
    // if (imageUpdated || locationUpdated) {
    // updateBufferedImage(imgVolatile, shapeRegion, w, h);
    // //			updateBufferedImage(imgBuffer, shapeRegion, w, h);
    // //			logger.info("INITIAL Image drawing time: " + (System.currentTimeMillis() - t1));
    // imgVolatile.createGraphics().drawImage(imgBuffer, 0, 0, this);
    // }
    // while (imgVolatile.contentsLost()) {
    // imgVolatile.createGraphics().drawImage(imgBuffer, 0, 0, this);
    // }
    // Store the last shape visible
    lastVisibleShape = shapeRegion;
    // Draw the image from the buffer
    // The call to super.paintComponent is delayed until here to try to stop occasional flickering on Apple's Java 6
    g.setColor(background);
    if (clipFull)
        paintFinalImage(g, imgBuffer, this);
    else
        // g2d.drawImage(imgBuffer, 0, 0, getWidth(), getHeight(), this);
        g.drawImage(imgBuffer, clip.x, clip.y, clip.x + clip.width, clip.y + clip.height, clip.x, clip.y, clip.x + clip.width, clip.y + clip.height, null);
    if (logger.isTraceEnabled()) {
        long t2 = System.currentTimeMillis();
        logger.trace("Final image drawing time: {}", (t2 - t1));
    }
    // Really useful only for debugging graphics
    if (!(g instanceof Graphics2D)) {
        imageUpdated = false;
        // Notify any listeners of shape changes
        if (shapeChanged)
            fireVisibleRegionChangedEvent(lastVisibleShape);
        return;
    }
    double downsample = getDownsampleFactor();
    float opacity = overlayOptions.getOpacity();
    Graphics2D g2d = (Graphics2D) g.create();
    // Apply required transform to the graphics object (rotation, scaling, shifting...)
    g2d.transform(transform);
    Composite previousComposite = g2d.getComposite();
    boolean paintCompletely = thumbnailIsFullImage || !doFasterRepaint;
    // var regionBounds = AwtTools.getImageRegion(clip, getZPosition(), getTPosition());
    if (opacity > 0 || PathPrefs.alwaysPaintSelectedObjectsProperty().get()) {
        if (opacity < 1) {
            AlphaComposite composite = AlphaComposite.getInstance(AlphaComposite.SRC_OVER, opacity);
            g2d.setComposite(composite);
        }
        Color color = getSuggestedOverlayColor();
        // Paint the overlay layers
        var imageData = this.imageDataProperty.get();
        for (PathOverlay overlay : allOverlayLayers.toArray(PathOverlay[]::new)) {
            logger.trace("Painting overlay: {}", overlay);
            if (overlay instanceof AbstractOverlay)
                ((AbstractOverlay) overlay).setPreferredOverlayColor(color);
            // overlay.paintOverlay(g2d, regionBounds, downsample, null, paintCompletely);
            overlay.paintOverlay(g2d, getServerBounds(), downsample, imageData, paintCompletely);
        }
    // if (hierarchyOverlay != null) {
    // hierarchyOverlay.setPreferredOverlayColor(color);
    // hierarchyOverlay.paintOverlay(g2d, getServerBounds(), downsampleFactor, null, paintCompletely);
    // }
    }
    // Paint the selected object
    PathObjectHierarchy hierarchy = getHierarchy();
    PathObject mainSelectedObject = getSelectedObject();
    Rectangle2D boundsRect = null;
    boolean useSelectedColor = PathPrefs.useSelectedColorProperty().get();
    boolean paintSelectedBounds = PathPrefs.paintSelectedBoundsProperty().get();
    for (PathObject selectedObject : hierarchy.getSelectionModel().getSelectedObjects().toArray(new PathObject[0])) {
        // TODO: Simplify this...
        if (selectedObject != null && selectedObject.hasROI() && selectedObject.getROI().getZ() == getZPosition() && selectedObject.getROI().getT() == getTPosition()) {
            if (!selectedObject.isDetection()) {
                // Ensure a selected ROI can be seen clearly
                if (previousComposite != null)
                    g2d.setComposite(previousComposite);
                g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
            }
            Rectangle boundsDisplayed = shapeRegion.getBounds();
            ROI pathROI = selectedObject.getROI();
            // if ((PathPrefs.getPaintSelectedBounds() || (selectedObject.isDetection() && !PathPrefs.getUseSelectedColor())) && !(pathROI instanceof RectangleROI)) {
            if (pathROI != null && (paintSelectedBounds || (!useSelectedColor)) && !(pathROI instanceof RectangleROI) && !pathROI.isEmpty()) {
                Shape boundsShape = null;
                if (pathROI.isPoint()) {
                    var hull = pathROI.getConvexHull();
                    if (hull != null)
                        boundsShape = hull.getShape();
                }
                if (boundsShape == null) {
                    boundsRect = AwtTools.getBounds2D(pathROI, boundsRect);
                    boundsShape = boundsRect;
                }
                // Tried to match to pixel boundaries... but resulted in too much jiggling
                // boundsShape.setFrame(
                // Math.round(boundsShape.getX()/downsampleFactor)*downsampleFactor-downsampleFactor,
                // Math.round(boundsShape.getY()/downsampleFactor)*downsampleFactor-downsampleFactor,
                // Math.round(boundsShape.getWidth()/downsampleFactor)*downsampleFactor+2*downsampleFactor,
                // Math.round(boundsShape.getHeight()/downsampleFactor)*downsampleFactor+2*downsampleFactor);
                // boundsShape.setFrame(boundsShape.getX()-downsampleFactor, boundsShape.getY()-downsampleFactor, boundsShape.getWidth()+2*downsampleFactor, boundsShape.getHeight()+2*downsampleFactor);
                PathHierarchyPaintingHelper.paintShape(boundsShape, g2d, getSuggestedOverlayColor(), PathHierarchyPaintingHelper.getCachedStroke(Math.max(downsample, 1) * 2), null);
            // boundsShape.setFrame(boundsShape.getX()+downsampleFactor, boundsShape.getY()-downsampleFactor, boundsShape.getWidth(), boundsShape.getHeight());
            // PathHierarchyPaintingHelper.paintShape(boundsShape, g2d, new Color(1f, 1f, 1f, 0.75f), PathHierarchyPaintingHelper.getCachedStroke(Math.max(downsampleFactor, 1)*2), null, downsampleFactor);
            }
            // in a cached way
            if ((selectedObject.isDetection() && PathPrefs.useSelectedColorProperty().get()) || !PathObjectTools.hierarchyContainsObject(hierarchy, selectedObject))
                PathHierarchyPaintingHelper.paintObject(selectedObject, false, g2d, boundsDisplayed, overlayOptions, getHierarchy().getSelectionModel(), downsample);
            // Paint ROI handles, if required
            if (selectedObject == mainSelectedObject && roiEditor.hasROI()) {
                Stroke strokeThick = PathHierarchyPaintingHelper.getCachedStroke(PathPrefs.annotationStrokeThicknessProperty().get() * downsample);
                Color color = useSelectedColor ? ColorToolsAwt.getCachedColor(PathPrefs.colorSelectedObjectProperty().get()) : null;
                if (color == null)
                    color = ColorToolsAwt.getCachedColor(ColorToolsFX.getDisplayedColorARGB(selectedObject));
                g2d.setStroke(strokeThick);
                // Draw ROI handles using adaptive size
                double maxHandleSize = getMaxROIHandleSize();
                double minHandleSize = downsample;
                PathHierarchyPaintingHelper.paintHandles(roiEditor, g2d, minHandleSize, maxHandleSize, color, ColorToolsAwt.getTranslucentColor(color));
            }
        }
    }
    // Notify any listeners of shape changes
    if (shapeChanged)
        fireVisibleRegionChangedEvent(lastVisibleShape);
    updateRepaintTimestamp();
}
Also used : AbstractOverlay(qupath.lib.gui.viewer.overlays.AbstractOverlay) PathObjectHierarchy(qupath.lib.objects.hierarchy.PathObjectHierarchy) Stroke(java.awt.Stroke) Shape(java.awt.Shape) AlphaComposite(java.awt.AlphaComposite) Composite(java.awt.Composite) AlphaComposite(java.awt.AlphaComposite) Color(java.awt.Color) Rectangle(java.awt.Rectangle) Rectangle2D(java.awt.geom.Rectangle2D) RectangleROI(qupath.lib.roi.RectangleROI) ROI(qupath.lib.roi.interfaces.ROI) BufferedImage(java.awt.image.BufferedImage) Graphics2D(java.awt.Graphics2D) PathObject(qupath.lib.objects.PathObject) RectangleROI(qupath.lib.roi.RectangleROI) PathOverlay(qupath.lib.gui.viewer.overlays.PathOverlay)

Aggregations

AlphaComposite (java.awt.AlphaComposite)1 Color (java.awt.Color)1 Composite (java.awt.Composite)1 Graphics2D (java.awt.Graphics2D)1 Rectangle (java.awt.Rectangle)1 Shape (java.awt.Shape)1 Stroke (java.awt.Stroke)1 Rectangle2D (java.awt.geom.Rectangle2D)1 BufferedImage (java.awt.image.BufferedImage)1 AbstractOverlay (qupath.lib.gui.viewer.overlays.AbstractOverlay)1 PathOverlay (qupath.lib.gui.viewer.overlays.PathOverlay)1 PathObject (qupath.lib.objects.PathObject)1 PathObjectHierarchy (qupath.lib.objects.hierarchy.PathObjectHierarchy)1 RectangleROI (qupath.lib.roi.RectangleROI)1 ROI (qupath.lib.roi.interfaces.ROI)1