Read an image

/*

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;
?? }
}