Source Code Cross Referenced for TiledImage.java in  » 6.0-JDK-Modules » Java-Advanced-Imaging » javax » media » jai » Java Source Code / Java DocumentationJava Source Code and Java Documentation

Java Source Code / Java Documentation
1. 6.0 JDK Core
2. 6.0 JDK Modules
3. 6.0 JDK Modules com.sun
4. 6.0 JDK Modules com.sun.java
5. 6.0 JDK Modules sun
6. 6.0 JDK Platform
7. Ajax
8. Apache Harmony Java SE
9. Aspect oriented
10. Authentication Authorization
11. Blogger System
12. Build
13. Byte Code
14. Cache
15. Chart
16. Chat
17. Code Analyzer
18. Collaboration
19. Content Management System
20. Database Client
21. Database DBMS
22. Database JDBC Connection Pool
23. Database ORM
24. Development
25. EJB Server geronimo
26. EJB Server GlassFish
27. EJB Server JBoss 4.2.1
28. EJB Server resin 3.1.5
29. ERP CRM Financial
30. ESB
31. Forum
32. GIS
33. Graphic Library
34. Groupware
35. HTML Parser
36. IDE
37. IDE Eclipse
38. IDE Netbeans
39. Installer
40. Internationalization Localization
41. Inversion of Control
42. Issue Tracking
43. J2EE
44. JBoss
45. JMS
46. JMX
47. Library
48. Mail Clients
49. Net
50. Parser
51. PDF
52. Portal
53. Profiler
54. Project Management
55. Report
56. RSS RDF
57. Rule Engine
58. Science
59. Scripting
60. Search Engine
61. Security
62. Sevlet Container
63. Source Control
64. Swing Library
65. Template Engine
66. Test Coverage
67. Testing
68. UML
69. Web Crawler
70. Web Framework
71. Web Mail
72. Web Server
73. Web Services
74. Web Services apache cxf 2.0.1
75. Web Services AXIS2
76. Wiki Engine
77. Workflow Engines
78. XML
79. XML UI
Java
Java Tutorial
Java Open Source
Jar File Download
Java Articles
Java Products
Java by API
Photoshop Tutorials
Maya Tutorials
Flash Tutorials
3ds-Max Tutorials
Illustrator Tutorials
GIMP Tutorials
C# / C Sharp
C# / CSharp Tutorial
C# / CSharp Open Source
ASP.Net
ASP.NET Tutorial
JavaScript DHTML
JavaScript Tutorial
JavaScript Reference
HTML / CSS
HTML CSS Reference
C / ANSI-C
C Tutorial
C++
C++ Tutorial
Ruby
PHP
Python
Python Tutorial
Python Open Source
SQL Server / T-SQL
SQL Server / T-SQL Tutorial
Oracle PL / SQL
Oracle PL/SQL Tutorial
PostgreSQL
SQL / MySQL
MySQL Tutorial
VB.Net
VB.Net Tutorial
Flash / Flex / ActionScript
VBA / Excel / Access / Word
XML
XML Tutorial
Microsoft Office PowerPoint 2007 Tutorial
Microsoft Office Excel 2007 Tutorial
Microsoft Office Word 2007 Tutorial
Java Source Code / Java Documentation » 6.0 JDK Modules » Java Advanced Imaging » javax.media.jai 
Source Cross Referenced  Class Diagram Java Document (Java Doc) 


