Source Code Cross Referenced for DeferredPlanarImage.java in  » GIS » GeoTools-2.4.1 » org » geotools » image » 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 » GIS » GeoTools 2.4.1 » org.geotools.image 
Source Cross Referenced  Class Diagram Java Document (Java Doc) 


001:        /*
002:         *    GeoTools - OpenSource mapping toolkit
003:         *    http://geotools.org
004:         *    (C) 2003-2006, Geotools Project Managment Committee (PMC)
005:         *    (C) 2003, Institut de Recherche pour le Développement
006:         *
007:         *    This library is free software; you can redistribute it and/or
008:         *    modify it under the terms of the GNU Lesser General Public
009:         *    License as published by the Free Software Foundation; either
010:         *    version 2.1 of the License, or (at your option) any later version.
011:         *
012:         *    This library is distributed in the hope that it will be useful,
013:         *    but WITHOUT ANY WARRANTY; without even the implied warranty of
014:         *    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
015:         *    Lesser General Public License for more details.
016:         */
017:        package org.geotools.image;
018:
019:        // J2SE dependencies
020:        import java.awt.Color;
021:        import java.awt.Point;
022:        import java.awt.image.ColorModel;
023:        import java.awt.image.DataBuffer;
024:        import java.awt.image.DataBufferByte;
025:        import java.awt.image.DataBufferDouble;
026:        import java.awt.image.DataBufferFloat;
027:        import java.awt.image.DataBufferInt;
028:        import java.awt.image.DataBufferShort;
029:        import java.awt.image.DataBufferUShort;
030:        import java.awt.image.IndexColorModel;
031:        import java.awt.image.Raster;
032:        import java.awt.image.RasterFormatException;
033:        import java.awt.image.RenderedImage;
034:        import java.awt.image.SampleModel;
035:        import java.awt.image.TileObserver;
036:        import java.awt.image.WritableRaster;
037:        import java.awt.image.WritableRenderedImage;
038:        import java.util.Arrays;
039:        import java.util.Map;
040:        import java.util.Vector;
041:        import java.util.logging.Level;
042:        import java.util.logging.LogRecord;
043:        import java.util.logging.Logger;
044:
045:        // JAI dependencies
046:        import javax.media.jai.ImageLayout;
047:        import javax.media.jai.PlanarImage;
048:        import javax.media.jai.TileComputationListener;
049:        import javax.media.jai.TileRequest;
050:        import javax.media.jai.TileScheduler;
051:
052:        // Geotools dependencies
053:        import org.geotools.resources.Utilities;
054:        import org.geotools.resources.XArray;
055:        import org.geotools.resources.i18n.Logging;
056:        import org.geotools.resources.i18n.LoggingKeys;
057:        import org.geotools.resources.image.ColorUtilities;
058:        import org.geotools.util.WeakValueHashMap;
059:
060:        /**
061:         * A tiled image to be used by renderer when the actual image may take a while to compute. This
062:         * image wraps an arbitrary {@linkplain RenderedImage rendered image}, which may (or may not) be
063:         * some image expensive to compute. When a tile is requested (through a call to {@link #getTile}
064:         * but the tile is not available in the wrapped image, then this class returns some default
065:         * (usually black) tile and start the real tile computation in a background thread. When the
066:         * actual tile is available, this class fire a {@link TileObserver#tileUpdate tileUpdate} event,
067:         * thus given a chance to a renderer to repaint again this image with the new tiles.
068:         * <p>
069:         * Simple example of use:
070:         *
071:         * <blockquote><pre>
072:         * public class Renderer extends JPanel implements TileObserver {
073:         *     private DeferredPlanarImage image;
074:         *
075:         *     public Renderer(RenderedImage toPaint) {
076:         *         image = new DeferredPlanarImage(toPaint);
077:         *         image.addTileObserver(this);
078:         *     }
079:         *
080:         *     public void tileUpdate(WritableRenderedImage source,
081:         *                            int tileX, int tileY, boolean willBeWritable)
082:         *     {
083:         *         repaint();
084:         *     }
085:         *
086:         *     public void paint(Graphics gr) {
087:         *         ((Graphics2D) gr).drawRenderedImage(image);
088:         *     }
089:         * }
090:         * </pre></blockquote>
091:         *
092:         * @since 2.3
093:         * @source $URL: http://svn.geotools.org/geotools/tags/2.4.1/modules/library/coverage/src/main/java/org/geotools/image/DeferredPlanarImage.java $
094:         * @version $Id: DeferredPlanarImage.java 27862 2007-11-12 19:51:19Z desruisseaux $
095:         * @author Remi Eve
096:         * @author Martin Desruisseaux
097:         */
098:        public final class DeferredPlanarImage extends PlanarImage implements 
099:                WritableRenderedImage, TileObserver, TileComputationListener {
100:            /**
101:             * The logger for information messages.
102:             */
103:            private static final Logger LOGGER = org.geotools.util.logging.Logging
104:                    .getLogger("org.geotools.image");
105:
106:            /**
107:             * The thickness (in pixels) of the box to draw around deferred tiles, or 0 for disabling
108:             * this feature. Current implementation draw a box only for {@link DataBufferByte} with
109:             * only one band.
110:             */
111:            private static final int BOX_THICKNESS = 2;
112:
113:            /**
114:             * An entry in the {@link #buffers} map. Contains a sample model and the sample value
115:             * used for filling the empty {@link DataBuffer} (usually 0, unless the color model had
116:             * a transparent pixel different from 0).
117:             */
118:            private static final class Entry {
119:                /** The sample model. */
120:                public final SampleModel model;
121:                /** The fill value.   */
122:                public final int fill;
123:                /** The box value.    */
124:                public final int box;
125:
126:                /** Constructs a new entry. */
127:                public Entry(final SampleModel model, final int fill,
128:                        final int box) {
129:                    this .model = model;
130:                    this .fill = fill;
131:                    this .box = box;
132:                }
133:
134:                /** Returns a hash code value for this entry. */
135:                public int hashCode() {
136:                    return model.hashCode();
137:                }
138:
139:                /** Compares this entry with the specified object. */
140:                public boolean equals(final Object object) {
141:                    if (object instanceof  Entry) {
142:                        final Entry that = (Entry) object;
143:                        return model.equals(that.model) && fill == that.fill
144:                                && box == that.box;
145:                    }
146:                    return false;
147:                }
148:            }
149:
150:            /**
151:             * Empty {@link DataBuffer} for a set of {@link SampleModel}.
152:             * Will be created only when first needed.
153:             */
154:            private static Map buffers;
155:
156:            /**
157:             * The maximum delay (in milliseconds) to wait for a tile with one million pixels (e.g.
158:             * a 1000&times;1000 tile). The actual {@link #delay} will be shorter if the tiles are
159:             * smaller; for example the delay is four time smaller for a 500&times;500 tile.  When
160:             * a requested tile is not yet available, the {@link #getTile} method will wait for a
161:             * maximum of {@code DELAY} milliseconds in case the tile computation would be very
162:             * fast. Set the delay to 0 in order to disable this feature.
163:             */
164:            private static final int DELAY = 500;
165:
166:            /**
167:             * The delay (in milliseconds) to wait for a tile. When a requested tile is not yet available,
168:             * the {@link #getTile} method will wait for a maximum of {@code delay} milliseconds in
169:             * case the tile computation would be very fast. Set the delay to 0 in order to disable this
170:             * feature.
171:             */
172:            private final int delay;
173:
174:            /**
175:             * The source image.
176:             */
177:            private final PlanarImage image;
178:
179:            /**
180:             * The tile observers, or {@code null} if none.
181:             */
182:            private TileObserver[] observers;
183:
184:            /**
185:             * The {@link TileRequest}s for a given tile.
186:             * Tile index are computed by {@link #getTileIndex}.
187:             * This array will be constructed only when first needed.
188:             */
189:            private transient TileRequest[] requests;
190:
191:            /**
192:             * Tells if a tile is request is waiting.
193:             * Tile index are computed by {@link #getTileIndex}.
194:             * This array will be constructed only when first needed.
195:             */
196:            private transient boolean[] waitings;
197:
198:            /**
199:             * Tells if a tile is in process of being computed.
200:             * Tile index are computed by {@link #getTileIndex}.
201:             * This array will be constructed only when first needed.
202:             */
203:            private transient Raster[] pendings;
204:
205:            /**
206:             * Constructs a new instance of {@code DeferredPlanarImage}.
207:             *
208:             * @param source The source image.
209:             */
210:            public DeferredPlanarImage(final RenderedImage source) {
211:                super (new ImageLayout(source), toVector(source), null);
212:                image = getSourceImage(0);
213:                image.addTileComputationListener(this );
214:                if (image instanceof  WritableRenderedImage) {
215:                    ((WritableRenderedImage) image).addTileObserver(this );
216:                }
217:                delay = (int) Math.min((((long) DELAY)
218:                        * (tileWidth * tileHeight) / 1000000), DELAY);
219:            }
220:
221:            /**
222:             * Wraps the specified image in a vector.
223:             *
224:             * @todo Should be inlined in the constructor if only Sun was to fix RFE #4093999
225:             *       ("Relax constraint on placement of this()/super() call in constructors").
226:             */
227:            private static Vector toVector(final RenderedImage image) {
228:                final Vector vector = new Vector(1);
229:                vector.add(image);
230:                return vector;
231:            }
232:
233:            /**
234:             * Returns the indice in {@link #requests} and {@link #pendings} array for the given tile.
235:             * The {@code x} index varies fastest.
236:             */
237:            private int getTileIndice(final int tileX, final int tileY) {
238:                assert tileX >= getMinTileX() && tileX <= getMaxTileX() : tileX;
239:                assert tileY >= getMinTileY() && tileY <= getMaxTileY() : tileY;
240:                return (tileY - getMinTileY()) * getNumXTiles()
241:                        + (tileX - getMinTileX());
242:            }
243:
244:            /**
245:             * Returns the specified tile, or a default one if the requested tile is not yet available.
246:             * If the requested tile is not immediately available, then an empty tile is returned and
247:             * a notification will be sent later through {@link TileObserver} when the real tile will
248:             * be available.
249:             *
250:             * @param  tileX Tile X index.
251:             * @param  tileY Tile Y index.
252:             * @return The requested tile.
253:             */
254:            public synchronized Raster getTile(final int tileX, final int tileY) {
255:                if (requests == null) {
256:                    requests = new TileRequest[getNumXTiles() * getNumYTiles()];
257:                }
258:                final int tileIndice = getTileIndice(tileX, tileY);
259:                TileRequest request = requests[tileIndice];
260:                if (request == null) {
261:                    request = image.queueTiles(new Point[] { new Point(tileX,
262:                            tileY) });
263:                    requests[tileIndice] = request;
264:                }
265:                switch (request.getTileStatus(tileX, tileY)) {
266:                default: {
267:                    LOGGER.warning("Unknow tile status");
268:                    // Fall through
269:                }
270:                case TileRequest.TILE_STATUS_CANCELLED: // Fall through
271:                case TileRequest.TILE_STATUS_FAILED: // Fall through
272:                case TileRequest.TILE_STATUS_COMPUTED:
273:                    return image.getTile(tileX, tileY);
274:                case TileRequest.TILE_STATUS_PENDING: // Fall through
275:                case TileRequest.TILE_STATUS_PROCESSING:
276:                    break;
277:                }
278:                /*
279:                 * The tile is not yet available. A background thread should be computing it right
280:                 * now. Wait a little bit in case the tile computation is very fast. If we can get
281:                 * the tile in a very short time, it would be more efficient than invoking some
282:                 * 'repaint()' method later.
283:                 */
284:                if (pendings != null) {
285:                    if (pendings[tileIndice] != null) {
286:                        return pendings[tileIndice];
287:                    }
288:                }
289:                if (delay != 0) {
290:                    if (waitings == null) {
291:                        waitings = new boolean[requests.length];
292:                    }
293:                    waitings[tileIndice] = true;
294:                    try {
295:                        wait(delay);
296:                    } catch (InterruptedException exception) {
297:                        // Somebody doesn't want to lets us sleep. Go back to work.
298:                    }
299:                    waitings[tileIndice] = false;
300:                    switch (request.getTileStatus(tileX, tileY)) {
301:                    default:
302:                        return image.getTile(tileX, tileY);
303:                    case TileRequest.TILE_STATUS_PENDING: // Fall through
304:                    case TileRequest.TILE_STATUS_PROCESSING:
305:                        break;
306:                    }
307:                }
308:                /*
309:                 * The tile is not yet available and seems to take a long time to compute.
310:                 * Flag that this tile will need to be repainted later and returns an empty tile.
311:                 */
312:                if (LOGGER.isLoggable(Level.FINER)) {
313:                    final LogRecord record = Logging.format(Level.FINER,
314:                            LoggingKeys.DEFERRED_TILE_PAINTING_$2, new Integer(
315:                                    tileX), new Integer(tileY));
316:                    record.setSourceClassName(DeferredPlanarImage.class
317:                            .getName());
318:                    record.setSourceMethodName("getTile");
319:                    LOGGER.log(record);
320:                }
321:                if (pendings == null) {
322:                    pendings = new Raster[requests.length];
323:                }
324:                final Point origin = new Point(tileXToX(tileX), tileYToY(tileY));
325:                final DataBuffer buffer = getDefaultDataBuffer(sampleModel,
326:                        colorModel);
327:                final Raster raster = Raster.createRaster(sampleModel, buffer,
328:                        origin);
329:                pendings[tileIndice] = raster;
330:                fireTileUpdate(tileX, tileY, true);
331:                return raster;
332:            }
333:
334:            /**
335:             * Returns a databuffer for the specified sample model. If the image use an
336:             * {@link IndexColorModel} and a {@linkplain IndexColorModel#getTransparentPixel
337:             * transparent pixel} is defined, then raster sample values are initilized to
338:             * the transparent pixel.
339:             */
340:            private static synchronized DataBuffer getDefaultDataBuffer(
341:                    final SampleModel sampleModel, final ColorModel colorModel) {
342:                int fill = 0;
343:                int box = 0;
344:                if (colorModel instanceof  IndexColorModel) {
345:                    final IndexColorModel colors = (IndexColorModel) colorModel;
346:                    fill = ColorUtilities.getTransparentPixel(colors);
347:                    if (BOX_THICKNESS > 0
348:                            && Math.min(sampleModel.getWidth(), sampleModel
349:                                    .getHeight()) >= 64) {
350:                        box = ColorUtilities.getColorIndex(colors,
351:                                Color.DARK_GRAY, fill);
352:                    } else {
353:                        // Avoid drawing the box if tiles are too small.
354:                        box = fill;
355:                    }
356:                }
357:                final Entry entry = new Entry(sampleModel, fill, box);
358:                if (buffers == null) {
359:                    buffers = new WeakValueHashMap();
360:                }
361:                DataBuffer buffer = (DataBuffer) buffers.get(entry);
362:                if (buffer != null) {
363:                    return buffer;
364:                }
365:                /*
366:                 * No suitable data buffer existed prior to this call. Create a new one and fill it
367:                 * with the transparent color. Note that no filling is needed if the transparent value
368:                 * is 0, since the data buffer is already initialized to 0.
369:                 */
370:                buffer = sampleModel.createDataBuffer();
371:                if (fill > 0) {
372:                    for (int bank = buffer.getNumBanks(); --bank >= 0;) {
373:                        fill(buffer, bank, fill);
374:                    }
375:                }
376:                /*
377:                 * Draw a box around the tile. This is just a visual clue about tile location.
378:                 * Current implementation draw a box only for type byte with a single band.
379:                 */
380:                if (BOX_THICKNESS > 0 && box != fill) {
381:                    if (sampleModel.getNumBands() == 1) {
382:                        final int width = sampleModel.getWidth();
383:                        int thickness = BOX_THICKNESS;
384:                        int offset = (width + 1) * thickness;
385:                        switch (buffer.getDataType()) {
386:                        case DataBuffer.TYPE_BYTE: {
387:                            final byte[] array = ((DataBufferByte) buffer)
388:                                    .getData(0);
389:                            Arrays.fill(array, 0, offset, (byte) box);
390:                            Arrays.fill(array, array.length - offset,
391:                                    array.length, (byte) box);
392:                            thickness *= 2;
393:                            while ((offset += width) < array.length) {
394:                                Arrays.fill(array, offset - thickness, offset,
395:                                        (byte) box);
396:                            }
397:                            break;
398:                        }
399:                        }
400:                    }
401:                }
402:                buffers.put(entry, buffer);
403:                return buffer;
404:            }
405:
406:            /**
407:             * Sets all values in the specified bank to the specified value.
408:             *
409:             * @param buffer The databuffer in which to set all sample values.
410:             * @param bank   Index of the bank to set.
411:             * @param value  The value.
412:             */
413:            private static void fill(final DataBuffer buffer, final int bank,
414:                    final int value) {
415:                switch (buffer.getDataType()) {
416:                case DataBuffer.TYPE_BYTE:
417:                    Arrays.fill(((DataBufferByte) buffer).getData(bank),
418:                            (byte) value);
419:                    break;
420:                case DataBuffer.TYPE_SHORT:
421:                    Arrays.fill(((DataBufferShort) buffer).getData(bank),
422:                            (short) value);
423:                    break;
424:                case DataBuffer.TYPE_USHORT:
425:                    Arrays.fill(((DataBufferUShort) buffer).getData(bank),
426:                            (short) value);
427:                    break;
428:                case DataBuffer.TYPE_INT:
429:                    Arrays.fill(((DataBufferInt) buffer).getData(bank),
430:                            (int) value);
431:                    break;
432:                case DataBuffer.TYPE_FLOAT:
433:                    Arrays.fill(((DataBufferFloat) buffer).getData(bank),
434:                            (float) value);
435:                    break;
436:                case DataBuffer.TYPE_DOUBLE:
437:                    Arrays.fill(((DataBufferDouble) buffer).getData(bank),
438:                            (double) value);
439:                    break;
440:                default:
441:                    throw new RasterFormatException(String.valueOf(buffer));
442:                }
443:            }
444:
445:            /**
446:             * A tile is about to be updated (it is either about to be grabbed for writing,
447:             * or it is being released from writing).
448:             */
449:            private void fireTileUpdate(final int tileX, final int tileY,
450:                    final boolean willBeWritable) {
451:                final TileObserver[] observers = this .observers; // Avoid the need for synchronisation.
452:                if (observers != null) {
453:                    final int length = observers.length;
454:                    for (int i = 0; i < length; i++) {
455:                        try {
456:                            observers[i].tileUpdate(this , tileX, tileY,
457:                                    willBeWritable);
458:                        } catch (RuntimeException cause) {
459:                            /*
460:                             * An exception occured in the user code. Unfortunatly, we are probably not in
461:                             * the mean user thread (e.g. the Swing thread).  This method is often invoked
462:                             * from some JAI's worker thread, which we don't want to corrupt. Log a warning
463:                             * for the user and lets the JAI's worker thread continue its work.
464:                             */
465:                            String message = cause.getLocalizedMessage();
466:                            if (message == null) {
467:                                message = Utilities.getShortClassName(cause);
468:                            }
469:                            final LogRecord record = new LogRecord(
470:                                    Level.WARNING, message);
471:                            record.setSourceClassName(observers[i].getClass()
472:                                    .getName());
473:                            record.setSourceMethodName("tileUpdate");
474:                            record.setThrown(cause);
475:                            LOGGER.log(record);
476:                        }
477:                    }
478:                }
479:            }
480:
481:            /**
482:             * Invoked when a tile has been computed.
483:             *
484:             * @param eventSource The caller of this method.
485:             * @param requests    The relevant tile computation requests as returned by the method
486:             *                    used to queue the tile.
487:             * @param image       The image for which tiles are being computed as specified to the
488:             *                    {@link TileScheduler}.
489:             * @param tileX       The X index of the tile in the tile array.
490:             * @param tileY       The Y index of the tile in the tile array.
491:             * @param tile        The computed tile.
492:             */
493:            public void tileComputed(final Object eventSource,
494:                    final TileRequest[] requests, final PlanarImage image,
495:                    final int tileX, final int tileY, final Raster tile) {
496:                synchronized (this ) {
497:                    final int tileIndice = getTileIndice(tileX, tileY);
498:                    if (waitings != null && waitings[tileIndice]) {
499:                        /*
500:                         * Notify the 'getTile(...)' method in only ONE thread that a tile is available.
501:                         * If tiles computation occurs in two or more background thread, then there is no
502:                         * garantee that the notified thread is really the one waiting for this particular
503:                         * tile. However, this is not a damageable problem; the delay hint may just not be
504:                         * accuratly respected (the actual delay may be shorter for wrongly notified tile).
505:                         */
506:                        notify();
507:                    }
508:                    if (pendings == null || pendings[tileIndice] == null) {
509:                        return;
510:                    }
511:                    pendings[tileIndice] = null;
512:                }
513:                fireTileUpdate(tileX, tileY, false);
514:            }
515:
516:            /**
517:             * Invoked when a tile computation has been cancelled. The default implementation does nothing.
518:             */
519:            public void tileCancelled(final Object eventSource,
520:                    final TileRequest[] requests, final PlanarImage image,
521:                    final int tileX, final int tileY) {
522:            }
523:
524:            /**
525:             * Invoked when a tile computation failed. Default implementation log a warning and lets the
526:             * program continue as usual. We are not throwing an exception since this failure will alter
527:             * the visual rendering, but will not otherwise harm the system.
528:             */
529:            public void tileComputationFailure(final Object eventSource,
530:                    final TileRequest[] requests, final PlanarImage image,
531:                    final int tileX, final int tileY, final Throwable cause) {
532:                final LogRecord record = new LogRecord(Level.WARNING, cause
533:                        .getLocalizedMessage());
534:                record.setSourceClassName(DeferredPlanarImage.class.getName());
535:                record.setSourceMethodName("getTile");
536:                record.setThrown(cause);
537:                LOGGER.log(record);
538:            }
539:
540:            /**
541:             * Invoked if the underlying image is writable and one of its tile changed.
542:             * This method forward the call to every registered listener.
543:             */
544:            public void tileUpdate(final WritableRenderedImage source,
545:                    final int tileX, final int tileY,
546:                    final boolean willBeWritable) {
547:                fireTileUpdate(tileX, tileY, willBeWritable);
548:            }
549:
550:            /**
551:             * Adds an observer. This observer will be notified everytime a tile initially empty become
552:             * available. If the observer is already present, it will receive multiple notifications.
553:             */
554:            public synchronized void addTileObserver(final TileObserver observer) {
555:                if (observer != null) {
556:                    if (observers == null) {
557:                        observers = new TileObserver[] { observer };
558:                    } else {
559:                        final int length = observers.length;
560:                        observers = (TileObserver[]) XArray.resize(observers,
561:                                length + 1);
562:                        observers[length] = observer;
563:                    }
564:                }
565:            }
566:
567:            /**
568:             * Removes an observer. If the observer was not registered, nothing happens.
569:             * If the observer was registered for multiple notifications, it will now be
570:             * registered for one fewer.
571:             */
572:            public synchronized void removeTileObserver(
573:                    final TileObserver observer) {
574:                if (observers != null) {
575:                    for (int i = observers.length; --i >= 0;) {
576:                        if (observers[i] == observer) {
577:                            observers = (TileObserver[]) XArray.remove(
578:                                    observers, i, 1);
579:                            break;
580:                        }
581:                    }
582:                }
583:            }
584:
585:            /**
586:             * Checks out a tile for writing. Since {@code DeferredPlanarImage} are not really
587:             * writable, this method throws an {@link UnsupportedOperationException}.
588:             */
589:            public WritableRaster getWritableTile(final int tileX,
590:                    final int tileY) {
591:                throw new UnsupportedOperationException();
592:            }
593:
594:            /**
595:             * Relinquishes the right to write to a tile. Since {@code DeferredPlanarImage} are
596:             * not really writable, this method throws an {@link IllegalStateException} (the state is
597:             * really illegal since {@link #getWritableTile} should never have succeeded).
598:             */
599:            public void releaseWritableTile(final int tileX, final int tileY) {
600:                throw new IllegalStateException();
601:            }
602:
603:            /**
604:             * Returns whether any tile is checked out for writing.
605:             */
606:            public boolean hasTileWriters() {
607:                final Raster[] pendings = this .pendings; // Avoid the need for synchronisation.
608:                if (pendings != null) {
609:                    final int length = pendings.length;
610:                    for (int i = 0; i < length; i++) {
611:                        if (pendings[i] != null) {
612:                            return true;
613:                        }
614:                    }
615:                }
616:                return false;
617:            }
618:
619:            /**
620:             * Returns whether a tile is currently checked out for writing.
621:             */
622:            public boolean isTileWritable(final int tileX, final int tileY) {
623:                final Raster[] pendings = this .pendings; // Avoid the need for synchronisation.
624:                return pendings != null
625:                        && pendings[getTileIndice(tileX, tileY)] != null;
626:            }
627:
628:            /**
629:             * Returns an array of {@link Point} objects indicating which tiles are
630:             * checked out for writing. Returns null if none are checked out.
631:             */
632:            public synchronized Point[] getWritableTileIndices() {
633:                Point[] indices = null;
634:                if (pendings != null) {
635:                    int count = 0;
636:                    final int minX = getMinTileX();
637:                    final int minY = getMinTileY();
638:                    final int numX = getNumXTiles();
639:                    final int length = pendings.length;
640:                    for (int i = 0; i < length; i++) {
641:                        if (pendings[i] != null) {
642:                            if (indices == null) {
643:                                indices = new Point[length - i];
644:                            }
645:                            final int x = i % numX + minX;
646:                            final int y = i / numX + minY;
647:                            assert getTileIndice(x, y) == i : i;
648:                            indices[count++] = new Point(x, y);
649:                        }
650:                    }
651:                    if (indices != null) {
652:                        indices = (Point[]) XArray.resize(indices, count);
653:                    }
654:                }
655:                return indices;
656:            }
657:
658:            /**
659:             * Sets a rectangle of the image to the contents of the raster. Since
660:             * {@code DeferredPlanarImage} are not really writable, this method
661:             * throws an {@link UnsupportedOperationException}.
662:             */
663:            public void setData(Raster r) {
664:                throw new UnsupportedOperationException();
665:            }
666:
667:            /**
668:             * Provides a hint that this image will no longer be accessed from a reference in user space.
669:             * <strong>NOTE: this method dispose the image given to the constructor as well.</strong>
670:             * This is because {@code DeferredPlanarImage} is used as a "view" of an other
671:             * image, and the user shouldn't know that he is not using directly the other image.
672:             */
673:            public synchronized void dispose() {
674:                if (image instanceof  WritableRenderedImage) {
675:                    ((WritableRenderedImage) image).removeTileObserver(this);
676:                }
677:                image.removeTileComputationListener(this);
678:                requests = null;
679:                waitings = null;
680:                pendings = null;
681:                super.dispose();
682:                image.dispose();
683:            }
684:        }
www.java2java.com | Contact Us
Copyright 2009 - 12 Demo Source and Support. All rights reserved.
All other trademarks are property of their respective owners.