/******************************************************************************* * Copyright (c) 2006-2012 * Software Technology Group, Dresden University of Technology * DevBoost GmbH, Berlin, Amtsgericht Charlottenburg, HRB 140026 * * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * Software Technology Group - TU Dresden, Germany; * DevBoost GmbH - Berlin, Germany * - initial API and implementation ******************************************************************************/ /* * @(#)ImageFigure.java 1.0 December 14, 2006 * * Copyright (c) 1996-2006 by the original authors of JHotDraw * and all its contributors. * All rights reserved. * * The copyright of this software is owned by the authors and * contributors of the JHotDraw project ("the copyright holders"). * You may not use, copy or modify this software, except in * accordance with the license agreement you entered into with * the copyright holders. For details see accompanying license terms. */ package org.jhotdraw.draw; import java.awt.*; import java.awt.event.*; import java.awt.geom.*; import java.awt.image.*; import java.io.*; import java.util.*; import javax.imageio.*; import javax.swing.*; import javax.swing.event.*; import org.jhotdraw.geom.*; import org.jhotdraw.io.Base64; import org.jhotdraw.util.*; import org.jhotdraw.xml.*; import static org.jhotdraw.draw.AttributeKeys.*; /** * A Figure that shows an Image. * <p> * A DrawingEditor should provide the ImageTool to create an ImageFigure. * * @see ImageTool * * @author Werner Randelshofer * @version 1.0 December 14, 2006 Created. */ public class ImageFigure extends AbstractAttributedDecoratedFigure implements ImageHolderFigure { /** * This rectangle describes the bounds into which we draw the image. */ private Rectangle2D.Double rectangle; /** * The image data. This can be null, if the image was created from a * BufferedImage. */ private byte[] imageData; /** * The buffered image. This can be null, if we haven't yet parsed the * imageData. */ private transient BufferedImage bufferedImage; /** Creates a new instance. */ public ImageFigure() { this(0, 0, 0, 0); } public ImageFigure(double x, double y, double width, double height) { rectangle = new Rectangle2D.Double(x, y, width, height); } // DRAWING protected void drawFigure(Graphics2D g) { if (AttributeKeys.FILL_COLOR.get(this) != null) { g.setColor(AttributeKeys.FILL_COLOR.get(this)); drawFill(g); } drawImage(g); if (STROKE_COLOR.get(this) != null && STROKE_WIDTH.get(this) > 0d) { g.setStroke(AttributeKeys.getStroke(this)); g.setColor(STROKE_COLOR.get(this)); drawStroke(g); } if (TEXT_COLOR.get(this) != null) { if (TEXT_SHADOW_COLOR.get(this) != null && TEXT_SHADOW_OFFSET.get(this) != null) { Dimension2DDouble d = TEXT_SHADOW_OFFSET.get(this); g.translate(d.width, d.height); g.setColor(TEXT_SHADOW_COLOR.get(this)); drawText(g); g.translate(-d.width, -d.height); } g.setColor(TEXT_COLOR.get(this)); drawText(g); } } protected void drawFill(Graphics2D g) { Rectangle2D.Double r = (Rectangle2D.Double) rectangle.clone(); double grow = AttributeKeys.getPerpendicularFillGrowth(this); Geom.grow(r, grow, grow); g.fill(r); } protected void drawImage(Graphics2D g) { BufferedImage image = getBufferedImage(); if (image != null) { g.drawImage(image, (int) rectangle.x, (int) rectangle.y, (int) rectangle.width, (int) rectangle.height, null); } else { g.setStroke(new BasicStroke()); g.setColor(Color.red); g.draw(rectangle); g.draw(new Line2D.Double(rectangle.x, rectangle.y, rectangle.x + rectangle.width, rectangle.y + rectangle.height)); g.draw(new Line2D.Double(rectangle.x + rectangle.width, rectangle.y, rectangle.x, rectangle.y + rectangle.height)); } } protected void drawStroke(Graphics2D g) { Rectangle2D.Double r = (Rectangle2D.Double) rectangle.clone(); double grow = AttributeKeys.getPerpendicularDrawGrowth(this); Geom.grow(r, grow, grow); g.draw(r); } // SHAPE AND BOUNDS public Rectangle2D.Double getBounds() { Rectangle2D.Double bounds = (Rectangle2D.Double) rectangle.clone(); return bounds; } @Override public Rectangle2D.Double getFigureDrawingArea() { Rectangle2D.Double r = (Rectangle2D.Double) rectangle.clone(); double grow = AttributeKeys.getPerpendicularHitGrowth(this); Geom.grow(r, grow, grow); return r; } /** * Checks if a Point2D.Double is inside the figure. */ public boolean figureContains(Point2D.Double p) { Rectangle2D.Double r = (Rectangle2D.Double) rectangle.clone(); double grow = AttributeKeys.getPerpendicularHitGrowth(this) + 1d; Geom.grow(r, grow, grow); return r.contains(p); } public void setBounds(Point2D.Double anchor, Point2D.Double lead) { rectangle.x = Math.min(anchor.x, lead.x); rectangle.y = Math.min(anchor.y, lead.y); rectangle.width = Math.max(0.1, Math.abs(lead.x - anchor.x)); rectangle.height = Math.max(0.1, Math.abs(lead.y - anchor.y)); } /** * Transforms the figure. * @param tx The transformation. */ public void transform(AffineTransform tx) { Point2D.Double anchor = getStartPoint(); Point2D.Double lead = getEndPoint(); setBounds( (Point2D.Double) tx.transform(anchor, anchor), (Point2D.Double) tx.transform(lead, lead)); } // ATTRIBUTES public void restoreTransformTo(Object geometry) { rectangle.setRect((Rectangle2D.Double) geometry); } public Object getTransformRestoreData() { return (Rectangle2D.Double) rectangle.clone(); } // EDITING @Override public Collection<Action> getActions(Point2D.Double p) { ResourceBundleUtil labels = ResourceBundleUtil.getBundle("org.jhotdraw.draw.Labels"); LinkedList<Action> actions = new LinkedList<Action>(); return actions; } // CONNECTING public Connector findConnector(Point2D.Double p, ConnectionFigure prototype) { // XXX - This doesn't work with a transformed rect return new ChopRectangleConnector(this); } public Connector findCompatibleConnector(Connector c, boolean isStartConnector) { // XXX - This doesn't work with a transformed rect return new ChopRectangleConnector(this); } // COMPOSITE FIGURES // CLONING public ImageFigure clone() { ImageFigure that = (ImageFigure) super.clone(); that.rectangle = (Rectangle2D.Double) this.rectangle.clone(); return that; } public void read(DOMInput in) throws IOException { super.read(in); if (in.getElementCount("imageData") > 0) { in.openElement("imageData"); String base64Data = in.getText(); if (base64Data != null) { setImageData(Base64.decode(base64Data)); } in.closeElement(); } } public void write(DOMOutput out) throws IOException { super.write(out); if (getImageData() != null) { out.openElement("imageData"); out.addText(Base64.encodeBytes(getImageData())); out.closeElement(); } } /** * Sets the image. * * @param imageData The image data. If this is null, a buffered image must * be provided. * @param bufferedImage An image constructed from the imageData. If this * is null, imageData must be provided. */ public void setImage(byte[] imageData, BufferedImage bufferedImage) { willChange(); this.imageData = imageData; this.bufferedImage = bufferedImage; changed(); } /** * Sets the image data. * This clears the buffered image. */ public void setImageData(byte[] imageData) { willChange(); this.imageData = imageData; this.bufferedImage = null; changed(); } /** * Sets the buffered image. * This clears the image data. */ public void setBufferedImage(BufferedImage image) { willChange(); this.imageData = null; this.bufferedImage = image; changed(); } /** * Gets the buffered image. If necessary, this method creates the buffered * image from the image data. */ public BufferedImage getBufferedImage() { if (bufferedImage == null && imageData != null) { try { bufferedImage = ImageIO.read(new ByteArrayInputStream(imageData)); } catch (IOException e) { e.printStackTrace(); // If we can't create a buffered image from the image data, // there is no use to keep the image data and try again, so // we drop the image data. imageData = null; } } return bufferedImage; } /** * Gets the image data. If necessary, this method creates the image * data from the buffered image. */ public byte[] getImageData() { if (bufferedImage != null && imageData == null) { try { ByteArrayOutputStream bout = new ByteArrayOutputStream(); ImageIO.write(bufferedImage, "PNG", bout); bout.close(); imageData = bout.toByteArray(); } catch (IOException e) { e.printStackTrace(); // If we can't create image data from the buffered image, // there is no use to keep the buffered image and try again, so // we drop the buffered image. bufferedImage = null; } } return imageData; } public void loadImage(File file) throws IOException { InputStream in = null; try { in = new FileInputStream(file); loadImage(in); } catch (Throwable t) { ResourceBundleUtil labels = ResourceBundleUtil.getBundle("org.jhotdraw.draw.Labels"); IOException e = new IOException(labels.getFormatted("file.failedToLoadImage.message", file.getName())); e.initCause(t); throw e; } finally { if (in != null) { in.close(); } } } public void loadImage(InputStream in) throws IOException { ByteArrayOutputStream baos = new ByteArrayOutputStream(); byte[] buf = new byte[512]; int bytesRead; while ((bytesRead = in.read(buf)) > 0) { baos.write(buf, 0, bytesRead); } BufferedImage img = ImageIO.read(new ByteArrayInputStream(baos.toByteArray())); if (img == null) { ResourceBundleUtil labels = ResourceBundleUtil.getBundle("org.jhotdraw.draw.Labels"); throw new IOException(labels.getFormatted("file.failedToLoadImage.message", in.toString())); } imageData = baos.toByteArray(); bufferedImage = img; } private void writeObject(ObjectOutputStream out) throws IOException { // The call to getImageData() ensures that we have serializable data // in the imageData array. getImageData(); out.defaultWriteObject(); } }