001: /*
002: * $RCSfile: ImageReadOpImage.java,v $
003: *
004: *
005: * Copyright (c) 2005 Sun Microsystems, Inc. All Rights Reserved.
006: *
007: * Redistribution and use in source and binary forms, with or without
008: * modification, are permitted provided that the following conditions
009: * are met:
010: *
011: * - Redistribution of source code must retain the above copyright
012: * notice, this list of conditions and the following disclaimer.
013: *
014: * - Redistribution in binary form must reproduce the above copyright
015: * notice, this list of conditions and the following disclaimer in
016: * the documentation and/or other materials provided with the
017: * distribution.
018: *
019: * Neither the name of Sun Microsystems, Inc. or the names of
020: * contributors may be used to endorse or promote products derived
021: * from this software without specific prior written permission.
022: *
023: * This software is provided "AS IS," without a warranty of any
024: * kind. ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND
025: * WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY,
026: * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY
027: * EXCLUDED. SUN MIDROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL
028: * NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF
029: * USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS
030: * DERIVATIVES. IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR
031: * ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL,
032: * CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND
033: * REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF OR
034: * INABILITY TO USE THIS SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE
035: * POSSIBILITY OF SUCH DAMAGES.
036: *
037: * You acknowledge that this software is not designed or intended for
038: * use in the design, construction, operation or maintenance of any
039: * nuclear facility.
040: *
041: * $Revision: 1.2 $
042: * $Date: 2006/07/14 21:43:57 $
043: * $State: Exp $
044: */
045: package com.sun.media.jai.imageioimpl;
046:
047: import java.awt.Dimension;
048: import java.awt.Point;
049: import java.awt.Rectangle;
050: import java.awt.image.BufferedImage;
051: import java.awt.image.ColorModel;
052: import java.awt.image.Raster;
053: import java.awt.image.SampleModel;
054: import java.awt.image.WritableRaster;
055: import java.io.InputStream;
056: import java.io.IOException;
057: import java.util.Iterator;
058: import java.util.Map;
059: import javax.imageio.ImageReader;
060: import javax.imageio.ImageReadParam;
061: import javax.imageio.ImageTypeSpecifier;
062: import javax.imageio.metadata.IIOMetadata;
063: import javax.imageio.spi.ImageReaderSpi;
064: import javax.imageio.stream.ImageInputStream;
065: import javax.media.jai.ImageLayout;
066: import javax.media.jai.OpImage;
067: import com.sun.media.jai.operator.ImageReadDescriptor;
068:
069: /**
070: * Implementation of the <code>OpImage</code> of the "ImageRead" operation.
071: */
072: final class ImageReadOpImage extends OpImage {
073:
074: /**
075: * XXX For testing only.
076: */
077: /* XXX
078: public static void main(String[] args) throws Throwable {
079: java.io.File file = new java.io.File(args[0]);
080: int imageIndex = args.length > 1 ?
081: Integer.valueOf(args[1]).intValue() : 0;
082: int tileSize = args.length > 2 ?
083: Integer.valueOf(args[2]).intValue() : 128;
084:
085: javax.imageio.stream.ImageInputStream stream =
086: new javax.imageio.stream.FileImageInputStream(file);
087:
088: Iterator iter = javax.imageio.ImageIO.getImageReaders(stream);
089: ImageReader imageReader = (ImageReader)iter.next();
090: imageReader.setInput(stream,
091: true, // seekForwardOnly
092: false); // ignoreMetadata
093:
094: ImageLayout layout = new ImageLayout();
095: layout.setTileWidth(tileSize).setTileHeight(tileSize);
096: //layout.setTileGridXOffset(42).setTileGridYOffset(7);
097:
098: ImageReadParam param = imageReader.getDefaultReadParam();
099: param.setSourceSubsampling(2, 2, 0, 0);
100: param.setSourceRegion(new Rectangle(128, 0, 256, 256));
101: param.setSourceBands(new int[] {2, 1, 0});
102: param.setDestinationBands(new int[] {0, 1, 2});
103:
104: OpImage image = new ImageReadOpImage(layout, // ImageLayout
105: null, // Map
106: param, // ImageReadParam
107: imageReader,
108: imageIndex,
109: true,
110: null); // streamToClose
111:
112: System.out.println(new ImageLayout(image));
113:
114: System.out.println("\nImage Properties:");
115: String[] propNames = image.getPropertyNames();
116: if(propNames != null) {
117: for(int i = 0; i < propNames.length; i++) {
118: System.out.println(i+" "+propNames[i]+" = "+
119: image.getProperty(propNames[i]));
120: }
121: }
122: System.out.println("");
123:
124: BufferedImage[] thumbnails = null;
125: Object thumbnailProp =
126: image.getProperty(ImageReadDescriptor.PROPERTY_NAME_THUMBNAILS);
127: if(thumbnailProp != java.awt.Image.UndefinedProperty) {
128: thumbnails = (BufferedImage[])thumbnailProp;
129: }
130:
131: java.awt.Frame frame =
132: new java.awt.Frame("ImageReadOpImage Test: "+file);
133: if(thumbnails != null) {
134: frame.setLayout(new java.awt.GridLayout(1, thumbnails.length+1));
135: }
136:
137: frame.add(new javax.media.jai.widget.ScrollingImagePanel(image,
138: image.getWidth(),
139: image.getHeight()));
140: if(thumbnails != null) {
141: for(int i= 0; i < thumbnails.length; i++) {
142: frame.add(new javax.media.jai.widget.ScrollingImagePanel(thumbnails[i],
143: thumbnails[i].getWidth(),
144: thumbnails[i].getHeight()));
145: }
146: }
147: frame.pack();
148: frame.show();
149: }
150: */
151:
152: /**
153: * The <code>ImageReadParam</code> used in reading the image.
154: */
155: private ImageReadParam param;
156:
157: /**
158: * The <code>ImageReader</code> used to read the image.
159: */
160: private ImageReader reader;
161:
162: /**
163: * The index of the image to be read.
164: */
165: private int imageIndex;
166:
167: /**
168: * Whether thumbnails are to be read.
169: */
170: private boolean readThumbnails;
171:
172: /**
173: * Whether stream metadata have been be read.
174: */
175: private boolean streamMetadataRead = false;
176:
177: /**
178: * Whether image metadata have been be read.
179: */
180: private boolean imageMetadataRead = false;
181:
182: /**
183: * A stream to be closed when the instance is disposed; may be null.
184: */
185: private ImageInputStream streamToClose;
186:
187: /**
188: * Destination to source X scale factor.
189: */
190: private int scaleX;
191:
192: /**
193: * Destination to source Y scale factor.
194: */
195: private int scaleY;
196:
197: /**
198: * Destination to source X translation factor.
199: */
200: private int transX;
201:
202: /**
203: * Destination to source Y translation factor.
204: */
205: private int transY;
206:
207: /**
208: * Derive the image layout based on the user-supplied layout,
209: * reading parameters, and image index.
210: */
211: private static ImageLayout layoutHelper(ImageLayout il,
212: ImageReadParam param, ImageReader reader, int imageIndex)
213: throws IOException {
214: ImageLayout layout = (il == null) ? new ImageLayout()
215: : (ImageLayout) il.clone();
216:
217: // --- Determine the image type. ---
218:
219: // If not provided in the original layout, set the SampleModel
220: // and ColorModel from the ImageReadParam, if supplied.
221: if (!layout.isValid(ImageLayout.SAMPLE_MODEL_MASK)
222: && !layout.isValid(ImageLayout.COLOR_MODEL_MASK)) {
223: // If an ImageReadParam has been supplied and has its
224: // destinationType set then use it. Otherwise default to
225: // the raw image type.
226: ImageTypeSpecifier imageType = (param != null && param
227: .getDestinationType() != null) ? param
228: .getDestinationType() : reader
229: .getRawImageType(imageIndex);
230:
231: // XXX The following block of code should not be necessary
232: // but for J2SE 1.4.0 FCS ImageReader.getRawImageType(0)
233: // returns null for earth.jpg, Bas-noir.jpg, etc.
234: if (imageType == null) {
235: Iterator imageTypes = reader.getImageTypes(imageIndex);
236: while (imageType == null && imageTypes.hasNext()) {
237: imageType = (ImageTypeSpecifier) imageTypes.next();
238: }
239: }
240:
241: // XXX Should an exception be thrown if imageType is null?
242: if (imageType != null) {
243: // Set the SampleModel and ColorModel.
244: layout.setSampleModel(imageType.getSampleModel());
245: layout.setColorModel(imageType.getColorModel());
246: }
247: }
248:
249: // --- Set up the destination bounds. ---
250:
251: // Calculate the computable destination bounds.
252: Dimension sourceSize = getSourceSize(param, reader, imageIndex);
253: Rectangle srcRegion = new Rectangle();
254: Rectangle destRegion = new Rectangle();
255: computeRegions(param, sourceSize.width, sourceSize.height,
256: layout.getMinX(null), // valid value or 0
257: layout.getMinY(null), // valid value or 0
258: false, srcRegion, destRegion);
259:
260: if (!destRegion.isEmpty()) {
261: // Backup layout image bounds with computable bounds.
262: if (!layout.isValid(ImageLayout.WIDTH_MASK)) {
263: layout.setWidth(destRegion.width);
264: }
265: if (!layout.isValid(ImageLayout.HEIGHT_MASK)) {
266: layout.setHeight(destRegion.height);
267: }
268: if (!layout.isValid(ImageLayout.MIN_X_MASK)) {
269: layout.setMinX(destRegion.x);
270: }
271: if (!layout.isValid(ImageLayout.MIN_Y_MASK)) {
272: layout.setMinY(destRegion.y);
273: }
274:
275: // Ensure the layout bounds intersect computable bounds.
276: Rectangle destBounds = new Rectangle(layout.getMinX(null),
277: layout.getMinY(null), layout.getWidth(null), layout
278: .getHeight(null));
279: if (destRegion.intersection(destBounds).isEmpty()) {
280: throw new IllegalArgumentException(I18N
281: .getString("ImageReadOpImage0"));
282: }
283: }
284:
285: // --- Set up the tile grid. ---
286:
287: if (!layout.isValid(ImageLayout.TILE_GRID_X_OFFSET_MASK)) {
288: layout.setTileGridXOffset(reader
289: .getTileGridXOffset(imageIndex));
290: }
291: if (!layout.isValid(ImageLayout.TILE_GRID_Y_OFFSET_MASK)) {
292: layout.setTileGridYOffset(reader
293: .getTileGridYOffset(imageIndex));
294: }
295: if (!layout.isValid(ImageLayout.TILE_WIDTH_MASK)) {
296: layout.setTileWidth(reader.getTileWidth(imageIndex));
297: }
298: if (!layout.isValid(ImageLayout.TILE_HEIGHT_MASK)) {
299: layout.setTileHeight(reader.getTileHeight(imageIndex));
300: }
301:
302: return layout;
303: }
304:
305: /**
306: * Returns whether an <code>ImageTypeSpecifier</code> may be used
307: * to read in the image at a specified index.
308: *
309: * XXX
310: */
311: private static boolean isCompatibleType(
312: ImageTypeSpecifier imageType, ImageReader reader,
313: int imageIndex) throws IOException {
314: Iterator imageTypes = reader.getImageTypes(imageIndex);
315:
316: boolean foundIt = false;
317: while (imageTypes.hasNext()) {
318: ImageTypeSpecifier type = (ImageTypeSpecifier) imageTypes
319: .next();
320: if (type.equals(imageType)) {
321: foundIt = true;
322: break;
323: }
324: }
325:
326: return foundIt;
327: }
328:
329: /**
330: * Returns the source region to be read. If the sourceRenderSize
331: * is being used it is returned; otherwise the raw source dimensions
332: * are returned.
333: *
334: * XXX
335: */
336: private static Dimension getSourceSize(ImageReadParam param,
337: ImageReader reader, int imageIndex) throws IOException {
338: Dimension sourceSize = null;
339: if (param != null && param.canSetSourceRenderSize()) {
340: sourceSize = param.getSourceRenderSize();
341: }
342: if (sourceSize == null) {
343: sourceSize = new Dimension(reader.getWidth(imageIndex),
344: reader.getHeight(imageIndex));
345: }
346: return sourceSize;
347: }
348:
349: /**
350: * XXX
351: */
352: // Code copied from ImageReader.java
353: private static Rectangle getSourceRegion(ImageReadParam param,
354: int srcWidth, int srcHeight) {
355: Rectangle sourceRegion = new Rectangle(0, 0, srcWidth,
356: srcHeight);
357: if (param != null) {
358: Rectangle region = param.getSourceRegion();
359: if (region != null) {
360: sourceRegion = sourceRegion.intersection(region);
361: }
362:
363: int subsampleXOffset = param.getSubsamplingXOffset();
364: int subsampleYOffset = param.getSubsamplingYOffset();
365: sourceRegion.x += subsampleXOffset;
366: sourceRegion.y += subsampleYOffset;
367: sourceRegion.width -= subsampleXOffset;
368: sourceRegion.height -= subsampleYOffset;
369: }
370:
371: return sourceRegion;
372: }
373:
374: /**
375: * XXX
376: */
377: // clipDestRegion: whether to clip destRegion to positive coordinates.
378: // Code based on method of same name in ImageReader.java
379: private static void computeRegions(ImageReadParam param,
380: int srcWidth, int srcHeight, int destMinX, int destMinY,
381: boolean clipDestRegion, Rectangle srcRegion,
382: Rectangle destRegion) {
383: if (srcRegion == null) {
384: throw new IllegalArgumentException("srcRegion == null");
385: }
386: if (destRegion == null) {
387: throw new IllegalArgumentException("destRegion == null");
388: }
389:
390: // Start with the entire source image
391: srcRegion.setBounds(0, 0, srcWidth, srcHeight);
392:
393: // Destination also starts with source image, as that is the
394: // maximum extent if there is no subsampling
395: destRegion.setBounds(destMinX, destMinY, srcWidth, srcHeight);
396:
397: // Clip that to the param region, if there is one
398: int periodX = 1;
399: int periodY = 1;
400: int gridX = 0;
401: int gridY = 0;
402: if (param != null) {
403: Rectangle paramSrcRegion = param.getSourceRegion();
404: if (paramSrcRegion != null) {
405: srcRegion.setBounds(srcRegion
406: .intersection(paramSrcRegion));
407: }
408: periodX = param.getSourceXSubsampling();
409: periodY = param.getSourceYSubsampling();
410: gridX = param.getSubsamplingXOffset();
411: gridY = param.getSubsamplingYOffset();
412: srcRegion.translate(gridX, gridY);
413: srcRegion.width -= gridX;
414: srcRegion.height -= gridY;
415: Point destinationOffset = param.getDestinationOffset();
416: destRegion.translate(destinationOffset.x,
417: destinationOffset.y);
418: }
419:
420: if (clipDestRegion) {
421: // Now clip any negative destination offsets, i.e. clip
422: // to the top and left of the destination image
423: if (destRegion.x < 0) {
424: int delta = -destRegion.x * periodX;
425: srcRegion.x += delta;
426: srcRegion.width -= delta;
427: destRegion.x = 0;
428: }
429: if (destRegion.y < 0) {
430: int delta = -destRegion.y * periodY;
431: srcRegion.y += delta;
432: srcRegion.height -= delta;
433: destRegion.y = 0;
434: }
435: }
436:
437: // Now clip the destination Region to the subsampled width and height
438: int subsampledWidth = (srcRegion.width + periodX - 1) / periodX;
439: int subsampledHeight = (srcRegion.height + periodY - 1)
440: / periodY;
441: destRegion.width = subsampledWidth;
442: destRegion.height = subsampledHeight;
443:
444: if (srcRegion.isEmpty() || destRegion.isEmpty()) {
445: throw new IllegalArgumentException(I18N
446: .getString("ImageReadOpImage1"));
447: }
448: }
449:
450: /**
451: * XXX
452: * NB: This class may reset the following fields of the ImageReadParam
453: * destinationOffset
454: * destinationType
455: * sourceRegion
456: */
457: ImageReadOpImage(ImageLayout layout, Map configuration,
458: ImageReadParam param, ImageReader reader, int imageIndex,
459: boolean readThumbnails, ImageInputStream streamToClose)
460: throws IOException {
461: super (null, layoutHelper(layout, param, reader, imageIndex),
462: configuration, false);
463:
464: // Revise parameter 'param' as needed.
465: if (param == null) {
466: // Get the ImageReadParam from the ImageReader.
467: param = reader.getDefaultReadParam();
468: } else if (param instanceof Cloneable) {
469: this .param = param;
470: } else if (param.getClass().getName().equals(
471: "javax.imageio.ImageReadParam")) {
472: // The ImageReadParam passed in is non-null. As the
473: // ImageReadParam class is not Cloneable, if the param
474: // class is simply ImageReadParam, then create a new
475: // ImageReadParam instance and set all its fields
476: // which were set in param. This will eliminate problems
477: // with concurrent modification of param for the cases
478: // in which there is not a special ImageReadparam used.
479:
480: // Create a new ImageReadParam instance.
481: ImageReadParam newParam = new ImageReadParam();
482:
483: // Set all fields which need to be set.
484:
485: // IIOParamController field.
486: if (param.hasController()) {
487: newParam.setController(param.getController());
488: }
489:
490: // Destination fields.
491: newParam.setDestination(param.getDestination());
492: if (param.getDestinationType() != null) {
493: // Set the destination type only if non-null as the
494: // setDestinationType() clears the destination field.
495: newParam.setDestinationType(param.getDestinationType());
496: }
497: newParam.setDestinationBands(param.getDestinationBands());
498: newParam.setDestinationOffset(param.getDestinationOffset());
499:
500: // Source fields.
501: newParam.setSourceBands(param.getSourceBands());
502: newParam.setSourceRegion(param.getSourceRegion());
503: if (param.getSourceMaxProgressivePass() != Integer.MAX_VALUE) {
504: newParam.setSourceProgressivePasses(param
505: .getSourceMinProgressivePass(), param
506: .getSourceNumProgressivePasses());
507: }
508: if (param.canSetSourceRenderSize()) {
509: newParam.setSourceRenderSize(param
510: .getSourceRenderSize());
511: }
512: newParam.setSourceSubsampling(
513: param.getSourceXSubsampling(), param
514: .getSourceYSubsampling(), param
515: .getSubsamplingXOffset(), param
516: .getSubsamplingYOffset());
517:
518: // Replace the local variable with the new ImageReadParam.
519: param = newParam;
520: }
521:
522: // Revise parameter 'readThumbnails' as needed.
523: if (readThumbnails && !reader.hasThumbnails(imageIndex)) {
524: // Unset thumbnail flag if not supported by ImageReader.
525: readThumbnails = false;
526: }
527:
528: // Set instance variables from (possibly revised) parameters.
529: this .param = param;
530: this .reader = reader;
531: this .imageIndex = imageIndex;
532: this .readThumbnails = readThumbnails;
533: this .streamToClose = streamToClose;
534:
535: // If an ImageTypeSpecifier is specified in the ImageReadParam
536: // but it is incompatible with the ImageReader, then attempt to
537: // replace it with a compatible one derived from this image.
538: if (param.getDestinationType() != null
539: && !isCompatibleType(param.getDestinationType(),
540: reader, imageIndex) && sampleModel != null
541: && colorModel != null) {
542: ImageTypeSpecifier newImageType = new ImageTypeSpecifier(
543: colorModel, sampleModel);
544: if (isCompatibleType(newImageType, reader, imageIndex)) {
545: param.setDestinationType(newImageType);
546: }
547: }
548:
549: // --- Compute the destination to source mapping coefficients. ---
550:
551: Dimension sourceSize = getSourceSize(param, reader, imageIndex);
552:
553: Rectangle srcRegion = getSourceRegion(param, sourceSize.width,
554: sourceSize.height);
555:
556: Point destinationOffset = this .param.getDestinationOffset();
557:
558: this .scaleX = this .param.getSourceXSubsampling();
559: this .scaleY = this .param.getSourceYSubsampling();
560: this .transX = srcRegion.x + this .param.getSubsamplingXOffset()
561: - this .param.getSourceXSubsampling()
562: * (minX + destinationOffset.x);
563: this .transY = srcRegion.y + this .param.getSubsamplingYOffset()
564: - this .param.getSourceYSubsampling()
565: * (minY + destinationOffset.y);
566:
567: // Replace the original destination offset with (0,0) as the
568: // destination-to-source mapping assimilates this value.
569: this .param.setDestinationOffset(new Point());
570: // XXX Need to unset other ImageReadParam settings either here
571: // or in computeTile(). Examine this issue taking into account
572: // synchronization.
573:
574: // Set the ImageReadParam property.
575: setProperty(ImageReadDescriptor.PROPERTY_NAME_IMAGE_READ_PARAM,
576: param);
577:
578: // Set the ImageReader property.
579: setProperty(ImageReadDescriptor.PROPERTY_NAME_IMAGE_READER,
580: reader);
581:
582: // If metadata are being read, set the value of the metadata
583: // properties to UndefinedProperty so that the property
584: // names will appear in the array of property names. The actual
585: // values will be retrieved when getProperty() is invoked.
586: if (!reader.isIgnoringMetadata()) {
587: // Get the service provider interface, if any.
588: ImageReaderSpi provider = reader.getOriginatingProvider();
589:
590: // Stream metadata.
591: if (provider == null
592: || provider
593: .isStandardStreamMetadataFormatSupported()
594: || provider.getNativeStreamMetadataFormatName() != null) {
595: // Assume an ImageReader with a null provider supports
596: // stream metadata.
597: setProperty(
598: ImageReadDescriptor.PROPERTY_NAME_METADATA_STREAM,
599: java.awt.Image.UndefinedProperty);
600: } else {
601: // Provider supports neither standard nor native stream
602: // metadata so set flag to suppress later reading attempt.
603: streamMetadataRead = true;
604: }
605:
606: // Image metadata.
607: if (provider == null
608: || provider
609: .isStandardImageMetadataFormatSupported()
610: || provider.getNativeImageMetadataFormatName() != null) {
611: // Assume an ImageReader with a null provider supports
612: // image metadata.
613: setProperty(
614: ImageReadDescriptor.PROPERTY_NAME_METADATA_IMAGE,
615: java.awt.Image.UndefinedProperty);
616: } else {
617: // Provider supports neither standard nor native image
618: // metadata so set flag to suppress later reading attempt.
619: imageMetadataRead = true;
620: }
621: }
622:
623: // If thumbnail read flag is set, set the value of the thumbnail
624: // property to UndefinedProperty so that the thumbnail property
625: // name will appear in the array of property names. The actual
626: // value will be retrieved when getProperty() is invoked.
627: if (readThumbnails && reader.readerSupportsThumbnails()) {
628: setProperty(ImageReadDescriptor.PROPERTY_NAME_THUMBNAILS,
629: java.awt.Image.UndefinedProperty);
630: }
631: }
632:
633: /**
634: * Returns false as ImageReaders might return Rasters
635: * via computeTile() tile that are internally cached.
636: */
637: public boolean computesUniqueTiles() {
638: return false;
639: }
640:
641: /**
642: * XXX
643: */
644: private Rectangle computeSourceRect(Rectangle destRect) {
645: Rectangle sourceRect = new Rectangle();
646:
647: sourceRect.x = scaleX * destRect.x + transX;
648: sourceRect.y = scaleY * destRect.y + transY;
649:
650: sourceRect.width = scaleX * (destRect.x + destRect.width)
651: + transX - sourceRect.x;
652: sourceRect.height = scaleY * (destRect.y + destRect.height)
653: + transY - sourceRect.y;
654:
655: return sourceRect;
656: }
657:
658: /**
659: * Computes a tile.
660: *
661: * @param tileX The X index of the tile.
662: * @param tileY The Y index of the tile.
663: */
664: public Raster computeTile(int tileX, int tileY) {
665: //XXX System.out.println("Tile ("+tileX+","+tileY+")");
666: // Create a new WritableRaster to represent this tile.
667: Point org = new Point(tileXToX(tileX), tileYToY(tileY));
668: //WritableRaster dest = Raster.createWritableRaster(sampleModel, org);
669: Rectangle rect = new Rectangle(org.x, org.y, tileWidth,
670: tileHeight);
671:
672: // Clip output rectangle to image bounds.
673: // Not sure what will happen here with the bounds intersection.
674: Rectangle destRect = rect.intersection(getBounds());
675: // XXX Check for destRect.isEmpty()?
676:
677: /* XXX delete
678: java.awt.geom.AffineTransform transform =
679: new java.awt.geom.AffineTransform(scaleX, 0, 0, scaleY,
680: transX, transY);
681: */
682: Rectangle srcRect = computeSourceRect(destRect);
683: /* XXX delete
684: transform.createTransformedShape(destRect).getBounds();
685: */
686:
687: WritableRaster readerTile = null;
688: try {
689: synchronized (reader) {
690: param.setSourceRegion(srcRect);
691: BufferedImage bi = reader.read(imageIndex, param);
692: WritableRaster ras = bi.getRaster();
693: readerTile = ras.createWritableChild(0, 0, ras
694: .getWidth(), ras.getHeight(), org.x, org.y,
695: null);
696: }
697: } catch (IOException e) {
698: throw new RuntimeException(e);
699: }
700:
701: WritableRaster tile = null;
702: if (sampleModel == readerTile.getSampleModel()) {
703: tile = readerTile;
704: } else {
705: // XXX As this method is synchronized, could a single
706: // destination be supplied to the reader instead of
707: // creating a new one?
708: tile = Raster.createWritableRaster(sampleModel, org);
709: tile.setRect(readerTile);
710: }
711:
712: return tile;
713: }
714:
715: /**
716: * Throws an IllegalArgumentException since the image has no image
717: * sources.
718: *
719: * @param sourceRect ignored.
720: * @param sourceIndex ignored.
721: *
722: * @throws IllegalArgumentException since the image has no sources.
723: */
724: public Rectangle mapSourceRect(Rectangle sourceRect, int sourceIndex) {
725: throw new IllegalArgumentException(I18N
726: .getString("ImageReadOpImage2"));
727: }
728:
729: /**
730: * Throws an IllegalArgumentException since the image has no image
731: * sources.
732: *
733: * @param destRect ignored.
734: * @param sourceIndex ignored.
735: *
736: * @throws IllegalArgumentException since the image has no sources.
737: */
738: public Rectangle mapDestRect(Rectangle destRect, int sourceIndex) {
739: throw new IllegalArgumentException(I18N
740: .getString("ImageReadOpImage2"));
741: }
742:
743: /**
744: * Gets a property from the property set of this image. If the
745: * property name is not recognized,
746: * <code>java.awt.Image.UndefinedProperty</code> will be returned.
747: *
748: * <p>This implementation first attempts to retrieve the property
749: * using the equivalent superclass method. If the returned value
750: * is not a valid property value, the requested property name is
751: * that of the image thumbnails property, the stream metadata
752: * property, or the image metadata property, and there has been no
753: * prior attempt to read the corresponding property value, then its
754: * reads the value and set the property. This implementation therefore
755: * defers reading of the image thumbnails, stream metadata, and image
756: * metadata values until the correpsonding property is actually
757: * requested.</p>
758: *
759: * @param name the name of the property to get, as a <code>String</code>.
760: *
761: * @return A reference to the property <code>Object</code>, or the value
762: * <code>java.awt.Image.UndefinedProperty</code>.
763: *
764: * @exception IllegalArgumentException if <code>propertyName</code>
765: * is <code>null</code>.
766: */
767: public Object getProperty(String name) {
768: // Attempt to get property from superclass method.
769: Object property = super .getProperty(name);
770:
771: // If thumbnail property name with undefined value and thumbnails
772: // are being read and an attempt to read them has not already been
773: // made, then read the thumbnails and set the property.
774: if ((property == null || property == java.awt.Image.UndefinedProperty)) {
775:
776: // Thumbnails
777: if (readThumbnails
778: && name
779: .equalsIgnoreCase(ImageReadDescriptor.PROPERTY_NAME_THUMBNAILS)) {
780:
781: // Lock the class to avoid a race condition here
782: // and with computeTile().
783: synchronized (reader) {
784: // First re-check the flag in case another thread
785: // got here first.
786: if (readThumbnails) {
787: try {
788: // Get number of thumbnails.
789: int numThumbnails = reader
790: .getNumThumbnails(imageIndex);
791:
792: if (numThumbnails > 0) {
793: // Read all thumbnails.
794: BufferedImage[] thumbnails = new BufferedImage[numThumbnails];
795: for (int i = 0; i < numThumbnails; i++) {
796: thumbnails[i] = reader
797: .readThumbnail(imageIndex,
798: i);
799: }
800:
801: // Set thumbnail property.
802: setProperty(
803: ImageReadDescriptor.PROPERTY_NAME_THUMBNAILS,
804: thumbnails);
805:
806: // Update return value.
807: property = thumbnails;
808: }
809: } catch (IOException e) {
810: throw new RuntimeException(e);
811: } finally {
812: // If return value is somehow null set it
813: // to UndefinedProperty.
814: if (property == null) {
815: property = java.awt.Image.UndefinedProperty;
816: }
817:
818: // Unset thumbnail flag to avert subsequent
819: // reading attempts in case this one failed.
820: readThumbnails = false;
821: }
822: }
823: }
824: } else if (!reader.isIgnoringMetadata()
825: && ((!streamMetadataRead && name
826: .equalsIgnoreCase(ImageReadDescriptor.PROPERTY_NAME_METADATA_STREAM)) || (!imageMetadataRead && name
827: .equalsIgnoreCase(ImageReadDescriptor.PROPERTY_NAME_METADATA_IMAGE)))) {
828:
829: // Lock the class to avoid a race condition here
830: // and with computeTile().
831: synchronized (reader) {
832:
833: // Set flag to indicate stream or image metadata.
834: boolean isStreamMetadata = name
835: .equalsIgnoreCase(ImageReadDescriptor.PROPERTY_NAME_METADATA_STREAM);
836:
837: // Recheck the appropriate flag.
838: if (!(isStreamMetadata ? streamMetadataRead
839: : imageMetadataRead)) {
840:
841: // Set property name.
842: String propertyName = isStreamMetadata ? ImageReadDescriptor.PROPERTY_NAME_METADATA_STREAM
843: : ImageReadDescriptor.PROPERTY_NAME_METADATA_IMAGE;
844:
845: IIOMetadata metadata = null;
846: try {
847: // Read metadata.
848: metadata = isStreamMetadata ? reader
849: .getStreamMetadata() : reader
850: .getImageMetadata(imageIndex);
851:
852: // Set metadata property.
853: if (metadata != null) {
854: setProperty(propertyName, metadata);
855: }
856:
857: // Update return value.
858: property = metadata;
859: } catch (IOException e) {
860: throw new RuntimeException(e);
861: } finally {
862: // If return value is somehow null set it
863: // to UndefinedProperty.
864: if (property == null) {
865: property = java.awt.Image.UndefinedProperty;
866: }
867:
868: // Set appropriate flag to avert subsequent
869: // reading attempts in case this one failed.
870: if (isStreamMetadata) {
871: streamMetadataRead = true;
872: } else {
873: imageMetadataRead = true;
874: }
875: }
876: }
877: }
878: }
879: }
880:
881: return property;
882: }
883:
884: /**
885: * Closes an <code>ImageInputStream</code> passed in, if any.
886: */
887: public void dispose() {
888: if (streamToClose != null) {
889: try {
890: streamToClose.close();
891: } catch (IOException e) {
892: // Ignore it.
893: }
894: }
895:
896: super.dispose();
897: }
898: }
|