package org.janelia.alignment; import java.awt.BasicStroke; import java.awt.Color; import java.awt.Font; import java.awt.FontMetrics; import java.awt.Graphics2D; import java.awt.Rectangle; import java.awt.Stroke; import java.awt.image.BufferedImage; import java.util.List; import org.janelia.alignment.spec.TileSpec; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * Simple utility to render bounding boxes for tiles. * If there is enough area to display them, tile identifiers are also rendered inside each box. * * @author Eric Trautman */ public class BoundingBoxRenderer { private final RenderParameters renderParameters; private final double xOffset; private final double yOffset; private final double scale; private final Color foregroundColor; private final Color backgroundColor; private final Stroke stroke; public BoundingBoxRenderer(final RenderParameters renderParameters, final Color foregroundColor) { this(renderParameters, foregroundColor, 1); } public BoundingBoxRenderer(final RenderParameters renderParameters, final Color foregroundColor, final float lineWidth) { this.renderParameters = renderParameters; this.xOffset = renderParameters.getX(); this.yOffset = renderParameters.getY(); this.scale = renderParameters.getScale(); this.foregroundColor = foregroundColor; if (renderParameters.getBackgroundRGBColor() == null) { this.backgroundColor = null; } else { this.backgroundColor = new Color(renderParameters.getBackgroundRGBColor()); } this.stroke = new BasicStroke(lineWidth); } public void render(final BufferedImage targetImage) throws IllegalArgumentException { final Graphics2D targetGraphics = targetImage.createGraphics(); targetGraphics.setColor(foregroundColor); targetGraphics.setStroke(stroke); if (backgroundColor != null) { targetGraphics.setBackground(backgroundColor); targetGraphics.clearRect(0, 0, targetImage.getWidth(), targetImage.getHeight()); } final List<TileSpec> tileSpecs = renderParameters.getTileSpecs(); final int maxCharactersPerLine = 12; int lineWidth = 0; int lineHeight = 0; int minBoxWidthForTileIdRendering = 0; if (tileSpecs.size() > 0) { targetGraphics.setFont(TILE_ID_FONT); final FontMetrics metrics = targetGraphics.getFontMetrics(); lineWidth = metrics.stringWidth("A") * maxCharactersPerLine; lineHeight = metrics.getHeight(); // add margin that should be good enough for 'typical' overlap minBoxWidthForTileIdRendering = (int) (lineWidth * 1.3) + 1; } Rectangle box; String tileId; int x; int y; int start; for (final TileSpec tileSpec : tileSpecs) { box = getScaledBox(tileSpec); targetGraphics.draw(box); if (box.width > minBoxWidthForTileIdRendering) { tileId = tileSpec.getTileId(); if (tileId != null) { x = box.x + ((box.width - lineWidth) / 2); // center tileId horizontally y = box.y + (box.height / 4); // shift tileId down from top to avoid 'typical' overlap start = 0; for (int stop = maxCharactersPerLine; stop < tileId.length(); stop += maxCharactersPerLine) { targetGraphics.drawString(tileId.substring(start, stop), x, y); y = y + lineHeight; start = stop; } if (start < tileId.length()) { targetGraphics.drawString(tileId.substring(start), x, y); } } } } if (renderParameters.isAddWarpFieldDebugOverlay()) { WarpFieldDebugRenderer.render(renderParameters, targetGraphics, targetImage.getWidth(), targetImage.getHeight()); } targetGraphics.dispose(); LOG.debug("render: exit, boxes for {} tiles rendered", tileSpecs.size()); } private Rectangle getScaledBox(final TileSpec tileSpec) { final double x = (tileSpec.getMinX() - xOffset) * scale; final double y = (tileSpec.getMinY() - yOffset) * scale; final double w = ((tileSpec.getMaxX() - xOffset) * scale) - x; final double h = ((tileSpec.getMaxY() - yOffset) * scale) - y; return new Rectangle((int) x, (int) y, (int) w, (int) h); } private static final Logger LOG = LoggerFactory.getLogger(BoundingBoxRenderer.class); private static final Font TILE_ID_FONT = new Font(Font.MONOSPACED, Font.PLAIN, 12); }