package org.simantics.diagram.export; import java.awt.Graphics2D; import java.awt.Point; import java.awt.RenderingHints; import java.awt.geom.AffineTransform; import java.awt.geom.Rectangle2D; import java.awt.image.BufferedImage; import java.io.File; import javax.imageio.ImageIO; import org.simantics.g2d.canvas.ICanvasContext; import org.simantics.g2d.diagram.DiagramHints; import org.simantics.g2d.diagram.DiagramUtils; import org.simantics.g2d.diagram.IDiagram; import org.simantics.g2d.participant.TransformUtil; import org.simantics.scenegraph.g2d.G2DRenderingHints; import org.simantics.scenegraph.utils.QualityHints; import org.simantics.utils.page.MarginUtils; public class ImageBuilder { File file; Double dpi; Point size; double margin; /** * * @param file File to write the image (optional) * @param dpi Dots Per Inch * @param size Image size in pixels * @param margin percentage of image width for margins. 0.05 is 5%. */ public ImageBuilder(File file, Double dpi, Point size, double margin) { this.file = file; this.dpi = dpi; this.size = size; this.margin = Math.max(margin,0.0); } /** * @param canvasContext * the canvas context to paint * @param writeResults * true to actually write the resulting Image to the file. */ public BufferedImage paint(ICanvasContext canvasContext) throws Exception { Graphics2D g2 = null; BufferedImage image = null; try { IDiagram diagram = canvasContext.getDefaultHintContext().getHint(DiagramHints.KEY_DIAGRAM); Rectangle2D diagramRect = DiagramUtils.getContentRect(diagram); if (diagramRect == null) diagramRect = new Rectangle2D.Double(0, 0, 100, 100); // add margins to content. double off = Math.max(diagramRect.getWidth(), diagramRect.getHeight()) * margin*0.5; diagramRect = new Rectangle2D.Double(diagramRect.getX() - off, diagramRect.getY() - off, diagramRect.getWidth() + off * 2.0, diagramRect.getHeight() + off * 2.0); // Make sure the transformation is reset. AffineTransform tr = new AffineTransform(); Rectangle2D controlArea; if (dpi != null) { double mmToInch = 1.0 / 25.0; controlArea = new Rectangle2D.Double(0, 0, diagramRect.getWidth() * mmToInch * dpi, diagramRect.getHeight() * mmToInch * dpi); } else { controlArea = new Rectangle2D.Double(0, 0, size.getX(), size.getY()); } canvasContext.getSingleItem(TransformUtil.class).fitArea(controlArea, diagramRect, MarginUtils.NO_MARGINS); int width = (int) controlArea.getWidth(); int height = (int) controlArea.getHeight(); long sizeBytes = width*height*3; long free = Runtime.getRuntime().freeMemory(); if (sizeBytes >= free) { // TODO: should we estimate memory required for rendering? System.gc(); free = Runtime.getRuntime().freeMemory(); } if (sizeBytes >= free) { throw new Exception("There is not enough available memory to create the image; required " + sizeBytes + ", available " + free); } image = new BufferedImage(width, height , BufferedImage.TYPE_3BYTE_BGR); g2 = (Graphics2D) image.getGraphics(); QualityHints.HIGH_QUALITY_HINTS.setQuality(g2); g2.setRenderingHint(RenderingHints.KEY_STROKE_CONTROL, RenderingHints.VALUE_STROKE_PURE); g2.setTransform(tr); g2.setClip(new Rectangle2D.Double(0, 0, controlArea.getWidth(), controlArea.getHeight())); g2.setRenderingHint(G2DRenderingHints.KEY_CONTROL_BOUNDS, new Rectangle2D.Double(0, 0, controlArea.getWidth(), controlArea.getHeight())); if (canvasContext.isLocked()) throw new IllegalStateException("cannot render image, canvas context is locked: " + canvasContext); canvasContext.getSceneGraph().render(g2); if (file != null) { String name = file.getName(); name = name.substring(name.lastIndexOf(".") + 1); ImageIO.write(image, name, file); } } finally { if (g2 != null) g2.dispose(); } return image; } }