Java tutorial
/* Java Media APIs: Cross-Platform Imaging, Media and Visualization Alejandro Terrazas Sams, Published November 2002, ISBN 0672320940 */ import java.awt.Rectangle; import java.awt.image.Raster; import java.io.IOException; import javax.imageio.IIOImage; import javax.imageio.ImageTypeSpecifier; import javax.imageio.ImageWriteParam; import javax.imageio.ImageWriter; import javax.imageio.metadata.IIOMetadata; import javax.imageio.metadata.IIOMetadataFormat; import javax.imageio.metadata.IIOMetadataNode; import javax.imageio.spi.ImageWriterSpi; import javax.imageio.stream.ImageOutputStream; import org.w3c.dom.Node; /** * ch5ImageWriter.java -- this class provides the functionality to write an * image of format ch5. */ public class ch5ImageWriter extends ImageWriter { public ch5ImageWriter(ImageWriterSpi originatingProvider) { super(originatingProvider); streamMetadataWritten = false; } /** * this method is used to convert an ImageReader's image metadata which is * in a particular format into image metadata that can be used for this * ImageWriter. Primarily this is used for transcoding (format conversion). * This ImageWriter does not support such conversions */ public IIOMetadata convertImageMetadata(IIOMetadata metadata, ImageTypeSpecifier specifier, ImageWriteParam param) { return null; } /** * this method is used to convert an ImageReader's stream metadata which is * in a particular format into stream metadata that can be used for this * ImageWriter. Primarily this is used for transcoding (format conversion). * This ImageWriter does not support such conversions */ public IIOMetadata convertStreamMetadata(IIOMetadata metadata, ImageWriteParam param) { return null; } /** * provide default values for the image metadata */ public IIOMetadata getDefaultImageMetadata(ImageTypeSpecifier specifier, ImageWriteParam param) { ch5ImageMetadata imagemd = new ch5ImageMetadata(); int width = raster.getWidth(); int height = raster.getHeight(); imagemd.initialize(width, height); // default image size return imagemd; } /** * provide default values for the stream metadata */ public IIOMetadata getDefaultStreamMetadata(ImageWriteParam param) { ch5StreamMetadata streammd = new ch5StreamMetadata(); streammd.initialize(1); // default number of images return streammd; } /** * write out the output image specified by index imageIndex using the * parameters specified by the ImageWriteParam object param */ public void write(IIOMetadata metadata, IIOImage iioimage, ImageWriteParam param) { Node root = null; Node dimensionsElementNode = null; if (iioimage.getRenderedImage() != null) raster = iioimage.getRenderedImage().getData(); else raster = iioimage.getRaster(); /* * since this format allows you to write multiple images, the * streamMetadataWritten variable makes sure the stream metadata is * written only once */ if (streamMetadataWritten == false) { if (metadata == null) metadata = getDefaultStreamMetadata(param); root = metadata.getAsTree("ch5.imageio.ch5stream_1.00"); dimensionsElementNode = root.getFirstChild(); Node numberImagesAttributeNode = dimensionsElementNode.getAttributes().getNamedItem("numberImages"); String numberImages = numberImagesAttributeNode.getNodeValue(); try { ios.writeBytes("5\n"); ios.writeBytes(numberImages); ios.flush(); } catch (IOException ioe) { System.err.println("IOException " + ioe.getMessage()); } streamMetadataWritten = true; } String widthString; String heightString; IIOMetadata imageMetadata = (ch5ImageMetadata) iioimage.getMetadata(); /* * don't really need image metadata object here since raster knows * necessary information */ if (imageMetadata == null) imageMetadata = getDefaultImageMetadata(null, param); root = imageMetadata.getAsTree("ch5.imageio.ch5image_1.00"); dimensionsElementNode = root.getFirstChild(); Node widthAttributeNode = dimensionsElementNode.getAttributes().getNamedItem("imageWidth"); widthString = widthAttributeNode.getNodeValue(); Node heightAttributeNode = dimensionsElementNode.getAttributes().getNamedItem("imageHeight"); heightString = heightAttributeNode.getNodeValue(); int sourceWidth = Integer.parseInt(widthString); int sourceHeight = Integer.parseInt(heightString); int destinationWidth = -1; int destinationHeight = -1; int sourceRegionWidth = -1; int sourceRegionHeight = -1; int sourceRegionXOffset = -1; int sourceRegionYOffset = -1; int xSubsamplingFactor = -1; int ySubsamplingFactor = -1; if (param == null) param = getDefaultWriteParam(); /* * get Rectangle object which will be used to clip the source image's * dimensions. */ Rectangle sourceRegion = param.getSourceRegion(); if (sourceRegion != null) { sourceRegionWidth = (int) sourceRegion.getWidth(); sourceRegionHeight = (int) sourceRegion.getHeight(); sourceRegionXOffset = (int) sourceRegion.getX(); sourceRegionYOffset = (int) sourceRegion.getY(); /* * correct for overextended source regions */ if (sourceRegionXOffset + sourceRegionWidth > sourceWidth) destinationWidth = sourceWidth - sourceRegionXOffset; else destinationWidth = sourceRegionWidth; if (sourceRegionYOffset + sourceRegionHeight > sourceHeight) destinationHeight = sourceHeight - sourceRegionYOffset; else destinationHeight = sourceRegionHeight; } else { destinationWidth = sourceWidth; destinationHeight = sourceHeight; sourceRegionXOffset = sourceRegionYOffset = 0; } /* * get subsampling factors */ xSubsamplingFactor = param.getSourceXSubsampling(); ySubsamplingFactor = param.getSourceYSubsampling(); destinationWidth = (destinationWidth - 1) / xSubsamplingFactor + 1; destinationHeight = (destinationHeight - 1) / ySubsamplingFactor + 1; byte[] sourceBuffer; byte[] destinationBuffer = new byte[destinationWidth]; try { ios.writeBytes(new String("\n")); ios.writeBytes(new String(destinationWidth + "\n")); ios.writeBytes(new String(destinationHeight + "\n")); int jj; int index; for (int j = 0; j < sourceWidth; j++) { sourceBuffer = (byte[]) raster.getDataElements(0, j, sourceWidth, 1, null); jj = j - sourceRegionYOffset; if (jj % ySubsamplingFactor == 0) { jj /= ySubsamplingFactor; if ((jj >= 0) && (jj < destinationHeight)) { for (int i = 0; i < destinationWidth; i++) { index = sourceRegionXOffset + i * xSubsamplingFactor; destinationBuffer[i] = sourceBuffer[index]; } ios.write(destinationBuffer, 0, destinationWidth); ios.flush(); } } } } catch (IOException e) { System.err.println("IOException: " + e.getMessage()); } } public void setOutput(Object output) { super.setOutput(output); if (output == null) throw new IllegalArgumentException("output is null"); if (!(output instanceof ImageOutputStream)) throw new IllegalArgumentException("output not an ImageOutputStream"); ios = (ImageOutputStream) output; streamMetadataWritten = false; } private ImageOutputStream ios; private boolean streamMetadataWritten; private Raster raster; } /** * ch5StreamMetadata.java -- holds stream metadata for the ch5 format. The * internal tree for holding this metadata is read only */ class ch5StreamMetadata extends IIOMetadata { static final String nativeMetadataFormatName = "ch5.imageio.ch5stream_1.00"; static final String nativeMetadataFormatClassName = "ch5.imageio.ch5stream"; static final String[] extraMetadataFormatNames = null; static final String[] extraMetadataFormatClassNames = null; static final boolean standardMetadataFormatSupported = false; public int numberImages; public ch5StreamMetadata() { super(standardMetadataFormatSupported, nativeMetadataFormatName, nativeMetadataFormatClassName, extraMetadataFormatNames, extraMetadataFormatClassNames); numberImages = -1; } public boolean isReadOnly() { return true; } /** * IIOMetadataFormat objects are meant to describe the structure of metadata * returned from the getAsTree method. In this case, no such description is * available */ public IIOMetadataFormat getMetadataFormat(String formatName) { if (formatName.equals(nativeMetadataFormatName)) { return null; } else { throw new IllegalArgumentException("Unrecognized format!"); } } /** * returns the stream metadata in a tree corresponding to the provided * formatName */ public Node getAsTree(String formatName) { if (formatName.equals(nativeMetadataFormatName)) { return getNativeTree(); } else { throw new IllegalArgumentException("Unrecognized format!"); } } /** * returns the stream metadata in a tree using the following format * <!ELEMENT ch5.imageio.ch5stream_1.00 (imageDimensions)> <!ATTLIST * imageDimensions numberImages CDATA #REQUIRED */ private Node getNativeTree() { IIOMetadataNode node; // scratch node IIOMetadataNode root = new IIOMetadataNode(nativeMetadataFormatName); // Image descriptor node = new IIOMetadataNode("imageDimensions"); node.setAttribute("numberImages", Integer.toString(numberImages)); root.appendChild(node); return root; } public void setFromTree(String formatName, Node root) { throw new IllegalStateException("Metadata is read-only!"); } public void mergeTree(String formatName, Node root) { throw new IllegalStateException("Metadata is read-only!"); } public void reset() { throw new IllegalStateException("Metadata is read-only!"); } /** * initialize the stream metadata element numberImages */ public void initialize(int numberImages) { this.numberImages = numberImages; } } /** * ch5ImageMetadata.java -- holds image metadata for the ch5 format. * The internal tree for holding this metadata is read only */ class ch5ImageMetadata extends IIOMetadata { static final String nativeMetadataFormatName = "ch5.imageio.ch5image_1.00"; static final String nativeMetadataFormatClassName = "ch5.imageio.ch5image"; static final String[] extraMetadataFormatNames = null; static final String[] extraMetadataFormatClassNames = null; static final boolean standardMetadataFormatSupported = false; public int imageWidth; public int imageHeight; public ch5ImageMetadata() { super(standardMetadataFormatSupported, nativeMetadataFormatName, nativeMetadataFormatClassName, extraMetadataFormatNames, extraMetadataFormatClassNames); imageWidth = -1; imageHeight = -1; } public boolean isReadOnly() { return true; } /** * IIOMetadataFormat objects are meant to describe the structure of * metadata returned from the getAsTree method. In this case, * no such description is available */ public IIOMetadataFormat getMetadataFormat(String formatName) { if (formatName.equals(nativeMetadataFormatName)) { return null; } else { throw new IllegalArgumentException("Unrecognized format!"); } } /** * returns the image metadata in a tree corresponding to the * provided formatName */ public Node getAsTree(String formatName) { if (formatName.equals(nativeMetadataFormatName)) { return getNativeTree(); } else { throw new IllegalArgumentException("Unrecognized format!"); } } /** * returns the image metadata in a tree using the following format * <!ELEMENT ch5.imageio.ch5image_1.00 (imageDimensions)> * <!ATTLIST imageDimensions * imageWidth CDATA #REQUIRED * imageHeight CDATA #REQUIRED */ private Node getNativeTree() { IIOMetadataNode root = new IIOMetadataNode(nativeMetadataFormatName); IIOMetadataNode node = new IIOMetadataNode("imageDimensions"); node.setAttribute("imageWidth", Integer.toString(imageWidth)); node.setAttribute("imageHeight", Integer.toString(imageHeight)); root.appendChild(node); return root; } public void setFromTree(String formatName, Node root) { throw new IllegalStateException("Metadata is read-only!"); } public void mergeTree(String formatName, Node root) { throw new IllegalStateException("Metadata is read-only!"); } public void reset() { throw new IllegalStateException("Metadata is read-only!"); } /** * initialize the image metadata elements width and height */ public void initialize(int width, int height) { imageWidth = width; imageHeight = height; } }