/* * This file is part of NodeBox. * * Copyright (C) 2008 Frederik De Bleser (frederik@pandora.be) * * NodeBox is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * NodeBox is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with NodeBox. If not, see <http://www.gnu.org/licenses/>. */ package nodebox.graphics; import javax.imageio.ImageIO; import java.awt.*; import java.awt.image.BufferedImage; import java.io.File; import java.io.IOException; import java.util.ArrayList; import java.util.Locale; public class Canvas extends AbstractTransformable { public static final double DEFAULT_WIDTH = 1000; public static final double DEFAULT_HEIGHT = 1000; private Color background = new Color(1, 1, 1); private double offsetX, offsetY; private double width, height; private ArrayList<Grob> items = new ArrayList<Grob>(); public Canvas() { this(DEFAULT_WIDTH, DEFAULT_HEIGHT); } public Canvas(double width, double height) { setSize(width, height); } public Canvas(Canvas other) { this.offsetX = other.offsetX; this.offsetY = other.offsetY; this.width = other.width; this.height = other.height; this.background = other.background == null ? null : other.background.clone(); for (Grob g : other.items) { add(g.clone()); } } /** * Convert the current canvas into a geometry object. * Only objects of a geometric nature can be present in the output. * * @return a Geometry object */ public Geometry asGeometry() { return asGeometry(true); } /** * Convert the current canvas into a geometry object. * Only objects of a geometric nature can be present in the output. * * @param clone if the items on the canvas need to be cloned. * @return a Geometry object */ public Geometry asGeometry(boolean clone) { Geometry g = new Geometry(); for (Grob item : items) { if (item instanceof Path) g.add((Path) (clone ? item.clone() : item)); else if (item instanceof Text) g.add(((Text) item).getPath()); else if (item instanceof Geometry) g.extend((Geometry) (clone ? item.clone() : item)); } return g; } public Color getBackground() { return background; } public Color setBackground(Color background) { return this.background = background; } public double getOffsetX() { return offsetX; } public void setOffsetX(double offsetX) { this.offsetX = offsetX; } public double getOffsetY() { return offsetY; } public void setOffsetY(double offsetY) { this.offsetY = offsetY; } public double getWidth() { return width; } public void setWidth(double width) { this.width = width; } public double getHeight() { return height; } public void setHeight(double height) { this.height = height; } public void setSize(double width, double height) { this.width = width; this.height = height; } //// Container operations //// public void add(Grob g) { items.add(g); } public int size() { return items.size(); } public void clear() { items.clear(); } public java.util.List<Grob> getItems() { return items; } public Grob get(int index) { try { return items.get(index); } catch (IndexOutOfBoundsException e) { return null; } } /** * Create copies of all grobs of the given group and append them to myself. * * @param c the canvas whose elements are appended. */ public void extend(Canvas c) { for (Grob grob : c.getItems()) { add(grob.clone()); } } public void transform(Transform t) { for (Grob g : items) { g.transform(t); } } //// Geometry //// public boolean isEmpty() { return items.isEmpty(); } /** * Returns the bounding box of the canvas. * <p/> * This does not compute the bounding boxes of the children, but always returns the requested canvas bounds. * * @return a bounding box with x/y at the center and width/height of the canvas. */ public Rect getBounds() { return new Rect(-width / 2 + offsetX, -height / 2 + offsetY, width, height); } public Canvas clone() { return new Canvas(this); } @Override public boolean equals(Object obj) { if (this == obj) return true; if (!(obj instanceof Canvas)) return false; Canvas other = (Canvas) obj; return width == other.width && height == other.height && background.equals(other.background) && super.equals(other); } //// Drawing //// public void inheritFromContext(GraphicsContext ctx) { // TODO: Implement } public void draw(Graphics2D g) { if (background != null) { g.setColor(background.getAwtColor()); g.fill(getBounds().getRectangle2D()); } g.clip(getBounds().getRectangle2D()); for (Grob grob : items) { grob.draw(g); } } public BufferedImage asImage() { Rect bounds = getBounds(); BufferedImage img = new BufferedImage((int) Math.round(bounds.getWidth()), (int) Math.round(bounds.getHeight()), BufferedImage.TYPE_INT_ARGB); Graphics2D g = img.createGraphics(); g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); g.translate(-bounds.getX(), -bounds.getY()); draw(g); img.flush(); return img; } public void save(File file) { if (file.getName().endsWith(".pdf")) { PDFRenderer.render(this, getBounds(), file); } else { try { ImageIO.write(asImage(), getFileExtension(file), file); } catch (IOException e) { throw new RuntimeException("Could not write image file " + file, e); } } } private String getFileExtension(File file) { String fileName = file.getName(); String ext = null; int i = fileName.lastIndexOf('.'); if (i > 0 && i < fileName.length() - 1) { ext = fileName.substring(i + 1).toLowerCase(Locale.US); } return ext; } @Override public String toString() { return "<" + getClass().getSimpleName() + ": " + width + ", " + height + ">"; } }