Source Code Cross Referenced for PlanarImage.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: PlanarImage.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.3 $
0009:         * $Date: 2006/06/16 19:55:56 $
0010:         * $State: Exp $
0011:         */
0012:        package javax.media.jai;
0013:
0014:        import java.awt.Graphics;
0015:        import java.awt.Image;
0016:        import java.awt.Point;
0017:        import java.awt.Rectangle;
0018:        import java.awt.Transparency;
0019:        import java.awt.color.ColorSpace;
0020:        import java.awt.image.BufferedImage;
0021:        import java.awt.image.ComponentSampleModel;
0022:        import java.awt.image.ColorModel;
0023:        import java.awt.image.DataBuffer;
0024:        import java.awt.image.DataBufferByte;
0025:        import java.awt.image.DataBufferShort;
0026:        import java.awt.image.DataBufferUShort;
0027:        import java.awt.image.DataBufferInt;
0028:        import java.awt.image.DirectColorModel;
0029:        import java.awt.image.IndexColorModel;
0030:        import java.awt.image.MultiPixelPackedSampleModel;
0031:        import java.awt.image.Raster;
0032:        import java.awt.image.RenderedImage;
0033:        import java.awt.image.SampleModel;
0034:        import java.awt.image.SinglePixelPackedSampleModel;
0035:        import java.awt.image.WritableRaster;
0036:        import java.awt.image.WritableRenderedImage;
0037:        import java.beans.PropertyChangeListener;
0038:        import java.lang.ref.WeakReference;
0039:        import java.util.Collections;
0040:        import java.util.Enumeration;
0041:        import java.util.Hashtable;
0042:        import java.util.HashSet;
0043:        import java.util.Iterator;
0044:        import java.util.List;
0045:        import java.util.Map;
0046:        import java.util.Set;
0047:        import java.util.Vector;
0048:        import javax.media.jai.RasterFactory;
0049:        import com.sun.media.jai.util.DataBufferUtils;
0050:        import com.sun.media.jai.util.ImageUtil;
0051:        import com.sun.media.jai.util.JDKWorkarounds;
0052:        import com.sun.media.jai.util.PropertyUtil;
0053:        import javax.media.jai.util.CaselessStringKey;
0054:
0055:        /**
0056:         * A <code>RenderedImage</code> is expressed as a collection of pixels.
0057:         * A pixel is defined as a 1-by-1 square; its origin is the top-left
0058:         * corner of the square (0, 0), and its energy center is located at the
0059:         * center of the square (0.5, 0.5).
0060:         *
0061:         * <p> This is the fundamental base class of Java Advanced Imaging (JAI)
0062:         * that represents a two-dimensional <code>RenderedImage</code>.
0063:         *
0064:         * <p> This class provides a home for the information and functionalities
0065:         * common to all the JAI classes that implement the
0066:         * <code>RenderedImage</code> interface, such as the image's layout,
0067:         * sources, properties, etc.  The image layout, sources, and properties
0068:         * may be set either at construction or subsequently using one of the
0069:         * mutator methods supplied for the respective attribute.  In general
0070:         * this class does not perform sanity checking on the state of its
0071:         * variables so it is very important that subclasses set them correctly.
0072:         * This is of particular importance with respect to the image layout.
0073:         *
0074:         * <p> The layout of a <code>PlanarImage</code> is specified by variables
0075:         * <code>minX</code>, <code>minY</code>, <code>width</code>,
0076:         * <code>height</code>, <code>tileGridXOffset</code>,
0077:         * <code>tileGridYOffset</code>, <code>tileWidth</code>,
0078:         * <code>tileHeight</code>, <code>sampleModel</code>, and
0079:         * <code>colorModel</code>.  These variables do not have any default settings
0080:         * so subclasses must set the appropriate ones at construction via the
0081:         * <code>ImageLayout</code> argument or subsequently using
0082:         * <code>setImageLayout()</code>.  Otherwise, unexpected errors may occur.
0083:         * Although these variables have <code>protected</code> access, it is
0084:         * strongly recommended that subclasses not set the values of these variables
0085:         * directly but rather via <code>setImageLayout()</code> which performs a
0086:         * certain few initializations based on the layout values.  The variables
0087:         * are defined to have <code>protected</code> access for convenience.
0088:         *
0089:         * <p> A <code>PlanarImage</code> may have any number of
0090:         * <code>RenderedImage</code> sources or no source at all.
0091:         *
0092:         * <p> All non-JAI <code>RenderedImage</code> instances must be
0093:         * converted into <code>PlanarImage</code>s by means of the
0094:         * <code>RenderedImageAdapter</code> and
0095:         * <code>WritableRenderedImageAdapter</code> classes.  The
0096:         * <code>wrapRenderedImage</code> method provides a convenient interface
0097:         * to both add a wrapper and take a snapshot if the image is writable.
0098:         * All of the <code>PlanarImage</code> constructors perform this wrapping
0099:         * automatically.  Images that already extend <code>PlanarImage</code>
0100:         * will be returned unchanged by <code>wrapRenderedImage</code>; that
0101:         * is, it is idempotent.
0102:         *
0103:         * <p> Going in the other direction, existing code that makes use of
0104:         * the <code>RenderedImage</code> interface will be able to use
0105:         * <code>PlanarImage</code>s directly, without any changes or
0106:         * recompilation.  Therefore, within JAI, two-dimensional images are
0107:         * returned from methods as <code>PlanarImage</code>s, even though
0108:         * incoming <code>RenderedImages</code> are accepted as arguments directly.
0109:         *
0110:         * <p> A <code>PlanarImage</code> may also have any number of properties
0111:         * of any type.  If or how a property is used depends on the individual
0112:         * subclass.  This class only stores the property information.  If any
0113:         * <code>PropertyChangeListener</code>s are registered they will receive
0114:         * a <code>PropertySourceChangeEvent</code> for each change in an image
0115:         * property.
0116:         *
0117:         * <p> In general, methods in this class are implemented such that they
0118:         * use any class variables directly instead of through their accessors for
0119:         * performance reasons.  Subclasses need to be careful when overriding this
0120:         * class' variable accessors that other appropriate methods are overriden
0121:         * as well.
0122:         *
0123:         * <p> <code>PlanarImage</code> implements a <code>createSnapshot</code>
0124:         * method that produces a new, immutable image with a copy of this
0125:         * image's current contents.  In practice, this snapshot is only a
0126:         * virtual copy; it is managed by the <code>SnapshotImage</code> class
0127:         * in such a way as to minimize copying and memory footprint generally.
0128:         * Multiple calls to <code>createSnapshot</code> make use of a single
0129:         * <code>SnapshotImage</code> per <code>PlanarImage</code> in order to
0130:         * centralize version management.  These mechanisms are transparent to
0131:         * the API user and are discussed here only for edification.
0132:         *
0133:         * <p> The source and sink lists have the effect of creating a graph
0134:         * structure between a set of <code>PlanarImage</code>s.  Note that
0135:         * the practice of making such bidirectional connections between
0136:         * images means that the garbage collector will not inform us when all
0137:         * user references to a node are lost, since there will still be
0138:         * internal references up until the point where the entire graph is
0139:         * detached from user space.  A solution is available in the form of
0140:         * <em>Reference Objects</em>; see <a
0141:         * href="http://java.sun.com/j2se/1.5.0/docs/guide/refobs/">
0142:         * http://java.sun.com/j2se/1.5.0/docs/guide/refobs/</a> for
0143:         * more information.  These classes include <em>weak references</em>
0144:         * that allow the Garbage Collector (GC) to collect objects they
0145:         * reference, setting the reference to <code>null</code> in the process.
0146:         *
0147:         * <p> The reference problem requires us to be careful about how we
0148:         * define the <i>reachability</i> of directed acyclic graph (DAG) nodes.
0149:         * If we were to allow nodes to be reached by arbitrary graph traversal,
0150:         * we would be unable to garbage collect any subgraphs of an active
0151:         * graph at all since any node may be reached from any other.  Instead,
0152:         * we define the set of reachable nodes as those that may be accessed
0153:         * directly from a reference in user code, or that are the source (not
0154:         * sink) of a reachable node.  Reachable nodes are always accessible,
0155:         * whether they are reached by traversing upwards or downwards in the DAG.
0156:         *
0157:         * <p> A DAG may also contain nodes that are not reachable, that is,
0158:         * they require a downward traversal at some point.  For example,
0159:         * assume a node <code>A</code> is reachable, and a call to
0160:         * <code>A.getSinks()</code> yields a <code>Vector</code> containing a
0161:         * reference to a previously unreachable node <code>B</code>.  The
0162:         * node <code>B</code> naturally becomes reachable by virtue of the
0163:         * new user reference pointing to it.  However, if the user were to
0164:         * relinquish that reference, the node might be garbage collected, and
0165:         * a future call to <code>A.getSinks()</code> might no longer include
0166:         * <code>B</code> in its return value.
0167:         *
0168:         * <p> Because the set of sinks of a node is inherently unstable, only
0169:         * the <code>getSinks</code> method is provided for external access to
0170:         * the sink vector at a node.  A hypothetical method such as
0171:         * <code>getSink</code> or <code>getNumSinks</code> would produce
0172:         * confusing results should a sink be garbage collected between that
0173:         * call and a subsequent call to <code>getSinks</code>.
0174:         *
0175:         * @see java.awt.image.RenderedImage
0176:         * @see java.lang.ref.Reference
0177:         * @see java.lang.ref.WeakReference
0178:         * @see ImageJAI
0179:         * @see OpImage
0180:         * @see RenderedImageAdapter
0181:         * @see SnapshotImage
0182:         * @see TiledImage
0183:         */
0184:        public abstract class PlanarImage implements  ImageJAI, RenderedImage {
0185:
0186:            /** The UID for this image. */
0187:            private Object UID;
0188:
0189:            /**
0190:             * The X coordinate of the image's top-left pixel.
0191:             */
0192:            protected int minX;
0193:
0194:            /**
0195:             * The Y coordinate of the image's top-left pixel.
0196:             */
0197:            protected int minY;
0198:
0199:            /**
0200:             * The image's width in number of pixels.
0201:             */
0202:            protected int width;
0203:
0204:            /**
0205:             * The image's height in number of pixels.
0206:             */
0207:            protected int height;
0208:
0209:            /** The image's bounds. */
0210:            // Initialize to an empty Rectangle so this object may always
0211:            // be used as a mutual exclusion lock in getBounds().
0212:            private Rectangle bounds = new Rectangle();
0213:
0214:            /**
0215:             * The X coordinate of the top-left pixel of tile (0, 0).
0216:             */
0217:            protected int tileGridXOffset;
0218:
0219:            /**
0220:             * The Y coordinate of the top-left pixel of tile (0, 0).
0221:             */
0222:            protected int tileGridYOffset;
0223:
0224:            /**
0225:             * The width of a tile in number of pixels.
0226:             */
0227:            protected int tileWidth;
0228:
0229:            /**
0230:             * The height of a tile in number of pixels.
0231:             */
0232:            protected int tileHeight;
0233:
0234:            /**
0235:             * The image's <code>SampleModel</code>.
0236:             */
0237:            protected SampleModel sampleModel = null;
0238:
0239:            /**
0240:             * The image's <code>ColorModel</code>.
0241:             */
0242:            protected ColorModel colorModel = null;
0243:
0244:            /**
0245:             * A <code>TileFactory</code> for use in
0246:             * {@link #createWritableRaster(SampleModel,Point)}.
0247:             * This field will be <code>null</code> unless initialized via the
0248:             * configuration properties passed to
0249:             * {@link #PlanarImage(ImageLayout,Vector,Map)}.
0250:             *
0251:             * @since JAI 1.1.2
0252:             */
0253:            protected TileFactory tileFactory = null;
0254:
0255:            /** The <code>PlanarImage</code> sources of the image. */
0256:            private Vector sources = null;
0257:
0258:            /** A set of <code>WeakReference</code>s to the sinks of the image. */
0259:            private Vector sinks = null;
0260:
0261:            /**
0262:             * A helper object to manage firing events.
0263:             *
0264:             * @since JAI 1.1
0265:             */
0266:            protected PropertyChangeSupportJAI eventManager = null;
0267:
0268:            /**
0269:             * A helper object to manage the image properties.
0270:             *
0271:             * @since JAI 1.1
0272:             */
0273:            protected WritablePropertySourceImpl properties = null;
0274:
0275:            /**
0276:             * A <code>SnapshotImage</code> that will centralize tile
0277:             * versioning for this image.
0278:             */
0279:            private SnapshotImage snapshot = null;
0280:
0281:            /** A <code>WeakReference</code> to this image. */
0282:            private WeakReference weakThis;
0283:
0284:            /** Cache of registered <code>TileComputationListener</code>s. */
0285:            private Set tileListeners = null;
0286:
0287:            private boolean disposed = false;
0288:
0289:            /** Array copy size, used by "cobble" methods. */
0290:            private static final int MIN_ARRAYCOPY_SIZE = 64;
0291:
0292:            /**
0293:             * The default constructor.
0294:             *
0295:             * <p> The <code>eventManager</code> and <code>properties</code>
0296:             * helper fields are initialized by this constructor; no other
0297:             * non-private fields are set.
0298:             */
0299:            public PlanarImage() {
0300:                this .weakThis = new WeakReference(this );
0301:
0302:                // Create an event manager.
0303:                eventManager = new PropertyChangeSupportJAI(this );
0304:
0305:                // Copy the properties by reference.
0306:                this .properties = new WritablePropertySourceImpl(null, null,
0307:                        eventManager);
0308:                this .UID = ImageUtil.generateID(this );
0309:            }
0310:
0311:            /**
0312:             * Constructor.
0313:             *
0314:             * <p> The image's layout is encapsulated in the <code>layout</code>
0315:             * argument.  Note that no verification is performed to determine whether
0316:             * the image layout has been set either at construction or subsequently.
0317:             *
0318:             * <p> This constructor does not provide any default settings for
0319:             * the layout variables so all of those that will be used later must
0320:             * be set in the <code>layout</code> argument or subsequently via
0321:             * <code>setImageLayout()</code> before the values are used.
0322:             * Otherwise, unexpected errors may occur.
0323:
0324:             * <p> If the <code>SampleModel</code> is non-<code>null</code> and the
0325:             * supplied tile dimensions are positive, then if the dimensions of the
0326:             * supplied <code>SampleModel</code> differ from the tile dimensions, a
0327:             * new <code>SampleModel</code> will be created for the image from the
0328:             * supplied <code>SampleModel</code> but with dimensions equal to those
0329:             * of a tile.
0330:             *
0331:             * <p> If both the <code>SampleModel</code> and the <code>ColorModel</code>
0332:             * in the supplied <code>ImageLayout</code> are non-<code>null</code>
0333:             * they will be tested for compatibility.  If the test fails an
0334:             * exception will be thrown.  The test is that
0335:             *
0336:             * <ul>
0337:             * <li> <code>ColorModel.isCompatibleSampleModel()</code> invoked on
0338:             * the <code>SampleModel</code> must return <code>true</code>, and
0339:             * <li> if the <code>ColorModel</code> is a
0340:             * <code>ComponentColorModel</code> then:
0341:             * <ul>
0342:             * <li>the number of bands of the <code>SampleModel</code> must equal
0343:             * the number of components of the <code>ColorModel</code>, and
0344:             * <li><code>SampleModel.getSampleSize(b) >= ColorModel.getComponentSize(b)</code>
0345:             * for all bands <code>b</code>.
0346:             * </ul>
0347:             * </ul>
0348:             *
0349:             * <p> The <code>sources</code> parameter contains a list of immediate
0350:             * sources of this image none of which may be <code>null</code>.  All
0351:             * <code>RenderedImage</code>s in the list are automatically converted
0352:             * into <code>PlanarImage</code>s when necessary.  If this image has
0353:             * no source, this argument should be <code>null</code>.
0354:             *
0355:             * <p> The <code>properties</code> parameter contains a mapping of image
0356:             * properties.  All map entries which have a key which is either a
0357:             * <code>String</code> or a <code>CaselessStringKey</code> are interpreted
0358:             * as image properties and will be copied to the property database of
0359:             * this image.  This parameter may be <code>null</code>.
0360:             *
0361:             * <p>If a {@link TileFactory}-valued mapping of the key
0362:             * {@link JAI#KEY_TILE_FACTORY} is present in
0363:             * <code>properties</code>, then set the instance variable
0364:             * <code>tileFactory</code> to the specified <code>TileFactory</code>.
0365:             * This <code>TileFactory</code> will be used by
0366:             * {@link #createWritableRaster(SampleModel,Point)} to create
0367:             * <code>Raster</code>s, notably in {@link #getData(Rectangle)},
0368:             * {@link #copyData(WritableRaster)}, and
0369:             * {@link #getExtendedData(Rectangle,BorderExtender)}.</p>
0370:             *
0371:             * <p> The event and property helper fields are initialized by this
0372:             * constructor.
0373:             *
0374:             * @param layout  The layout of this image or <code>null</code>.
0375:             * @param sources  The immediate sources of this image or
0376:             *                 <code>null</code>.
0377:             * @param properties  A <code>Map</code> containing the properties of
0378:             *                    this image or <code>null</code>.
0379:             *
0380:             * @throws IllegalArgumentException if a
0381:             *         <code>ColorModel</code> is specified in the layout and it is
0382:             *         incompatible with the <code>SampleModel</code>
0383:             * @throws IllegalArgumentException  If <code>sources</code>
0384:             *         is non-<code>null</code> and any object in
0385:             *         <code>sources</code> is <code>null</code>.
0386:             *
0387:             * @since JAI 1.1
0388:             */
0389:            public PlanarImage(ImageLayout layout, Vector sources,
0390:                    Map properties) {
0391:                this ();
0392:
0393:                // Set the image layout.
0394:                if (layout != null) {
0395:                    setImageLayout(layout);
0396:                }
0397:
0398:                // Set the image sources. All source Vector elements must be non-null.
0399:                // If any source is a RenderedImage it is converted to a PlanarImage
0400:                // before being set.
0401:                if (sources != null) {
0402:                    setSources(sources);
0403:                }
0404:
0405:                if (properties != null) {
0406:                    // Add properties from parameter.
0407:                    this .properties.addProperties(properties);
0408:
0409:                    // Set tileFactory if key present.
0410:                    if (properties.containsKey(JAI.KEY_TILE_FACTORY)) {
0411:                        Object factoryValue = properties
0412:                                .get(JAI.KEY_TILE_FACTORY);
0413:
0414:                        // Check the class type in case 'properties' is not
0415:                        // an instance of RenderingHints.
0416:                        if (factoryValue instanceof  TileFactory) {
0417:                            this .tileFactory = (TileFactory) factoryValue;
0418:                        }
0419:                    }
0420:                }
0421:            }
0422:
0423:            /**
0424:             * Sets the image bounds, tile grid layout,
0425:             * <code>SampleModel</code> and <code>ColorModel</code> using
0426:             * values from an <code>ImageLayout</code> object.
0427:             *
0428:             * <p> If either of the tile dimensions is not set in the passed in
0429:             * <code>ImageLayout</code> object, then the tile dimension in question
0430:             * will be set to the corresponding image dimension.
0431:             *
0432:             * <p> If either of the tile grid offsets is not set in the passed in
0433:             * <code>ImageLayout</code> object, then the tile grid offset in
0434:             * question will be set to 0. The same is true for the <code>minX</code>
0435:             * , <code>minY</code>, <code>width</code> and <code>height</code>
0436:             * fields, if no value is set in the passed in <code>ImageLayout</code>
0437:             * object, they will be set to 0.
0438:             *
0439:             * <p> If the <code>SampleModel</code> is non-<code>null</code> and the
0440:             * supplied tile dimensions are positive, then if the dimensions of the
0441:             * supplied <code>SampleModel</code> differ from the tile dimensions, a
0442:             * new <code>SampleModel</code> will be created for the image from the
0443:             * supplied <code>SampleModel</code> but with dimensions equal to those
0444:             * of a tile.
0445:             *
0446:             * <p> If both the <code>SampleModel</code> and the <code>ColorModel</code>
0447:             * in the supplied <code>ImageLayout</code> are non-<code>null</code>
0448:             * they will be tested for compatibility.  If the test fails an
0449:             * exception will be thrown.  The test is that
0450:             *
0451:             * <ul>
0452:             * <li> <code>ColorModel.isCompatibleSampleModel()</code> invoked on
0453:             * the <code>SampleModel</code> must return <code>true</code>, and
0454:             * <li> if the <code>ColorModel</code> is a
0455:             * <code>ComponentColorModel</code> then:
0456:             * <ul>
0457:             * <li>the number of bands of the <code>SampleModel</code> must equal
0458:             * the number of components of the <code>ColorModel</code>, and
0459:             * <li><code>SampleModel.getSampleSize(b) >= ColorModel.getComponentSize(b)</code>
0460:             * for all bands <code>b</code>.
0461:             * </ul>
0462:             * </ul>
0463:             *
0464:             * @param layout an ImageLayout that is used to selectively
0465:             *        override the image's layout, <code>SampleModel</code>,
0466:             *        and <code>ColorModel</code>.  Only valid fields, i.e.,
0467:             *        those for which <code>ImageLayout.isValid()</code> returns
0468:             *        <code>true</code> for the appropriate mask, are used.
0469:             *
0470:             * @throws <code>IllegalArgumentException</code> if <code>layout</code>
0471:             *         is <code>null</code>.
0472:             * @throws <code>IllegalArgumentException</code> if a
0473:             *         <code>ColorModel</code> is specified in the layout and it is
0474:             *         incompatible with the <code>SampleModel</code>
0475:             *
0476:             * @since JAI 1.1
0477:             */
0478:            protected void setImageLayout(ImageLayout layout) {
0479:                if (layout == null) {
0480:                    throw new IllegalArgumentException(JaiI18N
0481:                            .getString("Generic0"));
0482:                } else {
0483:                    // Set image bounds.
0484:                    if (layout.isValid(ImageLayout.MIN_X_MASK)) {
0485:                        minX = layout.getMinX(null);
0486:                    }
0487:                    if (layout.isValid(ImageLayout.MIN_Y_MASK)) {
0488:                        minY = layout.getMinY(null);
0489:                    }
0490:                    if (layout.isValid(ImageLayout.WIDTH_MASK)) {
0491:                        width = layout.getWidth(null);
0492:                    }
0493:                    if (layout.isValid(ImageLayout.HEIGHT_MASK)) {
0494:                        height = layout.getHeight(null);
0495:                    }
0496:
0497:                    // Set tile grid parameters.
0498:                    if (layout.isValid(ImageLayout.TILE_GRID_X_OFFSET_MASK)) {
0499:                        tileGridXOffset = layout.getTileGridXOffset(null);
0500:                    }
0501:                    if (layout.isValid(ImageLayout.TILE_GRID_Y_OFFSET_MASK)) {
0502:                        tileGridYOffset = layout.getTileGridYOffset(null);
0503:                    }
0504:                    if (layout.isValid(ImageLayout.TILE_WIDTH_MASK)) {
0505:                        tileWidth = layout.getTileWidth(null);
0506:                    } else {
0507:                        tileWidth = width;
0508:                    }
0509:                    if (layout.isValid(ImageLayout.TILE_HEIGHT_MASK)) {
0510:                        tileHeight = layout.getTileHeight(null);
0511:                    } else {
0512:                        tileHeight = height;
0513:                    }
0514:
0515:                    // Set SampleModel.
0516:                    if (layout.isValid(ImageLayout.SAMPLE_MODEL_MASK)) {
0517:                        sampleModel = layout.getSampleModel(null);
0518:                    }
0519:
0520:                    // Make the SampleModel dimensions equal to those of a tile.
0521:                    if (sampleModel != null
0522:                            && tileWidth > 0
0523:                            && tileHeight > 0
0524:                            && (sampleModel.getWidth() != tileWidth || sampleModel
0525:                                    .getHeight() != tileHeight)) {
0526:                        sampleModel = sampleModel.createCompatibleSampleModel(
0527:                                tileWidth, tileHeight);
0528:                    }
0529:
0530:                    // Set ColorModel.
0531:                    if (layout.isValid(ImageLayout.COLOR_MODEL_MASK)) {
0532:                        colorModel = layout.getColorModel(null);
0533:                    }
0534:                    if (colorModel != null && sampleModel != null) {
0535:                        if (!JDKWorkarounds.areCompatibleDataModels(
0536:                                sampleModel, colorModel)) {
0537:                            throw new IllegalArgumentException(JaiI18N
0538:                                    .getString("PlanarImage5"));
0539:                            /* XXX Begin debugging statements: to be deleted
0540:                            System.err.println("\n----- ERROR: "+
0541:                                               JaiI18N.getString("PlanarImage5"));
0542:                            System.err.println(getClass().getName());
0543:                            System.err.println(sampleModel.getClass().getName()+": "+
0544:                                               sampleModel);
0545:                            System.err.println("Transfer type = "+
0546:                                               sampleModel.getTransferType());
0547:                            System.err.println(colorModel.getClass().getName()+": "+
0548:                                               colorModel);
0549:                            System.err.println("");
0550:                            XXX End debugging statements */
0551:                        }
0552:                    }
0553:                }
0554:            }
0555:
0556:            /**
0557:             * Wraps an arbitrary <code>RenderedImage</code> to produce a
0558:             * <code>PlanarImage</code>.  <code>PlanarImage</code> adds
0559:             * various properties to an image, such as source and sink vectors
0560:             * and the ability to produce snapshots, that are necessary for
0561:             * JAI.
0562:             *
0563:             * <p> If the image is already a <code>PlanarImage</code>, it is
0564:             * simply returned unchanged.  Otherwise, the image is wrapped in
0565:             * a <code>RenderedImageAdapter</code> or
0566:             * <code>WritableRenderedImageAdapter</code> as appropriate.
0567:             *
0568:             * @param image  The <code>RenderedImage</code> to be converted into
0569:             *        a <code>PlanarImage</code>.
0570:             *
0571:             * @return A <code>PlanarImage</code> containing <code>image</code>'s
0572:             *         pixel data.
0573:             *
0574:             * @throws IllegalArgumentException  If <code>image</code> is
0575:             *         <code>null</code>.
0576:             */
0577:            public static PlanarImage wrapRenderedImage(RenderedImage image) {
0578:                if (image == null) {
0579:                    throw new IllegalArgumentException(JaiI18N
0580:                            .getString("Generic0"));
0581:                }
0582:
0583:                if (image instanceof  PlanarImage) {
0584:                    return (PlanarImage) image;
0585:                } else if (image instanceof  WritableRenderedImage) {
0586:                    return new WritableRenderedImageAdapter(
0587:                            (WritableRenderedImage) image);
0588:                } else {
0589:                    return new RenderedImageAdapter(image);
0590:                }
0591:            }
0592:
0593:            /**
0594:             * Creates a snapshot, that is, a virtual copy of the image's
0595:             * current contents.  If the image is not a
0596:             * <code>WritableRenderedImage</code>, it is returned unchanged.
0597:             * Otherwise, a <code>SnapshotImage</code> is created and the
0598:             * result of calling its <code>createSnapshot()</code> is
0599:             * returned.
0600:             *
0601:             * @return A <code>PlanarImage</code> with immutable contents.
0602:             */
0603:            public PlanarImage createSnapshot() {
0604:                if (this  instanceof  WritableRenderedImage) {
0605:                    if (snapshot == null) {
0606:                        synchronized (this ) {
0607:                            snapshot = new SnapshotImage(this );
0608:                        }
0609:                    }
0610:                    return snapshot.createSnapshot();
0611:
0612:                } else {
0613:                    return this ;
0614:                }
0615:            }
0616:
0617:            /**
0618:             * Returns the X coordinate of the left-most column of the image.
0619:             * The default implementation returns the corresponding instance variable.
0620:             */
0621:            public int getMinX() {
0622:                return minX;
0623:            }
0624:
0625:            /**
0626:             * Returns the X coordinate of the column immediately to the right
0627:             * of the right-most column of the image.
0628:             *
0629:             * <p> This method is implemented in terms of <code>getMinX()</code>
0630:             * and <code>getWidth()</code> so that subclasses which override
0631:             * those methods do not need to override this one.
0632:             */
0633:            public int getMaxX() {
0634:                return getMinX() + getWidth();
0635:            }
0636:
0637:            /**
0638:             * Returns the Y coordinate of the top-most row of the image.
0639:             * The default implementation returns the corresponding instance variable.
0640:             */
0641:            public int getMinY() {
0642:                return minY;
0643:            }
0644:
0645:            /**
0646:             * Returns the Y coordinate of the row immediately below the
0647:             * bottom-most row of the image.
0648:             *
0649:             * <p> This method is implemented in terms of <code>getMinY()</code>
0650:             * and <code>getHeight()</code> so that subclasses which override
0651:             * those methods do not need to override this one.
0652:             */
0653:            public int getMaxY() {
0654:                return getMinY() + getHeight();
0655:            }
0656:
0657:            /**
0658:             * Returns the width of the image in number of pixels.
0659:             * The default implementation returns the corresponding instance variable.
0660:             */
0661:            public int getWidth() {
0662:                return width;
0663:            }
0664:
0665:            /**
0666:             * Returns the height of the image in number of pixels.
0667:             * The default implementation returns the corresponding instance variable.
0668:             */
0669:            public int getHeight() {
0670:                return height;
0671:            }
0672:
0673:            /**
0674:             * Retrieve the number of image bands.  Note that this will not equal
0675:             * the number of color components if the image has an
0676:             * <code>IndexColorModel</code>.  This is equivalent to calling
0677:             * <code>getSampleModel().getNumBands()</code>.
0678:             *
0679:             * @since JAI 1.1
0680:             */
0681:            public int getNumBands() {
0682:                return getSampleModel().getNumBands();
0683:            }
0684:
0685:            /**
0686:             * Returns the image's bounds as a <code>Rectangle</code>.
0687:             *
0688:             * <p> The image's bounds are defined by the values returned by
0689:             * <code>getMinX()</code>, <code>getMinY()</code>,
0690:             * <code>getWidth()</code>, and <code>getHeight()</code>.
0691:             * A <code>Rectangle</code> is created based on these four methods and
0692:             * cached in this class.  Each time that this method is invoked, the
0693:             * bounds of this <code>Rectangle</code> are updated with the values
0694:             * returned by the four aforementioned accessors.
0695:             *
0696:             * <p> Because this method returns the <code>bounds</code> variable
0697:             * by reference, the caller should not change the settings of the
0698:             * <code>Rectangle</code>.  Otherwise, unexpected errors may occur.
0699:             * Likewise, if the caller expects this variable to be immutable it
0700:             * should clone the returned <code>Rectangle</code> if there is any
0701:             * possibility that it might be changed by the <code>PlanarImage</code>.
0702:             * This may generally occur only for instances of <code>RenderedOp</code>.
0703:             */
0704:            public Rectangle getBounds() {
0705:                synchronized (bounds) {
0706:                    bounds.setBounds(getMinX(), getMinY(), getWidth(),
0707:                            getHeight());
0708:                }
0709:
0710:                return bounds;
0711:            }
0712:
0713:            /**
0714:             * Returns the X coordinate of the top-left pixel of tile (0, 0).
0715:             * The default implementation returns the corresponding instance variable.
0716:             */
0717:            public int getTileGridXOffset() {
0718:                return tileGridXOffset;
0719:            }
0720:
0721:            /**
0722:             * Returns the Y coordinate of the top-left pixel of tile (0, 0).
0723:             * The default implementation returns the corresponding instance variable.
0724:             */
0725:            public int getTileGridYOffset() {
0726:                return tileGridYOffset;
0727:            }
0728:
0729:            /**
0730:             * Returns the width of a tile of this image in number of pixels.
0731:             * The default implementation returns the corresponding instance variable.
0732:             */
0733:            public int getTileWidth() {
0734:                return tileWidth;
0735:            }
0736:
0737:            /**
0738:             * Returns the height of a tile of this image in number of pixels.
0739:             * The default implementation returns the corresponding instance variable.
0740:             */
0741:            public int getTileHeight() {
0742:                return tileHeight;
0743:            }
0744:
0745:            /**
0746:             * Returns the horizontal index of the left-most column of tiles.
0747:             *
0748:             * <p> This method is implemented in terms of the static method
0749:             * <code>XToTileX()</code> applied to the values returned by primitive
0750:             * layout accessors and so does not need to be implemented by subclasses.
0751:             */
0752:            public int getMinTileX() {
0753:                return XToTileX(getMinX(), getTileGridXOffset(), getTileWidth());
0754:            }
0755:
0756:            /**
0757:             * Returns the horizontal index of the right-most column of tiles.
0758:             *
0759:             * <p> This method is implemented in terms of the static method
0760:             * <code>XToTileX()</code> applied to the values returned by primitive
0761:             * layout accessors and so does not need to be implemented by subclasses.
0762:             */
0763:            public int getMaxTileX() {
0764:                return XToTileX(getMinX() + getWidth() - 1,
0765:                        getTileGridXOffset(), getTileWidth());
0766:            }
0767:
0768:            /**
0769:             * Returns the number of tiles along the tile grid in the
0770:             * horizontal direction.
0771:             *
0772:             * <p> This method is implemented in terms of the static method
0773:             * <code>XToTileX()</code> applied to the values returned by primitive
0774:             * layout accessors and so does not need to be implemented by subclasses.
0775:             */
0776:            public int getNumXTiles() {
0777:                int x = getMinX();
0778:                int tx = getTileGridXOffset();
0779:                int tw = getTileWidth();
0780:                return XToTileX(x + getWidth() - 1, tx, tw)
0781:                        - XToTileX(x, tx, tw) + 1;
0782:            }
0783:
0784:            /**
0785:             * Returns the vertical index of the top-most row of tiles.
0786:             *
0787:             * <p> This method is implemented in terms of the static method
0788:             * <code>YToTileY()</code> applied to the values returned by primitive
0789:             * layout accessors and so does not need to be implemented by subclasses.
0790:             */
0791:            public int getMinTileY() {
0792:                return YToTileY(getMinY(), getTileGridYOffset(),
0793:                        getTileHeight());
0794:            }
0795:
0796:            /**
0797:             * Returns the vertical index of the bottom-most row of tiles.
0798:             *
0799:             * <p> This method is implemented in terms of the static method
0800:             * <code>YToTileY()</code> applied to the values returned by primitive
0801:             * layout accessors and so does not need to be implemented by subclasses.
0802:             */
0803:            public int getMaxTileY() {
0804:                return YToTileY(getMinY() + getHeight() - 1,
0805:                        getTileGridYOffset(), getTileHeight());
0806:            }
0807:
0808:            /**
0809:             * Returns the number of tiles along the tile grid in the vertical
0810:             * direction.
0811:             *
0812:             * <p> This method is implemented in terms of the static method
0813:             * <code>YToTileY()</code> applied to the values returned by primitive
0814:             * layout accessors and so does not need to be implemented by subclasses.
0815:             */
0816:            public int getNumYTiles() {
0817:                int y = getMinY();
0818:                int ty = getTileGridYOffset();
0819:                int th = getTileHeight();
0820:                return YToTileY(y + getHeight() - 1, ty, th)
0821:                        - YToTileY(y, ty, th) + 1;
0822:            }
0823:
0824:            /**
0825:             * Converts a pixel's X coordinate into a horizontal tile index
0826:             * relative to a given tile grid layout specified by its X offset
0827:             * and tile width.
0828:             *
0829:             * <p> If <code>tileWidth < 0</code>, the results of this method
0830:             * are undefined.  If <code>tileWidth == 0</code>, an
0831:             * <code>ArithmeticException</code> will be thrown.
0832:             *
0833:             * @throws ArithmeticException  If <code>tileWidth == 0</code>.
0834:             */
0835:            public static int XToTileX(int x, int tileGridXOffset, int tileWidth) {
0836:                x -= tileGridXOffset;
0837:                if (x < 0) {
0838:                    x += 1 - tileWidth; // force round to -infinity (ceiling)
0839:                }
0840:                return x / tileWidth;
0841:            }
0842:
0843:            /**
0844:             * Converts a pixel's Y coordinate into a vertical tile index
0845:             * relative to a given tile grid layout specified by its Y offset
0846:             * and tile height.
0847:             *
0848:             * <p> If <code>tileHeight < 0</code>, the results of this method
0849:             * are undefined.  If <code>tileHeight == 0</code>, an
0850:             * <code>ArithmeticException</code> will be thrown.
0851:             *
0852:             * @throws ArithmeticException  If <code>tileHeight == 0</code>.
0853:             */
0854:            public static int YToTileY(int y, int tileGridYOffset,
0855:                    int tileHeight) {
0856:                y -= tileGridYOffset;
0857:                if (y < 0) {
0858:                    y += 1 - tileHeight; // force round to -infinity (ceiling)
0859:                }
0860:                return y / tileHeight;
0861:            }
0862:
0863:            /**
0864:             * Converts a pixel's X coordinate into a horizontal tile index.
0865:             * No attempt is made to detect out-of-range coordinates.
0866:             *
0867:             * <p> This method is implemented in terms of the static method
0868:             * <code>XToTileX()</code> applied to the values returned by primitive
0869:             * layout accessors and so does not need to be implemented by subclasses.
0870:             *
0871:             * @param x the X coordinate of a pixel.
0872:             *
0873:             * @return the X index of the tile containing the pixel.
0874:             *
0875:             * @throws ArithmeticException  If the tile width of this image is 0.
0876:             */
0877:            public int XToTileX(int x) {
0878:                return XToTileX(x, getTileGridXOffset(), getTileWidth());
0879:            }
0880:
0881:            /**
0882:             * Converts a pixel's Y coordinate into a vertical tile index.  No
0883:             * attempt is made to detect out-of-range coordinates.
0884:             *
0885:             * <p> This method is implemented in terms of the static method
0886:             * <code>YToTileY()</code> applied to the values returned by primitive
0887:             * layout accessors and so does not need to be implemented by subclasses.
0888:             *
0889:             * @param y the Y coordinate of a pixel.
0890:             *
0891:             * @return the Y index of the tile containing the pixel.
0892:             *
0893:             * @throws ArithmeticException  If the tile height of this image is 0.
0894:             */
0895:            public int YToTileY(int y) {
0896:                return YToTileY(y, getTileGridYOffset(), getTileHeight());
0897:            }
0898:
0899:            /**
0900:             * Converts a horizontal tile index into the X coordinate of its
0901:             * upper left pixel relative to a given tile grid layout specified
0902:             * by its X offset and tile width.
0903:             */
0904:            public static int tileXToX(int tx, int tileGridXOffset,
0905:                    int tileWidth) {
0906:                return tx * tileWidth + tileGridXOffset;
0907:            }
0908:
0909:            /**
0910:             * Converts a vertical tile index into the Y coordinate of
0911:             * its upper left pixel relative to a given tile grid layout
0912:             * specified by its Y offset and tile height.
0913:             */
0914:            public static int tileYToY(int ty, int tileGridYOffset,
0915:                    int tileHeight) {
0916:                return ty * tileHeight + tileGridYOffset;
0917:            }
0918:
0919:            /**
0920:             * Converts a horizontal tile index into the X coordinate of its
0921:             * upper left pixel.  No attempt is made to detect out-of-range
0922:             * indices.
0923:             *
0924:             * <p> This method is implemented in terms of the static method
0925:             * <code>tileXToX()</code> applied to the values returned by primitive
0926:             * layout accessors and so does not need to be implemented by subclasses.
0927:             *
0928:             * @param tx the horizontal index of a tile.
0929:             * @return the X coordinate of the tile's upper left pixel.
0930:             */
0931:            public int tileXToX(int tx) {
0932:                return tileXToX(tx, getTileGridXOffset(), getTileWidth());
0933:            }
0934:
0935:            /**
0936:             * Converts a vertical tile index into the Y coordinate of its
0937:             * upper left pixel.  No attempt is made to detect out-of-range
0938:             * indices.
0939:             *
0940:             * <p> This method is implemented in terms of the static method
0941:             * <code>tileYToY()</code> applied to the values returned by primitive
0942:             * layout accessors and so does not need to be implemented by subclasses.
0943:             *
0944:             * @param ty the vertical index of a tile.
0945:             * @return the Y coordinate of the tile's upper left pixel.
0946:             */
0947:            public int tileYToY(int ty) {
0948:                return tileYToY(ty, getTileGridYOffset(), getTileHeight());
0949:            }
0950:
0951:            /**
0952:             * Returns a <code>Rectangle</code> indicating the active area of
0953:             * a given tile.  The <code>Rectangle</code> is defined as the
0954:             * intersection of the tile area and the image bounds.  No attempt
0955:             * is made to detect out-of-range indices; tile indices lying
0956:             * completely outside of the image will result in returning an
0957:             * empty <code>Rectangle</code> (width and/or height less than or
0958:             * equal to 0).
0959:             *
0960:             * <p> This method is implemented in terms of the primitive layout
0961:             * accessors and so does not need to be implemented by subclasses.
0962:             *
0963:             * @param tileX  The X index of the tile.
0964:             * @param tileY  The Y index of the tile.
0965:             *
0966:             * @return A <code>Rectangle</code>
0967:             */
0968:            public Rectangle getTileRect(int tileX, int tileY) {
0969:                return getBounds().intersection(
0970:                        new Rectangle(tileXToX(tileX), tileYToY(tileY),
0971:                                getTileWidth(), getTileHeight()));
0972:            }
0973:
0974:            /**
0975:             * Returns the <code>SampleModel</code> of the image.
0976:             * The default implementation returns the corresponding instance variable.
0977:             */
0978:            public SampleModel getSampleModel() {
0979:                return sampleModel;
0980:            }
0981:
0982:            /**
0983:             * Returns the <code>ColorModel</code> of the image.
0984:             * The default implementation returns the corresponding instance variable.
0985:             */
0986:            public ColorModel getColorModel() {
0987:                return colorModel;
0988:            }
0989:
0990:            /**
0991:             * Returns a <code>ComponentColorModel</code> created based on
0992:             * the indicated <code>dataType</code> and <code>numBands</code>.
0993:             *
0994:             * <p> The <code>dataType</code> must be one of <code>DataBuffer</code>'s
0995:             * <code>TYPE_BYTE</code>, <code>TYPE_USHORT</code>,
0996:             * <code>TYPE_INT</code>, <code>TYPE_FLOAT</code>, or
0997:             * <code>TYPE_DOUBLE</code>.
0998:             *
0999:             * <p> The <code>numBands</code> may range from 1 to 4, with the
1000:             * following <code>ColorSpace</code> and alpha settings:
1001:             * <ul>
1002:             * <li> <code>numBands = 1</code>: <code>CS_GRAY</code> without alpha;
1003:             * <li> <code>numBands = 2</code>: <code>CS_GRAY</code> with alpha;
1004:             * <li> <code>numBands = 3</code>: <code>CS_sRGB</code> without alpha;
1005:             * <li> <code>numBands = 4</code>: <code>CS_sRGB</code> with alpha.
1006:             * </ul>
1007:             * The transparency is set to <code>Transparency.TRANSLUCENT</code> if
1008:             * alpha is used and to <code>Transparency.OPAQUE</code> otherwise.
1009:             *
1010:             * <p> All other inputs result in a <code>null</code> return value.
1011:             *
1012:             * @param dataType  The data type of the <code>ColorModel</code>.
1013:             * @param numBands  The number of bands of the pixels the created
1014:             *        <code>ColorModel</code> is going to work with.
1015:             *
1016:             * @since JAI 1.1
1017:             */
1018:            public static ColorModel getDefaultColorModel(int dataType,
1019:                    int numBands) {
1020:                if (dataType < DataBuffer.TYPE_BYTE
1021:                        || dataType == DataBuffer.TYPE_SHORT
1022:                        || dataType > DataBuffer.TYPE_DOUBLE || numBands < 1
1023:                        || numBands > 4) {
1024:                    return null;
1025:                }
1026:
1027:                ColorSpace cs = numBands <= 2 ? ColorSpace
1028:                        .getInstance(ColorSpace.CS_GRAY) : ColorSpace
1029:                        .getInstance(ColorSpace.CS_sRGB);
1030:
1031:                boolean useAlpha = (numBands == 2) || (numBands == 4);
1032:                int transparency = useAlpha ? Transparency.TRANSLUCENT
1033:                        : Transparency.OPAQUE;
1034:
1035:                return RasterFactory.createComponentColorModel(dataType, cs,
1036:                        useAlpha, false, transparency);
1037:
1038:            }
1039:
1040:            /**
1041:             * Creates a <code>ColorModel</code> that may be used with the
1042:             * specified <code>SampleModel</code>.  If a suitable
1043:             * <code>ColorModel</code> cannot be found, this method returns
1044:             * <code>null</code>.
1045:             *
1046:             * <p> Suitable <code>ColorModel</code>s are guaranteed to exist
1047:             * for all instances of <code>ComponentSampleModel</code> whose
1048:             * <code>dataType</code> is not <code>DataBuffer.TYPE_SHORT</code>
1049:             * and with no more than 4 bands.  A <code>ComponentColorModel</code>
1050:             * of either type CS_GRAY or CS_sRGB is returned.
1051:             *
1052:             * <p> For 1- and 3- banded <code>SampleModel</code>s, the returned
1053:             * <code>ColorModel</code> will be opaque.  For 2- and 4-banded
1054:             * <code>SampleModel</code>s, the output will use alpha transparency.
1055:             *
1056:             * <p> Additionally, an instance of <code>DirectColorModel</code>
1057:             * will be created for instances of
1058:             * <code>SinglePixelPackedSampleModel</code> with no more than 4 bands.
1059:             *
1060:             * <p> Finally, an instance of <code>IndexColorModel</code>
1061:             * will be created for instances of
1062:             * <code>MultiPixelPackedSampleModel</code> with a single band and a
1063:             * pixel bit stride of unity.  This represents the case of binary data.
1064:             *
1065:             * <p> This method is intended as an useful utility for the creation
1066:             * of simple <code>ColorModel</code>s for some common cases.
1067:             * In more complex situations, it may be necessary to instantiate
1068:             * the appropriate <code>ColorModel</code>s directly.
1069:             *
1070:             * @return An instance of <code>ColorModel</code> that is suitable for
1071:             *         the supplied <code>SampleModel</code>, or <code>null</code>.
1072:             *
1073:             * @throws IllegalArgumentException  If <code>sm</code> is
1074:             *         <code>null</code>.
1075:             */
1076:            public static ColorModel createColorModel(SampleModel sm) {
1077:                if (sm == null) {
1078:                    throw new IllegalArgumentException(JaiI18N
1079:                            .getString("Generic0"));
1080:                }
1081:
1082:                int bands = sm.getNumBands();
1083:                if (bands < 1 || bands > 4) {
1084:                    return null;
1085:                }
1086:
1087:                if (sm instanceof  ComponentSampleModel) {
1088:                    return getDefaultColorModel(sm.getDataType(), bands);
1089:
1090:                } else if (sm instanceof  SinglePixelPackedSampleModel) {
1091:                    SinglePixelPackedSampleModel sppsm = (SinglePixelPackedSampleModel) sm;
1092:
1093:                    int[] bitMasks = sppsm.getBitMasks();
1094:                    int rmask = 0;
1095:                    int gmask = 0;
1096:                    int bmask = 0;
1097:                    int amask = 0;
1098:
1099:                    int numBands = bitMasks.length;
1100:                    if (numBands <= 2) {
1101:                        rmask = gmask = bmask = bitMasks[0];
1102:                        if (numBands == 2) {
1103:                            amask = bitMasks[1];
1104:                        }
1105:                    } else {
1106:                        rmask = bitMasks[0];
1107:                        gmask = bitMasks[1];
1108:                        bmask = bitMasks[2];
1109:                        if (numBands == 4) {
1110:                            amask = bitMasks[3];
1111:                        }
1112:                    }
1113:
1114:                    int[] sampleSize = sppsm.getSampleSize();
1115:                    int bits = 0;
1116:                    for (int i = 0; i < sampleSize.length; i++) {
1117:                        bits += sampleSize[i];
1118:                    }
1119:
1120:                    return new DirectColorModel(bits, rmask, gmask, bmask,
1121:                            amask);
1122:
1123:                } else if (ImageUtil.isBinary(sm)) {
1124:                    byte[] comp = new byte[] { (byte) 0x00, (byte) 0xFF };
1125:
1126:                    return new IndexColorModel(1, 2, comp, comp, comp);
1127:
1128:                } else { // unable to create an suitable ColorModel
1129:                    return null;
1130:                }
1131:            }
1132:
1133:            /**
1134:             * Returns the value of the instance variable <code>tileFactory</code>.
1135:             *
1136:             * @since JAI 1.1.2
1137:             */
1138:            public TileFactory getTileFactory() {
1139:                return tileFactory;
1140:            }
1141:
1142:            /**
1143:             * Returns the number of immediate <code>PlanarImage</code> sources
1144:             * this image has.  If this image has no source, this method returns
1145:             * 0.
1146:             */
1147:            public int getNumSources() {
1148:                return sources == null ? 0 : sources.size();
1149:            }
1150:
1151:            /**
1152:             * Returns this image's immediate source(s) in a <code>Vector</code>.
1153:             * If this image has no source, this method returns <code>null</code>.
1154:             */
1155:            public Vector getSources() {
1156:                if (getNumSources() == 0) {
1157:                    return null;
1158:                } else {
1159:                    synchronized (sources) {
1160:                        return (Vector) sources.clone();
1161:                    }
1162:                }
1163:            }
1164:
1165:            /**
1166:             * Returns the immediate source indicated by the index.  If there
1167:             * is no source corresponding to the specified index, this method
1168:             * throws an exception.
1169:             *
1170:             * @param index  The index of the desired source.
1171:             *
1172:             * @return A <code>PlanarImage</code> source.
1173:             *
1174:             * @throws ArrayIndexOutOfBoundsException  If this image has no
1175:             *         immediate source, or if the index is negative or greater
1176:             *         than the maximum source index.
1177:             *
1178:             * @deprecated as of JAI 1.1. Use <code>getSourceImage()</code>.
1179:             * @see PlanarImage#getSourceImage(int)
1180:             */
1181:            public PlanarImage getSource(int index) {
1182:                if (sources == null) {
1183:                    throw new ArrayIndexOutOfBoundsException(JaiI18N
1184:                            .getString("PlanarImage0"));
1185:                }
1186:
1187:                synchronized (sources) {
1188:                    return (PlanarImage) sources.get(index);
1189:                }
1190:            }
1191:
1192:            /**
1193:             * Sets the list of sources from a given <code>List</code> of
1194:             * <code>PlanarImage</code>s.  All of the existing sources are
1195:             * discarded.  Any <code>RenderedImage</code> sources in the supplied
1196:             * list are wrapped using <code>wrapRenderedImage()</code>.  The list
1197:             * of sinks of each prior <code>PlanarImage</code> source and of each
1198:             * current unwrapped <code>PlanarImage</code> source is adjusted as
1199:             * necessary such that this image is a sink of all such current sources
1200:             * but is removed as a sink of all such prior sources which are not
1201:             * also current.
1202:             *
1203:             * @param sourceList a <code>List</code> of <code>PlanarImage</code>s.
1204:             *
1205:             * @throws IllegalArgumentException  If <code>sourceList</code> is
1206:             *         <code>null</code> or contains any <code>null</code> elements.
1207:             */
1208:            protected void setSources(List sourceList) {
1209:                if (sourceList == null) {
1210:                    throw new IllegalArgumentException(JaiI18N
1211:                            .getString("Generic0"));
1212:                }
1213:
1214:                int size = sourceList.size();
1215:
1216:                synchronized (this ) {
1217:                    if (sources != null) {
1218:                        // Remove this image as a sink of prior PlanarImage sources.
1219:                        Iterator it = sources.iterator();
1220:                        while (it.hasNext()) {
1221:                            Object src = it.next();
1222:                            if (src instanceof  PlanarImage) {
1223:                                ((PlanarImage) src).removeSink(this );
1224:                            }
1225:                        }
1226:                    }
1227:                    sources = new Vector(size);
1228:                }
1229:
1230:                synchronized (sources) {
1231:                    for (int i = 0; i < size; i++) {
1232:                        Object sourceElement = sourceList.get(i);
1233:                        if (sourceElement == null) {
1234:                            throw new IllegalArgumentException(JaiI18N
1235:                                    .getString("PlanarImage7"));
1236:                        }
1237:
1238:                        sources
1239:                                .add(sourceElement instanceof  RenderedImage ? wrapRenderedImage((RenderedImage) sourceElement)
1240:                                        : sourceElement);
1241:
1242:                        // Add as a sink of any PlanarImage source.
1243:                        if (sourceElement instanceof  PlanarImage) {
1244:                            ((PlanarImage) sourceElement).addSink(this );
1245:                        }
1246:                    }
1247:                }
1248:            }
1249:
1250:            /**
1251:             * Removes all the sources of this image.  This image is removed from
1252:             * the list of sinks of any prior <code>PlanarImage</code>s sources.
1253:             */
1254:            protected void removeSources() {
1255:                if (sources != null) {
1256:                    synchronized (this ) {
1257:                        if (sources != null) {
1258:                            // Remove this image as a sink of prior PlanarImage sources.
1259:                            Iterator it = sources.iterator();
1260:                            while (it.hasNext()) {
1261:                                Object src = it.next();
1262:                                if (src instanceof  PlanarImage) {
1263:                                    ((PlanarImage) src).removeSink(this );
1264:                                }
1265:                            }
1266:                        }
1267:                        sources = null;
1268:                    }
1269:                }
1270:            }
1271:
1272:            /**
1273:             * Returns the immediate source indicated by the index.  If there
1274:             * is no source corresponding to the specified index, this method
1275:             * throws an exception.
1276:             *
1277:             * @param index  The index of the desired source.
1278:             *
1279:             * @return A <code>PlanarImage</code> source.
1280:             *
1281:             * @throws ArrayIndexOutOfBoundsException  If this image has no
1282:             *         immediate source, or if the index is negative or greater
1283:             *         than the maximum source index.
1284:             *
1285:             * @since JAI 1.1
1286:             */
1287:            public PlanarImage getSourceImage(int index) {
1288:                if (sources == null) {
1289:                    throw new ArrayIndexOutOfBoundsException(JaiI18N
1290:                            .getString("PlanarImage0"));
1291:                }
1292:
1293:                synchronized (sources) {
1294:                    return (PlanarImage) sources.get(index);
1295:                }
1296:            }
1297:
1298:            /**
1299:             * Returns the immediate source indicated by the index.  If there
1300:             * is no source corresponding to the specified index, this method
1301:             * throws an exception.
1302:             *
1303:             * @param index  The index of the desired source.
1304:             *
1305:             * @return An <code>Object</code> source.
1306:             *
1307:             * @throws ArrayIndexOutOfBoundsException  If this image has no
1308:             *         immediate source, or if the index is negative or greater
1309:             *         than the maximum source index.
1310:             *
1311:             * @since JAI 1.1
1312:             */
1313:            public Object getSourceObject(int index) {
1314:                if (sources == null) {
1315:                    throw new ArrayIndexOutOfBoundsException(JaiI18N
1316:                            .getString("PlanarImage0"));
1317:                }
1318:
1319:                synchronized (sources) {
1320:                    return sources.get(index);
1321:                }
1322:            }
1323:
1324:            /**
1325:             * Adds an <code>Object</code> source to the list of sources.
1326:             * If the source is a <code>RenderedImage</code> it is wrapped using
1327:             * <code>wrapRenderedImage()</code>.  If the unwrapped source is a
1328:             * <code>PlanarImage</code> then this image is added to its list of sinks.
1329:             *
1330:             * @param source An <code>Object</code> to be added as an
1331:             *        immediate source of this image.
1332:             *
1333:             * @throws IllegalArgumentException  If <code>source</code> is
1334:             *         <code>null</code>.
1335:             *
1336:             * @since JAI 1.1
1337:             */
1338:            protected void addSource(Object source) {
1339:                if (source == null) {
1340:                    throw new IllegalArgumentException(JaiI18N
1341:                            .getString("Generic0"));
1342:                }
1343:
1344:                if (sources == null) {
1345:                    synchronized (this ) {
1346:                        sources = new Vector();
1347:                    }
1348:                }
1349:
1350:                synchronized (sources) {
1351:                    // Add the source wrapping it if necessary.
1352:                    sources
1353:                            .add(source instanceof  RenderedImage ? wrapRenderedImage((RenderedImage) source)
1354:                                    : source);
1355:                }
1356:
1357:                if (source instanceof  PlanarImage) {
1358:                    ((PlanarImage) source).addSink(this );
1359:                }
1360:            }
1361:
1362:            /**
1363:             * Sets an immediate source of this image.  The source to be replaced
1364:             * with the new input <code>Object</code> is referred to by its
1365:             * index.  This image must already have a source corresponding to the
1366:             * specified index.  If the source is a <code>RenderedImage</code> it is
1367:             * wrapped using <code>wrapRenderedImage()</code>.  If the unwrapped
1368:             * source is a <code>PlanarImage</code> then this image is added to its
1369:             * list of sinks.  If a <code>PlanarImage</code> source previously
1370:             * existed at this index, this image is removed from its list of sinks.
1371:             *
1372:             * @param source  A <code>Object</code> source to be set.
1373:             * @param index  The index of the source to be set.
1374:             *
1375:             * @throws ArrayIndexOutOfBoundsException  If this image has no
1376:             *         immediate source, or if there is no source corresponding
1377:             *         to the index value.
1378:             * @throws IllegalArgumentException  If <code>source</code> is
1379:             *         <code>null</code>.
1380:             *
1381:             * @since JAI 1.1
1382:             */
1383:            protected void setSource(Object source, int index) {
1384:                if (source == null) {
1385:                    throw new IllegalArgumentException(JaiI18N
1386:                            .getString("Generic0"));
1387:                }
1388:
1389:                if (sources == null) {
1390:                    throw new ArrayIndexOutOfBoundsException(JaiI18N
1391:                            .getString("PlanarImage0"));
1392:                }
1393:
1394:                synchronized (sources) {
1395:                    if (index < sources.size()
1396:                            && sources.get(index) instanceof  PlanarImage) {
1397:                        getSourceImage(index).removeSink(this );
1398:                    }
1399:                    sources
1400:                            .set(
1401:                                    index,
1402:                                    source instanceof  RenderedImage ? wrapRenderedImage((RenderedImage) source)
1403:                                            : source);
1404:                }
1405:                if (source instanceof  PlanarImage) {
1406:                    ((PlanarImage) source).addSink(this );
1407:                }
1408:            }
1409:
1410:            /**
1411:             * Removes an <code>Object</code> source from the list of sources.
1412:             * If the source is a <code>PlanarImage</code> then this image
1413:             * is removed from its list of sinks.
1414:             *
1415:             * @param source  The <code>Object</code> source to be removed.
1416:             *
1417:             * @return <code>true</code> if the element was present,
1418:             *         <code>false</code> otherwise.
1419:             *
1420:             * @throws IllegalArgumentException  If <code>source</code> is
1421:             *         <code>null</code>.
1422:             *
1423:             * @since JAI 1.1
1424:             */
1425:            protected boolean removeSource(Object source) {
1426:                if (source == null) {
1427:                    throw new IllegalArgumentException(JaiI18N
1428:                            .getString("Generic0"));
1429:                }
1430:
1431:                if (sources == null) {
1432:                    return false;
1433:                }
1434:
1435:                synchronized (sources) {
1436:                    if (source instanceof  PlanarImage) {
1437:                        ((PlanarImage) source).removeSink(this );
1438:                    }
1439:                    return sources.remove(source);
1440:                }
1441:            }
1442:
1443:            /**
1444:             * Returns a <code>Vector</code> containing the currently available
1445:             * <code>PlanarImage</code> sinks of this image (images for which
1446:             * this image is a source), or <code>null</code> if no sinks are
1447:             * present.
1448:             *
1449:             * <p> Sinks are stored using weak references.  This means that
1450:             * the set of sinks may change between calls to
1451:             * <code>getSinks()</code> if the garbage collector happens to
1452:             * identify a sink as not otherwise reachable (reachability is
1453:             * discussed in the class comments for this class).
1454:             *
1455:             * <p> Since the pool of sinks may change as garbage collection
1456:             * occurs, <code>PlanarImage</code> does not implement either a
1457:             * <code>getSink(int index)</code> or a <code>getNumSinks()</code>
1458:             * method.  Instead, the caller must call <code>getSinks()</code>,
1459:             * which returns a Vector of normal references.  As long as the
1460:             * returned <code>Vector</code> is referenced from user code, the
1461:             * images it references are reachable and may be reliably
1462:             * accessed.
1463:             */
1464:            public Vector getSinks() {
1465:                Vector v = null;
1466:
1467:                if (sinks != null) {
1468:                    synchronized (sinks) {
1469:                        int size = sinks.size();
1470:                        v = new Vector(size);
1471:                        for (int i = 0; i < size; i++) {
1472:                            Object o = ((WeakReference) sinks.get(i)).get();
1473:
1474:                            if (o != null) {
1475:                                v.add(o);
1476:                            }
1477:                        }
1478:                    }
1479:
1480:                    if (v.size() == 0) {
1481:                        v = null;
1482:                    }
1483:                }
1484:                return v;
1485:            }
1486:
1487:            /**
1488:             * Adds an <code>Object</code> sink to the list of sinks.
1489:             *
1490:             * @return <code>true</code> if the element was added,
1491:             *         <code>false</code> otherwise.
1492:             *
1493:             * @throws IllegalArgumentException if
1494:             * <code>sink</code> is <code>null</code>.
1495:             *
1496:             * @since JAI 1.1
1497:             */
1498:            public synchronized boolean addSink(Object sink) {
1499:                if (sink == null) {
1500:                    throw new IllegalArgumentException(JaiI18N
1501:                            .getString("Generic0"));
1502:                }
1503:
1504:                if (sinks == null) {
1505:                    sinks = new Vector();
1506:                }
1507:
1508:                boolean result = false;
1509:                if (sink instanceof  PlanarImage) {
1510:                    result = sinks.add(((PlanarImage) sink).weakThis);
1511:                } else {
1512:                    result = sinks.add(new WeakReference(sink));
1513:                }
1514:
1515:                return result;
1516:            }
1517:
1518:            /**
1519:             * Removes an <code>Object</code> sink from the list of sinks.
1520:             *
1521:             * @return <code>true</code> if the element was present,
1522:             *         <code>false</code> otherwise.
1523:             *
1524:             * @throws IllegalArgumentException if
1525:             * <code>sink</code> is <code>null</code>.
1526:             *
1527:             * @since JAI 1.1
1528:             */
1529:            public synchronized boolean removeSink(Object sink) {
1530:                if (sink == null) {
1531:                    throw new IllegalArgumentException(JaiI18N
1532:                            .getString("Generic0"));
1533:                }
1534:
1535:                if (sinks == null) {
1536:                    return false;
1537:                }
1538:
1539:                boolean result = false;
1540:                if (sink instanceof  PlanarImage) {
1541:                    result = sinks.remove(((PlanarImage) sink).weakThis);
1542:                } else {
1543:                    Iterator it = sinks.iterator();
1544:                    while (it.hasNext()) {
1545:                        Object referent = ((WeakReference) it.next()).get();
1546:                        if (referent == sink) {
1547:                            // Remove the sink.
1548:                            it.remove();
1549:                            result = true;
1550:                            // Do not break: could be more than one.
1551:                        } else if (referent == null) {
1552:                            // A cleared reference: might as well remove it.
1553:                            it.remove(); // ignore return value here.
1554:                        }
1555:                    }
1556:                }
1557:
1558:                return result;
1559:            }
1560:
1561:            /**
1562:             * Adds a <code>PlanarImage</code> sink to the list of sinks.
1563:             *
1564:             * @param sink A <code>PlanarImage</code> to be added as a sink.
1565:             *
1566:             * @throws IllegalArgumentException  If <code>sink</code> is
1567:             *         <code>null</code>.
1568:             *
1569:             * @deprecated as of JAI 1.1. Use <code>addSink(Object)</code> instead.
1570:             */
1571:            protected void addSink(PlanarImage sink) {
1572:                if (sink == null) {
1573:                    throw new IllegalArgumentException(JaiI18N
1574:                            .getString("Generic0"));
1575:                }
1576:
1577:                if (sinks == null) {
1578:                    synchronized (this ) {
1579:                        sinks = new Vector();
1580:                    }
1581:                }
1582:
1583:                synchronized (sinks) {
1584:                    sinks.add(sink.weakThis);
1585:                }
1586:            }
1587:
1588:            /**
1589:             * Removes a <code>PlanarImage</code> sink from the list of sinks.
1590:             *
1591:             * @param sink  A <code>PlanarImage</code> sink to be removed.
1592:             *
1593:             * @return <code>true</code> if the element was present,
1594:             *         <code>false</code> otherwise.
1595:             *
1596:             * @throws IllegalArgumentException  If <code>sink</code> is
1597:             *         <code>null</code>.
1598:             * @throws IndexOutOfBoundsException  If <code>sink</code> is not
1599:             *         in the sink list.
1600:             *
1601:             * @deprecated as of JAI 1.1. Use <code>removeSink(Object)</code> instead.
1602:             */
1603:            protected boolean removeSink(PlanarImage sink) {
1604:                if (sink == null) {
1605:                    throw new IllegalArgumentException(JaiI18N
1606:                            .getString("Generic0"));
1607:                }
1608:
1609:                if (sinks == null) {
1610:                    return false;
1611:                }
1612:
1613:                synchronized (sinks) {
1614:                    return sinks.remove(sink.weakThis);
1615:                }
1616:            }
1617:
1618:            /** Removes all the sinks of this image. */
1619:            public void removeSinks() {
1620:                if (sinks != null) {
1621:                    synchronized (this ) {
1622:                        sinks = null;
1623:                    }
1624:                }
1625:            }
1626:
1627:            /**
1628:             * Returns the internal <code>Hashtable</code> containing the
1629:             * image properties by reference.
1630:             */
1631:            protected Hashtable getProperties() {
1632:                return (Hashtable) properties.getProperties();
1633:            }
1634:
1635:            /**
1636:             * Sets the <code>Hashtable</code> containing the image properties
1637:             * to a given <code>Hashtable</code>.  The <code>Hashtable</code>
1638:             * is incorporated by reference and must not be altered by other
1639:             * classes after this method is called.
1640:             */
1641:            protected void setProperties(Hashtable properties) {
1642:                this .properties.addProperties(properties);
1643:            }
1644:
1645:            /**
1646:             * Gets a property from the property set of this image.  If the
1647:             * property name is not recognized,
1648:             * <code>java.awt.Image.UndefinedProperty</code> will be returned.
1649:             *
1650:             * @param name the name of the property to get, as a <code>String</code>.
1651:             *
1652:             * @return A reference to the property <code>Object</code>, or the value
1653:             *         <code>java.awt.Image.UndefinedProperty</code>.
1654:             *
1655:             * @exception IllegalArgumentException if <code>propertyName</code>
1656:             *                                     is <code>null</code>.
1657:             */
1658:            public Object getProperty(String name) {
1659:                return properties.getProperty(name);
1660:            }
1661:
1662:            /**
1663:             * Returns the class expected to be returned by a request for
1664:             * the property with the specified name.  If this information
1665:             * is unavailable, <code>null</code> will be returned.
1666:             *
1667:             * @exception IllegalArgumentException if <code>name</code>
1668:             *                                     is <code>null</code>.
1669:             *
1670:             * @return The <code>Class</code> expected to be return by a
1671:             *         request for the value of this property or <code>null</code>.
1672:             *
1673:             * @since JAI 1.1
1674:             */
1675:            public Class getPropertyClass(String name) {
1676:                return properties.getPropertyClass(name);
1677:            }
1678:
1679:            /**
1680:             * Sets a property on a <code>PlanarImage</code>.  Some
1681:             * <code>PlanarImage</code> subclasses may ignore attempts to set
1682:             * properties.
1683:             *
1684:             * @param name a <code>String</code> containing the property's name.
1685:             * @param value the property, as a general <code>Object</code>.
1686:             *
1687:             * @throws IllegalArgumentException  If <code>name</code> or
1688:             *         <code>value</code> is <code>null</code>.
1689:             */
1690:            public void setProperty(String name, Object value) {
1691:                properties.setProperty(name, value);
1692:            }
1693:
1694:            /**
1695:             * Removes the named property from the <code>PlanarImage</code>.
1696:             * Some <code>PlanarImage</code> subclasses may ignore attempts to
1697:             * remove properties.
1698:             *
1699:             * @exception IllegalArgumentException if <code>name</code>
1700:             *                                     is <code>null</code>.
1701:             *
1702:             * @since JAI 1.1
1703:             */
1704:            public void removeProperty(String name) {
1705:                properties.removeProperty(name);
1706:            }
1707:
1708:            /**
1709:             * Returns a list of property names that are recognized by this image
1710:             * or <code>null</code> if none are recognized.
1711:             *
1712:             * @return an array of <code>String</code>s containing valid
1713:             *         property names or <code>null</code>.
1714:             */
1715:            public String[] getPropertyNames() {
1716:                return properties.getPropertyNames();
1717:            }
1718:
1719:            /**
1720:             * Returns an array of <code>String</code>s recognized as names by
1721:             * this property source that begin with the supplied prefix.  If
1722:             * no property names match, <code>null</code> will be returned.
1723:             * The comparison is done in a case-independent manner.
1724:             *
1725:             * <p> The default implementation calls
1726:             * <code>getPropertyNames()</code> and searches the list of names
1727:             * for matches.
1728:             *
1729:             * @return an array of <code>String</code>s giving the valid
1730:             *         property names.
1731:             *
1732:             * @throws IllegalArgumentException  If <code>prefix</code> is
1733:             *         <code>null</code>.
1734:             */
1735:            public String[] getPropertyNames(String prefix) {
1736:                return PropertyUtil
1737:                        .getPropertyNames(getPropertyNames(), prefix);
1738:            }
1739:
1740:            /**
1741:             * Add a PropertyChangeListener to the listener list. The
1742:             * listener is registered for all properties.
1743:             *
1744:             * @since JAI 1.1
1745:             */
1746:            public void addPropertyChangeListener(
1747:                    PropertyChangeListener listener) {
1748:                eventManager.addPropertyChangeListener(listener);
1749:            }
1750:
1751:            /**
1752:             * Add a PropertyChangeListener for a specific property. The
1753:             * listener will be invoked only when a call on
1754:             * firePropertyChange names that specific property.  The case of
1755:             * the name is ignored.
1756:             *
1757:             * @since JAI 1.1
1758:             */
1759:            public void addPropertyChangeListener(String propertyName,
1760:                    PropertyChangeListener listener) {
1761:                eventManager.addPropertyChangeListener(propertyName
1762:                        .toLowerCase(), listener);
1763:            }
1764:
1765:            /**
1766:             * Remove a PropertyChangeListener from the listener list. This
1767:             * removes a PropertyChangeListener that was registered for all
1768:             * properties.
1769:             *
1770:             * @since JAI 1.1
1771:             */
1772:            public void removePropertyChangeListener(
1773:                    PropertyChangeListener listener) {
1774:                eventManager.removePropertyChangeListener(listener);
1775:            }
1776:
1777:            /**
1778:             * Remove a PropertyChangeListener for a specific property.  The case
1779:             * of the name is ignored.
1780:             *
1781:             * @since JAI 1.1
1782:             */
1783:            public void removePropertyChangeListener(String propertyName,
1784:                    PropertyChangeListener listener) {
1785:                eventManager.removePropertyChangeListener(propertyName
1786:                        .toLowerCase(), listener);
1787:            }
1788:
1789:            private synchronized Set getTileComputationListeners(
1790:                    boolean createIfNull) {
1791:                if (createIfNull && tileListeners == null) {
1792:                    tileListeners = Collections.synchronizedSet(new HashSet());
1793:                }
1794:                return tileListeners;
1795:            }
1796:
1797:            /**
1798:             * Adds a <code>TileComputationListener</code> to the list of
1799:             * registered <code>TileComputationListener</code>s.  This listener
1800:             * will be notified when tiles requested via <code>queueTiles()</code>
1801:             * have been computed.
1802:             *
1803:             * @param listener The <code>TileComputationListener</code> to register.
1804:             * @throws IllegalArgumentException if <code>listener</code> is
1805:             *         <code>null</code>.
1806:             *
1807:             * @since JAI 1.1
1808:             */
1809:            public synchronized void addTileComputationListener(
1810:                    TileComputationListener listener) {
1811:                if (listener == null) {
1812:                    throw new IllegalArgumentException(JaiI18N
1813:                            .getString("Generic0"));
1814:                }
1815:
1816:                Set listeners = getTileComputationListeners(true);
1817:
1818:                listeners.add(listener);
1819:            }
1820:
1821:            /**
1822:             * Removes a <code>TileComputationListener</code> from the list of
1823:             * registered <code>TileComputationListener</code>s.
1824:             *
1825:             * @param listener The <code>TileComputationListener</code> to unregister.
1826:             * @throws IllegalArgumentException if <code>listener</code> is
1827:             *         <code>null</code>.
1828:             *
1829:             * @since JAI 1.1
1830:             */
1831:            public synchronized void removeTileComputationListener(
1832:                    TileComputationListener listener) {
1833:                if (listener == null) {
1834:                    throw new IllegalArgumentException(JaiI18N
1835:                            .getString("Generic0"));
1836:                }
1837:
1838:                Set listeners = getTileComputationListeners(false);
1839:
1840:                if (listeners != null) {
1841:                    listeners.remove(listener);
1842:                }
1843:            }
1844:
1845:            /**
1846:             * Retrieves a snapshot of the set of all registered
1847:             * <code>TileComputationListener</code>s as of the moment this
1848:             * method is invoked.
1849:             *
1850:             * @return All <code>TileComputationListener</code>s or
1851:             *         <code>null</code> if there are none.
1852:             *
1853:             * @since JAI 1.1
1854:             */
1855:            public TileComputationListener[] getTileComputationListeners() {
1856:
1857:                Set listeners = getTileComputationListeners(false);
1858:
1859:                if (listeners == null) {
1860:                    return null;
1861:                }
1862:
1863:                return (TileComputationListener[]) listeners
1864:                        .toArray(new TileComputationListener[listeners.size()]);
1865:            }
1866:
1867:            /**
1868:             * Within a given rectangle, store the list of tile seams of both
1869:             * X and Y directions into the corresponding split sequence.
1870:             *
1871:             * @param xSplits An <code>IntegerSequence</code> to which the
1872:             *        tile seams in the X direction are to be added.
1873:             * @param ySplits An <code>IntegerSequence</code> to which the
1874:             *        tile seams in the Y direction are to be added.
1875:             * @param rect The rectangular region of interest.
1876:             *
1877:             * @throws IllegalArgumentException  If <code>xSplits</code>,
1878:             *         <code>ySplits</code>, or <code>rect</code>
1879:             *         is <code>null</code>.
1880:             */
1881:            public void getSplits(IntegerSequence xSplits,
1882:                    IntegerSequence ySplits, Rectangle rect) {
1883:                if (xSplits == null || ySplits == null || rect == null) {
1884:                    throw new IllegalArgumentException(JaiI18N
1885:                            .getString("Generic0"));
1886:                }
1887:
1888:                int minTileX = XToTileX(rect.x);
1889:                int maxTileX = XToTileX(rect.x + rect.width - 1);
1890:                int xTilePos = tileXToX(minTileX);
1891:                for (int i = minTileX; i <= maxTileX; i++) {
1892:                    xSplits.insert(xTilePos);
1893:                    xTilePos += tileWidth;
1894:                }
1895:
1896:                int minTileY = YToTileY(rect.y);
1897:                int maxTileY = YToTileY(rect.y + rect.height - 1);
1898:                int yTilePos = tileYToY(minTileY);
1899:                for (int i = minTileY; i <= maxTileY; i++) {
1900:                    ySplits.insert(yTilePos);
1901:                    yTilePos += tileHeight;
1902:                }
1903:            }
1904:
1905:            /**
1906:             * Returns an array containing the indices of all tiles which overlap
1907:             * the specified <code>Rectangle</code>.  If the <code>Rectangle</code>
1908:             * does not intersect the image bounds then <code>null</code> will be
1909:             * returned.  If an array is returned, it will be ordered in terms of
1910:             * the row major ordering of its contained tile indices.  If the
1911:             * specified <code>Rectangle</code> is <code>null</code>, the tile
1912:             * indicies for the entire image will be returned.
1913:             *
1914:             * @param region The <code>Rectangle</code> of interest.
1915:             * @return An array of the indices of overlapping tiles or
1916:             *         <code>null</code> if <code>region</code> does not intersect
1917:             *         the image bounds.
1918:             *
1919:             * @since JAI 1.1
1920:             */
1921:            public Point[] getTileIndices(Rectangle region) {
1922:                if (region == null) {
1923:                    region = (Rectangle) getBounds().clone();
1924:                } else if (!region.intersects(getBounds())) {
1925:                    return null;
1926:                } else {
1927:                    region = region.intersection(getBounds());
1928:                    if (region.isEmpty()) {
1929:                        return null;
1930:                    }
1931:                }
1932:
1933:                if (region == null) {
1934:                    region = getBounds();
1935:                } else {
1936:                    Rectangle r = new Rectangle(getMinX(), getMinY(),
1937:                            getWidth() + 1, getHeight() + 1);
1938:                    if (!region.intersects(r)) {
1939:                        return null;
1940:                    } else {
1941:                        region = region.intersection(r);
1942:                    }
1943:                }
1944:
1945:                int minTileX = XToTileX(region.x);
1946:                int maxTileX = XToTileX(region.x + region.width - 1);
1947:                int minTileY = YToTileY(region.y);
1948:                int maxTileY = YToTileY(region.y + region.height - 1);
1949:
1950:                Point[] tileIndices = new Point[(maxTileY - minTileY + 1)
1951:                        * (maxTileX - minTileX + 1)];
1952:
1953:                int tileIndexOffset = 0;
1954:                for (int ty = minTileY; ty <= maxTileY; ty++) {
1955:                    for (int tx = minTileX; tx <= maxTileX; tx++) {
1956:                        tileIndices[tileIndexOffset++] = new Point(tx, ty);
1957:                    }
1958:                }
1959:
1960:                return tileIndices;
1961:            }
1962:
1963:            /**
1964:             * Returns <code>true</code> if and only if the intersection of
1965:             * the specified <code>Rectangle</code> with the image bounds
1966:             * overlaps more than one tile.
1967:             *
1968:             * @throws IllegalArgumentException if <code>rect</code> is
1969:             * <code>null</code>.
1970:             */
1971:            public boolean overlapsMultipleTiles(Rectangle rect) {
1972:                if (rect == null) {
1973:                    throw new IllegalArgumentException("rect == null!");
1974:                }
1975:
1976:                Rectangle xsect = rect.intersection(getBounds());
1977:
1978:                // 'true' if and only if non-empty and more than one tile in
1979:                // either horizontal or vertical direction.
1980:                return !xsect.isEmpty()
1981:                        && (XToTileX(xsect.x) != XToTileX(xsect.x + xsect.width
1982:                                - 1) || YToTileY(xsect.y) != YToTileY(xsect.y
1983:                                + xsect.height - 1));
1984:            }
1985:
1986:            /**
1987:             * Creates a <code>WritableRaster</code> with the specified
1988:             * <code>SampleModel</code> and location.  If <code>tileFactory</code>
1989:             * is non-<code>null</code>, it will be used to create the
1990:             * <code>WritableRaster</code>; otherwise
1991:             * {@link RasterFactory#createWritableRaster(SampleModel,Point)}
1992:             * will be used.
1993:             *
1994:             * @param sampleModel The <code>SampleModel</code> to use.
1995:             * @param location The origin of the <code>WritableRaster</code>; if
1996:             * <code>null</code>, <code>(0,&nbsp;0)</code> will be used.
1997:             *
1998:             * @throws IllegalArgumentException if <code>sampleModel</code> is
1999:             * <code>null</code>.
2000:             *
2001:             * @since JAI 1.1.2
2002:             */
2003:            protected final WritableRaster createWritableRaster(
2004:                    SampleModel sampleModel, Point location) {
2005:
2006:                if (sampleModel == null) {
2007:                    throw new IllegalArgumentException("sampleModel == null!");
2008:                }
2009:
2010:                return tileFactory != null ? tileFactory.createTile(
2011:                        sampleModel, location) : RasterFactory
2012:                        .createWritableRaster(sampleModel, location);
2013:            }
2014:
2015:            /**
2016:             * Returns the entire image in a single <code>Raster</code>.  For
2017:             * images with multiple tiles this will require creating a new
2018:             * <code>Raster</code> and copying data from multiple tiles into
2019:             * it ("cobbling").
2020:             *
2021:             * <p>The returned <code>Raster</code> is semantically a copy.
2022:             * This means that subsequent updates to this image will not be
2023:             * reflected in the returned <code>Raster</code>. For non-writable
2024:             * (immutable) images, the returned value may be a reference to the
2025:             * image's internal data. The returned <code>Raster</code> should
2026:             * be considered non-writable; any attempt to alter its pixel data
2027:             * (such as by casting it to a <code>WritableRaster</code> or obtaining
2028:             * and modifying its <code>DataBuffer</code>) may result in undefined
2029:             * behavior. The <code>copyData</code> method should be used if the
2030:             * returned <code>Raster</code> is to be modified.
2031:             *
2032:             * <p> For a very large image, more than
2033:             * <code>Integer.MAX_VALUE</code> entries could be required in the
2034:             * returned <code>Raster</code>'s underlying data array.  Since
2035:             * the Java language does not permit such an array, an
2036:             * <code>IllegalArgumentException</code> will be thrown.
2037:             *
2038:             * @return A <code>Raster</code> containing the entire image data.
2039:             *
2040:             * @throws IllegalArgumentException  If the size of the returned data
2041:             *         is too large to be stored in a single <code>Raster</code>.
2042:             */
2043:            public Raster getData() {
2044:                return getData(null);
2045:            }
2046:
2047:            /**
2048:             * Returns a specified region of this image in a <code>Raster</code>.
2049:             *
2050:             * <p> The returned <code>Raster</code> is semantically a copy.
2051:             * This means that subsequent updates to this image will not be
2052:             * reflected in the returned <code>Raster</code>. For non-writable
2053:             * (immutable) images, the returned value may be a reference to the
2054:             * image's internal data. The returned <code>Raster</code> should
2055:             * be considered non-writable; any attempt to alter its pixel data
2056:             * (such as by casting it to a <code>WritableRaster</code> or obtaining
2057:             * and modifying its <code>DataBuffer</code>) may result in undefined
2058:             * behavior. The <code>copyData</code> method should be used if the
2059:             * returned <code>Raster</code> is to be modified.
2060:             *
2061:             * <p> The region of the image to be returned is specified by a
2062:             * <code>Rectangle</code>. This region may go beyond this image's
2063:             * boundary. If so, the pixels in the areas outside this image's
2064:             * boundary are left unset.  Use <code>getExtendedData</code> if
2065:             * a specific extension policy is required.
2066:             *
2067:             * <p> The <code>region</code> parameter may also be
2068:             * <code>null</code>, in which case the entire image data is
2069:             * returned in the <code>Raster</code>.
2070:             *
2071:             * <p> If <code>region</code> is non-<code>null</code> but does
2072:             * not intersect the image bounds at all, an
2073:             * <code>IllegalArgumentException</code> will be thrown.
2074:             *
2075:             * <p> It is possible to request a region of an image that would
2076:             * require more than <code>Integer.MAX_VALUE</code> entries
2077:             * in the returned <code>Raster</code>'s underlying data array.
2078:             * Since the Java language does not permit such an array,
2079:             * an <code>IllegalArgumentException</code> will be thrown.
2080:             *
2081:             * @param region The rectangular region of this image to be
2082:             * returned, or <code>null</code>.
2083:             *
2084:             * @return A <code>Raster</code> containing the specified image data.
2085:             *
2086:             * @throws IllegalArgumentException  If the region does not
2087:             *         intersect the image bounds.
2088:             * @throws IllegalArgumentException  If the size of the returned data
2089:             *         is too large to be stored in a single <code>Raster</code>.
2090:             */
2091:            public Raster getData(Rectangle region) {
2092:                Rectangle b = getBounds(); // image's bounds
2093:
2094:                if (region == null) {
2095:                    region = b;
2096:                } else if (!region.intersects(b)) {
2097:                    throw new IllegalArgumentException(JaiI18N
2098:                            .getString("PlanarImage4"));
2099:                }
2100:
2101:                // Get the intersection of the region and the image bounds.
2102:                Rectangle xsect = region == b ? region : region.intersection(b);
2103:
2104:                // Compute tile indices over the intersection.
2105:                int startTileX = XToTileX(xsect.x);
2106:                int startTileY = YToTileY(xsect.y);
2107:                int endTileX = XToTileX(xsect.x + xsect.width - 1);
2108:                int endTileY = YToTileY(xsect.y + xsect.height - 1);
2109:
2110:                if (startTileX == endTileX && startTileY == endTileY
2111:                        && getTileRect(startTileX, startTileY).contains(region)) {
2112:                    // Requested region is within a single tile.
2113:                    Raster tile = getTile(startTileX, startTileY);
2114:
2115:                    if (this  instanceof  WritableRenderedImage) {
2116:                        // Returned Raster must not change if the corresponding
2117:                        // image data are modified so if this image is mutable
2118:                        // a copy must be created.
2119:                        SampleModel sm = tile.getSampleModel();
2120:                        if (sm.getWidth() != region.width
2121:                                || sm.getHeight() != region.height) {
2122:                            sm = sm.createCompatibleSampleModel(region.width,
2123:                                    region.height);
2124:                        }
2125:                        WritableRaster destinationRaster = createWritableRaster(
2126:                                sm, region.getLocation());
2127:                        Raster sourceRaster = tile.getBounds().equals(region) ? tile
2128:                                : tile.createChild(region.x, region.y,
2129:                                        region.width, region.height, region.x,
2130:                                        region.y, null);
2131:                        JDKWorkarounds.setRect(destinationRaster, sourceRaster);
2132:                        return destinationRaster;
2133:                    } else {
2134:                        // Image is immutable so returning the tile or a child
2135:                        // thereof is acceptable.
2136:                        return tile.getBounds().equals(region) ? tile
2137:                                : tile.createChild(region.x, region.y,
2138:                                        region.width, region.height, region.x,
2139:                                        region.y, null);
2140:                    }
2141:                } else {
2142:                    // Extract a region crossing tiles into a new WritableRaster
2143:                    WritableRaster dstRaster;
2144:                    SampleModel srcSM = getSampleModel();
2145:                    int dataType = srcSM.getDataType();
2146:                    int nbands = srcSM.getNumBands();
2147:                    boolean isBandChild = false;
2148:
2149:                    ComponentSampleModel csm = null;
2150:                    int[] bandOffs = null;
2151:
2152:                    boolean fastCobblePossible = false;
2153:                    if (srcSM instanceof  ComponentSampleModel) {
2154:                        csm = (ComponentSampleModel) srcSM;
2155:                        int ps = csm.getPixelStride();
2156:                        boolean isBandInt = (ps == 1 && nbands > 1);
2157:                        isBandChild = (ps > 1 && nbands != ps);
2158:                        if ((!isBandChild) && (!isBandInt)) {
2159:                            bandOffs = csm.getBandOffsets();
2160:                            int i;
2161:                            for (i = 0; i < nbands; i++) {
2162:                                if (bandOffs[i] >= nbands) {
2163:                                    break;
2164:                                }
2165:                            }
2166:                            if (i == nbands) {
2167:                                fastCobblePossible = true;
2168:                            }
2169:                        }
2170:                    }
2171:
2172:                    if (fastCobblePossible) {
2173:                        // For acceptable cases of ComponentSampleModel,
2174:                        // use an optimized cobbler which directly accesses the
2175:                        // tile DataBuffers, using arraycopy whenever possible.
2176:                        try {
2177:                            SampleModel interleavedSM = RasterFactory
2178:                                    .createPixelInterleavedSampleModel(
2179:                                            dataType, region.width,
2180:                                            region.height, nbands, region.width
2181:                                                    * nbands, bandOffs);
2182:                            dstRaster = createWritableRaster(interleavedSM,
2183:                                    region.getLocation());
2184:                        } catch (IllegalArgumentException e) {
2185:                            throw new IllegalArgumentException(JaiI18N
2186:                                    .getString("PlanarImage2"));
2187:                        }
2188:
2189:                        switch (dataType) {
2190:                        case DataBuffer.TYPE_BYTE:
2191:                            cobbleByte(region, dstRaster);
2192:                            break;
2193:                        case DataBuffer.TYPE_SHORT:
2194:                            cobbleShort(region, dstRaster);
2195:                            break;
2196:                        case DataBuffer.TYPE_USHORT:
2197:                            cobbleUShort(region, dstRaster);
2198:                            break;
2199:                        case DataBuffer.TYPE_INT:
2200:                            cobbleInt(region, dstRaster);
2201:                            break;
2202:                        case DataBuffer.TYPE_FLOAT:
2203:                            cobbleFloat(region, dstRaster);
2204:                            break;
2205:                        case DataBuffer.TYPE_DOUBLE:
2206:                            cobbleDouble(region, dstRaster);
2207:                            break;
2208:                        default:
2209:                            break;
2210:                        }
2211:                    } else {
2212:                        SampleModel sm = sampleModel;
2213:                        if (sm.getWidth() != region.width
2214:                                || sm.getHeight() != region.height) {
2215:                            sm = sm.createCompatibleSampleModel(region.width,
2216:                                    region.height);
2217:                        }
2218:
2219:                        try {
2220:                            dstRaster = createWritableRaster(sm, region
2221:                                    .getLocation());
2222:                        } catch (IllegalArgumentException e) {
2223:                            throw new IllegalArgumentException(JaiI18N
2224:                                    .getString("PlanarImage2"));
2225:                        }
2226:
2227:                        for (int j = startTileY; j <= endTileY; j++) {
2228:                            for (int i = startTileX; i <= endTileX; i++) {
2229:                                Raster tile = getTile(i, j);
2230:
2231:                                Rectangle subRegion = region.intersection(tile
2232:                                        .getBounds());
2233:                                Raster subRaster = tile.createChild(
2234:                                        subRegion.x, subRegion.y,
2235:                                        subRegion.width, subRegion.height,
2236:                                        subRegion.x, subRegion.y, null);
2237:
2238:                                if (sm instanceof  ComponentSampleModel
2239:                                        && isBandChild) {
2240:                                    // Need to handle this case specially, since
2241:                                    // setDataElements will not copy band child images
2242:                                    switch (sm.getDataType()) {
2243:                                    case DataBuffer.TYPE_FLOAT:
2244:                                        dstRaster
2245:                                                .setPixels(
2246:                                                        subRegion.x,
2247:                                                        subRegion.y,
2248:                                                        subRegion.width,
2249:                                                        subRegion.height,
2250:                                                        subRaster
2251:                                                                .getPixels(
2252:                                                                        subRegion.x,
2253:                                                                        subRegion.y,
2254:                                                                        subRegion.width,
2255:                                                                        subRegion.height,
2256:                                                                        new float[nbands
2257:                                                                                * subRegion.width
2258:                                                                                * subRegion.height]));
2259:                                        break;
2260:                                    case DataBuffer.TYPE_DOUBLE:
2261:                                        dstRaster
2262:                                                .setPixels(
2263:                                                        subRegion.x,
2264:                                                        subRegion.y,
2265:                                                        subRegion.width,
2266:                                                        subRegion.height,
2267:                                                        subRaster
2268:                                                                .getPixels(
2269:                                                                        subRegion.x,
2270:                                                                        subRegion.y,
2271:                                                                        subRegion.width,
2272:                                                                        subRegion.height,
2273:                                                                        new double[nbands
2274:                                                                                * subRegion.width
2275:                                                                                * subRegion.height]));
2276:                                        break;
2277:                                    default:
2278:                                        dstRaster
2279:                                                .setPixels(
2280:                                                        subRegion.x,
2281:                                                        subRegion.y,
2282:                                                        subRegion.width,
2283:                                                        subRegion.height,
2284:                                                        subRaster
2285:                                                                .getPixels(
2286:                                                                        subRegion.x,
2287:                                                                        subRegion.y,
2288:                                                                        subRegion.width,
2289:                                                                        subRegion.height,
2290:                                                                        new int[nbands
2291:                                                                                * subRegion.width
2292:                                                                                * subRegion.height]));
2293:                                        break;
2294:                                    }
2295:                                } else {
2296:                                    JDKWorkarounds
2297:                                            .setRect(dstRaster, subRaster);
2298:                                }
2299:
2300:                            }
2301:                        }
2302:                    }
2303:
2304:                    return dstRaster;
2305:                }
2306:            }
2307:
2308:            /** Copies the entire image into a single raster. */
2309:            public WritableRaster copyData() {
2310:                return copyData(null);
2311:            }
2312:
2313:            /**
2314:             * Copies an arbitrary rectangular region of this image's pixel
2315:             * data into a caller-supplied <code>WritableRaster</code>.
2316:             * The region to be copied is defined as the boundary of the
2317:             * <code>WritableRaster</code>, which can be obtained by calling
2318:             * <code>WritableRaster.getBounds()</code>.
2319:             *
2320:             * <p>The supplied <code>WritableRaster</code> may have a region
2321:             * that extends beyond this image's boundary, in which case only
2322:             * pixels in the part of the region that intersects this image
2323:             * are copied. The areas outside of this image's boundary are left
2324:             * untouched.
2325:             *
2326:             * <p>The supplied <code>WritableRaster</code> may also be
2327:             * <code>null</code>, in which case the entire image is copied
2328:             * into a newly-created <code>WritableRaster</code> with a
2329:             * <code>SampleModel</code> that is compatible with that of
2330:             * this image.
2331:             *
2332:             * @param raster  A <code>WritableRaster</code> to hold the copied
2333:             *        pixel data of this image.
2334:             *
2335:             * @return A reference to the supplied <code>WritableRaster</code>,
2336:             *         or to a new <code>WritableRaster</code> if the supplied
2337:             *         one was <code>null</code>.
2338:             */
2339:            public WritableRaster copyData(WritableRaster raster) {
2340:                Rectangle region; // the region to be copied
2341:                if (raster == null) { // copy the entire image
2342:                    region = getBounds();
2343:
2344:                    SampleModel sm = getSampleModel();
2345:                    if (sm.getWidth() != region.width
2346:                            || sm.getHeight() != region.height) {
2347:                        sm = sm.createCompatibleSampleModel(region.width,
2348:                                region.height);
2349:                    }
2350:                    raster = createWritableRaster(sm, region.getLocation());
2351:                } else {
2352:                    region = raster.getBounds().intersection(getBounds());
2353:
2354:                    if (region.isEmpty()) { // Raster is outside of image's boundary
2355:                        return raster;
2356:                    }
2357:                }
2358:
2359:                int startTileX = XToTileX(region.x);
2360:                int startTileY = YToTileY(region.y);
2361:                int endTileX = XToTileX(region.x + region.width - 1);
2362:                int endTileY = YToTileY(region.y + region.height - 1);
2363:
2364:                SampleModel[] sampleModels = { getSampleModel() };
2365:                int tagID = RasterAccessor.findCompatibleTag(sampleModels,
2366:                        raster.getSampleModel());
2367:
2368:                RasterFormatTag srcTag = new RasterFormatTag(getSampleModel(),
2369:                        tagID);
2370:                RasterFormatTag dstTag = new RasterFormatTag(raster
2371:                        .getSampleModel(), tagID);
2372:
2373:                for (int ty = startTileY; ty <= endTileY; ty++) {
2374:                    for (int tx = startTileX; tx <= endTileX; tx++) {
2375:                        Raster tile = getTile(tx, ty);
2376:                        Rectangle subRegion = region.intersection(tile
2377:                                .getBounds());
2378:
2379:                        RasterAccessor s = new RasterAccessor(tile, subRegion,
2380:                                srcTag, getColorModel());
2381:                        RasterAccessor d = new RasterAccessor(raster,
2382:                                subRegion, dstTag, null);
2383:                        ImageUtil.copyRaster(s, d);
2384:                    }
2385:                }
2386:                return raster;
2387:            }
2388:
2389:            /**
2390:             * Copies an arbitrary rectangular region of the
2391:             * <code>RenderedImage</code> into a caller-supplied
2392:             * <code>WritableRaster</code>.  The portion of the supplied
2393:             * <code>WritableRaster</code> that lies outside of the bounds of
2394:             * the image is computed by calling the given
2395:             * <code>BorderExtender</code>.  The supplied
2396:             * <code>WritableRaster</code> must have a
2397:             * <code>SampleModel</code> that is compatible with that of the
2398:             * image.
2399:             *
2400:             * @param dest a <code>WritableRaster</code> to hold the returned
2401:             * portion of the image.
2402:             * @param extender an instance of <code>BorderExtender</code>.
2403:             *
2404:             * @throws IllegalArgumentException  If <code>dest</code> or
2405:             *         <code>extender</code> is <code>null</code>.
2406:             */
2407:            public void copyExtendedData(WritableRaster dest,
2408:                    BorderExtender extender) {
2409:                if (dest == null || extender == null) {
2410:                    throw new IllegalArgumentException(JaiI18N
2411:                            .getString("Generic0"));
2412:                }
2413:
2414:                // If the Raster is within the image just copy directly.
2415:                Rectangle destBounds = dest.getBounds();
2416:                Rectangle imageBounds = getBounds();
2417:                if (imageBounds.contains(destBounds)) {
2418:                    copyData(dest);
2419:                    return;
2420:                }
2421:
2422:                // Get the intersection of the Raster and image bounds.
2423:                Rectangle isect = imageBounds.intersection(destBounds);
2424:
2425:                if (!isect.isEmpty()) {
2426:                    // Copy image data into the dest Raster.
2427:                    WritableRaster isectRaster = dest.createWritableChild(
2428:                            isect.x, isect.y, isect.width, isect.height,
2429:                            isect.x, isect.y, null);
2430:                    copyData(isectRaster);
2431:                }
2432:
2433:                // Extend the Raster.
2434:                extender.extend(dest, this );
2435:            }
2436:
2437:            /**
2438:             * Returns a copy of an arbitrary rectangular region of this image
2439:             * in a <code>Raster</code>.  The portion of the rectangle of
2440:             * interest ouside the bounds of the image will be computed by
2441:             * calling the given <code>BorderExtender</code>.  If the region
2442:             * falls entirely within the image, <code>extender</code> will not
2443:             * be used in any way.  Thus it is possible to use a
2444:             * <code>null</code> value for <code>extender</code> when it is
2445:             * known that no actual extension will be required.
2446:             *
2447:             * <p> The returned <code>Raster</code> should be considered
2448:             * non-writable; any attempt to alter its pixel data (such as by
2449:             * casting it to a <code>WritableRaster</code> or obtaining and
2450:             * modifying its <code>DataBuffer</code>) may result in undefined
2451:             * behavior. The <code>copyExtendedData</code> method should be
2452:             * used if the returned <code>Raster</code> is to be modified.
2453:             *
2454:             * @param region the region of the image to be returned.
2455:             * @param extender an instance of <code>BorderExtender</code>,
2456:             *        used only if the region exceeds the image bounds,
2457:             *        or <code>null</code>.
2458:             * @return a <code>Raster</code> containing the extended data.
2459:             *
2460:             * @throws IllegalArgumentException  If <code>region</code> is
2461:             *         <code>null</code>.
2462:             * @throws IllegalArgumentException  If the region exceeds the image
2463:             *         bounds and <code>extender</code> is <code>null</code>.
2464:             */
2465:            public Raster getExtendedData(Rectangle region,
2466:                    BorderExtender extender) {
2467:                if (region == null) {
2468:                    throw new IllegalArgumentException(JaiI18N
2469:                            .getString("Generic0"));
2470:                }
2471:
2472:                if (getBounds().contains(region)) {
2473:                    return getData(region);
2474:                }
2475:
2476:                if (extender == null) {
2477:                    throw new IllegalArgumentException(JaiI18N
2478:                            .getString("Generic0"));
2479:                }
2480:
2481:                // Create a WritableRaster of the desired size
2482:                SampleModel destSM = getSampleModel();
2483:                if (destSM.getWidth() != region.width
2484:                        || destSM.getHeight() != region.height) {
2485:                    destSM = destSM.createCompatibleSampleModel(region.width,
2486:                            region.height);
2487:                }
2488:
2489:                // Translate it
2490:                WritableRaster dest = createWritableRaster(destSM, region
2491:                        .getLocation());
2492:
2493:                copyExtendedData(dest, extender);
2494:                return dest;
2495:            }
2496:
2497:            /**
2498:             * Returns a copy of this image as a <code>BufferedImage</code>.
2499:             * A subarea of the image may be copied by supplying a
2500:             * <code>Rectangle</code> parameter; if it is set to
2501:             * <code>null</code>, the entire image is copied.  The supplied
2502:             * Rectangle will be clipped to the image bounds.  The image's
2503:             * <code>ColorModel</code> may be overridden by supplying a
2504:             * non-<code>null</code> second argument.  The resulting
2505:             * <code>ColorModel</code> must be non-<code>null</code> and
2506:             * appropriate for the image's <code>SampleModel</code>.
2507:             *
2508:             * <p> The resulting <code>BufferedImage</code> will contain the
2509:             * full requested area, but will always have its top-left corner
2510:             * translated (0, 0) as required by the <code>BufferedImage</code>
2511:             * interface.
2512:             *
2513:             * @param rect  The <code>Rectangle</code> of the image to be
2514:             *              copied, or <code>null</code> to indicate that the
2515:             *              entire image is to be copied.
2516:             *
2517:             * @param cm  A <code>ColorModel</code> used to override
2518:             *        this image's <code>ColorModel</code>, or <code>null</code>.
2519:             *        The caller is responsible for supplying a
2520:             *        <code>ColorModel</code> that is compatible with the image's
2521:             *        <code>SampleModel</code>.
2522:             *
2523:             * @throws IllegalArgumentException  If an incompatible, non-null
2524:             *         <code>ColorModel</code> is supplied.
2525:             * @throws IllegalArgumentException  If no <code>ColorModel</code> is
2526:             *         supplied, and the image <code>ColorModel</code> is
2527:             *         <code>null</code>.
2528:             */
2529:            public BufferedImage getAsBufferedImage(Rectangle rect,
2530:                    ColorModel cm) {
2531:                if (cm == null) {
2532:                    cm = getColorModel();
2533:                    if (cm == null) {
2534:                        throw new IllegalArgumentException(JaiI18N
2535:                                .getString("PlanarImage6"));
2536:                    }
2537:                }
2538:
2539:                if (!JDKWorkarounds.areCompatibleDataModels(sampleModel, cm)) {
2540:                    throw new IllegalArgumentException(JaiI18N
2541:                            .getString("PlanarImage3"));
2542:                }
2543:
2544:                if (rect == null) {
2545:                    rect = getBounds();
2546:                } else {
2547:                    rect = getBounds().intersection(rect);
2548:                }
2549:
2550:                SampleModel sm = sampleModel.getWidth() != rect.width
2551:                        || sampleModel.getHeight() != rect.height ? sampleModel
2552:                        .createCompatibleSampleModel(rect.width, rect.height)
2553:                        : sampleModel;
2554:
2555:                WritableRaster ras = createWritableRaster(sm, rect
2556:                        .getLocation());
2557:                copyData(ras);
2558:
2559:                if (rect.x != 0 || rect.y != 0) {
2560:                    // Move Raster to (0, 0)
2561:                    ras = RasterFactory.createWritableChild(ras, rect.x,
2562:                            rect.y, rect.width, rect.height, 0, 0, null);
2563:                }
2564:
2565:                return new BufferedImage(cm, ras, cm.isAlphaPremultiplied(),
2566:                        null);
2567:            }
2568:
2569:            /**
2570:             * Returns a copy of the entire image as a
2571:             * <code>BufferedImage</code>.  The image's
2572:             * <code>ColorModel</code> must be non-<code>null</code>, and
2573:             * appropriate for the image's <code>SampleModel</code>.
2574:             *
2575:             * @see java.awt.image.BufferedImage
2576:             */
2577:            public BufferedImage getAsBufferedImage() {
2578:                return getAsBufferedImage(null, null);
2579:            }
2580:
2581:            /**
2582:             * Returns a <code>Graphics</code> object that may be used to draw
2583:             * into this image.  By default, an
2584:             * <code>IllegalAccessError</code> is thrown.  Subclasses that
2585:             * support such drawing, such as <code>TiledImage</code>, may
2586:             * override this method to return a suitable <code>Graphics</code>
2587:             * object.
2588:             */
2589:            public Graphics getGraphics() {
2590:                throw new IllegalAccessError(JaiI18N.getString("PlanarImage1"));
2591:            }
2592:
2593:            /**
2594:             * Returns tile (<code>tileX</code>, <code>tileY</code>) as a
2595:             * <code>Raster</code>.  Note that <code>tileX</code> and
2596:             * <code>tileY</code> are indices into the tile array, not pixel
2597:             * locations.
2598:             *
2599:             * <p> Subclasses must override this method to return a
2600:             * non-<code>null</code> value for all tile indices between
2601:             * <code>getMinTile{X,Y}</code> and <code>getMaxTile{X,Y}</code>,
2602:             * inclusive.  Tile indices outside of this region should result
2603:             * in a return value of <code>null</code>.
2604:             *
2605:             * @param tileX  The X index of the requested tile in the tile array.
2606:             * @param tileY  The Y index of the requested tile in the tile array.
2607:             */
2608:            public abstract Raster getTile(int tileX, int tileY);
2609:
2610:            /**
2611:             * Returns the <code>Raster</code>s indicated by the
2612:             * <code>tileIndices</code> array.  This call allows certain
2613:             * <code>PlanarImage</code> subclasses such as
2614:             * <code>OpImage</code> to take advantage of the knowledge that
2615:             * multiple tiles are requested at once.
2616:             *
2617:             * @param tileIndices  An array of Points representing tile indices.
2618:             *
2619:             * @return An array of <code>Raster</code> containing the tiles
2620:             *         corresponding to the given tile indices.
2621:             *
2622:             * @throws IllegalArgumentException  If <code>tileIndices</code> is
2623:             *         <code>null</code>.
2624:             */
2625:            public Raster[] getTiles(Point[] tileIndices) {
2626:                if (tileIndices == null) {
2627:                    throw new IllegalArgumentException(JaiI18N
2628:                            .getString("Generic0"));
2629:                }
2630:
2631:                int size = tileIndices.length;
2632:                Raster tiles[] = new Raster[size];
2633:
2634:                for (int i = 0; i < tileIndices.length; i++) {
2635:                    Point p = tileIndices[i];
2636:                    tiles[i] = getTile(p.x, p.y);
2637:                }
2638:
2639:                return tiles;
2640:            }
2641:
2642:            /**
2643:             * Computes and returns all tiles in the image.  The tiles are returned
2644:             * in a sequence corresponding to the row-major order of their respective
2645:             * tile indices.  The returned array may of course be ignored, e.g., in
2646:             * the case of a subclass which caches the tiles and the intent is to
2647:             * force their computation.
2648:             */
2649:            public Raster[] getTiles() {
2650:                return getTiles(getTileIndices(getBounds()));
2651:            }
2652:
2653:            /**
2654:             * Queues a list of tiles for computation.  Registered listeners
2655:             * will be notified after each tile has been computed.
2656:             *
2657:             * <p> The <code>TileScheduler</code> of the default instance of the
2658:             * <code>JAI</code> class is used to process the tiles.  If this
2659:             * <code>TileScheduler</code> has a positive parallelism this
2660:             * method will be non-blocking.  The event source parameter passed to
2661:             * such listeners will be the <code>TileScheduler</code> and the image
2662:             * parameter will be this image.
2663:             *
2664:             * @param tileIndices A list of tile indices indicating which tiles
2665:             *        to schedule for computation.
2666:             * @throws IllegalArgumentException  If <code>tileIndices</code> is
2667:             *         <code>null</code>.
2668:             *
2669:             * @since JAI 1.1
2670:             */
2671:            public TileRequest queueTiles(Point[] tileIndices) {
2672:                if (tileIndices == null) {
2673:                    throw new IllegalArgumentException(JaiI18N
2674:                            .getString("Generic0"));
2675:                }
2676:
2677:                TileComputationListener[] listeners = getTileComputationListeners();
2678:                return JAI.getDefaultInstance().getTileScheduler()
2679:                        .scheduleTiles(this , tileIndices, listeners);
2680:            }
2681:
2682:            /**
2683:             * Issue an advisory cancellation request to nullify processing of
2684:             * the indicated tiles.  It is legal to implement this method as a no-op.
2685:             *
2686:             * <p> The cancellation request is forwarded to the
2687:             * <code>TileScheduler</code> of the default instance of the
2688:             * <code>JAI</code> class.
2689:             *
2690:             * @param request The request for which tiles are to be cancelled.
2691:             * @param tileIndices The tiles to be cancelled; may be <code>null</code>.
2692:             *        Any tiles not actually in the <code>TileRequest</code> will be
2693:             *        ignored.
2694:             * @throws IllegalArgumentException  If <code>request</code> is
2695:             *         <code>null</code>.
2696:             *
2697:             * @since JAI 1.1
2698:             */
2699:            public void cancelTiles(TileRequest request, Point[] tileIndices) {
2700:                if (request == null) {
2701:                    throw new IllegalArgumentException(JaiI18N
2702:                            .getString("Generic4"));
2703:                }
2704:
2705:                JAI.getDefaultInstance().getTileScheduler().cancelTiles(
2706:                        request, tileIndices);
2707:            }
2708:
2709:            /**
2710:             * Hints that the given tiles might be needed in the near future.
2711:             * Some implementations may spawn a thread or threads
2712:             * to compute the tiles while others may ignore the hint.
2713:             *
2714:             * <p> The <code>TileScheduler</code> of the default instance of the
2715:             * <code>JAI</code> class is used to prefetch the tiles.  If this
2716:             * <code>TileScheduler</code> has a positive prefetch parallelism
2717:             * this method will be non-blocking.
2718:             *
2719:             * @param tileIndices A list of tile indices indicating which tiles
2720:             *        to prefetch.
2721:             *
2722:             * @throws IllegalArgumentException  If <code>tileIndices</code> is
2723:             *         <code>null</code>.
2724:             */
2725:            public void prefetchTiles(Point[] tileIndices) {
2726:                if (tileIndices == null) {
2727:                    throw new IllegalArgumentException(JaiI18N
2728:                            .getString("Generic0"));
2729:                }
2730:
2731:                JAI.getDefaultInstance().getTileScheduler().prefetchTiles(this ,
2732:                        tileIndices);
2733:            }
2734:
2735:            /**
2736:             * Provides a hint that an image will no longer be accessed from a
2737:             * reference in user space.  The results are equivalent to those
2738:             * that occur when the program loses its last reference to this
2739:             * image, the garbage collector discovers this, and finalize is
2740:             * called.  This can be used as a hint in situations where waiting
2741:             * for garbage collection would be overly conservative.
2742:             *
2743:             * <p> <code>PlanarImage</code> defines this method to remove the
2744:             * image being disposed from the list of sinks in all of its
2745:             * source images.  Subclasses should call
2746:             * <code>super.dispose()</code> in their <code>dispose</code>
2747:             * methods, if any.
2748:             *
2749:             * <p> The results of referencing an image after a call to
2750:             * <code>dispose()</code> are undefined.
2751:             */
2752:            public synchronized void dispose() {
2753:                // Do nothing if dispose() has been called previously
2754:                if (disposed) {
2755:                    return;
2756:                }
2757:                disposed = true;
2758:
2759:                // Retrieve the sources as a Vector rather than using getSource()
2760:                // to enable compatibility with subclasses which may have sources
2761:                // which are not PlanarImages, e.g., as in RenderedOp.
2762:                Vector srcs = getSources();
2763:                if (srcs != null) {
2764:                    int numSources = srcs.size();
2765:                    for (int i = 0; i < numSources; i++) {
2766:                        Object src = srcs.get(i);
2767:                        if (src instanceof  PlanarImage) {
2768:                            ((PlanarImage) src).removeSink(this );
2769:                        }
2770:                    }
2771:                }
2772:            }
2773:
2774:            /**
2775:             * Performs cleanup prior to garbage collection.
2776:             *
2777:             * <p> <code>PlanarImage</code> defines this method to invoke
2778:             * the <code>dispose()</code> method.</p>
2779:             *
2780:             * @exception <code>Throwable</code> if an error occurs in the
2781:             *            garbage collector.
2782:             */
2783:            protected void finalize() throws Throwable {
2784:                dispose();
2785:            }
2786:
2787:            /** For debugging. */
2788:            private void printBounds() {
2789:                System.out.println("Bounds: [x=" + getMinX() + ", y="
2790:                        + getMinY() + ", width=" + getWidth() + ", height="
2791:                        + getHeight() + "]");
2792:            }
2793:
2794:            /** For debugging. */
2795:            private void printTile(int i, int j) {
2796:                int xmin = i * getTileWidth() + getTileGridXOffset();
2797:                int ymin = j * getTileHeight() + getTileGridYOffset();
2798:
2799:                Rectangle imageBounds = getBounds();
2800:                Rectangle tileBounds = new Rectangle(xmin, ymin,
2801:                        getTileWidth(), getTileHeight());
2802:                tileBounds = tileBounds.intersection(imageBounds);
2803:
2804:                Raster tile = getTile(i, j);
2805:
2806:                Rectangle realTileBounds = new Rectangle(tile.getMinX(), tile
2807:                        .getMinY(), tile.getWidth(), tile.getHeight());
2808:                System.out
2809:                        .println("Tile bounds (actual)   = " + realTileBounds);
2810:                System.out.println("Tile bounds (computed) = " + tileBounds);
2811:
2812:                xmin = tileBounds.x;
2813:                ymin = tileBounds.y;
2814:                int xmax = tileBounds.x + tileBounds.width - 1;
2815:                int ymax = tileBounds.y + tileBounds.height - 1;
2816:                int numBands = getSampleModel().getNumBands();
2817:                int[] val = new int[numBands];
2818:                int pi, pj;
2819:
2820:                for (pj = ymin; pj <= ymax; pj++) {
2821:                    for (pi = xmin; pi <= xmax; pi++) {
2822:                        tile.getPixel(pi, pj, val);
2823:                        if (numBands == 1) {
2824:                            System.out.print("(" + val[0] + ") ");
2825:                        } else if (numBands == 3) {
2826:                            System.out.print("(" + val[0] + "," + val[1] + ","
2827:                                    + val[2] + ") ");
2828:                        }
2829:                    }
2830:                    System.out.println();
2831:                }
2832:            }
2833:
2834:            /**
2835:             * Returns a <code>String</code> which includes the basic information
2836:             * of this image.
2837:             *
2838:             * @since JAI 1.1
2839:             */
2840:            public String toString() {
2841:                return "PlanarImage[" + "minX=" + minX + " minY=" + minY
2842:                        + " width=" + width + " height=" + height
2843:                        + " tileGridXOffset=" + tileGridXOffset
2844:                        + " tileGridYOffset=" + tileGridYOffset + " tileWidth="
2845:                        + tileWidth + " tileHeight=" + tileHeight
2846:                        + " sampleModel=" + sampleModel + " colorModel="
2847:                        + colorModel + "]";
2848:            }
2849:
2850:            private void cobbleByte(Rectangle bounds, Raster dstRaster) {
2851:
2852:                ComponentSampleModel dstSM = (ComponentSampleModel) dstRaster
2853:                        .getSampleModel();
2854:
2855:                int startX = XToTileX(bounds.x);
2856:                int startY = YToTileY(bounds.y);
2857:                int rectXend = bounds.x + bounds.width - 1;
2858:                int rectYend = bounds.y + bounds.height - 1;
2859:                int endX = XToTileX(rectXend);
2860:                int endY = YToTileY(rectYend);
2861:
2862:                //
2863:                //  Get parameters of destination raster
2864:                //
2865:                DataBufferByte dstDB = (DataBufferByte) dstRaster
2866:                        .getDataBuffer();
2867:                byte[] dst = dstDB.getData();
2868:                int dstPS = dstSM.getPixelStride();
2869:                int dstSS = dstSM.getScanlineStride();
2870:
2871:                boolean tileParamsSet = false;
2872:                ComponentSampleModel srcSM = null;
2873:                int srcPS = 0, srcSS = 0;
2874:                int xOrg, yOrg;
2875:                int srcX1, srcY1, srcX2, srcY2, srcW, srcH;
2876:
2877:                for (int y = startY; y <= endY; y++) {
2878:                    for (int x = startX; x <= endX; x++) {
2879:                        Raster tile = getTile(x, y);
2880:                        if (tile == null) {
2881:                            //
2882:                            // Out-of-bounds tile. Zero fill will be supplied
2883:                            // since dstRaster is initialized to zero
2884:                            //
2885:                            continue;
2886:                        }
2887:
2888:                        if (!tileParamsSet) {
2889:                            //
2890:                            // These are constant for all tiles,
2891:                            // so only set them once.
2892:                            //
2893:                            srcSM = (ComponentSampleModel) tile
2894:                                    .getSampleModel();
2895:                            srcPS = srcSM.getPixelStride();
2896:                            srcSS = srcSM.getScanlineStride();
2897:                            tileParamsSet = true;
2898:                        }
2899:
2900:                        //
2901:                        //  Intersect the tile and the rectangle
2902:                        //  Avoid use of Math.min/max
2903:                        //
2904:                        yOrg = y * tileHeight + tileGridYOffset;
2905:                        srcY1 = yOrg;
2906:                        srcY2 = srcY1 + tileHeight - 1;
2907:                        if (bounds.y > srcY1)
2908:                            srcY1 = bounds.y;
2909:                        if (rectYend < srcY2)
2910:                            srcY2 = rectYend;
2911:                        srcH = srcY2 - srcY1 + 1;
2912:
2913:                        xOrg = x * tileWidth + tileGridXOffset;
2914:                        srcX1 = xOrg;
2915:                        srcX2 = srcX1 + tileWidth - 1;
2916:                        if (bounds.x > srcX1)
2917:                            srcX1 = bounds.x;
2918:                        if (rectXend < srcX2)
2919:                            srcX2 = rectXend;
2920:                        srcW = srcX2 - srcX1 + 1;
2921:
2922:                        int dstX = srcX1 - bounds.x;
2923:                        int dstY = srcY1 - bounds.y;
2924:
2925:                        // Get the actual data array
2926:                        DataBufferByte srcDB = (DataBufferByte) tile
2927:                                .getDataBuffer();
2928:                        byte[] src = srcDB.getData();
2929:
2930:                        int nsamps = srcW * srcPS;
2931:                        boolean useArrayCopy = (nsamps >= MIN_ARRAYCOPY_SIZE);
2932:
2933:                        int ySrcIdx = (srcY1 - yOrg) * srcSS + (srcX1 - xOrg)
2934:                                * srcPS;
2935:                        int yDstIdx = dstY * dstSS + dstX * dstPS;
2936:                        if (useArrayCopy) {
2937:                            for (int row = 0; row < srcH; row++) {
2938:                                System.arraycopy(src, ySrcIdx, dst, yDstIdx,
2939:                                        nsamps);
2940:                                ySrcIdx += srcSS;
2941:                                yDstIdx += dstSS;
2942:                            }
2943:                        } else {
2944:                            for (int row = 0; row < srcH; row++) {
2945:                                int xSrcIdx = ySrcIdx;
2946:                                int xDstIdx = yDstIdx;
2947:                                int xEnd = xDstIdx + nsamps;
2948:                                while (xDstIdx < xEnd) {
2949:                                    dst[xDstIdx++] = src[xSrcIdx++];
2950:                                }
2951:                                ySrcIdx += srcSS;
2952:                                yDstIdx += dstSS;
2953:                            }
2954:                        }
2955:                    }
2956:                }
2957:            }
2958:
2959:            private void cobbleShort(Rectangle bounds, Raster dstRaster) {
2960:
2961:                ComponentSampleModel dstSM = (ComponentSampleModel) dstRaster
2962:                        .getSampleModel();
2963:
2964:                int startX = XToTileX(bounds.x);
2965:                int startY = YToTileY(bounds.y);
2966:                int rectXend = bounds.x + bounds.width - 1;
2967:                int rectYend = bounds.y + bounds.height - 1;
2968:                int endX = XToTileX(rectXend);
2969:                int endY = YToTileY(rectYend);
2970:
2971:                //
2972:                //  Get parameters of destination raster
2973:                //
2974:                DataBufferShort dstDB = (DataBufferShort) dstRaster
2975:                        .getDataBuffer();
2976:                short[] dst = dstDB.getData();
2977:                int dstPS = dstSM.getPixelStride();
2978:                int dstSS = dstSM.getScanlineStride();
2979:
2980:                boolean tileParamsSet = false;
2981:                ComponentSampleModel srcSM = null;
2982:                int srcPS = 0, srcSS = 0;
2983:                int xOrg, yOrg;
2984:                int srcX1, srcY1, srcX2, srcY2, srcW, srcH;
2985:
2986:                for (int y = startY; y <= endY; y++) {
2987:                    for (int x = startX; x <= endX; x++) {
2988:                        Raster tile = getTile(x, y);
2989:                        if (tile == null) {
2990:                            //
2991:                            // Out-of-bounds tile. Zero fill will be supplied
2992:                            // since dstRaster is initialized to zero
2993:                            //
2994:                            continue;
2995:                        }
2996:
2997:                        if (!tileParamsSet) {
2998:                            //
2999:                            // These are constant for all tiles,
3000:                            // so only set them once.
3001:                            //
3002:                            srcSM = (ComponentSampleModel) tile
3003:                                    .getSampleModel();
3004:                            srcPS = srcSM.getPixelStride();
3005:                            srcSS = srcSM.getScanlineStride();
3006:                            tileParamsSet = true;
3007:                        }
3008:
3009:                        //
3010:                        //  Intersect the tile and the rectangle
3011:                        //  Avoid use of Math.min/max
3012:                        //
3013:                        yOrg = y * tileHeight + tileGridYOffset;
3014:                        srcY1 = yOrg;
3015:                        srcY2 = srcY1 + tileHeight - 1;
3016:                        if (bounds.y > srcY1)
3017:                            srcY1 = bounds.y;
3018:                        if (rectYend < srcY2)
3019:                            srcY2 = rectYend;
3020:                        srcH = srcY2 - srcY1 + 1;
3021:
3022:                        xOrg = x * tileWidth + tileGridXOffset;
3023:                        srcX1 = xOrg;
3024:                        srcX2 = srcX1 + tileWidth - 1;
3025:                        if (bounds.x > srcX1)
3026:                            srcX1 = bounds.x;
3027:                        if (rectXend < srcX2)
3028:                            srcX2 = rectXend;
3029:                        srcW = srcX2 - srcX1 + 1;
3030:
3031:                        int dstX = srcX1 - bounds.x;
3032:                        int dstY = srcY1 - bounds.y;
3033:
3034:                        // Get the actual data array
3035:                        DataBufferShort srcDB = (DataBufferShort) tile
3036:                                .getDataBuffer();
3037:                        short[] src = srcDB.getData();
3038:
3039:                        int nsamps = srcW * srcPS;
3040:                        boolean useArrayCopy = (nsamps >= MIN_ARRAYCOPY_SIZE);
3041:
3042:                        int ySrcIdx = (srcY1 - yOrg) * srcSS + (srcX1 - xOrg)
3043:                                * srcPS;
3044:                        int yDstIdx = dstY * dstSS + dstX * dstPS;
3045:                        if (useArrayCopy) {
3046:                            for (int row = 0; row < srcH; row++) {
3047:                                System.arraycopy(src, ySrcIdx, dst, yDstIdx,
3048:                                        nsamps);
3049:                                ySrcIdx += srcSS;
3050:                                yDstIdx += dstSS;
3051:                            }
3052:                        } else {
3053:                            for (int row = 0; row < srcH; row++) {
3054:                                int xSrcIdx = ySrcIdx;
3055:                                int xDstIdx = yDstIdx;
3056:                                int xEnd = xDstIdx + nsamps;
3057:                                while (xDstIdx < xEnd) {
3058:                                    dst[xDstIdx++] = src[xSrcIdx++];
3059:                                }
3060:                                ySrcIdx += srcSS;
3061:                                yDstIdx += dstSS;
3062:                            }
3063:                        }
3064:                    }
3065:                }
3066:            }
3067:
3068:            private void cobbleUShort(Rectangle bounds, Raster dstRaster) {
3069:
3070:                ComponentSampleModel dstSM = (ComponentSampleModel) dstRaster
3071:                        .getSampleModel();
3072:
3073:                int startX = XToTileX(bounds.x);
3074:                int startY = YToTileY(bounds.y);
3075:                int rectXend = bounds.x + bounds.width - 1;
3076:                int rectYend = bounds.y + bounds.height - 1;
3077:                int endX = XToTileX(rectXend);
3078:                int endY = YToTileY(rectYend);
3079:
3080:                //
3081:                //  Get parameters of destination raster
3082:                //
3083:                DataBufferUShort dstDB = (DataBufferUShort) dstRaster
3084:                        .getDataBuffer();
3085:                short[] dst = dstDB.getData();
3086:                int dstPS = dstSM.getPixelStride();
3087:                int dstSS = dstSM.getScanlineStride();
3088:
3089:                boolean tileParamsSet = false;
3090:                ComponentSampleModel srcSM = null;
3091:                int srcPS = 0, srcSS = 0;
3092:                int xOrg, yOrg;
3093:                int srcX1, srcY1, srcX2, srcY2, srcW, srcH;
3094:
3095:                for (int y = startY; y <= endY; y++) {
3096:                    for (int x = startX; x <= endX; x++) {
3097:                        Raster tile = getTile(x, y);
3098:                        if (tile == null) {
3099:                            //
3100:                            // Out-of-bounds tile. Zero fill will be supplied
3101:                            // since dstRaster is initialized to zero
3102:                            //
3103:                            continue;
3104:                        }
3105:
3106:                        if (!tileParamsSet) {
3107:                            //
3108:                            // These are constant for all tiles,
3109:                            // so only set them once.
3110:                            //
3111:                            srcSM = (ComponentSampleModel) tile
3112:                                    .getSampleModel();
3113:                            srcPS = srcSM.getPixelStride();
3114:                            srcSS = srcSM.getScanlineStride();
3115:                            tileParamsSet = true;
3116:                        }
3117:
3118:                        //
3119:                        //  Intersect the tile and the rectangle
3120:                        //  Avoid use of Math.min/max
3121:                        //
3122:                        yOrg = y * tileHeight + tileGridYOffset;
3123:                        srcY1 = yOrg;
3124:                        srcY2 = srcY1 + tileHeight - 1;
3125:                        if (bounds.y > srcY1)
3126:                            srcY1 = bounds.y;
3127:                        if (rectYend < srcY2)
3128:                            srcY2 = rectYend;
3129:                        srcH = srcY2 - srcY1 + 1;
3130:
3131:                        xOrg = x * tileWidth + tileGridXOffset;
3132:                        srcX1 = xOrg;
3133:                        srcX2 = srcX1 + tileWidth - 1;
3134:                        if (bounds.x > srcX1)
3135:                            srcX1 = bounds.x;
3136:                        if (rectXend < srcX2)
3137:                            srcX2 = rectXend;
3138:                        srcW = srcX2 - srcX1 + 1;
3139:
3140:                        int dstX = srcX1 - bounds.x;
3141:                        int dstY = srcY1 - bounds.y;
3142:
3143:                        // Get the actual data array
3144:                        DataBufferUShort srcDB = (DataBufferUShort) tile
3145:                                .getDataBuffer();
3146:                        short[] src = srcDB.getData();
3147:
3148:                        int nsamps = srcW * srcPS;
3149:                        boolean useArrayCopy = (nsamps >= MIN_ARRAYCOPY_SIZE);
3150:
3151:                        int ySrcIdx = (srcY1 - yOrg) * srcSS + (srcX1 - xOrg)
3152:                                * srcPS;
3153:                        int yDstIdx = dstY * dstSS + dstX * dstPS;
3154:                        if (useArrayCopy) {
3155:                            for (int row = 0; row < srcH; row++) {
3156:                                System.arraycopy(src, ySrcIdx, dst, yDstIdx,
3157:                                        nsamps);
3158:                                ySrcIdx += srcSS;
3159:                                yDstIdx += dstSS;
3160:                            }
3161:                        } else {
3162:                            for (int row = 0; row < srcH; row++) {
3163:                                int xSrcIdx = ySrcIdx;
3164:                                int xDstIdx = yDstIdx;
3165:                                int xEnd = xDstIdx + nsamps;
3166:                                while (xDstIdx < xEnd) {
3167:                                    dst[xDstIdx++] = src[xSrcIdx++];
3168:                                }
3169:                                ySrcIdx += srcSS;
3170:                                yDstIdx += dstSS;
3171:                            }
3172:                        }
3173:                    }
3174:                }
3175:            }
3176:
3177:            private void cobbleInt(Rectangle bounds, Raster dstRaster) {
3178:
3179:                ComponentSampleModel dstSM = (ComponentSampleModel) dstRaster
3180:                        .getSampleModel();
3181:
3182:                int startX = XToTileX(bounds.x);
3183:                int startY = YToTileY(bounds.y);
3184:                int rectXend = bounds.x + bounds.width - 1;
3185:                int rectYend = bounds.y + bounds.height - 1;
3186:                int endX = XToTileX(rectXend);
3187:                int endY = YToTileY(rectYend);
3188:
3189:                //
3190:                //  Get parameters of destination raster
3191:                //
3192:                DataBufferInt dstDB = (DataBufferInt) dstRaster.getDataBuffer();
3193:                int[] dst = dstDB.getData();
3194:                int dstPS = dstSM.getPixelStride();
3195:                int dstSS = dstSM.getScanlineStride();
3196:
3197:                boolean tileParamsSet = false;
3198:                ComponentSampleModel srcSM = null;
3199:                int srcPS = 0, srcSS = 0;
3200:                int xOrg, yOrg;
3201:                int srcX1, srcY1, srcX2, srcY2, srcW, srcH;
3202:
3203:                for (int y = startY; y <= endY; y++) {
3204:                    for (int x = startX; x <= endX; x++) {
3205:                        Raster tile = getTile(x, y);
3206:                        if (tile == null) {
3207:                            //
3208:                            // Out-of-bounds tile. Zero fill will be supplied
3209:                            // since dstRaster is initialized to zero
3210:                            //
3211:                            continue;
3212:                        }
3213:
3214:                        if (!tileParamsSet) {
3215:                            //
3216:                            // These are constant for all tiles,
3217:                            // so only set them once.
3218:                            //
3219:                            srcSM = (ComponentSampleModel) tile
3220:                                    .getSampleModel();
3221:                            srcPS = srcSM.getPixelStride();
3222:                            srcSS = srcSM.getScanlineStride();
3223:                            tileParamsSet = true;
3224:                        }
3225:
3226:                        //
3227:                        //  Intersect the tile and the rectangle
3228:                        //  Avoid use of Math.min/max
3229:                        //
3230:                        yOrg = y * tileHeight + tileGridYOffset;
3231:                        srcY1 = yOrg;
3232:                        srcY2 = srcY1 + tileHeight - 1;
3233:                        if (bounds.y > srcY1)
3234:                            srcY1 = bounds.y;
3235:                        if (rectYend < srcY2)
3236:                            srcY2 = rectYend;
3237:                        srcH = srcY2 - srcY1 + 1;
3238:
3239:                        xOrg = x * tileWidth + tileGridXOffset;
3240:                        srcX1 = xOrg;
3241:                        srcX2 = srcX1 + tileWidth - 1;
3242:                        if (bounds.x > srcX1)
3243:                            srcX1 = bounds.x;
3244:                        if (rectXend < srcX2)
3245:                            srcX2 = rectXend;
3246:                        srcW = srcX2 - srcX1 + 1;
3247:
3248:                        int dstX = srcX1 - bounds.x;
3249:                        int dstY = srcY1 - bounds.y;
3250:
3251:                        // Get the actual data array
3252:                        DataBufferInt srcDB = (DataBufferInt) tile
3253:                                .getDataBuffer();
3254:                        int[] src = srcDB.getData();
3255:
3256:                        int nsamps = srcW * srcPS;
3257:                        boolean useArrayCopy = (nsamps >= MIN_ARRAYCOPY_SIZE);
3258:
3259:                        int ySrcIdx = (srcY1 - yOrg) * srcSS + (srcX1 - xOrg)
3260:                                * srcPS;
3261:                        int yDstIdx = dstY * dstSS + dstX * dstPS;
3262:                        if (useArrayCopy) {
3263:                            for (int row = 0; row < srcH; row++) {
3264:                                System.arraycopy(src, ySrcIdx, dst, yDstIdx,
3265:                                        nsamps);
3266:                                ySrcIdx += srcSS;
3267:                                yDstIdx += dstSS;
3268:                            }
3269:                        } else {
3270:                            for (int row = 0; row < srcH; row++) {
3271:                                int xSrcIdx = ySrcIdx;
3272:                                int xDstIdx = yDstIdx;
3273:                                int xEnd = xDstIdx + nsamps;
3274:                                while (xDstIdx < xEnd) {
3275:                                    dst[xDstIdx++] = src[xSrcIdx++];
3276:                                }
3277:                                ySrcIdx += srcSS;
3278:                                yDstIdx += dstSS;
3279:                            }
3280:                        }
3281:                    }
3282:                }
3283:            }
3284:
3285:            private void cobbleFloat(Rectangle bounds, Raster dstRaster) {
3286:
3287:                ComponentSampleModel dstSM = (ComponentSampleModel) dstRaster
3288:                        .getSampleModel();
3289:
3290:                int startX = XToTileX(bounds.x);
3291:                int startY = YToTileY(bounds.y);
3292:                int rectXend = bounds.x + bounds.width - 1;
3293:                int rectYend = bounds.y + bounds.height - 1;
3294:                int endX = XToTileX(rectXend);
3295:                int endY = YToTileY(rectYend);
3296:
3297:                //
3298:                //  Get parameters of destination raster
3299:                //
3300:                DataBuffer dstDB = dstRaster.getDataBuffer();
3301:                float[] dst = DataBufferUtils.getDataFloat(dstDB);
3302:                int dstPS = dstSM.getPixelStride();
3303:                int dstSS = dstSM.getScanlineStride();
3304:
3305:                boolean tileParamsSet = false;
3306:                ComponentSampleModel srcSM = null;
3307:                int srcPS = 0, srcSS = 0;
3308:                int xOrg, yOrg;
3309:                int srcX1, srcY1, srcX2, srcY2, srcW, srcH;
3310:
3311:                for (int y = startY; y <= endY; y++) {
3312:                    for (int x = startX; x <= endX; x++) {
3313:                        Raster tile = getTile(x, y);
3314:                        if (tile == null) {
3315:                            //
3316:                            // Out-of-bounds tile. Zero fill will be supplied
3317:                            // since dstRaster is initialized to zero
3318:                            //
3319:                            continue;
3320:                        }
3321:
3322:                        if (!tileParamsSet) {
3323:                            //
3324:                            // These are constant for all tiles,
3325:                            // so only set them once.
3326:                            //
3327:                            srcSM = (ComponentSampleModel) tile
3328:                                    .getSampleModel();
3329:                            srcPS = srcSM.getPixelStride();
3330:                            srcSS = srcSM.getScanlineStride();
3331:                            tileParamsSet = true;
3332:                        }
3333:
3334:                        //
3335:                        //  Intersect the tile and the rectangle
3336:                        //  Avoid use of Math.min/max
3337:                        //
3338:                        yOrg = y * tileHeight + tileGridYOffset;
3339:                        srcY1 = yOrg;
3340:                        srcY2 = srcY1 + tileHeight - 1;
3341:                        if (bounds.y > srcY1)
3342:                            srcY1 = bounds.y;
3343:                        if (rectYend < srcY2)
3344:                            srcY2 = rectYend;
3345:                        srcH = srcY2 - srcY1 + 1;
3346:
3347:                        xOrg = x * tileWidth + tileGridXOffset;
3348:                        srcX1 = xOrg;
3349:                        srcX2 = srcX1 + tileWidth - 1;
3350:                        if (bounds.x > srcX1)
3351:                            srcX1 = bounds.x;
3352:                        if (rectXend < srcX2)
3353:                            srcX2 = rectXend;
3354:                        srcW = srcX2 - srcX1 + 1;
3355:
3356:                        int dstX = srcX1 - bounds.x;
3357:                        int dstY = srcY1 - bounds.y;
3358:
3359:                        // Get the actual data array
3360:                        DataBuffer srcDB = tile.getDataBuffer();
3361:                        float[] src = DataBufferUtils.getDataFloat(srcDB);
3362:
3363:                        int nsamps = srcW * srcPS;
3364:                        boolean useArrayCopy = (nsamps >= MIN_ARRAYCOPY_SIZE);
3365:
3366:                        int ySrcIdx = (srcY1 - yOrg) * srcSS + (srcX1 - xOrg)
3367:                                * srcPS;
3368:                        int yDstIdx = dstY * dstSS + dstX * dstPS;
3369:                        if (useArrayCopy) {
3370:                            for (int row = 0; row < srcH; row++) {
3371:                                System.arraycopy(src, ySrcIdx, dst, yDstIdx,
3372:                                        nsamps);
3373:                                ySrcIdx += srcSS;
3374:                                yDstIdx += dstSS;
3375:                            }
3376:                        } else {
3377:                            for (int row = 0; row < srcH; row++) {
3378:                                int xSrcIdx = ySrcIdx;
3379:                                int xDstIdx = yDstIdx;
3380:                                int xEnd = xDstIdx + nsamps;
3381:                                while (xDstIdx < xEnd) {
3382:                                    dst[xDstIdx++] = src[xSrcIdx++];
3383:                                }
3384:                                ySrcIdx += srcSS;
3385:                                yDstIdx += dstSS;
3386:                            }
3387:                        }
3388:                    }
3389:                }
3390:            }
3391:
3392:            private void cobbleDouble(Rectangle bounds, Raster dstRaster) {
3393:
3394:                ComponentSampleModel dstSM = (ComponentSampleModel) dstRaster
3395:                        .getSampleModel();
3396:
3397:                int startX = XToTileX(bounds.x);
3398:                int startY = YToTileY(bounds.y);
3399:                int rectXend = bounds.x + bounds.width - 1;
3400:                int rectYend = bounds.y + bounds.height - 1;
3401:                int endX = XToTileX(rectXend);
3402:                int endY = YToTileY(rectYend);
3403:
3404:                //
3405:                //  Get parameters of destination raster
3406:                //
3407:                DataBuffer dstDB = dstRaster.getDataBuffer();
3408:                double[] dst = DataBufferUtils.getDataDouble(dstDB);
3409:                int dstPS = dstSM.getPixelStride();
3410:                int dstSS = dstSM.getScanlineStride();
3411:
3412:                boolean tileParamsSet = false;
3413:                ComponentSampleModel srcSM = null;
3414:                int srcPS = 0, srcSS = 0;
3415:                int xOrg, yOrg;
3416:                int srcX1, srcY1, srcX2, srcY2, srcW, srcH;
3417:
3418:                for (int y = startY; y <= endY; y++) {
3419:                    for (int x = startX; x <= endX; x++) {
3420:                        Raster tile = getTile(x, y);
3421:                        if (tile == null) {
3422:                            //
3423:                            // Out-of-bounds tile. Zero fill will be supplied
3424:                            // since dstRaster is initialized to zero
3425:                            //
3426:                            continue;
3427:                        }
3428:
3429:                        if (!tileParamsSet) {
3430:                            //
3431:                            // These are constant for all tiles,
3432:                            // so only set them once.
3433:                            //
3434:                            srcSM = (ComponentSampleModel) tile
3435:                                    .getSampleModel();
3436:                            srcPS = srcSM.getPixelStride();
3437:                            srcSS = srcSM.getScanlineStride();
3438:                            tileParamsSet = true;
3439:                        }
3440:
3441:                        //
3442:                        //  Intersect the tile and the rectangle
3443:                        //  Avoid use of Math.min/max
3444:                        //
3445:                        yOrg = y * tileHeight + tileGridYOffset;
3446:                        srcY1 = yOrg;
3447:                        srcY2 = srcY1 + tileHeight - 1;
3448:                        if (bounds.y > srcY1)
3449:                            srcY1 = bounds.y;
3450:                        if (rectYend < srcY2)
3451:                            srcY2 = rectYend;
3452:                        srcH = srcY2 - srcY1 + 1;
3453:
3454:                        xOrg = x * tileWidth + tileGridXOffset;
3455:                        srcX1 = xOrg;
3456:                        srcX2 = srcX1 + tileWidth - 1;
3457:                        if (bounds.x > srcX1)
3458:                            srcX1 = bounds.x;
3459:                        if (rectXend < srcX2)
3460:                            srcX2 = rectXend;
3461:                        srcW = srcX2 - srcX1 + 1;
3462:
3463:                        int dstX = srcX1 - bounds.x;
3464:                        int dstY = srcY1 - bounds.y;
3465:
3466:                        // Get the actual data array
3467:                        DataBuffer srcDB = tile.getDataBuffer();
3468:                        double[] src = DataBufferUtils.getDataDouble(srcDB);
3469:
3470:                        int nsamps = srcW * srcPS;
3471:                        boolean useArrayCopy = (nsamps >= MIN_ARRAYCOPY_SIZE);
3472:
3473:                        int ySrcIdx = (srcY1 - yOrg) * srcSS + (srcX1 - xOrg)
3474:                                * srcPS;
3475:                        int yDstIdx = dstY * dstSS + dstX * dstPS;
3476:                        if (useArrayCopy) {
3477:                            for (int row = 0; row < srcH; row++) {
3478:                                System.arraycopy(src, ySrcIdx, dst, yDstIdx,
3479:                                        nsamps);
3480:                                ySrcIdx += srcSS;
3481:                                yDstIdx += dstSS;
3482:                            }
3483:                        } else {
3484:                            for (int row = 0; row < srcH; row++) {
3485:                                int xSrcIdx = ySrcIdx;
3486:                                int xDstIdx = yDstIdx;
3487:                                int xEnd = xDstIdx + nsamps;
3488:                                while (xDstIdx < xEnd) {
3489:                                    dst[xDstIdx++] = src[xSrcIdx++];
3490:                                }
3491:                                ySrcIdx += srcSS;
3492:                                yDstIdx += dstSS;
3493:                            }
3494:                        }
3495:                    }
3496:                }
3497:            }
3498:
3499:            /**
3500:             * Returns a unique identifier (UID) for this <code>PlanarImage</code>.
3501:             * This UID may be used when the potential redundancy of the value
3502:             * returned by the <code>hashCode()</code> method is unacceptable.
3503:             * An example of this is in generating a key for storing image tiles
3504:             * in a cache.
3505:             */
3506:            public Object getImageID() {
3507:                return UID;
3508:            }
3509:        }
www.java2java.com | Contact Us
Copyright 2009 - 12 Demo Source and Support. All rights reserved.
All other trademarks are property of their respective owners.