Files
pj2/lib/edu/rit/swing/DisplayableIO.java
2016-04-27 01:07:35 -04:00

564 lines
17 KiB
Java

//******************************************************************************
//
// File: DisplayableIO.java
// Package: edu.rit.swing
// Unit: Class edu.rit.swing.DisplayableIO
//
// This Java source file is copyright (C) 2013 by Alan Kaminsky. All rights
// reserved. For further information, contact the author, Alan Kaminsky, at
// ark@cs.rit.edu.
//
// This Java source file is part of the Parallel Java 2 Library ("PJ2"). PJ2 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.
//
// PJ2 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.
//
// A copy of the GNU General Public License is provided in the file gpl.txt. You
// may also obtain a copy of the GNU General Public License on the World Wide
// Web at http://www.gnu.org/licenses/gpl.html.
//
//******************************************************************************
package edu.rit.swing;
import edu.rit.image.BilevelPngWriter;
import edu.rit.image.ByteImageQueue;
import edu.rit.image.ColorArray;
import edu.rit.image.ColorImageQueue;
import edu.rit.image.ColorPngWriter;
import edu.rit.image.GrayArray;
import edu.rit.image.GrayImageQueue;
import edu.rit.image.GrayPngWriter;
import edu.rit.image.PixelUnit;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.RenderingHints;
import java.awt.geom.Rectangle2D;
import java.awt.image.BufferedImage;
import java.awt.image.DataBufferByte;
import java.awt.image.DataBufferInt;
import java.awt.print.PageFormat;
import java.awt.print.Printable;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InterruptedIOException;
import java.io.OutputStream;
import javax.print.Doc;
import javax.print.DocFlavor;
import javax.print.PrintException;
import javax.print.SimpleDoc;
import javax.print.StreamPrintService;
import javax.print.StreamPrintServiceFactory;
/**
* Class DisplayableIO provides static methods for writing displayable objects
* as image files. You can write PNG files and PostScript files.
*
* @author Alan Kaminsky
* @version 19-Jul-2013
*/
public class DisplayableIO
{
// Prevent construction.
private DisplayableIO()
{
}
// Exported operations.
/**
* Write the given displayable object to a PNG file in the form of a bilevel
* (black and white) image. The default scale factor (one display unit = one
* pixel) is used. The default physical pixel dimensions (300×300
* pixels per inch) are used.
*
* @param theDisplayable Displayable object to write.
* @param theFile Image file.
*
* @exception IOException
* Thrown if an I/O error occurred.
*/
public static void writeBilevelPNGFile
(Displayable theDisplayable,
File theFile)
throws IOException
{
writeBilevelPNGFile (theDisplayable, theFile, 1.0);
}
/**
* Write the given displayable object to a PNG file in the form of a bilevel
* (black and white) image with the given scale factor. One display unit =
* <TT>theScale</TT> pixels. The default physical pixel dimensions
* (300&times;300 pixels per inch) are used.
*
* @param theDisplayable Displayable object to write.
* @param theFile Image file.
* @param theScale Scale factor for converting display units to
* pixels.
*
* @exception IOException
* Thrown if an I/O error occurred.
*/
public static void writeBilevelPNGFile
(Displayable theDisplayable,
File theFile,
double theScale)
throws IOException
{
writeBilevelPNGFile (theDisplayable, theFile, theScale, 300, 300);
}
/**
* Write the given displayable object to a PNG file in the form of a bilevel
* (black and white) image with the given scale factor and physical pixel
* dimensions. One display unit = <TT>theScale</TT> pixels.
*
* @param theDisplayable Displayable object to write.
* @param theFile Image file.
* @param theScale Scale factor for converting display units to
* pixels.
* @param xPpi Pixels per inch, X axis.
* @param yPpi Pixels per inch, Y axis.
*
* @exception IOException
* Thrown if an I/O error occurred.
*/
public static void writeBilevelPNGFile
(Displayable theDisplayable,
File theFile,
double theScale,
int xPpi,
int yPpi)
throws IOException
{
// Get displayable object's bounds.
Rectangle2D bounds = theDisplayable.getBoundingBox();
final int w = (int) (bounds.getWidth() * theScale + 0.5);
final int h = (int) (bounds.getHeight() * theScale + 0.5);
// Create a buffered image of the right size and type.
final BufferedImage bi = new BufferedImage
(w, h, BufferedImage.TYPE_BYTE_BINARY);
// Get graphics context to write into the buffered image.
Graphics2D g2d = bi.createGraphics();
// Fill with background paint.
g2d.setPaint (theDisplayable.getBackgroundPaint());
g2d.fill (new Rectangle2D.Double (0, 0, w, h));
// Scale graphics context by scale factor.
g2d.scale (theScale, theScale);
// Shift graphics context's origin to bounding box's origin.
g2d.translate
(- bounds.getX() * theScale,
- bounds.getY() * theScale);
// Draw the displayable object.
theDisplayable.draw (g2d);
// Write the PNG file.
OutputStream out = new BufferedOutputStream
(new FileOutputStream (theFile));
BilevelPngWriter writer = new BilevelPngWriter (h, w, out);
writer.setPixelDimensions (xPpi, yPpi, PixelUnit.PIXELS_PER_INCH);
final ByteImageQueue queue = writer.getImageQueue();
new Thread()
{
public void run()
{
byte[] data = ((DataBufferByte) bi.getRaster().getDataBuffer())
.getData();
int len = (w + 7)/8;
byte[] byteArray = new byte [w];
for (int row = 0; row < h; ++ row)
{
for (int col = 0; col < w; ++ col)
byteArray[col] = (byte)
((data[(len*row)+(col>>3)] >> (7 - (col & 7))) & 1);
try { queue.put (row, byteArray); }
catch (InterruptedException exc) {}
}
}
} .start();
try
{
writer.write();
out.close();
}
catch (InterruptedException exc)
{
throw new InterruptedIOException();
}
}
/**
* Write the given displayable object to a PNG file in the form of a
* grayscale image. The default scale factor (one display unit = one pixel)
* is used. The default physical pixel dimensions (300&times;300 pixels per
* inch) are used.
*
* @param theDisplayable Displayable object to write.
* @param theFile Image file.
*
* @exception IOException
* Thrown if an I/O error occurred.
*/
public static void writeGrayscalePNGFile
(Displayable theDisplayable,
File theFile)
throws IOException
{
writeGrayscalePNGFile (theDisplayable, theFile, 1.0);
}
/**
* Write the given displayable object to a PNG file in the form of a
* grayscale image with the given scale factor. One display unit =
* <TT>theScale</TT> pixels. The default physical pixel dimensions
* (300&times;300 pixels per inch) are used.
*
* @param theDisplayable Displayable object to write.
* @param theFile Image file.
* @param theScale Scale factor for converting display units to
* pixels.
*
* @exception IOException
* Thrown if an I/O error occurred.
*/
public static void writeGrayscalePNGFile
(Displayable theDisplayable,
File theFile,
double theScale)
throws IOException
{
writeGrayscalePNGFile (theDisplayable, theFile, theScale, 300, 300);
}
/**
* Write the given displayable object to a PNG file in the form of a
* grayscale image with the given scale factor and physical pixel
* dimensions. One display unit = <TT>theScale</TT> pixels.
*
* @param theDisplayable Displayable object to write.
* @param theFile Image file.
* @param theScale Scale factor for converting display units to
* pixels.
* @param xPpi Pixels per inch, X axis.
* @param yPpi Pixels per inch, Y axis.
*
* @exception IOException
* Thrown if an I/O error occurred.
*/
public static void writeGrayscalePNGFile
(Displayable theDisplayable,
File theFile,
double theScale,
int xPpi,
int yPpi)
throws IOException
{
// Get displayable object's bounds.
Rectangle2D bounds = theDisplayable.getBoundingBox();
final int w = (int) (bounds.getWidth() * theScale + 0.5);
final int h = (int) (bounds.getHeight() * theScale + 0.5);
// Create a buffered image of the right size and type.
final BufferedImage bi = new BufferedImage
(w, h, BufferedImage.TYPE_BYTE_GRAY);
// Get graphics context to write into the buffered image.
Graphics2D g2d = bi.createGraphics();
g2d.setRenderingHint
(RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON);
g2d.setRenderingHint
(RenderingHints.KEY_TEXT_ANTIALIASING,
RenderingHints.VALUE_TEXT_ANTIALIAS_ON);
// Fill with background paint.
g2d.setPaint (theDisplayable.getBackgroundPaint());
g2d.fill (new Rectangle2D.Double (0, 0, w, h));
// Scale graphics context by scale factor.
g2d.scale (theScale, theScale);
// Shift graphics context's origin to bounding box's origin.
g2d.translate
(- bounds.getX() * theScale,
- bounds.getY() * theScale);
// Draw the displayable object.
theDisplayable.draw (g2d);
// Write the PNG file.
OutputStream out = new BufferedOutputStream
(new FileOutputStream (theFile));
GrayPngWriter writer = new GrayPngWriter (h, w, out);
writer.setPixelDimensions (xPpi, yPpi, PixelUnit.PIXELS_PER_INCH);
final GrayImageQueue queue = writer.getImageQueue();
new Thread()
{
public void run()
{
byte[] data = ((DataBufferByte) bi.getRaster().getDataBuffer())
.getData();
GrayArray grayArray = new GrayArray (w);
for (int row = 0; row < h; ++ row)
{
for (int col = 0; col < w; ++ col)
grayArray.gray (col, data[w*row+col]);
try { queue.put (row, grayArray); }
catch (InterruptedException exc) {}
}
}
} .start();
try
{
writer.write();
out.close();
}
catch (InterruptedException exc)
{
throw new InterruptedIOException();
}
}
/**
* Write the given displayable object to a PNG file in the form of a 24-bit
* color image. The default scale factor (one display unit = one pixel) is
* used. The default physical pixel dimensions (300&times;300 pixels per
* inch) are used.
*
* @param theDisplayable Displayable object to write.
* @param theFile Image file.
*
* @exception IOException
* Thrown if an I/O error occurred.
*/
public static void writeColorPNGFile
(Displayable theDisplayable,
File theFile)
throws IOException
{
writeColorPNGFile (theDisplayable, theFile, 1.0);
}
/**
* Write the given displayable object to a PNG file in the form of a 24-bit
* color image with the given scale factor. One display unit =
* <TT>theScale</TT> pixels. The default physical pixel dimensions
* (300&times;300 pixels per inch) are used.
*
* @param theDisplayable Displayable object to write.
* @param theFile Image file.
* @param theScale Scale factor for converting display units to
* pixels.
*
* @exception IOException
* Thrown if an I/O error occurred.
*/
public static void writeColorPNGFile
(Displayable theDisplayable,
File theFile,
double theScale)
throws IOException
{
writeColorPNGFile (theDisplayable, theFile, theScale, 300, 300);
}
/**
* Write the given displayable object to a PNG file in the form of a 24-bit
* color image with the given scale factor and physical pixel dimensions.
* One display unit = <TT>theScale</TT> pixels.
*
* @param theDisplayable Displayable object to write.
* @param theFile Image file.
* @param theScale Scale factor for converting display units to
* pixels.
* @param xPpi Pixels per inch, X axis.
* @param yPpi Pixels per inch, Y axis.
*
* @exception IOException
* Thrown if an I/O error occurred.
*/
public static void writeColorPNGFile
(Displayable theDisplayable,
File theFile,
double theScale,
int xPpi,
int yPpi)
throws IOException
{
// Get displayable object's bounds.
Rectangle2D bounds = theDisplayable.getBoundingBox();
final int w = (int) (bounds.getWidth() * theScale + 0.5);
final int h = (int) (bounds.getHeight() * theScale + 0.5);
// Create a buffered image of the right size and type.
final BufferedImage bi = new BufferedImage
(w, h, BufferedImage.TYPE_INT_RGB);
// Get graphics context to write into the buffered image.
Graphics2D g2d = bi.createGraphics();
g2d.setRenderingHint
(RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON);
g2d.setRenderingHint
(RenderingHints.KEY_TEXT_ANTIALIASING,
RenderingHints.VALUE_TEXT_ANTIALIAS_ON);
// Fill with background paint.
g2d.setPaint (theDisplayable.getBackgroundPaint());
g2d.fill (new Rectangle2D.Double (0, 0, w, h));
// Scale graphics context by scale factor.
g2d.scale (theScale, theScale);
// Shift graphics context's origin to bounding box's origin.
g2d.translate
(- bounds.getX() * theScale,
- bounds.getY() * theScale);
// Draw the displayable object.
theDisplayable.draw (g2d);
// Write the PNG file.
OutputStream out = new BufferedOutputStream
(new FileOutputStream (theFile));
ColorPngWriter writer = new ColorPngWriter (h, w, out);
writer.setPixelDimensions (xPpi, yPpi, PixelUnit.PIXELS_PER_INCH);
final ColorImageQueue queue = writer.getImageQueue();
new Thread()
{
public void run()
{
int[] data = ((DataBufferInt) bi.getRaster().getDataBuffer())
.getData();
ColorArray colorArray = new ColorArray (w);
for (int row = 0; row < h; ++ row)
{
for (int col = 0; col < w; ++ col)
colorArray.rgb (col, data[w*row+col] & 0x00FFFFFF);
try { queue.put (row, colorArray); }
catch (InterruptedException exc) {}
}
}
} .start();
try
{
writer.write();
out.close();
}
catch (InterruptedException exc)
{
throw new InterruptedIOException();
}
}
/**
* Write the given displayable object to a PostScript file.
*
* @param theDisplayable Displayable object to write.
* @param theFile PostScript file.
*
* @exception IOException
* Thrown if an I/O error occurred.
*/
public static void writePostScriptFile
(Displayable theDisplayable,
File theFile)
throws IOException
{
// Find a suitable stream print service factory.
DocFlavor theFlavor = DocFlavor.SERVICE_FORMATTED.PRINTABLE;
StreamPrintServiceFactory[] factories =
StreamPrintServiceFactory.lookupStreamPrintServiceFactories
(theFlavor,
"application/postscript");
if (factories.length == 0)
{
throw new IOException ("Cannot find PostScript generator");
}
// Get displayable object's bounds.
final Displayable disp = theDisplayable;
final Rectangle2D bounds = theDisplayable.getBoundingBox();
final double w = bounds.getWidth();
final double h = bounds.getHeight();
// Create a printable object to print the displayable object.
Printable thePrintData = new Printable()
{
public int print
(Graphics g,
PageFormat pageFormat,
int pageIndex)
{
// Only print one page.
if (pageIndex != 0)
{
return Printable.NO_SUCH_PAGE;
}
// Get graphics context to write into the page.
Graphics2D g2d = (Graphics2D) g;
// Shift graphics context's origin to top left of imageable
// area.
g2d.translate
(pageFormat.getImageableX(),
pageFormat.getImageableY());
// Scale so drawable object fits within imageable area.
double xScale = pageFormat.getImageableWidth() / w;
double yScale = pageFormat.getImageableHeight() / h;
double scale = Math.min (1.0, Math.min (xScale, yScale));
g2d.scale (scale, scale);
// Shift graphics context's origin to bounding box's origin.
g2d.translate
(- bounds.getX() * scale,
- bounds.getY() * scale);
// Draw the displayable object.
disp.draw (g2d);
return Printable.PAGE_EXISTS;
}
};
// Print PostScript into file.
FileOutputStream fos = new FileOutputStream (theFile);
StreamPrintService sps = factories[0].getPrintService (fos);
Doc doc = new SimpleDoc (thePrintData, theFlavor, null);
try
{
sps.createPrintJob().print (doc, null);
}
catch (PrintException exc)
{
IOException exc2 = new IOException ("Cannot generate PostScript");
exc2.initCause (exc);
throw exc2;
}
}
}