/*
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
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;
/**
* ch5ImageReader.java -- this class provides the functionality to read an image
* of format ch5.
*/
public 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
)
;
}
}
}
/**
* 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;
}
}
|