/*
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.BufferedImage;
import java.awt.image.DataBuffer;
import java.awt.image.WritableRaster;
import java.io.IOException;
import java.util.Iterator;
import java.util.Locale;
import javax.imageio.IIOException;
import javax.imageio.ImageReadParam;
import javax.imageio.ImageReader;
import javax.imageio.ImageTypeSpecifier;
import javax.imageio.metadata.IIOMetadata;
import javax.imageio.metadata.IIOMetadataFormat;
import javax.imageio.metadata.IIOMetadataNode;
import javax.imageio.spi.ImageReaderSpi;
import javax.imageio.stream.ImageInputStream;
import org.w3c.dom.Node;
/**
* Simple, functional ImageReaderSpi used to understand how information
* regarding format name, suffices and mime types get passed to ImageIO static
* methods
*/
public class ch5ImageReaderSpi extends ImageReaderSpi {
static final String[] suffixes = { "ch5", "CH5" };
static final String[] names = { "ch5" };
static final String[] MIMETypes = { "image/ch5" };
static final String version = "1.00";
static final String readerCN = "ch5.imageio.plugins.ch5ImageReader";
static final String vendorName = "CompanyName";
//writerSpiNames
static final String[] wSN = { "ch5.imageio.plugins.ch5ImageWriterSpi" };
//StreamMetadataFormatNames and StreamMetadataFormatClassNames
static final boolean supportedStandardStreamMetadataFormat = false;
static final String nativeStreamMFN = "ch5.imageio.ch5stream_1.00";
static final String nativeStreamMFCN = "ch5.imageio.ch5stream";
static final String[] extraStreamMFN = null;
static final String[] extraStreamMFCN = null;
//ImageMetadataFormatNames and ImageMetadataFormatClassNames
static final boolean supportedStandardImageMetadataFormat = false;
static final String nativeImageMFN = "ch5.imageio.ch5image1.00";
static final String nativeImageMFCN = "ch5.imageio.ch5image";
static final String[] extraImageMFN = null;
static final String[] extraImageMFCN = null;
public ch5ImageReaderSpi() {
super(vendorName, version, names, suffixes, MIMETypes, readerCN, //readerClassName
STANDARD_INPUT_TYPE, wSN, //writerSpiNames
false, nativeStreamMFN, nativeStreamMFCN, extraStreamMFN,
extraStreamMFCN, false, nativeImageMFN, nativeImageMFCN,
extraImageMFN, extraImageMFCN);
}
public String getDescription(Locale locale) {
return "Demo ch5 image reader, version " + version;
}
public ImageReader createReaderInstance(Object extension) {
return new ch5ImageReader(this);
}
/**
* This method gets called when an application wants to see if the input
* image's format can be decoded by this ImageReader. In this case, we'll
* simply check the first byte of data to see if its a 5 which is the format
* type's magic number
*/
public boolean canDecodeInput(Object input) {
boolean reply = false;
ImageInputStream iis = (ImageInputStream) input;
iis.mark(); // mark where we are in ImageInputStream
try {
String magicNumber = iis.readLine().trim();
iis.reset(); // reset stream back to marked location
if (magicNumber.equals("5"))
reply = true;
} catch (IOException exception) {
}
return reply;
}
}
class ch5ImageReader extends ImageReader {
private ImageInputStream iis;
private ch5ImageMetadata[] imagemd;
private ch5StreamMetadata streammd;
public ch5ImageReader(ImageReaderSpi originatingProvider) {
super(originatingProvider);
}
/**
* return the ch5StreamMetadata object instantiated in the setStreamMetadata
* method
*/
public IIOMetadata getStreamMetadata() {
return streammd;
}
/**
* return the ch5ImageMetadata object instantiated in the setImageMetadata
* method
*/
public IIOMetadata getImageMetadata(int imageIndex) {
return imagemd[imageIndex];
}
/**
* this method sets the input for this ImageReader and also calls the
* setStreamMetadata method so that the numberImages field is available
*/
public void setInput(Object object, boolean seekForwardOnly) {
super.setInput(object, seekForwardOnly);
if (object == null)
throw new IllegalArgumentException("input is null");
if (!(object instanceof ImageInputStream)) {
String argString = "input not an ImageInputStream";
throw new IllegalArgumentException(argString);
}
iis = (ImageInputStream) object;
setStreamMetadata(iis);
}
/**
* this method provides suggestions for possible image types that will be
* used to decode the image specified by index imageIndex. By default, the
* first image type returned by this method will be the image type of the
* BufferedImage returned by the ImageReader's getDestination method. In
* this case, we are suggesting using an 8 bit grayscale image with no alpha
* component.
*/
public Iterator getImageTypes(int imageIndex) {
java.util.List l = new java.util.ArrayList();
;
int bits = 8;
/*
* can convert ch5 format into 8 bit grayscale image with no alpha
*/
l.add(ImageTypeSpecifier.createGrayscale(bits, DataBuffer.TYPE_BYTE,
false));
return l.iterator();
}
/**
* read in the input image specified by index imageIndex using the
* parameters specified by the ImageReadParam object param
*/
public BufferedImage read(int imageIndex, ImageReadParam param) {
checkIndex(imageIndex);
if (isSeekForwardOnly())
minIndex = imageIndex;
else
minIndex = 0;
BufferedImage bimage = null;
WritableRaster raster = null;
/*
* this method sets the image metadata so that we can use the getWidth
* and getHeight methods
*/
setImageMetadata(iis, imageIndex);
int srcWidth = getWidth(imageIndex);
int srcHeight = getHeight(imageIndex);
// initialize values to -1
int dstWidth = -1;
int dstHeight = -1;
int srcRegionWidth = -1;
int srcRegionHeight = -1;
int srcRegionXOffset = -1;
int srcRegionYOffset = -1;
int xSubsamplingFactor = -1;
int ySubsamplingFactor = -1;
if (param == null)
param = getDefaultReadParam();
Iterator imageTypes = getImageTypes(imageIndex);
try {
/*
* get the destination BufferedImage which will be filled using the
* input image's pixel data
*/
bimage = getDestination(param, imageTypes, srcWidth, srcHeight);
/*
* get Rectangle object which will be used to clip the source
* image's dimensions.
*/
Rectangle srcRegion = param.getSourceRegion();
if (srcRegion != null) {
srcRegionWidth = (int) srcRegion.getWidth();
srcRegionHeight = (int) srcRegion.getHeight();
srcRegionXOffset = (int) srcRegion.getX();
srcRegionYOffset = (int) srcRegion.getY();
/*
* correct for overextended source regions
*/
if (srcRegionXOffset + srcRegionWidth > srcWidth)
dstWidth = srcWidth - srcRegionXOffset;
else
dstWidth = srcRegionWidth;
if (srcRegionYOffset + srcRegionHeight > srcHeight)
dstHeight = srcHeight - srcRegionYOffset;
else
dstHeight = srcRegionHeight;
} else {
dstWidth = srcWidth;
dstHeight = srcHeight;
srcRegionXOffset = srcRegionYOffset = 0;
}
/*
* get subsampling factors
*/
xSubsamplingFactor = param.getSourceXSubsampling();
ySubsamplingFactor = param.getSourceYSubsampling();
/**
* dstWidth and dstHeight should be equal to bimage.getWidth() and
* bimage.getHeight() after these next two instructions
*/
dstWidth = (dstWidth - 1) / xSubsamplingFactor + 1;
dstHeight = (dstHeight - 1) / ySubsamplingFactor + 1;
} catch (IIOException e) {
System.err.println("Can't create destination BufferedImage");
}
raster = bimage.getWritableTile(0, 0);
/*
* using the parameters specified by the ImageReadParam object, read the
* image image data into the destination BufferedImage
*/
byte[] srcBuffer = new byte[srcWidth];
byte[] dstBuffer = new byte[dstWidth];
int jj;
int index;
try {
for (int j = 0; j < srcHeight; j++) {
iis.readFully(srcBuffer, 0, srcWidth);
jj = j - srcRegionYOffset;
if (jj % ySubsamplingFactor == 0) {
jj /= ySubsamplingFactor;
if ((jj >= 0) && (jj < dstHeight)) {
for (int i = 0; i < dstWidth; i++) {
index = srcRegionXOffset + i * xSubsamplingFactor;
dstBuffer[i] = srcBuffer[index];
}
raster.setDataElements(0, jj, dstWidth, 1, dstBuffer);
}
}
}
} catch (IOException e) {
bimage = null;
}
return bimage;
}
/**
* this method sets the image metadata for the image indexed by index
* imageIndex. This method is specific for the ch5 format and thus only sets
* the image width and image height
*/
private void setImageMetadata(ImageInputStream iis, int imageIndex) {
imagemd[imageIndex] = new ch5ImageMetadata();
try {
String s;
s = iis.readLine();
while (s.length() == 0)
s = iis.readLine();
imagemd[imageIndex].imageWidth = Integer.parseInt(s.trim());
s = iis.readLine();
imagemd[imageIndex].imageHeight = Integer.parseInt(s.trim());
} catch (IOException exception) {
}
}
/**
* this method sets the stream metadata for the images represented by the
* ImageInputStream iis. This method is specific for the ch5 format and thus
* only sets the numberImages field.
*/
private void setStreamMetadata(ImageInputStream iis) {
streammd = new ch5StreamMetadata();
try {
String magicNumber = iis.readLine();
int numImages = Integer.parseInt(iis.readLine().trim());
streammd.numberImages = numImages;
imagemd = new ch5ImageMetadata[streammd.numberImages];
} catch (IOException exception) {
}
}
/**
* This method can only be used after the stream metadata has been set
* (which occurs in the setInput method). Else it will return a -1
*/
public int getNumImages(boolean allowSearch) {
return streammd.numberImages;
}
/**
* This method can only be used after the stream metadata has been set
* (which occurs in the setInput method). Else it will return a -1
*/
public int getHeight(int imageIndex) {
if (imagemd == null)
return -1;
checkIndex(imageIndex);
return imagemd[imageIndex].imageHeight;
}
/**
* This method can only be used after the stream metadata has been set
* (which occurs in the setInput method). Else it will return a -1
*/
public int getWidth(int imageIndex) {
if (imagemd == null)
return -1;
checkIndex(imageIndex);
return imagemd[imageIndex].imageWidth;
}
private void checkIndex(int imageIndex) {
if (imageIndex >= streammd.numberImages) {
String argString = "imageIndex >= number of images";
throw new IndexOutOfBoundsException(argString);
}
if (imageIndex < minIndex) {
String argString = "imageIndex < minIndex";
throw new IndexOutOfBoundsException(argString);
}
}
}
/**
* 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;
}
}
/**
* 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;
}
}
|