/*******************************************************************************
* 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
******************************************************************************/
/*
* @(#)ImageOutputFormat.java 1.1 2007-12-16
*
* Copyright (c) 1996-2007 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.datatransfer.*;
import java.awt.geom.*;
import java.awt.image.*;
import java.io.*;
import java.util.*;
import javax.imageio.*;
import javax.swing.*;
import javax.swing.filechooser.*;
import org.jhotdraw.gui.datatransfer.*;
import org.jhotdraw.io.*;
/**
* An output format for exporting drawings using one of the image formats
* supported by javax.imageio.
*
* @author Werner Randelshofer
* @version 1.1 2007-12-16 Adapted to changes in OutputFormat.
* Added support for AttributeKeys.CANVAS_FILL_COLOR.
* <br>1.0 January 2, 2007 Created.
*/
public class ImageOutputFormat implements OutputFormat {
/**
* Format description used for the file filter.
*/
private String description;
/**
* File name extension used for the file filter.
*/
private String fileExtension;
/**
* Image IO image format name.
*/
private String formatName;
/**
* The image type must match the output format, for example, PNG supports
* BufferedImage.TYPE_INT_ARGB whereas GIF needs BufferedImage.TYPE_
*/
private int imageType;
/** Creates a new image output format for Portable Network Graphics PNG. */
public ImageOutputFormat() {
this("PNG", "Portable Network Graphics (PNG)", "png", BufferedImage.TYPE_INT_ARGB);
}
/** Creates a new image output format for the specified image format.
*
* @param formatName The format name for the javax.imageio.ImageIO object.
* @param description The format description to be used for the file filter.
* @param fileExtension The file extension to be used for file filter.
* @param bufferedImageType The BufferedImage type used to produce the image.
* The value of this parameter must match with the format name.
*/
public ImageOutputFormat(String formatName, String description, String fileExtension,
int bufferedImageType) {
this.formatName = formatName;
this.description = description;
this.fileExtension = fileExtension;
this.imageType = bufferedImageType;
}
public javax.swing.filechooser.FileFilter getFileFilter() {
return new ExtensionFileFilter(description, fileExtension);
}
public String getFileExtension() {
return fileExtension;
}
public JComponent getOutputFormatAccessory() {
return null;
}
/**
* Writes the drawing to the specified file.
* This method ensures that all figures of the drawing are visible on
* the image.
*/
public void write(File file, Drawing drawing) throws IOException {
BufferedOutputStream out = null;
try {
out = new BufferedOutputStream(new FileOutputStream(file));
write(out, drawing);
} finally {
if (out != null) {
out.close();
}
}
}
/**
* Writes the drawing to the specified output stream.
* This method ensures that all figures of the drawing are visible on
* the image.
*/
public void write(OutputStream out, Drawing drawing) throws IOException {
write(out, drawing, drawing.getChildren(), null, null);
}
/**
* Writes the drawing to the specified output stream.
* This method applies the specified transform to the drawing, and draws
* it on an image of the specified size.
*/
public void write(OutputStream out, Drawing drawing,
AffineTransform drawingTransform, Dimension imageSize) throws IOException {
write(out, drawing, drawing.getChildren(), drawingTransform, imageSize);
}
/**
* Writes the drawing to the specified output stream.
* This method ensures that all figures of the drawing are visible on
* the image.
*/
public Transferable createTransferable(Drawing drawing, java.util.List<Figure> figures, double scaleFactor) throws IOException {
return new ImageTransferable(toImage(drawing, figures, scaleFactor, true));
}
/**
* Writes the figures to the specified output stream.
* This method ensures that all figures of the drawing are visible on
* the image.
*/
public void write(OutputStream out, Drawing drawing, java.util.List<Figure> figures) throws IOException {
write(out, drawing, figures, null, null);
}
/**
* Writes the figures to the specified output stream.
* This method applies the specified transform to the drawing, and draws
* it on an image of the specified size.
*/
public void write(OutputStream out, Drawing drawing, java.util.List<Figure> figures,
AffineTransform drawingTransform, Dimension imageSize) throws IOException {
BufferedImage img;
if (drawingTransform == null || imageSize == null) {
img = toImage(drawing, figures, 1d, false);
} else {
img = toImage(drawing, figures, drawingTransform, imageSize);
}
ImageIO.write(img, formatName, out);
img.flush();
}
/**
* Creates a BufferedImage from the specified list of figures.
* <p>
* The images are drawn using the specified scale factor. If some figures
* have a drawing area located at negative coordinates, then the drawing
* coordinates are translated, so that all figures are visible on the
* image.
*
* @param drawing The drawing.
* @param figures A list of figures of the drawing.
* @param scaleFactor The scale factor used when drawing the figures.
* @param clipToFigures If this is true, the image is clipped to the figures.
* If this is false, the image includes the drawing area,
*/
public BufferedImage toImage(Drawing drawing,
java.util.List<Figure> figures,
double scaleFactor, boolean clipToFigures) {
// Determine the draw bounds of the figures
Rectangle2D.Double drawBounds = null;
for (Figure f : figures) {
if (drawBounds == null) {
drawBounds = f.getDrawingArea();
} else {
drawBounds.add(f.getDrawingArea());
}
}
if (clipToFigures) {
AffineTransform transform = new AffineTransform();
transform.translate(-drawBounds.x * scaleFactor,
-drawBounds.y * scaleFactor);
transform.scale(scaleFactor, scaleFactor);
return toImage(drawing, figures, transform,
new Dimension(
(int) (drawBounds.width * scaleFactor),
(int) (drawBounds.height * scaleFactor)
)
);
} else {
AffineTransform transform = new AffineTransform();
if (drawBounds.x < 0) {
transform.translate(-drawBounds.x * scaleFactor, 0);
}
if (drawBounds.y < 0) {
transform.translate(0, -drawBounds.y * scaleFactor);
}
transform.scale(scaleFactor, scaleFactor);
return toImage(drawing, figures, transform,
new Dimension(
(int) ((Math.max(0, drawBounds.x)+drawBounds.width) * scaleFactor),
(int) ((Math.max(0, drawBounds.y)+drawBounds.height) * scaleFactor)
)
);
}
}
/**
* Creates a BufferedImage from the specified list of figures.
*
* @param drawing The drawing.
* @param figures A list of figures of the drawing.
* @param transform The AffineTransform to be used when drawing
* the figures.
* @param imageSize The width and height of the image.
*/
public BufferedImage toImage(
Drawing drawing,
java.util.List<Figure> figures,
AffineTransform transform,
Dimension imageSize) {
// Create the buffered image and clear it
Color background = AttributeKeys.CANVAS_FILL_COLOR.get(drawing);
double opacity = AttributeKeys.CANVAS_FILL_OPACITY.get(drawing);
if (background == null) {
background = new Color(0xff, 0xff, 0xff, (int)(255 * opacity));
} else {
background = new Color(background.getRed(), background.getGreen(), background.getBlue(), (int)(background.getAlpha() * opacity));
}
BufferedImage buf = new BufferedImage(
imageSize.width, imageSize.height,
(background.getAlpha() == 255) ? BufferedImage.TYPE_INT_RGB : BufferedImage.TYPE_INT_ARGB
);
Graphics2D g = buf.createGraphics();
// Clear the buffered image with the background color
Composite savedComposite = g.getComposite();
g.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC));
g.setColor(background);
g.fillRect(0,0,buf.getWidth(),buf.getHeight());
g.setComposite(savedComposite);
// Draw the figures onto the buffered image
setRenderingHints(g);
g.transform(transform);
for (Figure f : figures) {
f.draw(g);
}
g.dispose();
// Convert the image, if it does not have the specified image type
if (imageType != BufferedImage.TYPE_INT_ARGB) {
BufferedImage buf2 = new BufferedImage(
buf.getWidth(), buf.getHeight(),
imageType
);
g = buf2.createGraphics();
setRenderingHints(g);
g.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC));
g.drawImage(buf, 0, 0, null);
g.dispose();
buf.flush();
buf = buf2;
}
return buf;
}
protected void setRenderingHints(Graphics2D g) {
g.setRenderingHint(RenderingHints.KEY_ALPHA_INTERPOLATION,
RenderingHints.VALUE_ALPHA_INTERPOLATION_QUALITY);
g.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON);
g.setRenderingHint(RenderingHints.KEY_COLOR_RENDERING,
RenderingHints.VALUE_COLOR_RENDER_QUALITY);
g.setRenderingHint(RenderingHints.KEY_FRACTIONALMETRICS,
RenderingHints.VALUE_FRACTIONALMETRICS_ON);
g.setRenderingHint(RenderingHints.KEY_INTERPOLATION,
RenderingHints.VALUE_INTERPOLATION_BICUBIC);
g.setRenderingHint(RenderingHints.KEY_RENDERING,
RenderingHints.VALUE_RENDER_QUALITY);
g.setRenderingHint(RenderingHints.KEY_STROKE_CONTROL,
RenderingHints.VALUE_STROKE_NORMALIZE);
g.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING,
RenderingHints.VALUE_TEXT_ANTIALIAS_ON);
}
}