001: /*
002: * $RCSfile: RemoteImage.java,v $
003: *
004: * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved.
005: *
006: * Use is subject to license terms.
007: *
008: * $Revision: 1.1 $
009: * $Date: 2005/02/11 04:57:19 $
010: * $State: Exp $
011: */
012: package javax.media.jai;
013:
014: import java.awt.Image;
015: import java.awt.Rectangle;
016: import java.awt.geom.AffineTransform;
017: import java.awt.image.ColorModel;
018: import java.awt.image.Raster;
019: import java.awt.image.RenderedImage;
020: import java.awt.image.SampleModel;
021: import java.awt.image.WritableRaster;
022: import java.awt.image.renderable.RenderableImage;
023: import java.awt.image.renderable.RenderContext;
024: import java.io.Serializable;
025: import java.net.InetAddress;
026: import java.rmi.Naming;
027: import java.rmi.RemoteException;
028: import java.util.Vector;
029: import javax.media.jai.remote.SerializableRenderedImage;
030: import com.sun.media.jai.rmi.RMIImage;
031: import com.sun.media.jai.rmi.RasterProxy;
032: import com.sun.media.jai.rmi.RenderContextProxy;
033:
034: /**
035: * A sub-class of <code>PlanarImage</code> which represents an image on a
036: * remote server machine.
037: *
038: * <p> The image may be constructed from a <code>RenderedImage</code> or from
039: * an imaging chain in either the rendered or renderable mode. Network errors
040: * (detected via throws of <code>RemoteException</code>s) are dealt with through retries;
041: * when the limit of retries is exceeded, a <code>null</code> Raster may be returned.
042: * The default number of retries is set to 5 and the default timeout is set
043: * to 1 second.
044: *
045: * <p> Note that the registry of the server will be used. In particular if
046: * an <code>OperationRegistry</code> was present in the
047: * <code>RenderingHints</code> used to construct a <code>RenderedOp</code>
048: * or <code>RenderableOp</code> it will not be serialized and transmitted
049: * to the server.
050: *
051: * <p> Image layout attributes, once requested, are cached locally for speed.
052: *
053: * @deprecated as of JAI 1.1 in favor of
054: * <code>javax.media.jai.remote.RemoteJAI</code>.
055: *
056: */
057: public class RemoteImage extends PlanarImage {
058:
059: /** The amount of time to wait between retries. */
060: static final int DEFAULT_TIMEOUT = 1000; // Milliseconds
061:
062: /** The default number of retries. */
063: static final int DEFAULT_NUM_RETRIES = 5;
064:
065: /** Index of local variable. */
066: static final int VAR_MIN_X = 0;
067: /** Index of local variable. */
068: static final int VAR_MIN_Y = 1;
069: /** Index of local variable. */
070: static final int VAR_WIDTH = 2;
071: /** Index of local variable. */
072: static final int VAR_HEIGHT = 3;
073: /** Index of local variable. */
074: static final int VAR_TILE_WIDTH = 4;
075: /** Index of local variable. */
076: static final int VAR_TILE_HEIGHT = 5;
077: /** Index of local variable. */
078: static final int VAR_TILE_GRID_X_OFFSET = 6;
079: /** Index of local variable. */
080: static final int VAR_TILE_GRID_Y_OFFSET = 7;
081: /** Index of local variable. */
082: static final int VAR_SAMPLE_MODEL = 8;
083: /** Index of local variable. */
084: static final int VAR_COLOR_MODEL = 9;
085: /** Index of local variable. */
086: static final int VAR_SOURCES = 10;
087: /** Index of local variable. */
088: static final int NUM_VARS = 11;
089:
090: /** The class of the serializable representation of a NULL property. */
091: private static final Class NULL_PROPERTY_CLASS = com.sun.media.jai.rmi.RMIImageImpl.NULL_PROPERTY
092: .getClass();
093:
094: /** The RMIImage our data will come from. */
095: protected RMIImage remoteImage;
096:
097: /** The RMI ID of this object. */
098: private Long id = null;
099:
100: /** Valid bits for locally cached variables. */
101: protected boolean[] fieldValid = new boolean[NUM_VARS];
102:
103: /** Locally cached version of properties. */
104: protected String[] propertyNames = null;
105:
106: /** The amount of time between retries (milliseconds). */
107: protected int timeout = DEFAULT_TIMEOUT;
108:
109: /** The number of retries. */
110: protected int numRetries = DEFAULT_NUM_RETRIES;
111:
112: /** The bounds of this image. */
113: private Rectangle imageBounds = null;
114:
115: private static Vector vectorize(RenderedImage image) {
116: Vector v = new Vector(1);
117: v.add(image);
118: return v;
119: }
120:
121: /**
122: * Constructs a <code>RemoteImage</code> from a <code>RenderedImage</code>.
123: *
124: * <p> The <code>RenderedImage</code> source should ideally be a lightweight
125: * reference to an image available locally on the server or over a
126: * further network link.
127: *
128: * <p> Although it is legal to use any <code>RenderedImage</code>, one should be
129: * aware that this will require copying of the image data via transmission
130: * over a network link.
131: *
132: * <p> The name of the server must be supplied in the form appropriate to
133: * the implementation. In the reference port of JAI, RMI is used to
134: * implement remote imaging so that the server name must be supplied in
135: * the format
136: * <pre>
137: * host:port
138: * </pre>
139: * where the port number is optional and may be supplied only if
140: * the host name is supplied. If this parameter is <code>null</code> the default
141: * is to search for the RMIImage service on the local host at the
142: * default <i>rmiregistry</i> port (1099).
143: *
144: * @param serverName The name of the server in the appropriate format.
145: * @param source A <code>RenderedImage</code> source which must not be <code>null</code>.
146: * @throws IllegalArgumentException if <code>source</code> is
147: * <code>null</code>.
148: */
149: public RemoteImage(String serverName, RenderedImage source) {
150: super (null, null, null);
151:
152: if (serverName == null)
153: serverName = getLocalHostAddress();
154:
155: // Look for a separator indicating the remote image chaining hack
156: // in which case the serverName argument contains host[:port]::id
157: // where id is the RMI ID of the image on the indicated server.
158: int index = serverName.indexOf("::");
159: boolean remoteChainingHack = index != -1;
160:
161: if (!remoteChainingHack && source == null) {
162: // Don't throw the NullPointerException if it's the hack.
163: throw new IllegalArgumentException(JaiI18N
164: .getString("RemoteImage1"));
165: }
166:
167: if (remoteChainingHack) {
168: // Extract the RMI ID from the servername string and replace
169: // the original serverName string with one of the usual type.
170: id = Long.valueOf(serverName.substring(index + 2));
171: serverName = serverName.substring(0, index);
172: }
173:
174: // Construct the remote RMI image.
175: getRMIImage(serverName);
176:
177: if (!remoteChainingHack) {
178: // Get the RMI ID for this object.
179: getRMIID();
180: }
181:
182: // Cache the server name and RMI ID in a property.
183: setRMIProperties(serverName);
184:
185: if (source != null) { // Source may be null only for the hack.
186: try {
187: if (source instanceof Serializable) {
188: remoteImage.setSource(id, source);
189: } else {
190: remoteImage.setSource(id,
191: new SerializableRenderedImage(source));
192: }
193: } catch (RemoteException e) {
194: throw new RuntimeException(e.getMessage());
195: }
196: }
197: }
198:
199: /**
200: * Constructs a <code>RemoteImage</code> from a <code>RenderedOp</code>,
201: * i.e., an imaging directed acyclic graph (DAG).
202: *
203: * <p> This DAG will be copied over to the server where it will be
204: * transformed into an <code>OpImage</code> chain using the server's local
205: * <code>OperationRegistry</code> and available <code>RenderedImageFactory</code> objects.
206: *
207: * <p> The name of the server must be supplied in the form appropriate to
208: * the implementation. In the reference port of JAI, RMI is used to
209: * implement remote imaging so that the server name must be supplied in
210: * the format
211: * <pre>
212: * host:port
213: * </pre>
214: * where the port number is optional and may be supplied only if
215: * the host name is supplied. If this parameter is <code>null</code> the default
216: * is to search for the RMIImage service on the local host at the
217: * default <i>rmiregistry</i> port (1099).
218: *
219: * <p> Note that the properties of the <code>RemoteImage</code> will be
220: * those of the <code>RenderedOp</code> node and not of its rendering.
221: *
222: * @param serverName The name of the server in the appropriate format.
223: * @param source A <code>RenderedOp</code> source which must not be <code>null</code>.
224: * @throws IllegalArgumentException if <code>source</code> is
225: * <code>null</code>.
226: */
227: public RemoteImage(String serverName, RenderedOp source) {
228: super (null, null, null);
229:
230: if (serverName == null)
231: serverName = getLocalHostAddress();
232:
233: if (source == null) {
234: throw new IllegalArgumentException(JaiI18N
235: .getString("RemoteImage1"));
236: }
237:
238: // Construct the remote RMI image.
239: getRMIImage(serverName);
240:
241: // Get the RMI ID for this object.
242: getRMIID();
243:
244: // Cache the server name and RMI ID in a property.
245: setRMIProperties(serverName);
246:
247: try {
248: remoteImage.setSource(id, source);
249: } catch (RemoteException e) {
250: throw new RuntimeException(e.getMessage());
251: }
252: }
253:
254: /**
255: * Constructs a <code>RemoteImage</code> from a <code>RenderableOp</code>
256: * and <code>RenderContext</code>.
257: * The entire <code>RenderableOp</code> DAG will be copied over to the server.
258: *
259: * <p> The name of the server must be supplied in the form appropriate to
260: * the implementation. In the reference port of JAI, RMI is used to
261: * implement remote imaging so that the server name must be supplied in
262: * the format
263: * <pre>
264: * host:port
265: * </pre>
266: * where the port number is optional and may be supplied only if
267: * the host name is supplied. If this parameter is <code>null</code> the default
268: * is to search for the RMIImage service on the local host at the
269: * default <i>rmiregistry</i> port (1099).
270: *
271: * <p> Note that the properties of the <code>RemoteImage</code> will be
272: * those of the <code>RenderableOp</code> node and not of its rendering.
273: *
274: * @param serverName The name of the server in the appropriate format.
275: * @param source A <code>RenderableOp</code> source which must not be <code>null</code>.
276: * @param renderContext The rendering context which may be <code>null</code>.
277: * @throws IllegalArgumentException if <code>source</code> is
278: * <code>null</code>.
279: */
280: public RemoteImage(String serverName, RenderableOp source,
281: RenderContext renderContext) {
282: super (null, null, null);
283:
284: if (serverName == null)
285: serverName = getLocalHostAddress();
286:
287: if (source == null) {
288: throw new IllegalArgumentException(JaiI18N
289: .getString("RemoteImage1"));
290: }
291:
292: if (renderContext == null) {
293: renderContext = new RenderContext(new AffineTransform());
294: }
295:
296: // Construct the remote RMI image.
297: getRMIImage(serverName);
298:
299: // Get the RMI ID for this object.
300: getRMIID();
301:
302: // Cache the server name and RMI ID in a property.
303: setRMIProperties(serverName);
304:
305: // Create the serializable form of the <code>RenderContext</code>.
306: RenderContextProxy rcp = new RenderContextProxy(renderContext);
307:
308: try {
309: remoteImage.setSource(id, source, rcp);
310: } catch (RemoteException e) {
311: e.printStackTrace();
312: throw new RuntimeException(e.getMessage());
313: }
314: }
315:
316: /**
317: * Construct an RMIImage on the indicated server.
318: *
319: * <p> The name of the server must be supplied in the form
320: * <pre>
321: * host:port
322: * </pre>
323: * where the port number is optional and may be supplied only if
324: * the host name is supplied. If this parameter is <code>null</code> the default
325: * is to search for the RMIImage service on the local host at the
326: * default <i>rmiregistry</i> port (1099).
327: *
328: * <p> The result is cached in the instance variable "remoteImage".
329: *
330: * @param serverName The name of the server in the format described.
331: */
332: private void getRMIImage(String serverName) {
333: // Set the server name to the local host if null.
334: if (serverName == null)
335: serverName = getLocalHostAddress();
336:
337: // Derive the service name.
338: String serviceName = new String("rmi://" + serverName + "/"
339: + RMIImage.RMI_IMAGE_SERVER_NAME);
340:
341: // Look up the remote object.
342: remoteImage = null;
343: try {
344: remoteImage = (RMIImage) Naming.lookup(serviceName);
345: } catch (Exception e) {
346: throw new RuntimeException(e.getMessage());
347: }
348: }
349:
350: /**
351: * Get the default server name, that is, the local host.
352: * When the provided server name is <code>null</code>, use
353: * this method to obtain the local host IP address as the server name.
354: */
355: private String getLocalHostAddress() {
356: String serverName;
357: try {
358: serverName = InetAddress.getLocalHost().getHostAddress();
359: } catch (Exception e) {
360: throw new RuntimeException(e.getMessage());
361: }
362: return serverName;
363: }
364:
365: /**
366: * Get the unique ID to be used to refer to this object on the server.
367: * The result is cached in the instance variable "id".
368: */
369: private void getRMIID() {
370: try {
371: id = remoteImage.getRemoteID();
372: } catch (Exception e) {
373: throw new RuntimeException(e.getMessage());
374: }
375: }
376:
377: /**
378: * Cache the argument and the RMI ID as local properties.
379: * This is a gross hack to permit chaining of remote images.
380: *
381: * @param serverName The server name as described in the constructors.
382: */
383: private void setRMIProperties(String serverName) {
384: setProperty(getClass().getName() + ".serverName", serverName);
385: setProperty(getClass().getName() + ".id", id);
386: }
387:
388: /**
389: * Disposes of any resources allocated for remote operation.
390: */
391: protected void finalize() {
392: try {
393: remoteImage.dispose(id);
394: } catch (Exception e) {
395: // Ignore the Exception.
396: }
397: }
398:
399: /**
400: * Set the amount of time between retries.
401: *
402: * @param timeout The time interval between retries (milliseconds). If
403: * this is non-positive the time interval is not changed.
404: */
405: public void setTimeout(int timeout) {
406: if (timeout > 0) {
407: this .timeout = timeout;
408: }
409: }
410:
411: /**
412: * Gets the amount of time between retries.
413: */
414: public int getTimeout() {
415: return timeout;
416: }
417:
418: /**
419: * Set the number of retries.
420: *
421: * @param numRetries The number of retries. If this is non-positive the
422: * number of retries is not changed.
423: */
424: public void setNumRetries(int numRetries) {
425: if (numRetries > 0) {
426: this .numRetries = numRetries;
427: }
428: }
429:
430: /**
431: * Gets the number of retries.
432: */
433: public int getNumRetries() {
434: return numRetries;
435: }
436:
437: /**
438: * Cause an instance variable of the remote object to be cached
439: * locally, retrying a given number of times with a given timeout.
440: *
441: * @param fieldIndex the index of the desired field.
442: * @param retries the maximum number of retries; must be positive.
443: * @param timeout the timeout interval between retries, in milliseconds;
444: * must be positive.
445: * @throws <code>ArrayIndexOutOfBoundsException</code> if fieldIndex
446: * is negative or >= NUM_VARS.
447: * @throws <code>IllegalArgumentException</code> if retries or timeout
448: * is non-positive.
449: */
450: protected void requestField(int fieldIndex, int retries, int timeout) {
451: if (retries < 0) {
452: throw new IllegalArgumentException(JaiI18N
453: .getString("RemoteImage3"));
454: } else if (timeout < 0) {
455: throw new IllegalArgumentException(JaiI18N
456: .getString("RemoteImage4"));
457: }
458:
459: int count = 0;
460:
461: if (fieldValid[fieldIndex])
462: return;
463:
464: while (count++ < retries) {
465: try {
466: switch (fieldIndex) {
467: case VAR_MIN_X:
468: minX = remoteImage.getMinX(id);
469: break;
470: case VAR_MIN_Y:
471: minY = remoteImage.getMinY(id);
472: break;
473: case VAR_WIDTH:
474: width = remoteImage.getWidth(id);
475: break;
476: case VAR_HEIGHT:
477: height = remoteImage.getHeight(id);
478: break;
479: case VAR_TILE_WIDTH:
480: tileWidth = remoteImage.getTileWidth(id);
481: break;
482: case VAR_TILE_HEIGHT:
483: tileHeight = remoteImage.getTileHeight(id);
484: break;
485: case VAR_TILE_GRID_X_OFFSET:
486: tileGridXOffset = remoteImage
487: .getTileGridXOffset(id);
488: break;
489: case VAR_TILE_GRID_Y_OFFSET:
490: tileGridYOffset = remoteImage
491: .getTileGridYOffset(id);
492: break;
493: case VAR_SAMPLE_MODEL:
494: sampleModel = (SampleModel) remoteImage
495: .getSampleModel(id).getSampleModel();
496: break;
497: case VAR_COLOR_MODEL:
498: colorModel = (ColorModel) remoteImage
499: .getColorModel(id).getColorModel();
500: break;
501: case VAR_SOURCES: {
502: Vector localSources = remoteImage.getSources(id);
503: int numSources = localSources.size();
504: for (int i = 0; i < numSources; i++) {
505: RenderedImage src = (RenderedImage) localSources
506: .get(i);
507: addSource(PlanarImage.wrapRenderedImage(src));
508: }
509: }
510: break;
511: }
512:
513: fieldValid[fieldIndex] = true;
514: return;
515: } catch (RemoteException e) {
516: System.err.println(JaiI18N.getString("RemoteImage0"));
517: try {
518: java.lang.Thread.sleep(timeout);
519: } catch (java.lang.InterruptedException f) {
520: }
521: }
522: }
523: }
524:
525: /**
526: * Causes an instance variable of the remote object to be cached
527: * locally, retrying with a default/user specified timeout.
528: *
529: * @param fieldIndex the index of the desired field.
530: * @throws <code>ArrayIndexOutOfBoundsException</code> if fieldIndex
531: * is negative or >= NUM_VARS.
532: */
533: protected void requestField(int fieldIndex) {
534: requestField(fieldIndex, numRetries, timeout);
535: }
536:
537: /**
538: * Returns the X coordinate of the leftmost column of the image.
539: */
540: public int getMinX() {
541: requestField(VAR_MIN_X);
542: return minX;
543: }
544:
545: /**
546: * Returns the X coordinate of the column immediately to the right
547: * of the rightmost column of the image.
548: */
549: public int getMaxX() {
550: requestField(VAR_MIN_X);
551: requestField(VAR_WIDTH);
552: return minX + width;
553: }
554:
555: /** Returns the Y coordinate of the uppermost row of the image. */
556: public int getMinY() {
557: requestField(VAR_MIN_Y);
558: return minY;
559: }
560:
561: /**
562: * Returns the Y coordinate of the row immediately below the
563: * bottom row of the image.
564: */
565: public int getMaxY() {
566: requestField(VAR_MIN_Y);
567: requestField(VAR_HEIGHT);
568: return minY + height;
569: }
570:
571: /** Returns the width of the <code>RemoteImage</code> in pixels. */
572: public int getWidth() {
573: requestField(VAR_WIDTH);
574: return width;
575: }
576:
577: /** Returns the height of the <code>RemoteImage</code> in pixels. */
578: public int getHeight() {
579: requestField(VAR_HEIGHT);
580: return height;
581: }
582:
583: /** Returns the width of a tile in pixels. */
584: public int getTileWidth() {
585: requestField(VAR_TILE_WIDTH);
586: return tileWidth;
587: }
588:
589: /** Returns the height of a tile in pixels. */
590: public int getTileHeight() {
591: requestField(VAR_TILE_HEIGHT);
592: return tileHeight;
593: }
594:
595: /** Returns the X offset of the tile grid. */
596: public int getTileGridXOffset() {
597: requestField(VAR_TILE_GRID_X_OFFSET);
598: return tileGridXOffset;
599: }
600:
601: /** Returns the Y offset of the tile grid. */
602: public int getTileGridYOffset() {
603: requestField(VAR_TILE_GRID_Y_OFFSET);
604: return tileGridYOffset;
605: }
606:
607: /** Returns the <code>SampleModel</code> associated with this image. */
608: public SampleModel getSampleModel() {
609: requestField(VAR_SAMPLE_MODEL);
610: return sampleModel;
611: }
612:
613: /** Returns the <code>ColorModel</code> associated with this image. */
614: public ColorModel getColorModel() {
615: requestField(VAR_COLOR_MODEL);
616: return colorModel;
617: }
618:
619: /**
620: * Returns a vector of <code>RenderedImage</code>s that are the sources of
621: * image data for this <code>RenderedImage</code>. Note that this method
622: * will often return <code>null</code>.
623: */
624: public Vector getSources() {
625: requestField(VAR_SOURCES);
626: return super .getSources();
627: }
628:
629: /**
630: * Gets a property from the property set of this image.
631: * If the property name is not recognized, java.awt.Image.UndefinedProperty
632: * will be returned.
633: *
634: * @param name the name of the property to get, as a String.
635: * @return a reference to the property <code>Object</code>, or the value
636: * java.awt.Image.UndefinedProperty.
637: *
638: * @exception IllegalArgumentException if <code>propertyName</code>
639: * is <code>null</code>.
640: */
641: public Object getProperty(String name) {
642: // Try to get property locally.
643: Object property = super .getProperty(name);
644:
645: if (property == null || property == Image.UndefinedProperty) {
646: // We have never requested this property, get it from the server.
647: int count = 0;
648: while (count++ < numRetries) {
649: try {
650: property = remoteImage.getProperty(id, name);
651: if (NULL_PROPERTY_CLASS.isInstance(property)) {
652: property = Image.UndefinedProperty;
653: }
654: break;
655: } catch (RemoteException e) {
656: try {
657: java.lang.Thread.sleep(timeout);
658: } catch (java.lang.InterruptedException f) {
659: }
660: }
661: }
662:
663: if (property == null) {
664: property = Image.UndefinedProperty;
665: }
666:
667: if (property != Image.UndefinedProperty) {
668: setProperty(name, property); // Cache property locally
669: }
670: }
671:
672: return property;
673: }
674:
675: /** Returns a list of names recognized by getProperty. */
676: public String[] getPropertyNames() {
677: // Retrieve local property names.
678: String[] localPropertyNames = super .getPropertyNames();
679:
680: // Put local names in a Vector.
681: Vector names = new Vector();
682: if (localPropertyNames != null) {
683: for (int i = 0; i < localPropertyNames.length; i++) {
684: names.add(localPropertyNames[i]);
685: }
686: }
687:
688: // Get the remote property names.
689: int count = 0;
690: String[] remotePropertyNames = null;
691: while (count++ < numRetries) {
692: try {
693: remotePropertyNames = remoteImage.getPropertyNames(id);
694: break;
695: } catch (RemoteException e) {
696: try {
697: java.lang.Thread.sleep(timeout);
698: } catch (java.lang.InterruptedException f) {
699: }
700: }
701: }
702:
703: // Put the remote names, if any, in the Vector.
704: if (remotePropertyNames != null) {
705: for (int i = 0; i < remotePropertyNames.length; i++) {
706: if (!names.contains(remotePropertyNames[i])) {
707: names.add(remotePropertyNames[i]);
708: }
709: }
710: }
711:
712: // Set the return value from the vector.
713: propertyNames = names.size() == 0 ? null : (String[]) names
714: .toArray(new String[names.size()]);
715:
716: return propertyNames;
717: }
718:
719: /**
720: * Returns tile (x, y). Note that x and y are indexes into the
721: * tile array not pixel locations. The <code>Raster</code> that is returned
722: * is a copy.
723: *
724: * @param x the X index of the requested tile in the tile array
725: * @param y the Y index of the requested tile in the tile array
726: */
727: public Raster getTile(int x, int y) {
728: int count = 0;
729:
730: while (count++ < numRetries) {
731: try {
732: RasterProxy rp = remoteImage.getTile(id, x, y);
733: return rp.getRaster();
734: } catch (RemoteException e) {
735: try {
736: java.lang.Thread.sleep(timeout);
737: } catch (java.lang.InterruptedException f) {
738: }
739: }
740: }
741: return null;
742: }
743:
744: /**
745: * Returns the image as one large tile.
746: */
747: public Raster getData() {
748: int count = 0;
749:
750: while (count++ < numRetries) {
751: try {
752: RasterProxy rp = remoteImage.getData(id);
753: return rp.getRaster();
754: } catch (RemoteException e) {
755: try {
756: java.lang.Thread.sleep(timeout);
757: } catch (java.lang.InterruptedException f) {
758: }
759: }
760: }
761: return null;
762: }
763:
764: /**
765: * Returns an arbitrary rectangular region of the <code>RemoteImage</code>.
766: *
767: * <p> The <code>rect</code> parameter may be
768: * <code>null</code>, in which case the entire image data is
769: * returned in the <code>Raster</code>.
770: *
771: * <p> If <code>rect</code> is non-<code>null</code> but does
772: * not intersect the image bounds at all, an
773: * <code>IllegalArgumentException</code> will be thrown.
774: *
775: * @param rect The <code>Rectangle</code> of interest.
776: */
777:
778: public Raster getData(Rectangle rect) {
779: if (imageBounds == null) {
780: imageBounds = getBounds();
781: }
782: if (rect == null) {
783: rect = imageBounds;
784: } else if (!rect.intersects(imageBounds)) {
785: throw new IllegalArgumentException(JaiI18N
786: .getString("RemoteImage2"));
787: }
788:
789: int count = 0;
790:
791: while (count++ < numRetries) {
792: try {
793: RasterProxy rp = remoteImage.getData(id, rect);
794: return rp.getRaster();
795: } catch (RemoteException e) {
796: try {
797: java.lang.Thread.sleep(timeout);
798: } catch (java.lang.InterruptedException f) {
799: }
800: }
801: }
802: return null;
803: }
804:
805: /**
806: * Returns an arbitrary rectangular region of the <code>RemoteImage</code>
807: * in a user-supplied <code>WritableRaster</code>.
808: * The rectangular region is the entire image if the argument is
809: * <code>null</code> or the intersection of the argument bounds with the image
810: * bounds if the region is non-<code>null</code>.
811: * If the argument is non-<code>null</code> but has bounds which have an empty
812: * intersection with the image bounds the return value will be <code>null</code>.
813: * The return value may also be <code>null</code> if the argument is non-<code>null</code> but
814: * is incompatible with the <code>Raster</code> returned from the remote image.
815: */
816: public WritableRaster copyData(WritableRaster raster) {
817: int count = 0;
818:
819: Rectangle bounds = ((raster == null) ? new Rectangle(getMinX(),
820: getMinY(), getWidth(), getHeight()) : raster
821: .getBounds());
822:
823: while (count++ < numRetries) {
824: try {
825: RasterProxy rp = remoteImage.copyData(id, bounds);
826: try {
827: if (raster == null) {
828: raster = (WritableRaster) rp.getRaster();
829: } else {
830: raster.setDataElements(bounds.x, bounds.y,
831: (Raster) rp.getRaster());
832: }
833: break;
834: } catch (ArrayIndexOutOfBoundsException e) {
835: raster = null;
836: break;
837: }
838: } catch (RemoteException e) {
839: try {
840: java.lang.Thread.sleep(timeout);
841: } catch (java.lang.InterruptedException f) {
842: }
843: }
844: }
845:
846: return raster;
847: }
848: }
|