Source code: org/modama/jaiutil/codecs/SimpleRenderedImage.java
1 /*
2 * @(#)SimpleRenderedImage.java 13.2 02/05/08
3 *
4 * Copyright (c) 2002 Sun Microsystems, Inc. All Rights Reserved.
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions are met:
8 *
9 * -Redistributions of source code must retain the above copyright notice, this
10 * list of conditions and the following disclaimer.
11 *
12 * -Redistribution in binary form must reproduct the above copyright notice,
13 * this list of conditions and the following disclaimer in the documentation
14 * and/or other materials provided with the distribution.
15 *
16 * Neither the name of Sun Microsystems, Inc. or the names of contributors may
17 * be used to endorse or promote products derived from this software without
18 * specific prior written permission.
19 *
20 * This software is provided "AS IS," without a warranty of any kind. ALL
21 * EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, INCLUDING ANY
22 * IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE OR
23 * NON-INFRINGEMENT, ARE HEREBY EXCLUDED. SUN AND ITS LICENSORS SHALL NOT BE
24 * LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING
25 * OR DISTRIBUTING THE SOFTWARE OR ITS DERIVATIVES. IN NO EVENT WILL SUN OR ITS
26 * LICENSORS BE LIABLE FOR ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT,
27 * INDIRECT, SPECIAL, CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER
28 * CAUSED AND REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF
29 * OR INABILITY TO USE SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE
30 * POSSIBILITY OF SUCH DAMAGES.
31 *
32 * You acknowledge that Software is not designed,licensed or intended for use in
33 * the design, construction, operation or maintenance of any nuclear facility.
34 */
35
36 package org.modama.jaiutil.codecs;
37
38 import java.awt.Point;
39 import java.awt.Rectangle;
40 import java.awt.image.Raster;
41 import java.awt.image.WritableRaster;
42 import java.awt.image.RenderedImage;
43 import java.awt.image.ColorModel;
44 import java.awt.image.SampleModel;
45 import java.util.Enumeration;
46 import java.util.Hashtable;
47 import java.util.Iterator;
48 import java.util.Vector;
49
50 /**
51 * A simple class implemented the <code>RenderedImage</code>
52 * interface. Only the <code>getTile()</code> method needs to be
53 * implemented by subclasses. The instance variables must also be
54 * filled in properly.
55 *
56 * <p> Normally in JAI <code>PlanarImage</code> is used for this
57 * purpose, but in the interest of modularity the
58 * use of <code>PlanarImage</code> has been avoided.
59 */
60
61 public abstract class SimpleRenderedImage implements RenderedImage
62 {
63
64 /** The X coordinate of the image's upper-left pixel. */
65 protected int minX;
66
67 /** The Y coordinate of the image's upper-left pixel. */
68 protected int minY;
69
70 /** The image's width in pixels. */
71 protected int width;
72
73 /** The image's height in pixels. */
74 protected int height;
75
76 /** The width of a tile. */
77 protected int tileWidth;
78
79 /** The height of a tile. */
80 protected int tileHeight;
81
82 /** The X coordinate of the upper-left pixel of tile (0, 0). */
83 protected int tileGridXOffset = 0;
84
85 /** The Y coordinate of the upper-left pixel of tile (0, 0). */
86 protected int tileGridYOffset = 0;
87
88 /** The image's SampleModel. */
89 protected SampleModel sampleModel = null;
90
91 /** The image's ColorModel. */
92 protected ColorModel colorModel = null;
93
94 /** The image's sources, stored in a Vector. */
95 protected Vector sources = new Vector();
96
97 /** A Hashtable containing the image properties. */
98 protected Hashtable properties = new Hashtable();
99
100 public SimpleRenderedImage()
101 {
102 }
103
104 /** Returns the X coordinate of the leftmost column of the image. */
105 public int getMinX()
106 {
107 return minX;
108 }
109
110 /**
111 * Returns the X coordinate of the column immediatetely to the
112 * right of the rightmost column of the image. getMaxX() is
113 * implemented in terms of getMinX() and getWidth() and so does
114 * not need to be implemented by subclasses.
115 */
116 public final int getMaxX()
117 {
118 return getMinX() + getWidth();
119 }
120
121 /** Returns the X coordinate of the uppermost row of the image. */
122 public int getMinY()
123 {
124 return minY;
125 }
126
127 /**
128 * Returns the Y coordinate of the row immediately below the
129 * bottom row of the image. getMaxY() is implemented in terms of
130 * getMinY() and getHeight() and so does not need to be
131 * implemented by subclasses.
132 */
133 public final int getMaxY()
134 {
135 return getMinY() + getHeight();
136 }
137
138 /** Returns the width of the image. */
139 public int getWidth()
140 {
141 return width;
142 }
143
144 /** Returns the height of the image. */
145 public int getHeight()
146 {
147 return height;
148 }
149
150 /** Returns a Rectangle indicating the image bounds. */
151 public Rectangle getBounds()
152 {
153 return new Rectangle( getMinX(), getMinY(),
154 getWidth(), getHeight() );
155 }
156
157 /** Returns the width of a tile. */
158 public int getTileWidth()
159 {
160 return tileWidth;
161 }
162
163 /** Returns the height of a tile. */
164 public int getTileHeight()
165 {
166 return tileHeight;
167 }
168
169 /**
170 * Returns the X coordinate of the upper-left pixel of tile (0, 0).
171 */
172 public int getTileGridXOffset()
173 {
174 return tileGridXOffset;
175 }
176
177 /**
178 * Returns the Y coordinate of the upper-left pixel of tile (0, 0).
179 */
180 public int getTileGridYOffset()
181 {
182 return tileGridYOffset;
183 }
184
185 /**
186 * Returns the horizontal index of the leftmost column of tiles.
187 * getMinTileX() is implemented in terms of getMinX()
188 * and so does not need to be implemented by subclasses.
189 */
190 public int getMinTileX()
191 {
192 return XToTileX( getMinX() );
193 }
194
195 /**
196 * Returns the horizontal index of the rightmost column of tiles.
197 * getMaxTileX() is implemented in terms of getMaxX()
198 * and so does not need to be implemented by subclasses.
199 */
200 public int getMaxTileX()
201 {
202 return XToTileX( getMaxX() - 1 );
203 }
204
205 /**
206 * Returns the number of tiles along the tile grid in the
207 * horizontal direction. getNumXTiles() is implemented in terms
208 * of getMinTileX() and getMaxTileX() and so does not need to be
209 * implemented by subclasses.
210 */
211 public int getNumXTiles()
212 {
213 return getMaxTileX() - getMinTileX() + 1;
214 }
215
216 /**
217 * Returns the vertical index of the uppermost row of tiles. getMinTileY()
218 * is implemented in terms of getMinY() and so does not need to be
219 * implemented by subclasses.
220 */
221 public int getMinTileY()
222 {
223 return YToTileY( getMinY() );
224 }
225
226 /**
227 * Returns the vertical index of the bottom row of tiles. getMaxTileY()
228 * is implemented in terms of getMaxY() and so does not need to
229 * be implemented by subclasses.
230 */
231 public int getMaxTileY()
232 {
233 return YToTileY( getMaxY() - 1 );
234 }
235
236 /**
237 * Returns the number of tiles along the tile grid in the vertical
238 * direction. getNumYTiles() is implemented in terms
239 * of getMinTileY() and getMaxTileY() and so does not need to be
240 * implemented by subclasses.
241 */
242 public int getNumYTiles()
243 {
244 return getMaxTileY() - getMinTileY() + 1;
245 }
246
247 /** Returns the SampleModel of the image. */
248 public SampleModel getSampleModel()
249 {
250 return sampleModel;
251 }
252
253 /** Returns the ColorModel of the image. */
254 public ColorModel getColorModel()
255 {
256 return colorModel;
257 }
258
259 /**
260 * Gets a property from the property set of this image. If the
261 * property name is not recognized,
262 * <code>java.awt.Image.UndefinedProperty</code> will be returned.
263 *
264 * @param name the name of the property to get, as a
265 * <code>String</code>. @return a reference to the property
266 * <code>Object</code>, or the value
267 * <code>java.awt.Image.UndefinedProperty.</code>
268 */
269 public Object getProperty( String name )
270 {
271 name = name.toLowerCase();
272 return properties.get( name );
273 }
274
275 /**
276 * Returns a list of the properties recognized by this image. If
277 * no properties are available, <code>null</code> will be
278 * returned.
279 *
280 * @return an array of <code>String</code>s representing valid
281 * property names.
282 */
283 public String[] getPropertyNames()
284 {
285 String[] names = new String[properties.size()];
286 int index = 0;
287
288 Enumeration e = properties.keys();
289 while( e.hasMoreElements() )
290 {
291 String name = ( String )e.nextElement();
292 names[index++] = name;
293 }
294
295 return names;
296 }
297
298 /**
299 * Returns an array of <code>String</code>s recognized as names by
300 * this property source that begin with the supplied prefix. If
301 * no property names match, <code>null</code> will be returned.
302 * The comparison is done in a case-independent manner.
303 *
304 * <p> The default implementation calls
305 * <code>getPropertyNames()</code> and searches the list of names
306 * for matches.
307 *
308 * @return an array of <code>String</code>s giving the valid
309 * property names.
310 */
311 public String[] getPropertyNames( String prefix )
312 {
313 String propertyNames[] = getPropertyNames();
314 if( propertyNames == null )
315 {
316 return null;
317 }
318
319 prefix = prefix.toLowerCase();
320
321 Vector names = new Vector();
322 for( int i = 0; i < propertyNames.length; i++ )
323 {
324 if( propertyNames[i].startsWith( prefix ) )
325 {
326 names.addElement( propertyNames[i] );
327 }
328 }
329
330 if( names.size() == 0 )
331 {
332 return null;
333 }
334
335 // Copy the strings from the Vector over to a String array.
336 String prefixNames[] = new String[names.size()];
337 int count = 0;
338 for( Iterator it = names.iterator(); it.hasNext(); )
339 {
340 prefixNames[count++] = ( String )it.next();
341 }
342
343 return prefixNames;
344 }
345
346 // Utility methods.
347
348 /**
349 * Converts a pixel's X coordinate into a horizontal tile index
350 * relative to a given tile grid layout specified by its X offset
351 * and tile width.
352 */
353 public static int XToTileX( int x, int tileGridXOffset, int tileWidth )
354 {
355 x -= tileGridXOffset;
356 if( x < 0 )
357 {
358 x += 1 - tileWidth; // Force round to -infinity
359 }
360 return x / tileWidth;
361 }
362
363 /**
364 * Converts a pixel's Y coordinate into a vertical tile index
365 * relative to a given tile grid layout specified by its Y offset
366 * and tile height.
367 */
368 public static int YToTileY( int y, int tileGridYOffset, int tileHeight )
369 {
370 y -= tileGridYOffset;
371 if( y < 0 )
372 {
373 y += 1 - tileHeight; // Force round to -infinity
374 }
375 return y / tileHeight;
376 }
377
378 /**
379 * Converts a pixel's X coordinate into a horizontal tile index.
380 * This is a convenience method. No attempt is made to detect
381 * out-of-range coordinates.
382 *
383 * @param x the X coordinate of a pixel.
384 * @return the X index of the tile containing the pixel.
385 */
386 public int XToTileX( int x )
387 {
388 return XToTileX( x, getTileGridXOffset(), getTileWidth() );
389 }
390
391 /**
392 * Converts a pixel's Y coordinate into a vertical tile index.
393 * This is a convenience method. No attempt is made to detect
394 * out-of-range coordinates.
395 *
396 * @param y the Y coordinate of a pixel.
397 * @return the Y index of the tile containing the pixel.
398 */
399 public int YToTileY( int y )
400 {
401 return YToTileY( y, getTileGridYOffset(), getTileHeight() );
402 }
403
404 /**
405 * Converts a horizontal tile index into the X coordinate of its
406 * upper left pixel relative to a given tile grid layout specified
407 * by its X offset and tile width.
408 */
409 public static int tileXToX( int tx, int tileGridXOffset, int tileWidth )
410 {
411 return tx * tileWidth + tileGridXOffset;
412 }
413
414 /**
415 * Converts a vertical tile index into the Y coordinate of
416 * its upper left pixel relative to a given tile grid layout
417 * specified by its Y offset and tile height.
418 */
419 public static int tileYToY( int ty, int tileGridYOffset, int tileHeight )
420 {
421 return ty * tileHeight + tileGridYOffset;
422 }
423
424 /**
425 * Converts a horizontal tile index into the X coordinate of its
426 * upper left pixel. This is a convenience method. No attempt is made
427 * to detect out-of-range indices.
428 *
429 * @param tx the horizontal index of a tile.
430 * @return the X coordinate of the tile's upper left pixel.
431 */
432 public int tileXToX( int tx )
433 {
434 return tx * tileWidth + tileGridXOffset;
435 }
436
437 /**
438 * Converts a vertical tile index into the Y coordinate of its
439 * upper left pixel. This is a convenience method. No attempt is made
440 * to detect out-of-range indices.
441 *
442 * @param ty the vertical index of a tile.
443 * @return the Y coordinate of the tile's upper left pixel.
444 */
445 public int tileYToY( int ty )
446 {
447 return ty * tileHeight + tileGridYOffset;
448 }
449
450 public Vector getSources()
451 {
452 return null;
453 }
454
455 /**
456 * Returns the entire image in a single Raster. For images with
457 * multiple tiles this will require making a copy.
458 *
459 * <p> The returned Raster is semantically a copy. This means
460 * that updates to the source image will not be reflected in the
461 * returned Raster. For non-writable (immutable) source images,
462 * the returned value may be a reference to the image's internal
463 * data. The returned Raster should be considered non-writable;
464 * any attempt to alter its pixel data (such as by casting it to
465 * WritableRaster or obtaining and modifying its DataBuffer) may
466 * result in undefined behavior. The copyData method should be
467 * used if the returned Raster is to be modified.
468 *
469 * @return a Raster containing a copy of this image's data.
470 */
471 public Raster getData()
472 {
473 Rectangle rect = new Rectangle( getMinX(), getMinY(),
474 getWidth(), getHeight() );
475 return getData( rect );
476 }
477
478 /**
479 * Returns an arbitrary rectangular region of the RenderedImage
480 * in a Raster. The rectangle of interest will be clipped against
481 * the image bounds.
482 *
483 * <p> The returned Raster is semantically a copy. This means
484 * that updates to the source image will not be reflected in the
485 * returned Raster. For non-writable (immutable) source images,
486 * the returned value may be a reference to the image's internal
487 * data. The returned Raster should be considered non-writable;
488 * any attempt to alter its pixel data (such as by casting it to
489 * WritableRaster or obtaining and modifying its DataBuffer) may
490 * result in undefined behavior. The copyData method should be
491 * used if the returned Raster is to be modified.
492 *
493 * @param bounds the region of the RenderedImage to be returned.
494 */
495 public Raster getData( Rectangle bounds )
496 {
497 int startX = XToTileX( bounds.x );
498 int startY = YToTileY( bounds.y );
499 int endX = XToTileX( bounds.x + bounds.width - 1 );
500 int endY = YToTileY( bounds.y + bounds.height - 1 );
501 Raster tile;
502
503 if( ( startX == endX ) && ( startY == endY ) )
504 {
505 tile = getTile( startX, startY );
506 return tile.createChild( bounds.x, bounds.y,
507 bounds.width, bounds.height,
508 bounds.x, bounds.y, null );
509 } else
510 {
511 // Create a WritableRaster of the desired size
512 SampleModel sm =
513 sampleModel.createCompatibleSampleModel( bounds.width,
514 bounds.height );
515
516 // Translate it
517 WritableRaster dest =
518 Raster.createWritableRaster( sm, bounds.getLocation() );
519
520 for( int j = startY; j <= endY; j++ )
521 {
522 for( int i = startX; i <= endX; i++ )
523 {
524 tile = getTile( i, j );
525 Rectangle tileRect = tile.getBounds();
526 Rectangle intersectRect =
527 bounds.intersection( tile.getBounds() );
528 Raster liveRaster = tile.createChild( intersectRect.x,
529 intersectRect.y,
530 intersectRect.width,
531 intersectRect.height,
532 intersectRect.x,
533 intersectRect.y,
534 null );
535 dest.setDataElements( 0, 0, liveRaster );
536 }
537 }
538 return dest;
539 }
540 }
541
542 /**
543 * Copies an arbitrary rectangular region of the RenderedImage
544 * into a caller-supplied WritableRaster. The region to be
545 * computed is determined by clipping the bounds of the supplied
546 * WritableRaster against the bounds of the image. The supplied
547 * WritableRaster must have a SampleModel that is compatible with
548 * that of the image.
549 *
550 * <p> If the raster argument is null, the entire image will
551 * be copied into a newly-created WritableRaster with a SampleModel
552 * that is compatible with that of the image.
553 *
554 * @param dest a WritableRaster to hold the returned portion of
555 * the image.
556 * @return a reference to the supplied WritableRaster, or to a
557 * new WritableRaster if the supplied one was null.
558 */
559 public WritableRaster copyData( WritableRaster dest )
560 {
561 Rectangle bounds;
562 Raster tile;
563
564 if( dest == null )
565 {
566 bounds = getBounds();
567 Point p = new Point( minX, minY );
568 /* A SampleModel to hold the entire image. */
569 SampleModel sm = sampleModel.createCompatibleSampleModel(
570 width, height );
571 dest = Raster.createWritableRaster( sm, p );
572 } else
573 {
574 bounds = dest.getBounds();
575 }
576
577 int startX = XToTileX( bounds.x );
578 int startY = YToTileY( bounds.y );
579 int endX = XToTileX( bounds.x + bounds.width - 1 );
580 int endY = YToTileY( bounds.y + bounds.height - 1 );
581
582 for( int j = startY; j <= endY; j++ )
583 {
584 for( int i = startX; i <= endX; i++ )
585 {
586 tile = getTile( i, j );
587 Rectangle tileRect = tile.getBounds();
588 Rectangle intersectRect =
589 bounds.intersection( tile.getBounds() );
590 Raster liveRaster = tile.createChild( intersectRect.x,
591 intersectRect.y,
592 intersectRect.width,
593 intersectRect.height,
594 intersectRect.x,
595 intersectRect.y,
596 null );
597
598 /*
599 * WritableRaster.setDataElements takes into account of
600 * inRaster's minX and minY and add these to x and y. Since
601 * liveRaster has the origin at the correct location, the
602 * following call should not again give these coordinates in
603 * places of x and y.
604 */
605 dest.setDataElements( 0, 0, liveRaster );
606 }
607 }
608 return dest;
609 }
610 }