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