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