Source code: org/apache/batik/ext/awt/image/codec/SimpleRenderedImage.java
1 /*
2
3 Copyright 2001,2003 The Apache Software Foundation
4
5 Licensed under the Apache License, Version 2.0 (the "License");
6 you may not use this file except in compliance with the License.
7 You may obtain a copy of the License at
8
9 http://www.apache.org/licenses/LICENSE-2.0
10
11 Unless required by applicable law or agreed to in writing, software
12 distributed under the License is distributed on an "AS IS" BASIS,
13 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 See the License for the specific language governing permissions and
15 limitations under the License.
16
17 */
18 package org.apache.batik.ext.awt.image.codec;
19
20 import java.awt.Point;
21 import java.awt.Rectangle;
22 import java.awt.image.ColorModel;
23 import java.awt.image.Raster;
24 import java.awt.image.RenderedImage;
25 import java.awt.image.SampleModel;
26 import java.awt.image.WritableRaster;
27 import java.util.Enumeration;
28 import java.util.Hashtable;
29 import java.util.Iterator;
30 import java.util.Vector;
31
32 /**
33 * A simple class implemented the <code>RenderedImage</code>
34 * interface. Only the <code>getTile()</code> method needs to be
35 * implemented by subclasses. The instance variables must also be
36 * filled in properly.
37 *
38 * <p> Normally in JAI <code>PlanarImage</code> is used for this
39 * purpose, but in the interest of modularity the
40 * use of <code>PlanarImage</code> has been avoided.
41 */
42 public abstract class SimpleRenderedImage implements RenderedImage {
43
44 /** The X coordinate of the image's upper-left pixel. */
45 protected int minX;
46
47 /** The Y coordinate of the image's upper-left pixel. */
48 protected int minY;
49
50 /** The image's width in pixels. */
51 protected int width;
52
53 /** The image's height in pixels. */
54 protected int height;
55
56 /** The width of a tile. */
57 protected int tileWidth;
58
59 /** The height of a tile. */
60 protected int tileHeight;
61
62 /** The X coordinate of the upper-left pixel of tile (0, 0). */
63 protected int tileGridXOffset = 0;
64
65 /** The Y coordinate of the upper-left pixel of tile (0, 0). */
66 protected int tileGridYOffset = 0;
67
68 /** The image's SampleModel. */
69 protected SampleModel sampleModel = null;
70
71 /** The image's ColorModel. */
72 protected ColorModel colorModel = null;
73
74 /** The image's sources, stored in a Vector. */
75 protected Vector sources = new Vector();
76
77 /** A Hashtable containing the image properties. */
78 protected Hashtable properties = new Hashtable();
79
80 public SimpleRenderedImage() {}
81
82 /** Returns the X coordinate of the leftmost column of the image. */
83 public int getMinX() {
84 return minX;
85 }
86
87 /**
88 * Returns the X coordinate of the column immediatetely to the
89 * right of the rightmost column of the image. getMaxX() is
90 * implemented in terms of getMinX() and getWidth() and so does
91 * not need to be implemented by subclasses.
92 */
93 public final int getMaxX() {
94 return getMinX() + getWidth();
95 }
96
97 /** Returns the X coordinate of the uppermost row of the image. */
98 public int getMinY() {
99 return minY;
100 }
101
102 /**
103 * Returns the Y coordinate of the row immediately below the
104 * bottom row of the image. getMaxY() is implemented in terms of
105 * getMinY() and getHeight() and so does not need to be
106 * implemented by subclasses.
107 */
108 public final int getMaxY() {
109 return getMinY() + getHeight();
110 }
111
112 /** Returns the width of the image. */
113 public int getWidth() {
114 return width;
115 }
116
117 /** Returns the height of the image. */
118 public int getHeight() {
119 return height;
120 }
121
122 /** Returns a Rectangle indicating the image bounds. */
123 public Rectangle getBounds() {
124 return new Rectangle(getMinX(), getMinY(),
125 getWidth(), getHeight());
126 }
127
128 /** Returns the width of a tile. */
129 public int getTileWidth() {
130 return tileWidth;
131 }
132
133 /** Returns the height of a tile. */
134 public int getTileHeight() {
135 return tileHeight;
136 }
137
138 /**
139 * Returns the X coordinate of the upper-left pixel of tile (0, 0).
140 */
141 public int getTileGridXOffset() {
142 return tileGridXOffset;
143 }
144
145 /**
146 * Returns the Y coordinate of the upper-left pixel of tile (0, 0).
147 */
148 public int getTileGridYOffset() {
149 return tileGridYOffset;
150 }
151
152 /**
153 * Returns the horizontal index of the leftmost column of tiles.
154 * getMinTileX() is implemented in terms of getMinX()
155 * and so does not need to be implemented by subclasses.
156 */
157 public int getMinTileX() {
158 return XToTileX(getMinX());
159 }
160
161 /**
162 * Returns the horizontal index of the rightmost column of tiles.
163 * getMaxTileX() is implemented in terms of getMaxX()
164 * and so does not need to be implemented by subclasses.
165 */
166 public int getMaxTileX() {
167 return XToTileX(getMaxX() - 1);
168 }
169
170 /**
171 * Returns the number of tiles along the tile grid in the
172 * horizontal direction. getNumXTiles() is implemented in terms
173 * of getMinTileX() and getMaxTileX() and so does not need to be
174 * implemented by subclasses.
175 */
176 public int getNumXTiles() {
177 return getMaxTileX() - getMinTileX() + 1;
178 }
179
180 /**
181 * Returns the vertical index of the uppermost row of tiles. getMinTileY()
182 * is implemented in terms of getMinY() and so does not need to be
183 * implemented by subclasses.
184 */
185 public int getMinTileY() {
186 return YToTileY(getMinY());
187 }
188
189 /**
190 * Returns the vertical index of the bottom row of tiles. getMaxTileY()
191 * is implemented in terms of getMaxY() and so does not need to
192 * be implemented by subclasses.
193 */
194 public int getMaxTileY() {
195 return YToTileY(getMaxY() - 1);
196 }
197
198 /**
199 * Returns the number of tiles along the tile grid in the vertical
200 * direction. getNumYTiles() is implemented in terms
201 * of getMinTileY() and getMaxTileY() and so does not need to be
202 * implemented by subclasses.
203 */
204 public int getNumYTiles() {
205 return getMaxTileY() - getMinTileY() + 1;
206 }
207
208 /** Returns the SampleModel of the image. */
209 public SampleModel getSampleModel() {
210 return sampleModel;
211 }
212
213 /** Returns the ColorModel of the image. */
214 public ColorModel getColorModel() {
215 return colorModel;
216 }
217
218 /**
219 * Gets a property from the property set of this image. If the
220 * property name is not recognized,
221 * <code>java.awt.Image.UndefinedProperty</code> will be returned.
222 *
223 * @param name the name of the property to get, as a
224 * <code>String</code>. @return a reference to the property
225 * <code>Object</code>, or the value
226 * <code>java.awt.Image.UndefinedProperty.</code>
227 */
228 public Object getProperty(String name) {
229 name = name.toLowerCase();
230 return properties.get(name);
231 }
232
233 /**
234 * Returns a list of the properties recognized by this image. If
235 * no properties are available, <code>null</code> will be
236 * returned.
237 *
238 * @return an array of <code>String</code>s representing valid
239 * property names.
240 */
241 public String[] getPropertyNames() {
242 String[] names = new String[properties.size()];
243 int index = 0;
244
245 Enumeration e = properties.keys();
246 while (e.hasMoreElements()) {
247 String name = (String)e.nextElement();
248 names[index++] = name;
249 }
250
251 return names;
252 }
253
254 /**
255 * Returns an array of <code>String</code>s recognized as names by
256 * this property source that begin with the supplied prefix. If
257 * no property names match, <code>null</code> will be returned.
258 * The comparison is done in a case-independent manner.
259 *
260 * <p> The default implementation calls
261 * <code>getPropertyNames()</code> and searches the list of names
262 * for matches.
263 *
264 * @return an array of <code>String</code>s giving the valid
265 * property names.
266 */
267 public String[] getPropertyNames(String prefix) {
268 String propertyNames[] = getPropertyNames();
269 if (propertyNames == null) {
270 return null;
271 }
272
273 prefix = prefix.toLowerCase();
274
275 Vector names = new Vector();
276 for (int i = 0; i < propertyNames.length; i++) {
277 if (propertyNames[i].startsWith(prefix)) {
278 names.addElement(propertyNames[i]);
279 }
280 }
281
282 if (names.size() == 0) {
283 return null;
284 }
285
286 // Copy the strings from the Vector over to a String array.
287 String prefixNames[] = new String[names.size()];
288 int count = 0;
289 for (Iterator it = names.iterator(); it.hasNext(); ) {
290 prefixNames[count++] = (String)it.next();
291 }
292
293 return prefixNames;
294 }
295
296 // Utility methods.
297
298 /**
299 * Converts a pixel's X coordinate into a horizontal tile index
300 * relative to a given tile grid layout specified by its X offset
301 * and tile width.
302 */
303 public static int XToTileX(int x, int tileGridXOffset, int tileWidth) {
304 x -= tileGridXOffset;
305 if (x < 0) {
306 x += 1 - tileWidth; // Force round to -infinity
307 }
308 return x/tileWidth;
309 }
310
311 /**
312 * Converts a pixel's Y coordinate into a vertical tile index
313 * relative to a given tile grid layout specified by its Y offset
314 * and tile height.
315 */
316 public static int YToTileY(int y, int tileGridYOffset, int tileHeight) {
317 y -= tileGridYOffset;
318 if (y < 0) {
319 y += 1 - tileHeight; // Force round to -infinity
320 }
321 return y/tileHeight;
322 }
323
324 /**
325 * Converts a pixel's X coordinate into a horizontal tile index.
326 * This is a convenience method. No attempt is made to detect
327 * out-of-range coordinates.
328 *
329 * @param x the X coordinate of a pixel.
330 * @return the X index of the tile containing the pixel.
331 */
332 public int XToTileX(int x) {
333 return XToTileX(x, getTileGridXOffset(), getTileWidth());
334 }
335
336 /**
337 * Converts a pixel's Y coordinate into a vertical tile index.
338 * This is a convenience method. No attempt is made to detect
339 * out-of-range coordinates.
340 *
341 * @param y the Y coordinate of a pixel.
342 * @return the Y index of the tile containing the pixel.
343 */
344 public int YToTileY(int y) {
345 return YToTileY(y, getTileGridYOffset(), getTileHeight());
346 }
347
348 /**
349 * Converts a horizontal tile index into the X coordinate of its
350 * upper left pixel relative to a given tile grid layout specified
351 * by its X offset and tile width.
352 */
353 public static int tileXToX(int tx, int tileGridXOffset, int tileWidth) {
354 return tx*tileWidth + tileGridXOffset;
355 }
356
357 /**
358 * Converts a vertical tile index into the Y coordinate of
359 * its upper left pixel relative to a given tile grid layout
360 * specified by its Y offset and tile height.
361 */
362 public static int tileYToY(int ty, int tileGridYOffset, int tileHeight) {
363 return ty*tileHeight + tileGridYOffset;
364 }
365
366 /**
367 * Converts a horizontal tile index into the X coordinate of its
368 * upper left pixel. This is a convenience method. No attempt is made
369 * to detect out-of-range indices.
370 *
371 * @param tx the horizontal index of a tile.
372 * @return the X coordinate of the tile's upper left pixel.
373 */
374 public int tileXToX(int tx) {
375 return tx*tileWidth + tileGridXOffset;
376 }
377
378 /**
379 * Converts a vertical tile index into the Y coordinate of its
380 * upper left pixel. This is a convenience method. No attempt is made
381 * to detect out-of-range indices.
382 *
383 * @param ty the vertical index of a tile.
384 * @return the Y coordinate of the tile's upper left pixel.
385 */
386 public int tileYToY(int ty) {
387 return ty*tileHeight + tileGridYOffset;
388 }
389
390 public Vector getSources() {
391 return null;
392 }
393
394 /**
395 * Returns the entire image in a single Raster. For images with
396 * multiple tiles this will require making a copy.
397 *
398 * <p> The returned Raster is semantically a copy. This means
399 * that updates to the source image will not be reflected in the
400 * returned Raster. For non-writable (immutable) source images,
401 * the returned value may be a reference to the image's internal
402 * data. The returned Raster should be considered non-writable;
403 * any attempt to alter its pixel data (such as by casting it to
404 * WritableRaster or obtaining and modifying its DataBuffer) may
405 * result in undefined behavior. The copyData method should be
406 * used if the returned Raster is to be modified.
407 *
408 * @return a Raster containing a copy of this image's data.
409 */
410 public Raster getData() {
411 Rectangle rect = new Rectangle(getMinX(), getMinY(),
412 getWidth(), getHeight());
413 return getData(rect);
414 }
415
416 /**
417 * Returns an arbitrary rectangular region of the RenderedImage
418 * in a Raster. The rectangle of interest will be clipped against
419 * the image bounds.
420 *
421 * <p> The returned Raster is semantically a copy. This means
422 * that updates to the source image will not be reflected in the
423 * returned Raster. For non-writable (immutable) source images,
424 * the returned value may be a reference to the image's internal
425 * data. The returned Raster should be considered non-writable;
426 * any attempt to alter its pixel data (such as by casting it to
427 * WritableRaster or obtaining and modifying its DataBuffer) may
428 * result in undefined behavior. The copyData method should be
429 * used if the returned Raster is to be modified.
430 *
431 * @param bounds the region of the RenderedImage to be returned.
432 */
433 public Raster getData(Rectangle bounds) {
434 int startX = XToTileX(bounds.x);
435 int startY = YToTileY(bounds.y);
436 int endX = XToTileX(bounds.x + bounds.width - 1);
437 int endY = YToTileY(bounds.y + bounds.height - 1);
438 Raster tile;
439
440 if ((startX == endX) && (startY == endY)) {
441 tile = getTile(startX, startY);
442 return tile.createChild(bounds.x, bounds.y,
443 bounds.width, bounds.height,
444 bounds.x, bounds.y, null);
445 } else {
446 // Create a WritableRaster of the desired size
447 SampleModel sm =
448 sampleModel.createCompatibleSampleModel(bounds.width,
449 bounds.height);
450
451 // Translate it
452 WritableRaster dest =
453 Raster.createWritableRaster(sm, bounds.getLocation());
454
455 for (int j = startY; j <= endY; j++) {
456 for (int i = startX; i <= endX; i++) {
457 tile = getTile(i, j);
458 Rectangle intersectRect =
459 bounds.intersection(tile.getBounds());
460 Raster liveRaster = tile.createChild(intersectRect.x,
461 intersectRect.y,
462 intersectRect.width,
463 intersectRect.height,
464 intersectRect.x,
465 intersectRect.y,
466 null);
467 dest.setDataElements(0, 0, liveRaster);
468 }
469 }
470 return dest;
471 }
472 }
473
474 /**
475 * Copies an arbitrary rectangular region of the RenderedImage
476 * into a caller-supplied WritableRaster. The region to be
477 * computed is determined by clipping the bounds of the supplied
478 * WritableRaster against the bounds of the image. The supplied
479 * WritableRaster must have a SampleModel that is compatible with
480 * that of the image.
481 *
482 * <p> If the raster argument is null, the entire image will
483 * be copied into a newly-created WritableRaster with a SampleModel
484 * that is compatible with that of the image.
485 *
486 * @param dest a WritableRaster to hold the returned portion of
487 * the image.
488 * @return a reference to the supplied WritableRaster, or to a
489 * new WritableRaster if the supplied one was null.
490 */
491 public WritableRaster copyData(WritableRaster dest) {
492 Rectangle bounds;
493 Raster tile;
494
495 if (dest == null) {
496 bounds = getBounds();
497 Point p = new Point(minX, minY);
498 /* A SampleModel to hold the entire image. */
499 SampleModel sm = sampleModel.createCompatibleSampleModel(
500 width, height);
501 dest = Raster.createWritableRaster(sm, p);
502 } else {
503 bounds = dest.getBounds();
504 }
505
506 int startX = XToTileX(bounds.x);
507 int startY = YToTileY(bounds.y);
508 int endX = XToTileX(bounds.x + bounds.width - 1);
509 int endY = YToTileY(bounds.y + bounds.height - 1);
510
511 for (int j = startY; j <= endY; j++) {
512 for (int i = startX; i <= endX; i++) {
513 tile = getTile(i, j);
514 Rectangle intersectRect =
515 bounds.intersection(tile.getBounds());
516 Raster liveRaster = tile.createChild(intersectRect.x,
517 intersectRect.y,
518 intersectRect.width,
519 intersectRect.height,
520 intersectRect.x,
521 intersectRect.y,
522 null);
523
524 /*
525 * WritableRaster.setDataElements takes into account of
526 * inRaster's minX and minY and add these to x and y. Since
527 * liveRaster has the origin at the correct location, the
528 * following call should not again give these coordinates in
529 * places of x and y.
530 */
531 dest.setDataElements(0, 0, liveRaster);
532 }
533 }
534 return dest;
535 }
536 }