0001:        /*
0002:         * $RCSfile: TiledImage.java,v $
0003:         *
0004:         * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved.
0005:         *
0006:         * Use is subject to license terms.
0007:         *
0008:         * $Revision: 1.2 $
0009:         * $Date: 2007/08/31 16:25:50 $
0010:         * $State: Exp $
0011:         */
0012:        package javax.media.jai;
0013:
0014:        import java.awt.Graphics;
0015:        import java.awt.Graphics2D;
0016:        import java.awt.Point;
0017:        import java.awt.Rectangle;
0018:        import java.awt.Shape;
0019:        import java.awt.geom.AffineTransform;
0020:        import java.awt.geom.Area;
0021:        import java.awt.geom.PathIterator;
0022:        import java.awt.image.BandedSampleModel;
0023:        import java.awt.image.ColorModel;
0024:        import java.awt.image.ComponentSampleModel;
0025:        import java.awt.image.DataBuffer;
0026:        import java.awt.image.Raster;
0027:        import java.awt.image.RenderedImage;
0028:        import java.awt.image.SampleModel;
0029:        import java.awt.image.TileObserver;
0030:        import java.awt.image.WritableRaster;
0031:        import java.awt.image.WritableRenderedImage;
0032:        import java.awt.image.renderable.ParameterBlock;
0033:        import java.beans.PropertyChangeEvent;
0034:        import java.beans.PropertyChangeListener;
0035:        import java.util.Enumeration;
0036:        import java.util.Iterator;
0037:        import java.util.LinkedList;
0038:        import java.util.Vector;
0039:        import com.sun.media.jai.util.JDKWorkarounds;
0040:
0041:        /**
0042:         * A concrete implementation of <code>WritableRenderedImage</code>.
0043:         *
0044:         * <p> <code>TiledImage</code> is the main class for writable images
0045:         * in JAI.  <code>TiledImage</code> provides a straightforward
0046:         * implementation of the <code>WritableRenderedImage</code> interface,
0047:         * taking advantage of that interface's ability to describe images
0048:         * with multiple tiles.  The tiles of a
0049:         * <code>WritableRenderedImage</code> must share a
0050:         * <code>SampleModel</code>, which determines their width, height, and
0051:         * pixel format.  The tiles form a regular grid, which may occupy any
0052:         * rectangular region of the plane.  Tile pixels the locations of which
0053:         * lie outside the stated image bounds have undefined values.
0054:         *
0055:         * <p> The contents of a <code>TiledImage</code> are defined by a
0056:         * single <code>RenderedImage</code> source provided by means of one of
0057:         * the <code>set()</code> methods or to a constructor which accepts a
0058:         * <code>RenderedImage</code>.  The <code>set()</code> methods provide
0059:         * a way to selectively overwrite a portion of a <code>TiledImage</code>,
0060:         * possibly using a region of interest (ROI).
0061:         *
0062:         * <p> <code>TiledImage</code> also supports direct manipulation of
0063:         * pixels by means of the <code>getWritableTile()</code> method.  This
0064:         * method returns a <code>WritableRaster</code> that can be modified directly.
0065:         * Such changes become visible to readers according to the regular
0066:         * thread synchronization rules of the Java virtual machine; JAI makes
0067:         * no additional guarantees.  When a writer is finished modifying a
0068:         * tile, it should call <code>releaseWritableTile()</code>.  A
0069:         * shortcut is to call <code>setData()</code>, which copies a rectangular
0070:         * region or an area specified by a <code>ROI</code> from a supplied
0071:         * <code>Raster</code> directly into the <code>TiledImage</code>.
0072:         *
0073:         * <p> A final way to modify the contents of a <code>TiledImage</code>
0074:         * is through calls to the object returned by <code>createGraphics()</code>.
0075:         * This returns a <code>Graphics2D</code> object that can be used to draw
0076:         * line art, text, and images in the usual Abstract Window Toolkit (AWT)
0077:         * manner.
0078:         *
0079:         * <p> A <code>TiledImage</code> does not attempt to maintain
0080:         * synchronous state on its own.  That task is left to
0081:         * <code>SnapshotImage</code>.  If a synchronous (unchangeable) view
0082:         * of a <code>TiledImage</code> is desired, its
0083:         * <code>createSnapshot()</code> method must be used.  Otherwise,
0084:         * changes due to calls to set() or direct writing of tiles by objects
0085:         * that call <code>getWritableTile()</code> will be visible.
0086:         *
0087:         * <p> <code>TiledImage</code> does not actually cause its tiles to be
0088:         * copied from the specified source until their contents are demanded.
0089:         * Once a tile has been computed, its contents may be discarded if it can
0090:         * be determined that it can be recomputed identically from the source.
0091:         * The <code>lockTile()</code> method forces a tile to be computed and
0092:         * maintained for the lifetime of the <code>TiledImage</code>.
0093:         *
0094:         * @see SnapshotImage
0095:         * @see java.awt.image.RenderedImage
0096:         * @see java.awt.image.WritableRenderedImage
0097:         *
0098:         */
0099:        public class TiledImage extends PlanarImage implements 
0100:                WritableRenderedImage, PropertyChangeListener {
0101:
0102:            /** The number of tiles in the X direction. */
0103:            protected int tilesX;
0104:
0105:            /** The number of tiles in the Y direction. */
0106:            protected int tilesY;
0107:
0108:            /** The index of the leftmost column of tiles. */
0109:            protected int minTileX;
0110:
0111:            /** The index of the uppermost row of tiles. */
0112:            protected int minTileY;
0113:
0114:            /** The tile array. */
0115:            protected WritableRaster[][] tiles;
0116:
0117:            /** The number of writers of each tile; -1 indicates a locked tile. */
0118:            protected int[][] writers;
0119:
0120:            /** The current set of TileObservers. */
0121:            protected Vector tileObservers = null;
0122:
0123:            /** Whether DataBuffers are shared with the source image. */
0124:            private boolean areBuffersShared = false;
0125:
0126:            /** The parent TiledImage if this one was created using getSubImage(). */
0127:            private TiledImage parent = null;
0128:
0129:            /** The SampleModel of the TiledImage ancestor. */
0130:            private SampleModel ancestorSampleModel = null;
0131:
0132:            /** The sub-banding list with respect to the ancestor. */
0133:            private int[] bandList = null;
0134:
0135:            /** The number of writable tiles; shared with all ancestors. */
0136:            private int[] numWritableTiles = null;
0137:
0138:            /** The ROI to be used with the source image of uncomputed tiles. */
0139:            private ROI srcROI = null;
0140:
0141:            /** The bounds of the intersection of the source image bounds with
0142:               those of this image and with the source ROI if present. */
0143:            private Rectangle overlapBounds = null;
0144:
0145:            /**
0146:             * Derives a <code>SampleModel</code> with the specified dimensions
0147:             * from the input <code>SampleModel</code>.  If the input
0148:             * <code>SampleModel</code> already has these dimensions, it is
0149:             * used directly; otherwise a new <code>SampleModel</code> of the
0150:             * required dimensions is derived and returned.
0151:             */
0152:            private static SampleModel coerceSampleModel(
0153:                    SampleModel sampleModel, int sampleModelWidth,
0154:                    int sampleModelHeight) {
0155:                return (sampleModel.getWidth() == sampleModelWidth && sampleModel
0156:                        .getHeight() == sampleModelHeight) ? sampleModel
0157:                        : sampleModel.createCompatibleSampleModel(
0158:                                sampleModelWidth, sampleModelHeight);
0159:            }
0160:
0161:            /*
0162:             * Derives the values of minTileX, minTileY, tilesX, and tilesY
0163:             * from minX, minY, width, height, tileGridXOffset, tileGridYOffset,
0164:             * tileWidth, and tileHeight.  If the image has a parent, its tile
0165:             * grid minima are set to those of the parent.
0166:             *
0167:             * @param parent The parent TiledImage.
0168:             */
0169:            private void initTileGrid(TiledImage parent) {
0170:                if (parent != null) {
0171:                    this .minTileX = parent.minTileX;
0172:                    this .minTileY = parent.minTileY;
0173:                } else {
0174:                    this .minTileX = getMinTileX();
0175:                    this .minTileY = getMinTileY();
0176:                }
0177:
0178:                int maxTileX = getMaxTileX();
0179:                int maxTileY = getMaxTileY();
0180:
0181:                this .tilesX = maxTileX - minTileX + 1;
0182:                this .tilesY = maxTileY - minTileY + 1;
0183:            }
0184:
0185:            /**
0186:             * Constructs a <code>TiledImage</code> with a given layout,
0187:             * <code>SampleModel</code>, and <code>ColorModel</code>.  The
0188:             * width and height of the image tiles will be respectively equal
0189:             * to the width and height of the <code>SampleModel</code>. The
0190:             * <code>tileFactory</code> instance variable will be set to the
0191:             * value of the <code>JAI.KEY_TILE_FACTORY</code> hint set on
0192:             * the default instance of <code>JAI</code>.
0193:             *
0194:             * @param minX The X coordinate of the upper-left pixel
0195:             * @param minY The Y coordinate of the upper-left pixel.
0196:             * @param width The width of the image.
0197:             * @param height The height of the image.
0198:             * @param tileGridXOffset The X coordinate of the upper-left
0199:             *        pixel of tile (0, 0).
0200:             * @param tileGridYOffset The Y coordinate of the upper-left
0201:             *        pixel of tile (0, 0).
0202:             * @param tileSampleModel A <code>SampleModel</code> with which to be
0203:             *        compatible.
0204:             * @param colorModel A <code>ColorModel</code> to associate with the
0205:             *        image.
0206:             */
0207:            public TiledImage(int minX, int minY, int width, int height,
0208:                    int tileGridXOffset, int tileGridYOffset,
0209:                    SampleModel tileSampleModel, ColorModel colorModel) {
0210:                this (null, minX, minY, width, height, tileGridXOffset,
0211:                        tileGridYOffset, tileSampleModel, colorModel);
0212:            }
0213:
0214:            /**
0215:             * Constructs a child TiledImage. If the parent is null it is a root
0216:             * TiledImage and all instance variables will be allocated.
0217:             */
0218:            private TiledImage(TiledImage parent, int minX, int minY,
0219:                    int width, int height, int tileGridXOffset,
0220:                    int tileGridYOffset, SampleModel sampleModel,
0221:                    ColorModel colorModel) {
0222:                super (new ImageLayout(minX, minY, width, height,
0223:                        tileGridXOffset, tileGridYOffset, sampleModel
0224:                                .getWidth(), sampleModel.getHeight(),
0225:                        sampleModel, colorModel), null, null);
0226:                initTileGrid(parent);
0227:
0228:                if (parent == null) {
0229:                    this .tiles = new WritableRaster[tilesX][tilesY];
0230:                    this .writers = new int[tilesX][tilesY];
0231:                    tileObservers = new Vector();
0232:                    numWritableTiles = new int[1];
0233:                    numWritableTiles[0] = 0;
0234:                    ancestorSampleModel = sampleModel;
0235:                } else {
0236:                    this .parent = parent;
0237:                    this .tiles = parent.tiles;
0238:                    this .writers = parent.writers;
0239:                    tileObservers = parent.tileObservers;
0240:                    numWritableTiles = parent.numWritableTiles;
0241:                    ancestorSampleModel = parent.ancestorSampleModel;
0242:                }
0243:
0244:                tileFactory = (TileFactory) JAI.getDefaultInstance()
0245:                        .getRenderingHint(JAI.KEY_TILE_FACTORY);
0246:            }
0247:
0248:            /**
0249:             * Constructs a <code>TiledImage</code> with a
0250:             * <code>SampleModel</code> that is compatible with a given
0251:             * <code>SampleModel</code>, and given tile dimensions.  The width
0252:             * and height are taken from the <code>SampleModel</code>, and the
0253:             * image begins at a specified point.  The <code>ColorModel</code>
0254:             * will be derived from the <code>SampleModel</code> using the
0255:             * <code>createColorModel</code> method of <code>PlanarImage</code>.
0256:             * Note that this implies that the <code>ColorModel</code> could be
0257:             * <code>null</code>.
0258:             *
0259:             * @param origin A <code>Point</code> indicating the image's upper
0260:             *        left corner.
0261:             * @param sampleModel A <code>SampleModel</code> with which to be
0262:             *        compatible.
0263:             * @param tileWidth The desired tile width.
0264:             * @param tileHeight The desired tile height.
0265:             *
0266:             * @deprecated as of JAI 1.1.
0267:             */
0268:            public TiledImage(Point origin, SampleModel sampleModel,
0269:                    int tileWidth, int tileHeight) {
0270:                this (origin.x, origin.y, sampleModel.getWidth(), sampleModel
0271:                        .getHeight(), origin.x, origin.y, coerceSampleModel(
0272:                        sampleModel, tileWidth, tileHeight), PlanarImage
0273:                        .createColorModel(sampleModel));
0274:            }
0275:
0276:            /**
0277:             * Constructs a <code>TiledImage</code> starting at the global
0278:             * coordinate origin.  The <code>ColorModel</code>
0279:             * will be derived from the <code>SampleModel</code> using the
0280:             * <code>createColorModel</code> method of <code>PlanarImage</code>.
0281:             * Note that this implies that the <code>ColorModel</code> could be
0282:             * <code>null</code>.
0283:             *
0284:             * @param sampleModel A <code>SampleModel</code> with which to be
0285:             *        compatible.
0286:             * @param tileWidth The desired tile width.
0287:             * @param tileHeight The desired tile height.
0288:             *
0289:             * @deprecated as of JAI 1.1.
0290:             */
0291:            public TiledImage(SampleModel sampleModel, int tileWidth,
0292:                    int tileHeight) {
0293:                this (0, 0, sampleModel.getWidth(), sampleModel.getHeight(), 0,
0294:                        0,
0295:                        coerceSampleModel(sampleModel, tileWidth, tileHeight),
0296:                        PlanarImage.createColorModel(sampleModel));
0297:            }
0298:
0299:            /**
0300:             * Constructs a <code>TiledImage</code> equivalent to a given
0301:             * <code>RenderedImage</code> but with specific tile dimensions.
0302:             * Actual copying of the pixel data from the <code>RenderedImage</code>
0303:             * will be deferred until the first time they are requested from the
0304:             * <code>TiledImage</code>.
0305:             *
0306:             * @param source The source <code>RenderedImage</code>.
0307:             * @param tileWidth The desired tile width.
0308:             * @param tileHeight The desired tile height.
0309:             *
0310:             * @since JAI 1.1
0311:             */
0312:            public TiledImage(RenderedImage source, int tileWidth,
0313:                    int tileHeight) {
0314:                this (source.getMinX(), source.getMinY(), source.getWidth(),
0315:                        source.getHeight(), source.getTileGridXOffset(), source
0316:                                .getTileGridYOffset(), coerceSampleModel(source
0317:                                .getSampleModel(), tileWidth, tileHeight),
0318:                        source.getColorModel());
0319:
0320:                set(source);
0321:            }
0322:
0323:            /**
0324:             * Constructs a <code>TiledImage</code> equivalent to a given
0325:             * <code>RenderedImage</code>.  Actual copying of the pixel data from
0326:             * the <code>RenderedImage</code> will be deferred until the first time
0327:             * they are requested from the <code>TiledImage</code>.  The tiles of
0328:             * the <code>TiledImage</code> may optionally share
0329:             * <code>DataBuffer</code>s with the tiles of the source image but it
0330:             * should be realized in this case that data written into the
0331:             * <code>TiledImage</code> will be visible in the source image.
0332:             *
0333:             * @param source The source <code>RenderedImage</code>.
0334:             * @param areBuffersShared Whether the tile <code>DataBuffer</code>s
0335:             *                         of the source are re-used in the tiles of
0336:             *			       this image.  If <code>false</code> new
0337:             *			       <code>WritableRaster</code>s will be
0338:             *			       created.
0339:             *
0340:             * @since JAI 1.1
0341:             */
0342:            public TiledImage(RenderedImage source, boolean areBuffersShared) {
0343:                this (source, source.getTileWidth(), source.getTileHeight());
0344:
0345:                // Do not re-use source tiles if the source does not compute unique tiles.
0346:                RenderedImage sourceRendering = source instanceof  RenderedOp ? ((RenderedOp) source)
0347:                        .getRendering()
0348:                        : source;
0349:                boolean suppressBufferSharing = sourceRendering instanceof  OpImage
0350:                        && !((OpImage) sourceRendering).computesUniqueTiles();
0351:
0352:                this .areBuffersShared = areBuffersShared
0353:                        && !suppressBufferSharing;
0354:            }
0355:
0356:            /**
0357:             * Returns a <code>TiledImage</code> making use of an
0358:             * interleaved <code>SampleModel</code> with a given layout,
0359:             * number of bands, and data type.  The <code>ColorModel</code>
0360:             * will be derived from the <code>SampleModel</code> using the
0361:             * <code>createColorModel</code> method of <code>PlanarImage</code>.
0362:             * Note that this implies that the <code>ColorModel</code> could be
0363:             * <code>null</code>.
0364:             *
0365:             * @param minX The X coordinate of the upper-left pixel
0366:             * @param minY The Y coordinate of the upper-left pixel.
0367:             * @param width The width of the image.
0368:             * @param height The height of the image.
0369:             * @param numBands The number of bands in the image.
0370:             * @param dataType The data type, from among the constants
0371:             *        <code>DataBuffer.TYPE_*</code>.
0372:             * @param tileWidth The tile width.
0373:             * @param tileHeight The tile height.
0374:             * @param bandOffsets An array of non-duplicated integers between 0 and
0375:             *        <code>numBands - 1</code> of length <code>numBands</code>
0376:             *        indicating the relative offset of each band.
0377:             *
0378:             * @deprecated as of JAI 1.1.
0379:             */
0380:            public static TiledImage createInterleaved(int minX, int minY,
0381:                    int width, int height, int numBands, int dataType,
0382:                    int tileWidth, int tileHeight, int[] bandOffsets) {
0383:                SampleModel sm = RasterFactory
0384:                        .createPixelInterleavedSampleModel(dataType, tileWidth,
0385:                                tileHeight, numBands, numBands * tileWidth,
0386:                                bandOffsets);
0387:                return new TiledImage(minX, minY, width, height, minX, minY,
0388:                        sm, PlanarImage.createColorModel(sm));
0389:            }
0390:
0391:            /**
0392:             * Returns a <code>TiledImage</code> making use of an
0393:             * banded <code>SampleModel</code> with a given layout,
0394:             * number of bands, and data type.  The <code>ColorModel</code>
0395:             * will be derived from the <code>SampleModel</code> using the
0396:             * <code>createColorModel</code> method of <code>PlanarImage</code>.
0397:             * Note that this implies that the <code>ColorModel</code> could be
0398:             * <code>null</code>.
0399:             *
0400:             * @param minX The X coordinate of the upper-left pixel
0401:             * @param minY The Y coordinate of the upper-left pixel.
0402:             * @param width The width of the image.
0403:             * @param height The height of the image.
0404:             * @param dataType The data type, from among the constants
0405:             *        <code>DataBuffer.TYPE_*</code>.
0406:             * @param tileWidth The tile width.
0407:             * @param tileHeight The tile height.
0408:             * @param bankIndices An array of <code>int</code>s indicating the
0409:             *        index of the bank to use for each band.  Bank indices
0410:             *        may be duplicated.
0411:             * @param bandOffsets An array of integers indicating the starting
0412:             *        offset of each band within its bank.  Bands stored in
0413:             *        the same bank must have sufficiently different offsets
0414:             *        so as not to overlap.
0415:             *
0416:             * @deprecated as of JAI 1.1.
0417:             */
0418:            public static TiledImage createBanded(int minX, int minY,
0419:                    int width, int height, int dataType, int tileWidth,
0420:                    int tileHeight, int[] bankIndices, int[] bandOffsets) {
0421:                SampleModel sm = new BandedSampleModel(dataType, tileWidth,
0422:                        tileHeight, tileWidth, bankIndices, bandOffsets);
0423:                return new TiledImage(minX, minY, width, height, minX, minY,
0424:                        sm, PlanarImage.createColorModel(sm));
0425:            }
0426:
0427:            /**
0428:             * Overlays a rectangular area of pixels from an image onto a tile.
0429:             *
0430:             * @param tile
0431:             * @param im
0432:             * @param rect
0433:             */
0434:            private void overlayPixels(WritableRaster tile, RenderedImage im,
0435:                    Rectangle rect) {
0436:                // Create a child of tile occupying the intersection area
0437:                WritableRaster child = tile.createWritableChild(rect.x, rect.y,
0438:                        rect.width, rect.height, rect.x, rect.y, bandList);
0439:
0440:                im.copyData(child);
0441:            }
0442:
0443:            /**
0444:             * Overlays a set of pixels described by an Area from an image
0445:             * onto a tile.
0446:             *
0447:             * @param tile
0448:             * @param im
0449:             * @param a
0450:             */
0451:            private void overlayPixels(WritableRaster tile, RenderedImage im,
0452:                    Area a) {
0453:                ROIShape rs = new ROIShape(a);
0454:                Rectangle bounds = rs.getBounds();
0455:                LinkedList rectList = rs.getAsRectangleList(bounds.x, bounds.y,
0456:                        bounds.width, bounds.height);
0457:                int numRects = rectList.size();
0458:                for (int i = 0; i < numRects; i++) {
0459:                    Rectangle rect = (Rectangle) rectList.get(i);
0460:                    WritableRaster child = tile.createWritableChild(rect.x,
0461:                            rect.y, rect.width, rect.height, rect.x, rect.y,
0462:                            bandList);
0463:                    im.copyData(child);
0464:                }
0465:            }
0466:
0467:            /**
0468:             * Overlays a set of pixels described by a bitmask
0469:             * onto a tile.
0470:             */
0471:            private void overlayPixels(WritableRaster tile, RenderedImage im,
0472:                    Rectangle rect, int[][] bitmask) {
0473:                Raster r = im.getData(rect);
0474:
0475:                // If this is sub-banded child image, create a child of the
0476:                // tile into which to write.
0477:                if (bandList != null) {
0478:                    tile = tile.createWritableChild(rect.x, rect.y, rect.width,
0479:                            rect.height, rect.x, rect.y, bandList);
0480:                }
0481:
0482:                // Create a buffer suitable for transferring pixels
0483:                Object data = r.getDataElements(rect.x, rect.y, null);
0484:
0485:                // The bitmask passed in might have undefined values outside
0486:                // the specified rect - therefore make sure that those bits
0487:                // are ignored.
0488:                int leftover = rect.width % 32;
0489:                int bitWidth = ((rect.width + 31) / 32)
0490:                        - (leftover > 0 ? 1 : 0);
0491:                int y = rect.y;
0492:
0493:                for (int j = 0; j < rect.height; j++, y++) {
0494:                    int[] rowMask = bitmask[j];
0495:                    int i, x = rect.x;
0496:
0497:                    for (i = 0; i < bitWidth; i++) {
0498:                        int mask32 = rowMask[i];
0499:                        int bit = 0x80000000;
0500:
0501:                        for (int b = 0; b < 32; b++, x++) {
0502:                            if ((mask32 & bit) != 0) {
0503:                                r.getDataElements(x, y, data);
0504:                                tile.setDataElements(x, y, data);
0505:                            }
0506:                            bit >>>= 1;
0507:                        }
0508:                    }
0509:
0510:                    if (leftover > 0) {
0511:                        int mask32 = rowMask[i];
0512:                        int bit = 0x80000000;
0513:
0514:                        for (int b = 0; b < leftover; b++, x++) {
0515:                            if ((mask32 & bit) != 0) {
0516:                                r.getDataElements(x, y, data);
0517:                                tile.setDataElements(x, y, data);
0518:                            }
0519:                            bit >>>= 1;
0520:                        }
0521:                    }
0522:                }
0523:            }
0524:
0525:            /**
0526:             * Overlays a given <code>RenderedImage</code> on top of the
0527:             * current contents of the <code>TiledImage</code>.  The source
0528:             * image must have a <code>SampleModel</code> compatible with that
0529:             * of this image.  If the source image does not overlap this image
0530:             * then invoking this method will have no effect.
0531:             *
0532:             * <p> The source image is added as a fallback <code>PropertySource</code>
0533:             * for the <code>TiledImage</code>: if a given property is not set directly
0534:             * on the <code>TiledImage</code> an attempt will be made to obtain its
0535:             * value from the source image.
0536:             *
0537:             * @param im A <code>RenderedImage</code> source to overlay.
0538:             *
0539:             * @throws <code>IllegalArgumentException</code> if <code>im</code> is
0540:             *         <code>null</code>.
0541:             */
0542:            public void set(RenderedImage im) {
0543:
0544:                if (im == null) {
0545:                    throw new IllegalArgumentException(JaiI18N
0546:                            .getString("Generic0"));
0547:                }
0548:
0549:                // Same source: do nothing.
0550:                if (getNumSources() > 0 && im == getSourceImage(0)) {
0551:                    return;
0552:                }
0553:
0554:                Rectangle imRect = new Rectangle(im.getMinX(), im.getMinY(), im
0555:                        .getWidth(), im.getHeight());
0556:
0557:                // Return if the source image does not overlap this image.
0558:                if ((imRect = imRect.intersection(getBounds())).isEmpty()) {
0559:                    return;
0560:                }
0561:
0562:                // Unset buffer sharing flag.
0563:                areBuffersShared = false;
0564:
0565:                // Set tile index limits.
0566:                int txMin = XToTileX(imRect.x);
0567:                int tyMin = YToTileY(imRect.y);
0568:                int txMax = XToTileX(imRect.x + imRect.width - 1);
0569:                int tyMax = YToTileY(imRect.y + imRect.height - 1);
0570:
0571:                // Loop over all in-bound tiles, find ones that have been computed
0572:                for (int j = tyMin; j <= tyMax; j++) {
0573:                    for (int i = txMin; i <= txMax; i++) {
0574:                        WritableRaster t;
0575:                        if ((t = tiles[i - minTileX][j - minTileY]) != null
0576:                                && !isTileLocked(i, j)) {
0577:                            Rectangle tileRect = getTileRect(i, j);
0578:                            tileRect = tileRect.intersection(imRect);
0579:                            if (!tileRect.isEmpty()) {
0580:                                overlayPixels(t, im, tileRect);
0581:                            }
0582:                        }
0583:                    }
0584:                }
0585:
0586:                // Cache the (wrapped) source image and clear the source ROI.
0587:                PlanarImage src = PlanarImage.wrapRenderedImage(im);
0588:                if (getNumSources() == 0) {
0589:                    addSource(src);
0590:                } else {
0591:                    setSource(src, 0);
0592:                }
0593:                srcROI = null;
0594:                overlapBounds = imRect;
0595:
0596:                // Add the source as fallback PropertySource.
0597:                properties.addProperties(src);
0598:            }
0599:
0600:            /**
0601:             * Overlays a given <code>RenderedImage</code> on top of the
0602:             * current contents of the <code>TiledImage</code> and its
0603:             * intersection with the supplied ROI.  The source
0604:             * image must have a <code>SampleModel</code> compatible with that
0605:             * of this image.  If the source image and the region of interest
0606:             * do not both overlap this image then invoking this method will
0607:             * have no effect.
0608:             *
0609:             * <p> The source image is added as a fallback <code>PropertySource</code>
0610:             * for the <code>TiledImage</code>: if a given property is not set directly
0611:             * on the <code>TiledImage</code> an attempt will be made to obtain its
0612:             * value from the source image.
0613:             *
0614:             * @param im A <code>RenderedImage</code> source to overlay.
0615:             * @param roi The region of interest.
0616:             *
0617:             * @throws <code>IllegalArgumentException</code> either parameter is
0618:             *         <code>null</code>.
0619:             */
0620:            public void set(RenderedImage im, ROI roi) {
0621:
0622:                if (im == null) {
0623:                    throw new IllegalArgumentException(JaiI18N
0624:                            .getString("Generic0"));
0625:                }
0626:
0627:                // Same source: do nothing.
0628:                if (getNumSources() > 0 && im == getSourceImage(0)) {
0629:                    return;
0630:                }
0631:
0632:                Rectangle imRect = new Rectangle(im.getMinX(), im.getMinY(), im
0633:                        .getWidth(), im.getHeight());
0634:
0635:                // Return if there is not a common intersection among this
0636:                // image and the source image and the region of interest.
0637:                Rectangle overlap = imRect.intersection(roi.getBounds());
0638:                if (overlap.isEmpty()
0639:                        || (overlap = overlap.intersection(getBounds()))
0640:                                .isEmpty()) {
0641:                    return;
0642:                }
0643:
0644:                // Unset buffer sharing flag.
0645:                areBuffersShared = false;
0646:
0647:                // Set tile index limits.
0648:                int txMin = XToTileX(overlap.x);
0649:                int tyMin = YToTileY(overlap.y);
0650:                int txMax = XToTileX(overlap.x + overlap.width - 1);
0651:                int tyMax = YToTileY(overlap.y + overlap.height - 1);
0652:
0653:                Shape roiShape = roi.getAsShape();
0654:                Area roiArea = null;
0655:                if (roiShape != null) {
0656:                    roiArea = new Area(roiShape);
0657:                }
0658:
0659:                // Loop over all in-bound tiles, find ones that have been computed
0660:                for (int j = tyMin; j <= tyMax; j++) {
0661:                    for (int i = txMin; i <= txMax; i++) {
0662:                        WritableRaster t;
0663:                        if ((t = tiles[i - minTileX][j - minTileY]) != null
0664:                                && !isTileLocked(i, j)) {
0665:                            Rectangle rect = getTileRect(i, j).intersection(
0666:                                    overlap);
0667:                            if (!rect.isEmpty()) {
0668:                                if (roiShape != null) {
0669:                                    Area a = new Area(rect);
0670:                                    a.intersect(roiArea);
0671:
0672:                                    if (!a.isEmpty()) {
0673:                                        overlayPixels(t, im, a);
0674:                                    }
0675:                                } else {
0676:                                    int[][] bitmask = roi.getAsBitmask(rect.x,
0677:                                            rect.y, rect.width, rect.height,
0678:                                            null);
0679:
0680:                                    if (bitmask != null && bitmask.length > 0) {
0681:                                        overlayPixels(t, im, rect, bitmask);
0682:                                    }
0683:                                }
0684:                            }
0685:                        }
0686:                    }
0687:                }
0688:
0689:                // Cache the (wrapped) source image and the source ROI.
0690:                PlanarImage src = PlanarImage.wrapRenderedImage(im);
0691:                if (getNumSources() == 0) {
0692:                    addSource(src);
0693:                } else {
0694:                    setSource(src, 0);
0695:                }
0696:                srcROI = roi;
0697:                overlapBounds = overlap;
0698:
0699:                // Add the source as fallback PropertySource.
0700:                properties.addProperties(src);
0701:            }
0702:
0703:            /**
0704:             * Creates a <code>Graphics</code> object that can be used to
0705:             * paint text and graphics onto the <code>TiledImage</code>.
0706:             * The <code>TiledImage</code> must be of integral data type
0707:             * or an <code>UnsupportedOperationException</code> will be thrown.
0708:             *
0709:             * @deprecated as of JAI 1.1.
0710:             */
0711:            public Graphics getGraphics() {
0712:                return createGraphics();
0713:            }
0714:
0715:            /**
0716:             * Creates a <code>Graphics2D</code> object that can be used to
0717:             * paint text and graphics onto the <code>TiledImage</code>.
0718:             * The <code>TiledImage</code> must be of integral data type
0719:             * or an <code>UnsupportedOperationException</code> will be thrown.
0720:             */
0721:            public Graphics2D createGraphics() {
0722:                int dataType = sampleModel.getDataType();
0723:                if (dataType != DataBuffer.TYPE_BYTE
0724:                        && dataType != DataBuffer.TYPE_SHORT
0725:                        && dataType != DataBuffer.TYPE_USHORT
0726:                        && dataType != DataBuffer.TYPE_INT) {
0727:                    throw new UnsupportedOperationException(JaiI18N
0728:                            .getString("TiledImage0"));
0729:                }
0730:                return new TiledImageGraphics(this );
0731:            }
0732:
0733:            /**
0734:             * Returns a <code>TiledImage</code> that shares the tile
0735:             * <code>Raster</code>s of this image.  The returned image
0736:             * occupies a sub-area of the parent image, and possesses a
0737:             * possibly permuted subset of the parent's bands.  The two images
0738:             * share a common coordinate system.
0739:             *
0740:             * <p> The image bounds are clipped against the bounds of the
0741:             * parent image.
0742:             *
0743:             * <p> If the specified <code>ColorModel</code> is <code>null</code>
0744:             * then the <code>ColorModel</code> of the sub-image will be set to
0745:             * <code>null</code> unless <code>bandSelect</code> is either
0746:             * <code>null</code> or equal in length to the number of bands in the
0747:             * image in which cases the sub-image <code>ColorModel</code> will be
0748:             * set to that of the current image.
0749:             *
0750:             * @param x the minimum X coordinate of the subimage.
0751:             * @param y the minimum Y coordinate of the subimage.
0752:             * @param w the width of the subimage.
0753:             * @param h the height of the subimage.
0754:             * @param bandSelect an array of band indices; if null,
0755:             *                   all bands are selected.
0756:             * @param cm the <code>ColorModel</code> of the sub-image.
0757:             *
0758:             * @return The requested sub-image or <code>null</code> if either
0759:             *         the specified rectangular area or its intersection with
0760:             *         the current image is empty.
0761:             *
0762:             * @since JAI 1.1
0763:             */
0764:            public TiledImage getSubImage(int x, int y, int w, int h,
0765:                    int[] bandSelect, ColorModel cm) {
0766:                // Check for empty overlap.
0767:                Rectangle subImageBounds = new Rectangle(x, y, w, h);
0768:                if (subImageBounds.isEmpty()) {
0769:                    return null;
0770:                }
0771:                Rectangle overlap = subImageBounds.intersection(getBounds());
0772:                if (overlap.isEmpty()) {
0773:                    return null;
0774:                }
0775:
0776:                // Use the original SampleModel or create a subset of it.
0777:                SampleModel sm = bandSelect != null ? getSampleModel()
0778:                        .createSubsetSampleModel(bandSelect) : getSampleModel();
0779:
0780:                // Set the ColorModel.
0781:                if (cm == null
0782:                        && (bandSelect == null || bandSelect.length == getSampleModel()
0783:                                .getNumBands())) {
0784:                    cm = getColorModel();
0785:                }
0786:
0787:                // Create the sub-image.
0788:                TiledImage subImage = new TiledImage(this , overlap.x,
0789:                        overlap.y, overlap.width, overlap.height,
0790:                        getTileGridXOffset(), getTileGridYOffset(), sm, cm);
0791:
0792:                // Derive sub-image sub-band list with respect to the ancestor
0793:                // TiledImage.  It is possible here that an
0794:                // ArrayIndexOutOfBoundsException could be thrown if the user
0795:                // is not careful.
0796:                int[] subBandList = null;
0797:                if (bandSelect != null) { // "this" is being sub-banded.
0798:                    if (bandList != null) { //"this" is a sub-band child.
0799:                        // Derive the sub-band list using the sub-band list of
0800:                        // "this" and the list passed in.
0801:                        subBandList = new int[bandSelect.length];
0802:                        for (int band = 0; band < bandSelect.length; band++) {
0803:                            subBandList[band] = bandList[bandSelect[band]];
0804:                        }
0805:                    } else { // "this" is not a sub-band child.
0806:                        // Set the sub-band list to the list passed in.
0807:                        subBandList = bandSelect;
0808:                    }
0809:                } else { // "this" is not being sub-banded.
0810:                    // Pass on the sub-band list of "this".
0811:                    subBandList = bandList;
0812:                }
0813:
0814:                // Set the sub-band list of the newly created sub-image.
0815:                subImage.bandList = subBandList;
0816:
0817:                return subImage;
0818:            }
0819:
0820:            /**
0821:             * Returns a <code>TiledImage</code> that shares the tile
0822:             * <code>Raster</code>s of this image.  The returned image
0823:             * occupies a sub-area of the parent image, and possesses a
0824:             * possibly permuted subset of the parent's bands.  The two images
0825:             * share a common coordinate system.  The <code>ColorModel</code>
0826:             * will be derived from the sub-image <code>SampleModel</code> using the
0827:             * <code>createColorModel</code> method of <code>PlanarImage</code>.
0828:             * Note that this implies that the <code>ColorModel</code> could be
0829:             * <code>null</code>.
0830:             *
0831:             * <p> The image bounds are clipped against the bounds of the
0832:             * parent image.
0833:             *
0834:             * @param x the minimum X coordinate of the subimage.
0835:             * @param y the minimum Y coordinate of the subimage.
0836:             * @param w the width of the subimage.
0837:             * @param h the height of the subimage.
0838:             * @param bandSelect an array of band indices; if null,
0839:             *                   all bands are selected.
0840:             *
0841:             * @return The requested sub-image or <code>null</code> if either
0842:             *         the specified rectangular area or its intersection with
0843:             *         the current image is empty.
0844:             *
0845:             * @deprecated as of JAI 1.1.
0846:             */
0847:            public TiledImage getSubImage(int x, int y, int w, int h,
0848:                    int[] bandSelect) {
0849:                SampleModel sm = bandSelect != null ? getSampleModel()
0850:                        .createSubsetSampleModel(bandSelect) : getSampleModel();
0851:                return getSubImage(x, y, w, h, bandSelect, createColorModel(sm));
0852:            }
0853:
0854:            /**
0855:             * Returns a <code>TiledImage</code> that shares the tile
0856:             * <code>Raster</code>s of this image.  The returned image
0857:             * occupies a subarea of the parent image.  The two images share a
0858:             * common coordinate system.
0859:             *
0860:             * <p> The image bounds are clipped against the bounds of the
0861:             * parent image.
0862:             *
0863:             * @param x the minimum X coordinate of the subimage.
0864:             * @param y the minimum Y coordinate of the subimage.
0865:             * @param w the width of the subimage.
0866:             * @param h the height of the subimage.
0867:             */
0868:            public TiledImage getSubImage(int x, int y, int w, int h) {
0869:                return getSubImage(x, y, w, h, null, null);
0870:            }
0871:
0872:            /**
0873:             * Returns a <code>TiledImage</code> that shares the tile
0874:             * <code>Raster</code>s of this image.
0875:             *
0876:             * <p> If the specified <code>ColorModel</code> is <code>null</code>
0877:             * then the <code>ColorModel</code> of the sub-image will be set to
0878:             * <code>null</code> unless <code>bandSelect</code> is equal in length
0879:             * to the number of bands in the image in which cases the sub-image
0880:             * <code>ColorModel</code> will be set to that of the current image.
0881:             *
0882:             * @param bandSelect an array of band indices.
0883:             * @param cm the <code>ColorModel</code> of the sub-image.
0884:             *
0885:             * @throws <code>IllegalArgumentException</code> is <code>bandSelect</code>
0886:             *         is <code>null</code>.
0887:             *
0888:             * @since JAI 1.1
0889:             */
0890:            public TiledImage getSubImage(int[] bandSelect, ColorModel cm) {
0891:                if (bandSelect == null) {
0892:                    throw new IllegalArgumentException(JaiI18N
0893:                            .getString("Generic0"));
0894:                }
0895:                return getSubImage(getMinX(), getMinY(), getWidth(),
0896:                        getHeight(), bandSelect, cm);
0897:            }
0898:
0899:            /**
0900:             * Returns a <code>TiledImage</code> that shares the tile
0901:             * <code>Raster</code>s of this image.  The returned image
0902:             * occupies the same area as the parent image, and possesses a
0903:             * possibly permuted subset of the parent's bands.  The
0904:             * <code>ColorModel</code> will be derived from the sub-image
0905:             * <code>SampleModel</code> using the <code>createColorModel</code>
0906:             * method of <code>PlanarImage</code>.  Note that this implies that
0907:             * the <code>ColorModel</code> could be <code>null</code>.
0908:             *
0909:             * @param bandSelect an array of band indices.
0910:             *
0911:             * @deprecated as of JAI 1.1.
0912:             */
0913:            public TiledImage getSubImage(int[] bandSelect) {
0914:                if (bandSelect == null) {
0915:                    throw new IllegalArgumentException(JaiI18N
0916:                            .getString("Generic0"));
0917:                }
0918:                return getSubImage(getMinX(), getMinY(), getWidth(),
0919:                        getHeight(), bandSelect); // Deliberately using 5-param version.
0920:            }
0921:
0922:            /**
0923:             * Forces the requested tile to be computed if has not already been so
0924:             * and if a source is available.
0925:             *
0926:             * @throws ArrayIndexOutOfBoundsException if at least one of the supplied
0927:             *         tile indices is out of the image.
0928:             */
0929:            private void createTile(int tileX, int tileY) {
0930:                PlanarImage src = getNumSources() > 0 ? getSourceImage(0)
0931:                        : null;
0932:
0933:                // If this is a child image with no source for uncomputed tiles
0934:                // forward the call to the parent.
0935:                if (src == null && parent != null) {
0936:                    parent.createTile(tileX, tileY);
0937:                    return;
0938:                }
0939:
0940:                synchronized (tiles) {
0941:                    // Do nothing if tile is non-null, i.e., already computed.
0942:                    if (tiles[tileX - minTileX][tileY - minTileY] == null) {
0943:                        // If sharing buffers, do so.
0944:                        if (areBuffersShared) {
0945:                            Raster srcTile = src.getTile(tileX, tileY);
0946:                            if (srcTile instanceof  WritableRaster) {
0947:                                tiles[tileX - minTileX][tileY - minTileY] = (WritableRaster) srcTile;
0948:                            } else {
0949:                                Point location = new Point(srcTile.getMinX(),
0950:                                        srcTile.getMinY());
0951:                                tiles[tileX - minTileX][tileY - minTileY] = Raster
0952:                                        .createWritableRaster(sampleModel,
0953:                                                srcTile.getDataBuffer(),
0954:                                                location);
0955:                            }
0956:                            return;
0957:                        }
0958:
0959:                        // Create the tile using the ancestor SampleModel.
0960:                        tiles[tileX - minTileX][tileY - minTileY] = createWritableRaster(
0961:                                ancestorSampleModel, new Point(tileXToX(tileX),
0962:                                        tileYToY(tileY)));
0963:                        WritableRaster tile = tiles[tileX - minTileX][tileY
0964:                                - minTileY];
0965:
0966:                        // If a source is available try to set the tile's data.
0967:                        if (src != null) {
0968:                            // Get the bounds of the tile's support.
0969:                            Rectangle tileRect = getTileRect(tileX, tileY);
0970:
0971:                            // Determine the intersection of the tile and the overlap.
0972:                            Rectangle rect = overlapBounds
0973:                                    .intersection(tileRect);
0974:
0975:                            // Bail if this doesn't intersect the effective overlap.
0976:                            if (rect.isEmpty()) {
0977:                                return;
0978:                            }
0979:
0980:                            // If a source ROI is present, use it.
0981:                            if (srcROI != null) {
0982:                                // Attempt to get the ROI as a Shape.
0983:                                Shape roiShape = srcROI.getAsShape();
0984:
0985:                                if (roiShape != null) {
0986:                                    // Determine the area of overlap.
0987:                                    Area a = new Area(rect);
0988:                                    a.intersect(new Area(roiShape));
0989:
0990:                                    if (!a.isEmpty()) {
0991:                                        // If the area is non-empty overlay the pixels.
0992:                                        overlayPixels(tile, src, a);
0993:                                    }
0994:                                } else {
0995:                                    int[][] bitmask = srcROI.getAsBitmask(
0996:                                            rect.x, rect.y, rect.width,
0997:                                            rect.height, null);
0998:
0999:                                    overlayPixels(tile, src, rect, bitmask);
1000:                                }
1001:                            } else {
1002:                                // If the intersection equals the tile area, copy data into
1003:                                // the entire tile.  If the tile straddles the edge of the
1004:                                // source, copy only into the intersection.
1005:                                if (!rect.isEmpty()) {
1006:                                    if (bandList == null
1007:                                            && rect.equals(tileRect)) {
1008:                                        // The current image has the same bands in the
1009:                                        // same order as its ancestor TiledImage and
1010:                                        // the requested tile is completely within "src".
1011:                                        if (tileRect.equals(tile.getBounds()))
1012:                                            src.copyData(tile);
1013:                                        else
1014:                                            src.copyData(tile
1015:                                                    .createWritableChild(
1016:                                                            rect.x, rect.y,
1017:                                                            rect.width,
1018:                                                            rect.height,
1019:                                                            rect.x, rect.y,
1020:                                                            null));
1021:                                    } else {
1022:                                        overlayPixels(tile, src, rect);
1023:                                    }
1024:                                }
1025:                            }
1026:                        }
1027:                    }
1028:                }
1029:            }
1030:
1031:            /**
1032:             * Retrieves a particular tile from the image for reading only.
1033:             * The tile will be computed if it hasn't been previously.
1034:             * Any attempt to write to the tile will produce undefined results.
1035:             *
1036:             * @param tileX the X index of the tile.
1037:             * @param tileY the Y index of the tile.
1038:             */
1039:            public Raster getTile(int tileX, int tileY) {
1040:                if (tileX < minTileX || tileY < minTileY
1041:                        || tileX > getMaxTileX() || tileY > getMaxTileY()) {
1042:                    return null;
1043:                }
1044:
1045:                createTile(tileX, tileY);
1046:
1047:                // For non-sub-banded image return the tile directly.
1048:                if (bandList == null) {
1049:                    return (Raster) tiles[tileX - minTileX][tileY - minTileY];
1050:                }
1051:
1052:                // For sub-banded image return appropriate band subset.
1053:                Raster r = (Raster) tiles[tileX - minTileX][tileY - minTileY];
1054:
1055:                return r.createChild(r.getMinX(), r.getMinY(), r.getWidth(), r
1056:                        .getHeight(), r.getMinX(), r.getMinY(), bandList);
1057:            }
1058:
1059:            /**
1060:             * Retrieves a particular tile from the image for reading and writing.
1061:             * If the tile is locked, null will be returned.  Otherwise, the tile
1062:             * will be computed if it hasn't been previously.  Updates of the tile
1063:             * will become visible to readers of this image as they occur.
1064:             *
1065:             * @param tileX the X index of the tile.
1066:             * @param tileY the Y index of the tile.
1067:             * @return The requested tile or null if the tile is locked.
1068:             */
1069:            public WritableRaster getWritableTile(int tileX, int tileY) {
1070:                if (tileX < minTileX || tileY < minTileY
1071:                        || tileX > getMaxTileX() || tileY > getMaxTileY()) {
1072:                    return null;
1073:                }
1074:
1075:                if (isTileLocked(tileX, tileY)) {
1076:                    return null;
1077:                }
1078:
1079:                createTile(tileX, tileY);
1080:                ++writers[tileX - minTileX][tileY - minTileY];
1081:
1082:                if (writers[tileX - minTileX][tileY - minTileY] == 1) {
1083:                    numWritableTiles[0]++;
1084:
1085:                    Enumeration e = tileObservers.elements();
1086:                    while (e.hasMoreElements()) {
1087:                        TileObserver t = (TileObserver) e.nextElement();
1088:                        t.tileUpdate(this , tileX, tileY, true);
1089:                    }
1090:                }
1091:
1092:                // For non-sub-banded image return the tile directly.
1093:                if (bandList == null) {
1094:                    return tiles[tileX - minTileX][tileY - minTileY];
1095:                }
1096:
1097:                // For sub-banded image return appropriate band subset.
1098:                WritableRaster wr = tiles[tileX - minTileX][tileY - minTileY];
1099:
1100:                return wr.createWritableChild(wr.getMinX(), wr.getMinY(), wr
1101:                        .getWidth(), wr.getHeight(), wr.getMinX(),
1102:                        wr.getMinY(), bandList);
1103:            }
1104:
1105:            /**
1106:             * Indicates that a writer is done updating a tile.
1107:             * The effects of attempting to release a tile that has not been
1108:             * grabbed, or releasing a tile more than once are undefined.
1109:             *
1110:             * @param tileX the X index of the tile.
1111:             * @param tileY the Y index of the tile.
1112:             */
1113:            public void releaseWritableTile(int tileX, int tileY) {
1114:                if (isTileLocked(tileX, tileY)) {
1115:                    return;
1116:                }
1117:
1118:                --writers[tileX - minTileX][tileY - minTileY];
1119:
1120:                if (writers[tileX - minTileX][tileY - minTileY] < 0) {
1121:                    throw new RuntimeException(JaiI18N.getString("TiledImage1"));
1122:                }
1123:
1124:                if (writers[tileX - minTileX][tileY - minTileY] == 0) {
1125:                    numWritableTiles[0]--;
1126:
1127:                    Enumeration e = tileObservers.elements();
1128:                    while (e.hasMoreElements()) {
1129:                        TileObserver t = (TileObserver) e.nextElement();
1130:                        t.tileUpdate(this , tileX, tileY, false);
1131:                    }
1132:                }
1133:            }
1134:
1135:            /**
1136:             * Forces a tile to be computed, and its contents stored
1137:             * indefinitely.  A tile may not be locked if it is currently
1138:             * writable.  This method should only be used within JAI, in
1139:             * order to optimize memory allocation.
1140:             *
1141:             * @param tileX the X index of the tile.
1142:             * @param tileY the Y index of the tile.
1143:             * @return Whether the tile was successfully locked.
1144:             */
1145:            protected boolean lockTile(int tileX, int tileY) {
1146:                if (tileX < minTileX || tileY < minTileY
1147:                        || tileX > getMaxTileX() || tileY > getMaxTileY()) {
1148:                    return false;
1149:                }
1150:
1151:                // Return false if the tile is writable.
1152:                if (isTileWritable(tileX, tileY)) {
1153:                    return false;
1154:                }
1155:
1156:                // Force the tile to be computed if it has not yet been.
1157:                createTile(tileX, tileY);
1158:
1159:                // Set the corresponding writers count to -1.
1160:                writers[tileX - minTileX][tileY - minTileY] = -1;
1161:
1162:                return true;
1163:            }
1164:
1165:            /**
1166:             * Returns <code>true</code> if a tile is locked.
1167:             *
1168:             * @param tileX the X index of the tile.
1169:             * @param tileY the Y index of the tile.
1170:             * @return Whether the tile is locked.
1171:             */
1172:            protected boolean isTileLocked(int tileX, int tileY) {
1173:                return writers[tileX - minTileX][tileY - minTileY] < 0;
1174:            }
1175:
1176:            /**
1177:             * Sets a region of a <code>TiledImage</code> to be a copy of a
1178:             * supplied <code>Raster</code>.  The <code>Raster</code>'s
1179:             * coordinate system is used to position it within the image.
1180:             * The computation of all overlapping tiles will be forced prior
1181:             * to modification of the data of the affected area.
1182:             *
1183:             * @param r a <code>Raster</code> containing pixels to be copied
1184:             * into the <code>TiledImage</code>.
1185:             */
1186:            public void setData(Raster r) {
1187:                // Return if the intersection of the image and Raster bounds is empty.
1188:                Rectangle rBounds = r.getBounds();
1189:                if ((rBounds = rBounds.intersection(getBounds())).isEmpty()) {
1190:                    return;
1191:                }
1192:
1193:                // Set tile index limits.
1194:                int txMin = XToTileX(rBounds.x);
1195:                int tyMin = YToTileY(rBounds.y);
1196:                int txMax = XToTileX(rBounds.x + rBounds.width - 1);
1197:                int tyMax = YToTileY(rBounds.y + rBounds.height - 1);
1198:
1199:                for (int ty = tyMin; ty <= tyMax; ty++) {
1200:                    for (int tx = txMin; tx <= txMax; tx++) {
1201:                        WritableRaster wr = getWritableTile(tx, ty);
1202:                        if (wr != null) {
1203:                            // XXX bpb 02/04/1999
1204:                            // All this checking shouldn't be necessary except
1205:                            // that it doesn't look as if WritableRaster.setRect()
1206:                            // correctly accounts for cases wherein the parameter
1207:                            // Raster is not contained in the target WritableRaster.
1208:                            Rectangle tileRect = getTileRect(tx, ty);
1209:                            if (tileRect.contains(rBounds)) {
1210:                                JDKWorkarounds.setRect(wr, r, 0, 0);
1211:                            } else {
1212:                                Rectangle xsect = rBounds
1213:                                        .intersection(tileRect);
1214:                                Raster rChild = r.createChild(xsect.x, xsect.y,
1215:                                        xsect.width, xsect.height, xsect.x,
1216:                                        xsect.y, null);
1217:                                WritableRaster wChild = wr.createWritableChild(
1218:                                        xsect.x, xsect.y, xsect.width,
1219:                                        xsect.height, xsect.x, xsect.y, null);
1220:                                JDKWorkarounds.setRect(wChild, rChild, 0, 0);
1221:                            }
1222:                            releaseWritableTile(tx, ty);
1223:                        }
1224:                    }
1225:                }
1226:            }
1227:
1228:            /**
1229:             * Sets a region of a <code>TiledImage</code> to be a copy of a
1230:             * supplied <code>Raster</code>.  The <code>Raster</code>'s
1231:             * coordinate system is used to position it within the image.
1232:             * The computation of all overlapping tiles will be forced prior
1233:             * to modification of the data of the affected area.
1234:             *
1235:             * @param r a <code>Raster</code> containing pixels to be copied
1236:             * into the <code>TiledImage</code>.
1237:             * @param roi The region of interest.
1238:             */
1239:            public void setData(Raster r, ROI roi) {
1240:                // Return if the intersection of the image bounds, the Raster,
1241:                // and the ROI bounds is empty.
1242:                Rectangle rBounds = r.getBounds();
1243:                if ((rBounds = rBounds.intersection(getBounds())).isEmpty()
1244:                        || (rBounds = rBounds.intersection(roi.getBounds()))
1245:                                .isEmpty()) {
1246:                    return;
1247:                }
1248:
1249:                // Get the Rectangle list representation of the ROI.
1250:                LinkedList rectList = roi.getAsRectangleList(rBounds.x,
1251:                        rBounds.y, rBounds.width, rBounds.height);
1252:
1253:                // Set tile index limits.
1254:                int txMin = XToTileX(rBounds.x);
1255:                int tyMin = YToTileY(rBounds.y);
1256:                int txMax = XToTileX(rBounds.x + rBounds.width - 1);
1257:                int tyMax = YToTileY(rBounds.y + rBounds.height - 1);
1258:
1259:                int numRects = rectList.size();
1260:
1261:                for (int ty = tyMin; ty <= tyMax; ty++) {
1262:                    for (int tx = txMin; tx <= txMax; tx++) {
1263:                        WritableRaster wr = getWritableTile(tx, ty);
1264:                        if (wr != null) {
1265:                            Rectangle tileRect = getTileRect(tx, ty);
1266:                            for (int i = 0; i < numRects; i++) {
1267:                                Rectangle rect = (Rectangle) rectList.get(i);
1268:                                rect = rect.intersection(tileRect);
1269:                                // XXX: Should the if-block below be split as in
1270:                                // set(RenderedImage, ROI) above?
1271:                                if (!rect.isEmpty()) {
1272:                                    Raster rChild = r.createChild(rect.x,
1273:                                            rect.y, rect.width, rect.height,
1274:                                            rect.x, rect.y, null);
1275:                                    WritableRaster wChild = wr
1276:                                            .createWritableChild(rect.x,
1277:                                                    rect.y, rect.width,
1278:                                                    rect.height, rect.x,
1279:                                                    rect.y, null);
1280:                                    JDKWorkarounds
1281:                                            .setRect(wChild, rChild, 0, 0);
1282:                                }
1283:                            }
1284:                            releaseWritableTile(tx, ty);
1285:                        }
1286:                    }
1287:                }
1288:            }
1289:
1290:            /**
1291:             * Informs this <code>TiledImage</code> that another object is
1292:             * interested in being notified whenever any tile becomes writable
1293:             * or ceases to be writable.  A tile becomes writable when it is
1294:             * not currently writable and <code>getWritableTile()</code> is
1295:             * called.  A tile ceases to be writable when
1296:             * <code>releaseTile()</code> is called and the number of calls to
1297:             * <code>getWritableTile()</code> and
1298:             * <code>releaseWritableTile()</code> are identical.
1299:             *
1300:             * <p> It is the responsibility of the <code>TiledImage</code> to
1301:             * inform all registered <code>TileObserver</code> objects of such
1302:             * changes in tile writability before the writer has a chance to
1303:             * make any modifications.
1304:             *
1305:             * @param observer An object implementing the
1306:             * <code>TileObserver</code> interface.
1307:             */
1308:            public void addTileObserver(TileObserver observer) {
1309:                tileObservers.addElement(observer);
1310:            }
1311:
1312:            /**
1313:             * Informs this <code>TiledImage</code> that a particular TileObserver no
1314:             * longer wishes to receive updates on tile writability status.
1315:             * The result of attempting to remove a listener that is not
1316:             * registered is undefined.
1317:             *
1318:             * @param observer An object implementing the
1319:             * <code>TileObserver</code> interface.
1320:             */
1321:            public void removeTileObserver(TileObserver observer) {
1322:                tileObservers.removeElement(observer);
1323:            }
1324:
1325:            /**
1326:             * Returns a list of tiles that are currently held by one or more
1327:             * writers or <code>null</code> of no tiles are so held.
1328:             *
1329:             * @return An array of <code>Point</code>s representing tile indices
1330:             *         or <code>null</code>.
1331:             */
1332:            public Point[] getWritableTileIndices() {
1333:                Point[] indices = null;
1334:
1335:                if (hasTileWriters()) {
1336:                    Vector v = new Vector();
1337:                    int count = 0;
1338:
1339:                    for (int j = 0; j < tilesY; j++) {
1340:                        for (int i = 0; i < tilesX; i++) {
1341:                            if (writers[i][j] > 0) {
1342:                                v.addElement(new Point(i + minTileX, j
1343:                                        + minTileY));
1344:                                ++count;
1345:                            }
1346:                        }
1347:                    }
1348:
1349:                    indices = new Point[count];
1350:                    for (int k = 0; k < count; k++) {
1351:                        indices[k] = (Point) v.elementAt(k);
1352:                    }
1353:                }
1354:
1355:                return indices;
1356:            }
1357:
1358:            /**
1359:             * Returns <code>true</code> if any tile is being held by a
1360:             * writer, <code>false</code> otherwise.  This provides a quick
1361:             * way to check whether it is necessary to make copies of tiles --
1362:             * if there are no writers, it is safe to use the tiles directly,
1363:             * while registering to learn of future writers.
1364:             */
1365:            public boolean hasTileWriters() {
1366:                return numWritableTiles[0] > 0;
1367:            }
1368:
1369:            /**
1370:             * Returns <code>true</code> if a tile has writers.
1371:             *
1372:             * @param tileX the X index of the tile.
1373:             * @param tileY the Y index of the tile.
1374:             */
1375:            public boolean isTileWritable(int tileX, int tileY) {
1376:                return writers[tileX - minTileX][tileY - minTileY] > 0;
1377:            }
1378:
1379:            /**
1380:             * Sets the <code>tiles</code> array to <code>null</code> so that
1381:             * the image may be used again.
1382:             *
1383:             * @throws IllegalStateException if <code>hasTileWriters()</code>
1384:             * returns <code>true</code>.
1385:             *
1386:             * @since JAI 1.1.2
1387:             */
1388:            public void clearTiles() {
1389:                if (hasTileWriters()) {
1390:                    throw new IllegalStateException(JaiI18N
1391:                            .getString("TiledImage2"));
1392:                }
1393:                tiles = null;
1394:            }
1395:
1396:            /*
1397:            private int[] XToTileXArray = null;
1398:            private int[] YToTileYArray = null;
1399:
1400:            private void initTileArrays() {
1401:                if (XTileTileXArray == null) {
1402:                    XToTileXArray = new int[width];
1403:                    YToTileYArray = new int[height];
1404:
1405:                    for (int i = 0; i < width; i++) {
1406:                        XToTileXArray[i] = XToTileX(minX + i);
1407:                    }
1408:
1409:                    for (int j = 0; j < height; j++) {
1410:                        YToTileYArray[j] = YToTileY(minY + j);
1411:                    }
1412:                }
1413:            }
1414:             */
1415:
1416:            /**
1417:             * Sets a sample of a pixel to a given <code>int</code> value.
1418:             *
1419:             * @param x The X coordinate of the pixel.
1420:             * @param y The Y coordinate of the pixel.
1421:             * @param b The band of the sample within the pixel.
1422:             * @param s The value to which to set the sample.
1423:             */
1424:            public void setSample(int x, int y, int b, int s) {
1425:                int tileX = XToTileX(x);
1426:                int tileY = YToTileY(y);
1427:                WritableRaster t = getWritableTile(tileX, tileY);
1428:                if (t != null) {
1429:                    t.setSample(x, y, b, s);
1430:                }
1431:                releaseWritableTile(tileX, tileY);
1432:            }
1433:
1434:            /**
1435:             * Returns the value of a given sample of a pixel as an <code>int</code>.
1436:             *
1437:             * @param x The X coordinate of the pixel.
1438:             * @param y The Y coordinate of the pixel.
1439:             * @param b The band of the sample within the pixel.
1440:             */
1441:            public int getSample(int x, int y, int b) {
1442:                int tileX = XToTileX(x);
1443:                int tileY = YToTileY(y);
1444:                Raster t = getTile(tileX, tileY);
1445:                return t.getSample(x, y, b);
1446:            }
1447:
1448:            /**
1449:             * Sets a sample of a pixel to a given <code>float</code> value.
1450:             *
1451:             * @param x The X coordinate of the pixel.
1452:             * @param y The Y coordinate of the pixel.
1453:             * @param b The band of the sample within the pixel.
1454:             * @param s The value to which to set the sample.
1455:             */
1456:            public void setSample(int x, int y, int b, float s) {
1457:                int tileX = XToTileX(x);
1458:                int tileY = YToTileY(y);
1459:                WritableRaster t = getWritableTile(tileX, tileY);
1460:                if (t != null) {
1461:                    t.setSample(x, y, b, s);
1462:                }
1463:                releaseWritableTile(tileX, tileY);
1464:            }
1465:
1466:            /**
1467:             * Returns the value of a given sample of a pixel as a <code>float</code>.
1468:             *
1469:             * @param x The X coordinate of the pixel.
1470:             * @param y The Y coordinate of the pixel.
1471:             * @param b The band of the sample within the pixel.
1472:             */
1473:            public float getSampleFloat(int x, int y, int b) {
1474:                int tileX = XToTileX(x);
1475:                int tileY = YToTileY(y);
1476:                Raster t = getTile(tileX, tileY);
1477:                return t.getSampleFloat(x, y, b);
1478:            }
1479:
1480:            /**
1481:             * Sets a sample of a pixel to a given <code>double</code> value.
1482:             *
1483:             * @param x The X coordinate of the pixel.
1484:             * @param y The Y coordinate of the pixel.
1485:             * @param b The band of the sample within the pixel.
1486:             * @param s The value to which to set the sample.
1487:             */
1488:            public void setSample(int x, int y, int b, double s) {
1489:                int tileX = XToTileX(x);
1490:                int tileY = YToTileY(y);
1491:                WritableRaster t = getWritableTile(tileX, tileY);
1492:                if (t != null) {
1493:                    t.setSample(x, y, b, s);
1494:                }
1495:                releaseWritableTile(tileX, tileY);
1496:            }
1497:
1498:            /**
1499:             * Returns the value of a given sample of a pixel as a <code>double</code>.
1500:             *
1501:             * @param x The X coordinate of the pixel.
1502:             * @param y The Y coordinate of the pixel.
1503:             * @param b The band of the sample within the pixel.
1504:             */
1505:            public double getSampleDouble(int x, int y, int b) {
1506:                int tileX = XToTileX(x);
1507:                int tileY = YToTileY(y);
1508:                Raster t = getTile(tileX, tileY);
1509:                return t.getSampleDouble(x, y, b);
1510:            }
1511:
1512:            /**
1513:             * Implementation of <code>PropertyChangeListener</code>.
1514:             *
1515:             * <p> When invoked with an event emitted by the source image specified
1516:             * for this <code>TiledImage</code> and the event is either a
1517:             * <code>PropertyChangeEventJAI</code> named "InvalidRegion"
1518:             * (case-insensitive) or a <code>RenderingChangeEvent</code>, then
1519:             * all tiles which overlap the intersection of the invalid region and the
1520:             * region of interest specified for this image (if any) will
1521:             * be cleared.  If the event is a <code>RenderingChangeEvent</code> then
1522:             * the invalid region will be obtained from the <code>getInvalidRegion</code>
1523:             * method of the event object; if a <code>PropertyChangeEventJAI</code>
1524:             * it will be obtained from the <code>getNewValue()</code> method.
1525:             * In either case, a new <code>PropertyChangeEventJAI</code> will be
1526:             * fired to all registered listeners of the property name
1527:             * "InvalidRegion" and to all known sinks which are
1528:             * <code>PropertyChangeListener</code>s.  Its old and new values will
1529:             * contain the previous and current invalid regions.  This may be used to
1530:             * determine which tiles must be re-requested.  The
1531:             * <code>TiledImage</code> itself will not re-request the data.
1532:             *
1533:             * @since JAI 1.1
1534:             */
1535:            public synchronized void propertyChange(PropertyChangeEvent evt) {
1536:                PlanarImage src = getNumSources() > 0 ? getSourceImage(0)
1537:                        : null;
1538:
1539:                if (evt.getSource() == src
1540:                        && (evt instanceof  RenderingChangeEvent || (evt instanceof  PropertyChangeEventJAI && evt
1541:                                .getPropertyName().equalsIgnoreCase(
1542:                                        "InvalidRegion")))) {
1543:
1544:                    // Get the region.
1545:                    Shape invalidRegion = evt instanceof  RenderingChangeEvent ? ((RenderingChangeEvent) evt)
1546:                            .getInvalidRegion()
1547:                            : (Shape) evt.getNewValue();
1548:
1549:                    // If empty, all is valid.
1550:                    Rectangle invalidBounds = invalidRegion.getBounds();
1551:                    if (invalidBounds.isEmpty()) {
1552:                        return;
1553:                    }
1554:
1555:                    // Intersect with ROI.
1556:                    Area invalidArea = new Area(invalidRegion);
1557:                    if (srcROI != null) {
1558:                        Shape roiShape = srcROI.getAsShape();
1559:                        if (roiShape != null) {
1560:                            invalidArea.intersect(new Area(roiShape));
1561:                        } else {
1562:                            LinkedList rectList = srcROI.getAsRectangleList(
1563:                                    invalidBounds.x, invalidBounds.y,
1564:                                    invalidBounds.width, invalidBounds.height);
1565:                            Iterator it = rectList.iterator();
1566:                            while (it.hasNext() && !invalidArea.isEmpty()) {
1567:                                invalidArea.intersect(new Area((Rectangle) it
1568:                                        .next()));
1569:                            }
1570:                        }
1571:                    }
1572:
1573:                    // If empty, all is valid.
1574:                    if (invalidArea.isEmpty()) {
1575:                        return;
1576:                    }
1577:
1578:                    // Determine all possible overlapping tiles.
1579:                    Point[] tileIndices = getTileIndices(invalidArea
1580:                            .getBounds());
1581:                    int numIndices = tileIndices.length;
1582:
1583:                    // Clear any tiles which intersect the invalid area.
1584:                    for (int i = 0; i < numIndices; i++) {
1585:                        int tx = tileIndices[i].x;
1586:                        int ty = tileIndices[i].y;
1587:                        Raster tile = tiles[tx][ty];
1588:                        if ((tile != null)
1589:                                && invalidArea.intersects(tile.getBounds())) {
1590:                            tiles[tx][ty] = null;
1591:                        }
1592:                    }
1593:
1594:                    if (eventManager.hasListeners("InvalidRegion")) {
1595:                        // Determine the old invalid region.
1596:                        Shape oldInvalidRegion = new Rectangle(); // default is empty.
1597:
1598:                        // If there is a ROI, the old invalid region is the
1599:                        // complement of the ROI within the image bounds.
1600:                        if (srcROI != null) {
1601:                            Area oldInvalidArea = new Area(getBounds());
1602:                            Shape roiShape = srcROI.getAsShape();
1603:                            if (roiShape != null) {
1604:                                oldInvalidArea.subtract(new Area(roiShape));
1605:                            } else {
1606:                                Rectangle oldInvalidBounds = oldInvalidArea
1607:                                        .getBounds();
1608:                                LinkedList rectList = srcROI
1609:                                        .getAsRectangleList(oldInvalidBounds.x,
1610:                                                oldInvalidBounds.y,
1611:                                                oldInvalidBounds.width,
1612:                                                oldInvalidBounds.height);
1613:                                Iterator it = rectList.iterator();
1614:                                while (it.hasNext()
1615:                                        && !oldInvalidArea.isEmpty()) {
1616:                                    oldInvalidArea.subtract(new Area(
1617:                                            (Rectangle) it.next()));
1618:                                }
1619:                            }
1620:                            oldInvalidRegion = oldInvalidArea;
1621:                        }
1622:
1623:                        // Fire an InvalidRegion event.
1624:                        PropertyChangeEventJAI irEvt = new PropertyChangeEventJAI(
1625:                                this , "InvalidRegion", oldInvalidRegion,
1626:                                invalidRegion);
1627:
1628:                        // Fire an event to all registered PropertyChangeListeners.
1629:                        eventManager.firePropertyChange(irEvt);
1630:
1631:                        // Fire an event to all PropertyChangeListener sinks.
1632:                        Vector sinks = getSinks();
1633:                        if (sinks != null) {
1634:                            int numSinks = sinks.size();
1635:                            for (int i = 0; i < numSinks; i++) {
1636:                                Object sink = sinks.get(i);
1637:                                if (sink instanceof  PropertyChangeListener) {
1638:                                    ((PropertyChangeListener) sink)
1639:                                            .propertyChange(irEvt);
1640:                                }
1641:                            }
1642:                        }
1643:                    }
1644:                }
1645:            }
1646:        }
www.java2java.com | Contact Us
Copyright 2009 - 12 Demo Source and Support. All rights reserved.
All other trademarks are property of their respective owners.