1 /* 2 * Copyright (c) 1997, 2017, 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 26 package java.awt.image; 27 28 import java.awt.Graphics2D; 29 import java.awt.GraphicsEnvironment; 30 import java.awt.Point; 31 import java.awt.Rectangle; 32 import java.awt.Transparency; 33 import java.awt.GraphicsConfiguration; 34 import java.awt.color.ColorSpace; 35 import java.security.AccessController; 36 import java.security.PrivilegedAction; 37 import java.util.Hashtable; 38 import java.util.Set; 39 import java.util.Vector; 40 41 import sun.awt.image.ByteComponentRaster; 42 import sun.awt.image.BytePackedRaster; 43 import sun.awt.image.IntegerComponentRaster; 44 import sun.awt.image.OffScreenImageSource; 45 import sun.awt.image.ShortComponentRaster; 46 47 /** 48 * 49 * The {@code BufferedImage} subclass describes an {@link 50 * java.awt.Image Image} with an accessible buffer of image data. 51 * A {@code BufferedImage} is comprised of a {@link ColorModel} and a 52 * {@link Raster} of image data. 53 * The number and types of bands in the {@link SampleModel} of the 54 * {@code Raster} must match the number and types required by the 55 * {@code ColorModel} to represent its color and alpha components. 56 * All {@code BufferedImage} objects have an upper left corner 57 * coordinate of (0, 0). Any {@code Raster} used to construct a 58 * {@code BufferedImage} must therefore have minX=0 and minY=0. 59 * 60 * <p> 61 * This class relies on the data fetching and setting methods 62 * of {@code Raster}, 63 * and on the color characterization methods of {@code ColorModel}. 64 * 65 * @see ColorModel 66 * @see Raster 67 * @see WritableRaster 68 */ 69 public class BufferedImage extends java.awt.Image 70 implements WritableRenderedImage, Transparency 71 { 72 private int imageType = TYPE_CUSTOM; 73 private ColorModel colorModel; 74 private final WritableRaster raster; 75 private OffScreenImageSource osis; 76 private Hashtable<String, Object> properties; 77 private GraphicsConfiguration graphicsConfig = null; 78 79 /** 80 * Image Type Constants 81 */ 82 83 /** 84 * Image type is not recognized so it must be a customized 85 * image. This type is only used as a return value for the getType() 86 * method. 87 */ 88 public static final int TYPE_CUSTOM = 0; 89 90 /** 91 * Represents an image with 8-bit RGB color components packed into 92 * integer pixels. The image has a {@link DirectColorModel} without 93 * alpha. 94 * When data with non-opaque alpha is stored 95 * in an image of this type, 96 * the color data must be adjusted to a non-premultiplied form 97 * and the alpha discarded, 98 * as described in the 99 * {@link java.awt.AlphaComposite} documentation. 100 */ 101 public static final int TYPE_INT_RGB = 1; 102 103 /** 104 * Represents an image with 8-bit RGBA color components packed into 105 * integer pixels. The image has a {@code DirectColorModel} 106 * with alpha. The color data in this image is considered not to be 107 * premultiplied with alpha. When this type is used as the 108 * {@code imageType} argument to a {@code BufferedImage} 109 * constructor, the created image is consistent with images 110 * created in the JDK1.1 and earlier releases. 111 */ 112 public static final int TYPE_INT_ARGB = 2; 113 114 /** 115 * Represents an image with 8-bit RGBA color components packed into 116 * integer pixels. The image has a {@code DirectColorModel} 117 * with alpha. The color data in this image is considered to be 118 * premultiplied with alpha. 119 */ 120 public static final int TYPE_INT_ARGB_PRE = 3; 121 122 /** 123 * Represents an image with 8-bit RGB color components, corresponding 124 * to a Windows- or Solaris- style BGR color model, with the colors 125 * Blue, Green, and Red packed into integer pixels. There is no alpha. 126 * The image has a {@link DirectColorModel}. 127 * When data with non-opaque alpha is stored 128 * in an image of this type, 129 * the color data must be adjusted to a non-premultiplied form 130 * and the alpha discarded, 131 * as described in the 132 * {@link java.awt.AlphaComposite} documentation. 133 */ 134 public static final int TYPE_INT_BGR = 4; 135 136 /** 137 * Represents an image with 8-bit RGB color components, corresponding 138 * to a Windows-style BGR color model) with the colors Blue, Green, 139 * and Red stored in 3 bytes. There is no alpha. The image has a 140 * {@code ComponentColorModel}. 141 * When data with non-opaque alpha is stored 142 * in an image of this type, 143 * the color data must be adjusted to a non-premultiplied form 144 * and the alpha discarded, 145 * as described in the 146 * {@link java.awt.AlphaComposite} documentation. 147 */ 148 public static final int TYPE_3BYTE_BGR = 5; 149 150 /** 151 * Represents an image with 8-bit RGBA color components with the colors 152 * Blue, Green, and Red stored in 3 bytes and 1 byte of alpha. The 153 * image has a {@code ComponentColorModel} with alpha. The 154 * color data in this image is considered not to be premultiplied with 155 * alpha. The byte data is interleaved in a single 156 * byte array in the order A, B, G, R 157 * from lower to higher byte addresses within each pixel. 158 */ 159 public static final int TYPE_4BYTE_ABGR = 6; 160 161 /** 162 * Represents an image with 8-bit RGBA color components with the colors 163 * Blue, Green, and Red stored in 3 bytes and 1 byte of alpha. The 164 * image has a {@code ComponentColorModel} with alpha. The color 165 * data in this image is considered to be premultiplied with alpha. 166 * The byte data is interleaved in a single byte array in the order 167 * A, B, G, R from lower to higher byte addresses within each pixel. 168 */ 169 public static final int TYPE_4BYTE_ABGR_PRE = 7; 170 171 /** 172 * Represents an image with 5-6-5 RGB color components (5-bits red, 173 * 6-bits green, 5-bits blue) with no alpha. This image has 174 * a {@code DirectColorModel}. 175 * When data with non-opaque alpha is stored 176 * in an image of this type, 177 * the color data must be adjusted to a non-premultiplied form 178 * and the alpha discarded, 179 * as described in the 180 * {@link java.awt.AlphaComposite} documentation. 181 */ 182 public static final int TYPE_USHORT_565_RGB = 8; 183 184 /** 185 * Represents an image with 5-5-5 RGB color components (5-bits red, 186 * 5-bits green, 5-bits blue) with no alpha. This image has 187 * a {@code DirectColorModel}. 188 * When data with non-opaque alpha is stored 189 * in an image of this type, 190 * the color data must be adjusted to a non-premultiplied form 191 * and the alpha discarded, 192 * as described in the 193 * {@link java.awt.AlphaComposite} documentation. 194 */ 195 public static final int TYPE_USHORT_555_RGB = 9; 196 197 /** 198 * Represents a unsigned byte grayscale image, non-indexed. This 199 * image has a {@code ComponentColorModel} with a CS_GRAY 200 * {@link ColorSpace}. 201 * When data with non-opaque alpha is stored 202 * in an image of this type, 203 * the color data must be adjusted to a non-premultiplied form 204 * and the alpha discarded, 205 * as described in the 206 * {@link java.awt.AlphaComposite} documentation. 207 */ 208 public static final int TYPE_BYTE_GRAY = 10; 209 210 /** 211 * Represents an unsigned short grayscale image, non-indexed). This 212 * image has a {@code ComponentColorModel} with a CS_GRAY 213 * {@code ColorSpace}. 214 * When data with non-opaque alpha is stored 215 * in an image of this type, 216 * the color data must be adjusted to a non-premultiplied form 217 * and the alpha discarded, 218 * as described in the 219 * {@link java.awt.AlphaComposite} documentation. 220 */ 221 public static final int TYPE_USHORT_GRAY = 11; 222 223 /** 224 * Represents an opaque byte-packed 1, 2, or 4 bit image. The 225 * image has an {@link IndexColorModel} without alpha. When this 226 * type is used as the {@code imageType} argument to the 227 * {@code BufferedImage} constructor that takes an 228 * {@code imageType} argument but no {@code ColorModel} 229 * argument, a 1-bit image is created with an 230 * {@code IndexColorModel} with two colors in the default 231 * sRGB {@code ColorSpace}: {0, 0, 0} and 232 * {255, 255, 255}. 233 * 234 * <p> Images with 2 or 4 bits per pixel may be constructed via 235 * the {@code BufferedImage} constructor that takes a 236 * {@code ColorModel} argument by supplying a 237 * {@code ColorModel} with an appropriate map size. 238 * 239 * <p> Images with 8 bits per pixel should use the image types 240 * {@code TYPE_BYTE_INDEXED} or {@code TYPE_BYTE_GRAY} 241 * depending on their {@code ColorModel}. 242 243 * <p> When color data is stored in an image of this type, 244 * the closest color in the colormap is determined 245 * by the {@code IndexColorModel} and the resulting index is stored. 246 * Approximation and loss of alpha or color components 247 * can result, depending on the colors in the 248 * {@code IndexColorModel} colormap. 249 */ 250 public static final int TYPE_BYTE_BINARY = 12; 251 252 /** 253 * Represents an indexed byte image. When this type is used as the 254 * {@code imageType} argument to the {@code BufferedImage} 255 * constructor that takes an {@code imageType} argument 256 * but no {@code ColorModel} argument, an 257 * {@code IndexColorModel} is created with 258 * a 256-color 6/6/6 color cube palette with the rest of the colors 259 * from 216-255 populated by grayscale values in the 260 * default sRGB ColorSpace. 261 * 262 * <p> When color data is stored in an image of this type, 263 * the closest color in the colormap is determined 264 * by the {@code IndexColorModel} and the resulting index is stored. 265 * Approximation and loss of alpha or color components 266 * can result, depending on the colors in the 267 * {@code IndexColorModel} colormap. 268 */ 269 public static final int TYPE_BYTE_INDEXED = 13; 270 271 private static final int DCM_RED_MASK = 0x00ff0000; 272 private static final int DCM_GREEN_MASK = 0x0000ff00; 273 private static final int DCM_BLUE_MASK = 0x000000ff; 274 private static final int DCM_ALPHA_MASK = 0xff000000; 275 private static final int DCM_565_RED_MASK = 0xf800; 276 private static final int DCM_565_GRN_MASK = 0x07E0; 277 private static final int DCM_565_BLU_MASK = 0x001F; 278 private static final int DCM_555_RED_MASK = 0x7C00; 279 private static final int DCM_555_GRN_MASK = 0x03E0; 280 private static final int DCM_555_BLU_MASK = 0x001F; 281 private static final int DCM_BGR_RED_MASK = 0x0000ff; 282 private static final int DCM_BGR_GRN_MASK = 0x00ff00; 283 private static final int DCM_BGR_BLU_MASK = 0xff0000; 284 285 286 private static native void initIDs(); 287 static { 288 ColorModel.loadLibraries(); 289 initIDs(); 290 } 291 292 /** 293 * Constructs a {@code BufferedImage} of one of the predefined 294 * image types. The {@code ColorSpace} for the image is the 295 * default sRGB space. 296 * @param config graphics configuration 297 * @param width width of the created image 298 * @param height height of the created image 299 * @param imageType type of the created image 300 */ 301 public BufferedImage(GraphicsConfiguration config, int width, 302 int height, 303 int imageType) { 304 this(width, height, imageType); 305 this.graphicsConfig = config; 306 } 307 308 /** 309 * Constructs a {@code BufferedImage} of one of the predefined 310 * image types. The {@code ColorSpace} for the image is the 311 * default sRGB space. 312 * @param width width of the created image 313 * @param height height of the created image 314 * @param imageType type of the created image 315 * @see ColorSpace 316 * @see #TYPE_INT_RGB 317 * @see #TYPE_INT_ARGB 318 * @see #TYPE_INT_ARGB_PRE 319 * @see #TYPE_INT_BGR 320 * @see #TYPE_3BYTE_BGR 321 * @see #TYPE_4BYTE_ABGR 322 * @see #TYPE_4BYTE_ABGR_PRE 323 * @see #TYPE_BYTE_GRAY 324 * @see #TYPE_USHORT_GRAY 325 * @see #TYPE_BYTE_BINARY 326 * @see #TYPE_BYTE_INDEXED 327 * @see #TYPE_USHORT_565_RGB 328 * @see #TYPE_USHORT_555_RGB 329 */ 330 public BufferedImage(int width, 331 int height, 332 int imageType) { 333 switch (imageType) { 334 case TYPE_INT_RGB: 335 { 336 colorModel = new DirectColorModel(24, 337 0x00ff0000, // Red 338 0x0000ff00, // Green 339 0x000000ff, // Blue 340 0x0 // Alpha 341 ); 342 raster = colorModel.createCompatibleWritableRaster(width, 343 height); 344 } 345 break; 346 347 case TYPE_INT_ARGB: 348 { 349 colorModel = ColorModel.getRGBdefault(); 350 351 raster = colorModel.createCompatibleWritableRaster(width, 352 height); 353 } 354 break; 355 356 case TYPE_INT_ARGB_PRE: 357 { 358 colorModel = new 359 DirectColorModel( 360 ColorSpace.getInstance(ColorSpace.CS_sRGB), 361 32, 362 0x00ff0000,// Red 363 0x0000ff00,// Green 364 0x000000ff,// Blue 365 0xff000000,// Alpha 366 true, // Alpha Premultiplied 367 DataBuffer.TYPE_INT 368 ); 369 raster = colorModel.createCompatibleWritableRaster(width, 370 height); 371 } 372 break; 373 374 case TYPE_INT_BGR: 375 { 376 colorModel = new DirectColorModel(24, 377 0x000000ff, // Red 378 0x0000ff00, // Green 379 0x00ff0000 // Blue 380 ); 381 raster = colorModel.createCompatibleWritableRaster(width, 382 height); 383 } 384 break; 385 386 case TYPE_3BYTE_BGR: 387 { 388 ColorSpace cs = ColorSpace.getInstance(ColorSpace.CS_sRGB); 389 int[] nBits = {8, 8, 8}; 390 int[] bOffs = {2, 1, 0}; 391 colorModel = new ComponentColorModel(cs, nBits, false, false, 392 Transparency.OPAQUE, 393 DataBuffer.TYPE_BYTE); 394 raster = Raster.createInterleavedRaster(DataBuffer.TYPE_BYTE, 395 width, height, 396 width*3, 3, 397 bOffs, null); 398 } 399 break; 400 401 case TYPE_4BYTE_ABGR: 402 { 403 ColorSpace cs = ColorSpace.getInstance(ColorSpace.CS_sRGB); 404 int[] nBits = {8, 8, 8, 8}; 405 int[] bOffs = {3, 2, 1, 0}; 406 colorModel = new ComponentColorModel(cs, nBits, true, false, 407 Transparency.TRANSLUCENT, 408 DataBuffer.TYPE_BYTE); 409 raster = Raster.createInterleavedRaster(DataBuffer.TYPE_BYTE, 410 width, height, 411 width*4, 4, 412 bOffs, null); 413 } 414 break; 415 416 case TYPE_4BYTE_ABGR_PRE: 417 { 418 ColorSpace cs = ColorSpace.getInstance(ColorSpace.CS_sRGB); 419 int[] nBits = {8, 8, 8, 8}; 420 int[] bOffs = {3, 2, 1, 0}; 421 colorModel = new ComponentColorModel(cs, nBits, true, true, 422 Transparency.TRANSLUCENT, 423 DataBuffer.TYPE_BYTE); 424 raster = Raster.createInterleavedRaster(DataBuffer.TYPE_BYTE, 425 width, height, 426 width*4, 4, 427 bOffs, null); 428 } 429 break; 430 431 case TYPE_BYTE_GRAY: 432 { 433 ColorSpace cs = ColorSpace.getInstance(ColorSpace.CS_GRAY); 434 int[] nBits = {8}; 435 colorModel = new ComponentColorModel(cs, nBits, false, true, 436 Transparency.OPAQUE, 437 DataBuffer.TYPE_BYTE); 438 raster = colorModel.createCompatibleWritableRaster(width, 439 height); 440 } 441 break; 442 443 case TYPE_USHORT_GRAY: 444 { 445 ColorSpace cs = ColorSpace.getInstance(ColorSpace.CS_GRAY); 446 int[] nBits = {16}; 447 colorModel = new ComponentColorModel(cs, nBits, false, true, 448 Transparency.OPAQUE, 449 DataBuffer.TYPE_USHORT); 450 raster = colorModel.createCompatibleWritableRaster(width, 451 height); 452 } 453 break; 454 455 case TYPE_BYTE_BINARY: 456 { 457 byte[] arr = {(byte)0, (byte)0xff}; 458 459 colorModel = new IndexColorModel(1, 2, arr, arr, arr); 460 raster = Raster.createPackedRaster(DataBuffer.TYPE_BYTE, 461 width, height, 1, 1, null); 462 } 463 break; 464 465 case TYPE_BYTE_INDEXED: 466 { 467 // Create a 6x6x6 color cube 468 int[] cmap = new int[256]; 469 int i=0; 470 for (int r=0; r < 256; r += 51) { 471 for (int g=0; g < 256; g += 51) { 472 for (int b=0; b < 256; b += 51) { 473 cmap[i++] = (r<<16)|(g<<8)|b; 474 } 475 } 476 } 477 // And populate the rest of the cmap with gray values 478 int grayIncr = 256/(256-i); 479 480 // The gray ramp will be between 18 and 252 481 int gray = grayIncr*3; 482 for (; i < 256; i++) { 483 cmap[i] = (gray<<16)|(gray<<8)|gray; 484 gray += grayIncr; 485 } 486 487 colorModel = new IndexColorModel(8, 256, cmap, 0, false, -1, 488 DataBuffer.TYPE_BYTE); 489 raster = Raster.createInterleavedRaster(DataBuffer.TYPE_BYTE, 490 width, height, 1, null); 491 } 492 break; 493 494 case TYPE_USHORT_565_RGB: 495 { 496 colorModel = new DirectColorModel(16, 497 DCM_565_RED_MASK, 498 DCM_565_GRN_MASK, 499 DCM_565_BLU_MASK 500 ); 501 raster = colorModel.createCompatibleWritableRaster(width, 502 height); 503 } 504 break; 505 506 case TYPE_USHORT_555_RGB: 507 { 508 colorModel = new DirectColorModel(15, 509 DCM_555_RED_MASK, 510 DCM_555_GRN_MASK, 511 DCM_555_BLU_MASK 512 ); 513 raster = colorModel.createCompatibleWritableRaster(width, 514 height); 515 } 516 break; 517 518 default: 519 throw new IllegalArgumentException ("Unknown image type " + 520 imageType); 521 } 522 523 this.imageType = imageType; 524 } 525 526 /** 527 * Constructs a {@code BufferedImage} of one of the predefined 528 * image types: 529 * TYPE_BYTE_BINARY or TYPE_BYTE_INDEXED. 530 * 531 * <p> If the image type is TYPE_BYTE_BINARY, the number of 532 * entries in the color model is used to determine whether the 533 * image should have 1, 2, or 4 bits per pixel. If the color model 534 * has 1 or 2 entries, the image will have 1 bit per pixel. If it 535 * has 3 or 4 entries, the image with have 2 bits per pixel. If 536 * it has between 5 and 16 entries, the image will have 4 bits per 537 * pixel. Otherwise, an IllegalArgumentException will be thrown. 538 * 539 * @param width width of the created image 540 * @param height height of the created image 541 * @param imageType type of the created image 542 * @param cm {@code IndexColorModel} of the created image 543 * @throws IllegalArgumentException if the imageType is not 544 * TYPE_BYTE_BINARY or TYPE_BYTE_INDEXED or if the imageType is 545 * TYPE_BYTE_BINARY and the color map has more than 16 entries. 546 * @see #TYPE_BYTE_BINARY 547 * @see #TYPE_BYTE_INDEXED 548 */ 549 public BufferedImage (int width, 550 int height, 551 int imageType, 552 IndexColorModel cm) { 553 if (cm.hasAlpha() && cm.isAlphaPremultiplied()) { 554 throw new IllegalArgumentException("This image types do not have "+ 555 "premultiplied alpha."); 556 } 557 558 switch(imageType) { 559 case TYPE_BYTE_BINARY: 560 int bits; // Will be set below 561 int mapSize = cm.getMapSize(); 562 if (mapSize <= 2) { 563 bits = 1; 564 } else if (mapSize <= 4) { 565 bits = 2; 566 } else if (mapSize <= 16) { 567 bits = 4; 568 } else { 569 throw new IllegalArgumentException 570 ("Color map for TYPE_BYTE_BINARY " + 571 "must have no more than 16 entries"); 572 } 573 raster = Raster.createPackedRaster(DataBuffer.TYPE_BYTE, 574 width, height, 1, bits, null); 575 break; 576 577 case TYPE_BYTE_INDEXED: 578 raster = Raster.createInterleavedRaster(DataBuffer.TYPE_BYTE, 579 width, height, 1, null); 580 break; 581 default: 582 throw new IllegalArgumentException("Invalid image type (" + 583 imageType+"). Image type must"+ 584 " be either TYPE_BYTE_BINARY or "+ 585 " TYPE_BYTE_INDEXED"); 586 } 587 588 if (!cm.isCompatibleRaster(raster)) { 589 throw new IllegalArgumentException("Incompatible image type and IndexColorModel"); 590 } 591 592 colorModel = cm; 593 this.imageType = imageType; 594 } 595 596 /** 597 * Constructs a new {@code BufferedImage} with a specified 598 * {@code ColorModel} and {@code Raster}. If the number and 599 * types of bands in the {@code SampleModel} of the 600 * {@code Raster} do not match the number and types required by 601 * the {@code ColorModel} to represent its color and alpha 602 * components, a {@link RasterFormatException} is thrown. This 603 * method can multiply or divide the color {@code Raster} data by 604 * alpha to match the {@code alphaPremultiplied} state 605 * in the {@code ColorModel}. Properties for this 606 * {@code BufferedImage} can be established by passing 607 * in a {@link Hashtable} of {@code String}/{@code Object} 608 * pairs. 609 * @param cm {@code ColorModel} for the new image 610 * @param raster {@code Raster} for the image data 611 * @param isRasterPremultiplied if {@code true}, the data in 612 * the raster has been premultiplied with alpha. 613 * @param properties {@code Hashtable} of 614 * {@code String}/{@code Object} pairs. 615 * @exception RasterFormatException if the number and 616 * types of bands in the {@code SampleModel} of the 617 * {@code Raster} do not match the number and types required by 618 * the {@code ColorModel} to represent its color and alpha 619 * components. 620 * @exception IllegalArgumentException if 621 * {@code raster} is incompatible with {@code cm} 622 * @see ColorModel 623 * @see Raster 624 * @see WritableRaster 625 */ 626 627 628 /* 629 * 630 * FOR NOW THE CODE WHICH DEFINES THE RASTER TYPE IS DUPLICATED BY DVF 631 * SEE THE METHOD DEFINERASTERTYPE @ RASTEROUTPUTMANAGER 632 * 633 */ 634 public BufferedImage (ColorModel cm, 635 WritableRaster raster, 636 boolean isRasterPremultiplied, 637 Hashtable<?,?> properties) { 638 639 if (!cm.isCompatibleRaster(raster)) { 640 throw new 641 IllegalArgumentException("Raster "+raster+ 642 " is incompatible with ColorModel "+ 643 cm); 644 } 645 646 if ((raster.minX != 0) || (raster.minY != 0)) { 647 throw new 648 IllegalArgumentException("Raster "+raster+ 649 " has minX or minY not equal to zero: " 650 + raster.minX + " " + raster.minY); 651 } 652 653 colorModel = cm; 654 this.raster = raster; 655 if (properties != null && !properties.isEmpty()) { 656 this.properties = new Hashtable<>(); 657 for (final Object key : properties.keySet()) { 658 if (key instanceof String) { 659 this.properties.put((String) key, properties.get(key)); 660 } 661 } 662 } 663 int numBands = raster.getNumBands(); 664 boolean isAlphaPre = cm.isAlphaPremultiplied(); 665 final boolean isStandard = isStandard(cm, raster); 666 ColorSpace cs; 667 668 // Force the raster data alpha state to match the premultiplied 669 // state in the color model 670 coerceData(isRasterPremultiplied); 671 672 SampleModel sm = raster.getSampleModel(); 673 cs = cm.getColorSpace(); 674 int csType = cs.getType(); 675 if (csType != ColorSpace.TYPE_RGB) { 676 if (csType == ColorSpace.TYPE_GRAY && 677 isStandard && 678 cm instanceof ComponentColorModel) { 679 // Check if this might be a child raster (fix for bug 4240596) 680 if (sm instanceof ComponentSampleModel && 681 ((ComponentSampleModel)sm).getPixelStride() != numBands) { 682 imageType = TYPE_CUSTOM; 683 } else if (raster instanceof ByteComponentRaster && 684 raster.getNumBands() == 1 && 685 cm.getComponentSize(0) == 8 && 686 ((ByteComponentRaster)raster).getPixelStride() == 1) { 687 imageType = TYPE_BYTE_GRAY; 688 } else if (raster instanceof ShortComponentRaster && 689 raster.getNumBands() == 1 && 690 cm.getComponentSize(0) == 16 && 691 ((ShortComponentRaster)raster).getPixelStride() == 1) { 692 imageType = TYPE_USHORT_GRAY; 693 } 694 } else { 695 imageType = TYPE_CUSTOM; 696 } 697 return; 698 } 699 700 if ((raster instanceof IntegerComponentRaster) && 701 (numBands == 3 || numBands == 4)) { 702 IntegerComponentRaster iraster = 703 (IntegerComponentRaster) raster; 704 // Check if the raster params and the color model 705 // are correct 706 int pixSize = cm.getPixelSize(); 707 if (iraster.getPixelStride() == 1 && 708 isStandard && 709 cm instanceof DirectColorModel && 710 (pixSize == 32 || pixSize == 24)) 711 { 712 // Now check on the DirectColorModel params 713 DirectColorModel dcm = (DirectColorModel) cm; 714 int rmask = dcm.getRedMask(); 715 int gmask = dcm.getGreenMask(); 716 int bmask = dcm.getBlueMask(); 717 if (rmask == DCM_RED_MASK && gmask == DCM_GREEN_MASK && 718 bmask == DCM_BLUE_MASK) 719 { 720 if (dcm.getAlphaMask() == DCM_ALPHA_MASK) { 721 imageType = (isAlphaPre 722 ? TYPE_INT_ARGB_PRE 723 : TYPE_INT_ARGB); 724 } 725 else { 726 // No Alpha 727 if (!dcm.hasAlpha()) { 728 imageType = TYPE_INT_RGB; 729 } 730 } 731 } // if (dcm.getRedMask() == DCM_RED_MASK && 732 else if (rmask == DCM_BGR_RED_MASK && gmask == DCM_BGR_GRN_MASK 733 && bmask == DCM_BGR_BLU_MASK) { 734 if (!dcm.hasAlpha()) { 735 imageType = TYPE_INT_BGR; 736 } 737 } // if (rmask == DCM_BGR_RED_MASK && 738 } // if (iraster.getPixelStride() == 1 739 } // ((raster instanceof IntegerComponentRaster) && 740 else if ((cm instanceof IndexColorModel) && (numBands == 1) && 741 isStandard && 742 (!cm.hasAlpha() || !isAlphaPre)) 743 { 744 IndexColorModel icm = (IndexColorModel) cm; 745 int pixSize = icm.getPixelSize(); 746 747 if (raster instanceof BytePackedRaster) { 748 imageType = TYPE_BYTE_BINARY; 749 } // if (raster instanceof BytePackedRaster) 750 else if (raster instanceof ByteComponentRaster) { 751 ByteComponentRaster braster = (ByteComponentRaster) raster; 752 if (braster.getPixelStride() == 1 && pixSize <= 8) { 753 imageType = TYPE_BYTE_INDEXED; 754 } 755 } 756 } // else if (cm instanceof IndexColorModel) && (numBands == 1)) 757 else if ((raster instanceof ShortComponentRaster) 758 && (cm instanceof DirectColorModel) 759 && isStandard 760 && (numBands == 3) 761 && !cm.hasAlpha()) 762 { 763 DirectColorModel dcm = (DirectColorModel) cm; 764 if (dcm.getRedMask() == DCM_565_RED_MASK) { 765 if (dcm.getGreenMask() == DCM_565_GRN_MASK && 766 dcm.getBlueMask() == DCM_565_BLU_MASK) { 767 imageType = TYPE_USHORT_565_RGB; 768 } 769 } 770 else if (dcm.getRedMask() == DCM_555_RED_MASK) { 771 if (dcm.getGreenMask() == DCM_555_GRN_MASK && 772 dcm.getBlueMask() == DCM_555_BLU_MASK) { 773 imageType = TYPE_USHORT_555_RGB; 774 } 775 } 776 } // else if ((cm instanceof IndexColorModel) && (numBands == 1)) 777 else if ((raster instanceof ByteComponentRaster) 778 && (cm instanceof ComponentColorModel) 779 && isStandard 780 && (raster.getSampleModel() instanceof PixelInterleavedSampleModel) 781 && (numBands == 3 || numBands == 4)) 782 { 783 ComponentColorModel ccm = (ComponentColorModel) cm; 784 PixelInterleavedSampleModel csm = 785 (PixelInterleavedSampleModel)raster.getSampleModel(); 786 ByteComponentRaster braster = (ByteComponentRaster) raster; 787 int[] offs = csm.getBandOffsets(); 788 if (ccm.getNumComponents() != numBands) { 789 throw new RasterFormatException("Number of components in "+ 790 "ColorModel ("+ 791 ccm.getNumComponents()+ 792 ") does not match # in "+ 793 " Raster ("+numBands+")"); 794 } 795 int[] nBits = ccm.getComponentSize(); 796 boolean is8bit = true; 797 for (int i=0; i < numBands; i++) { 798 if (nBits[i] != 8) { 799 is8bit = false; 800 break; 801 } 802 } 803 if (is8bit && 804 braster.getPixelStride() == numBands && 805 offs[0] == numBands-1 && 806 offs[1] == numBands-2 && 807 offs[2] == numBands-3) 808 { 809 if (numBands == 3 && !ccm.hasAlpha()) { 810 imageType = TYPE_3BYTE_BGR; 811 } 812 else if (offs[3] == 0 && ccm.hasAlpha()) { 813 imageType = (isAlphaPre 814 ? TYPE_4BYTE_ABGR_PRE 815 : TYPE_4BYTE_ABGR); 816 } 817 } 818 } // else if ((raster instanceof ByteComponentRaster) && 819 } 820 821 private static boolean isStandard(ColorModel cm, WritableRaster wr) { 822 final Class<? extends ColorModel> cmClass = cm.getClass(); 823 final Class<? extends WritableRaster> wrClass = wr.getClass(); 824 final Class<? extends SampleModel> smClass = wr.getSampleModel().getClass(); 825 826 final PrivilegedAction<Boolean> checkClassLoadersAction = 827 new PrivilegedAction<Boolean>() 828 { 829 830 @Override 831 public Boolean run() { 832 final ClassLoader std = System.class.getClassLoader(); 833 834 return (cmClass.getClassLoader() == std) && 835 (smClass.getClassLoader() == std) && 836 (wrClass.getClassLoader() == std); 837 } 838 }; 839 return AccessController.doPrivileged(checkClassLoadersAction); 840 } 841 842 /** 843 * Returns the image type. If it is not one of the known types, 844 * TYPE_CUSTOM is returned. 845 * @return the image type of this {@code BufferedImage}. 846 * @see #TYPE_INT_RGB 847 * @see #TYPE_INT_ARGB 848 * @see #TYPE_INT_ARGB_PRE 849 * @see #TYPE_INT_BGR 850 * @see #TYPE_3BYTE_BGR 851 * @see #TYPE_4BYTE_ABGR 852 * @see #TYPE_4BYTE_ABGR_PRE 853 * @see #TYPE_BYTE_GRAY 854 * @see #TYPE_BYTE_BINARY 855 * @see #TYPE_BYTE_INDEXED 856 * @see #TYPE_USHORT_GRAY 857 * @see #TYPE_USHORT_565_RGB 858 * @see #TYPE_USHORT_555_RGB 859 * @see #TYPE_CUSTOM 860 */ 861 public int getType() { 862 return imageType; 863 } 864 865 /** 866 * Returns the {@code ColorModel}. 867 * @return the {@code ColorModel} of this 868 * {@code BufferedImage}. 869 */ 870 public ColorModel getColorModel() { 871 return colorModel; 872 } 873 874 /** 875 * Returns the {@code GraphicsConfiguration}. 876 * @return the {@code GraphicsConfiguration} of this 877 * {@code BufferedImage}. 878 */ 879 public GraphicsConfiguration getGraphicsConfig() { 880 return graphicsConfig; 881 } 882 883 /** 884 * Returns the {@link WritableRaster}. 885 * @return the {@code WritableRaster} of this 886 * {@code BufferedImage}. 887 */ 888 public WritableRaster getRaster() { 889 return raster; 890 } 891 892 893 /** 894 * Returns a {@code WritableRaster} representing the alpha 895 * channel for {@code BufferedImage} objects 896 * with {@code ColorModel} objects that support a separate 897 * spatial alpha channel, such as {@code ComponentColorModel} and 898 * {@code DirectColorModel}. Returns {@code null} if there 899 * is no alpha channel associated with the {@code ColorModel} in 900 * this image. This method assumes that for all 901 * {@code ColorModel} objects other than 902 * {@code IndexColorModel}, if the {@code ColorModel} 903 * supports alpha, there is a separate alpha channel 904 * which is stored as the last band of image data. 905 * If the image uses an {@code IndexColorModel} that 906 * has alpha in the lookup table, this method returns 907 * {@code null} since there is no spatially discrete alpha 908 * channel. This method creates a new 909 * {@code WritableRaster}, but shares the data array. 910 * @return a {@code WritableRaster} or {@code null} if this 911 * {@code BufferedImage} has no alpha channel associated 912 * with its {@code ColorModel}. 913 */ 914 public WritableRaster getAlphaRaster() { 915 return colorModel.getAlphaRaster(raster); 916 } 917 918 /** 919 * Returns an integer pixel in the default RGB color model 920 * (TYPE_INT_ARGB) and default sRGB colorspace. Color 921 * conversion takes place if this default model does not match 922 * the image {@code ColorModel}. There are only 8-bits of 923 * precision for each color component in the returned data when using 924 * this method. 925 * 926 * <p> 927 * 928 * An {@code ArrayOutOfBoundsException} may be thrown 929 * if the coordinates are not in bounds. 930 * However, explicit bounds checking is not guaranteed. 931 * 932 * @param x the X coordinate of the pixel from which to get 933 * the pixel in the default RGB color model and sRGB 934 * color space 935 * @param y the Y coordinate of the pixel from which to get 936 * the pixel in the default RGB color model and sRGB 937 * color space 938 * @return an integer pixel in the default RGB color model and 939 * default sRGB colorspace. 940 * @see #setRGB(int, int, int) 941 * @see #setRGB(int, int, int, int, int[], int, int) 942 */ 943 public int getRGB(int x, int y) { 944 return colorModel.getRGB(raster.getDataElements(x, y, null)); 945 } 946 947 /** 948 * Returns an array of integer pixels in the default RGB color model 949 * (TYPE_INT_ARGB) and default sRGB color space, 950 * from a portion of the image data. Color conversion takes 951 * place if the default model does not match the image 952 * {@code ColorModel}. There are only 8-bits of precision for 953 * each color component in the returned data when 954 * using this method. With a specified coordinate (x, y) in the 955 * image, the ARGB pixel can be accessed in this way: 956 * 957 * <pre> 958 * pixel = rgbArray[offset + (y-startY)*scansize + (x-startX)]; </pre> 959 * 960 * <p> 961 * 962 * An {@code ArrayOutOfBoundsException} may be thrown 963 * if the region is not in bounds. 964 * However, explicit bounds checking is not guaranteed. 965 * 966 * @param startX the starting X coordinate 967 * @param startY the starting Y coordinate 968 * @param w width of region 969 * @param h height of region 970 * @param rgbArray if not {@code null}, the rgb pixels are 971 * written here 972 * @param offset offset into the {@code rgbArray} 973 * @param scansize scanline stride for the {@code rgbArray} 974 * @return array of RGB pixels. 975 * @see #setRGB(int, int, int) 976 * @see #setRGB(int, int, int, int, int[], int, int) 977 */ 978 public int[] getRGB(int startX, int startY, int w, int h, 979 int[] rgbArray, int offset, int scansize) { 980 int yoff = offset; 981 int off; 982 Object data; 983 int nbands = raster.getNumBands(); 984 int dataType = raster.getDataBuffer().getDataType(); 985 switch (dataType) { 986 case DataBuffer.TYPE_BYTE: 987 data = new byte[nbands]; 988 break; 989 case DataBuffer.TYPE_USHORT: 990 data = new short[nbands]; 991 break; 992 case DataBuffer.TYPE_INT: 993 data = new int[nbands]; 994 break; 995 case DataBuffer.TYPE_FLOAT: 996 data = new float[nbands]; 997 break; 998 case DataBuffer.TYPE_DOUBLE: 999 data = new double[nbands]; 1000 break; 1001 default: 1002 throw new IllegalArgumentException("Unknown data buffer type: "+ 1003 dataType); 1004 } 1005 1006 if (rgbArray == null) { 1007 rgbArray = new int[offset+h*scansize]; 1008 } 1009 1010 for (int y = startY; y < startY+h; y++, yoff+=scansize) { 1011 off = yoff; 1012 for (int x = startX; x < startX+w; x++) { 1013 rgbArray[off++] = colorModel.getRGB(raster.getDataElements(x, 1014 y, 1015 data)); 1016 } 1017 } 1018 1019 return rgbArray; 1020 } 1021 1022 1023 /** 1024 * Sets a pixel in this {@code BufferedImage} to the specified 1025 * RGB value. The pixel is assumed to be in the default RGB color 1026 * model, TYPE_INT_ARGB, and default sRGB color space. For images 1027 * with an {@code IndexColorModel}, the index with the nearest 1028 * color is chosen. 1029 * 1030 * <p> 1031 * 1032 * An {@code ArrayOutOfBoundsException} may be thrown 1033 * if the coordinates are not in bounds. 1034 * However, explicit bounds checking is not guaranteed. 1035 * 1036 * @param x the X coordinate of the pixel to set 1037 * @param y the Y coordinate of the pixel to set 1038 * @param rgb the RGB value 1039 * @see #getRGB(int, int) 1040 * @see #getRGB(int, int, int, int, int[], int, int) 1041 */ 1042 public void setRGB(int x, int y, int rgb) { 1043 raster.setDataElements(x, y, colorModel.getDataElements(rgb, null)); 1044 } 1045 1046 /** 1047 * Sets an array of integer pixels in the default RGB color model 1048 * (TYPE_INT_ARGB) and default sRGB color space, 1049 * into a portion of the image data. Color conversion takes place 1050 * if the default model does not match the image 1051 * {@code ColorModel}. There are only 8-bits of precision for 1052 * each color component in the returned data when 1053 * using this method. With a specified coordinate (x, y) in the 1054 * this image, the ARGB pixel can be accessed in this way: 1055 * <pre> 1056 * pixel = rgbArray[offset + (y-startY)*scansize + (x-startX)]; 1057 * </pre> 1058 * WARNING: No dithering takes place. 1059 * 1060 * <p> 1061 * 1062 * An {@code ArrayOutOfBoundsException} may be thrown 1063 * if the region is not in bounds. 1064 * However, explicit bounds checking is not guaranteed. 1065 * 1066 * @param startX the starting X coordinate 1067 * @param startY the starting Y coordinate 1068 * @param w width of the region 1069 * @param h height of the region 1070 * @param rgbArray the rgb pixels 1071 * @param offset offset into the {@code rgbArray} 1072 * @param scansize scanline stride for the {@code rgbArray} 1073 * @see #getRGB(int, int) 1074 * @see #getRGB(int, int, int, int, int[], int, int) 1075 */ 1076 public void setRGB(int startX, int startY, int w, int h, 1077 int[] rgbArray, int offset, int scansize) { 1078 int yoff = offset; 1079 int off; 1080 Object pixel = null; 1081 1082 for (int y = startY; y < startY+h; y++, yoff+=scansize) { 1083 off = yoff; 1084 for (int x = startX; x < startX+w; x++) { 1085 pixel = colorModel.getDataElements(rgbArray[off++], pixel); 1086 raster.setDataElements(x, y, pixel); 1087 } 1088 } 1089 } 1090 1091 1092 /** 1093 * Returns the width of the {@code BufferedImage}. 1094 * @return the width of this {@code BufferedImage} 1095 */ 1096 public int getWidth() { 1097 return raster.getWidth(); 1098 } 1099 1100 /** 1101 * Returns the height of the {@code BufferedImage}. 1102 * @return the height of this {@code BufferedImage} 1103 */ 1104 public int getHeight() { 1105 return raster.getHeight(); 1106 } 1107 1108 /** 1109 * Returns the width of the {@code BufferedImage}. 1110 * @param observer ignored 1111 * @return the width of this {@code BufferedImage} 1112 */ 1113 public int getWidth(ImageObserver observer) { 1114 return raster.getWidth(); 1115 } 1116 1117 /** 1118 * Returns the height of the {@code BufferedImage}. 1119 * @param observer ignored 1120 * @return the height of this {@code BufferedImage} 1121 */ 1122 public int getHeight(ImageObserver observer) { 1123 return raster.getHeight(); 1124 } 1125 1126 /** 1127 * Returns the object that produces the pixels for the image. 1128 * @return the {@link ImageProducer} that is used to produce the 1129 * pixels for this image. 1130 * @see ImageProducer 1131 */ 1132 public ImageProducer getSource() { 1133 if (osis == null) { 1134 if (properties == null) { 1135 properties = new Hashtable<>(); 1136 } 1137 osis = new OffScreenImageSource(this, properties); 1138 } 1139 return osis; 1140 } 1141 1142 1143 /** 1144 * Returns a property of the image by name. Individual property names 1145 * are defined by the various image formats. If a property is not 1146 * defined for a particular image, this method returns the 1147 * {@code UndefinedProperty} field. If the properties 1148 * for this image are not yet known, then this method returns 1149 * {@code null} and the {@code ImageObserver} object is 1150 * notified later. The property name "comment" should be used to 1151 * store an optional comment that can be presented to the user as a 1152 * description of the image, its source, or its author. 1153 * @param name the property name 1154 * @param observer the {@code ImageObserver} that receives 1155 * notification regarding image information 1156 * @return an {@link Object} that is the property referred to by the 1157 * specified {@code name} or {@code null} if the 1158 * properties of this image are not yet known. 1159 * @throws NullPointerException if the property name is null. 1160 * @see ImageObserver 1161 * @see java.awt.Image#UndefinedProperty 1162 */ 1163 public Object getProperty(String name, ImageObserver observer) { 1164 return getProperty(name); 1165 } 1166 1167 /** 1168 * Returns a property of the image by name. 1169 * @param name the property name 1170 * @return an {@code Object} that is the property referred to by 1171 * the specified {@code name}. 1172 * @throws NullPointerException if the property name is null. 1173 */ 1174 public Object getProperty(String name) { 1175 if (name == null) { 1176 throw new NullPointerException("null property name is not allowed"); 1177 } 1178 if (properties == null) { 1179 return java.awt.Image.UndefinedProperty; 1180 } 1181 Object o = properties.get(name); 1182 if (o == null) { 1183 o = java.awt.Image.UndefinedProperty; 1184 } 1185 return o; 1186 } 1187 1188 /** 1189 * This method returns a {@link Graphics2D}, but is here 1190 * for backwards compatibility. {@link #createGraphics() createGraphics} is more 1191 * convenient, since it is declared to return a 1192 * {@code Graphics2D}. 1193 * @return a {@code Graphics2D}, which can be used to draw into 1194 * this image. 1195 */ 1196 public java.awt.Graphics getGraphics() { 1197 return createGraphics(); 1198 } 1199 1200 /** 1201 * Creates a {@code Graphics2D}, which can be used to draw into 1202 * this {@code BufferedImage}. 1203 * @return a {@code Graphics2D}, used for drawing into this 1204 * image. 1205 */ 1206 public Graphics2D createGraphics() { 1207 GraphicsEnvironment env = 1208 GraphicsEnvironment.getLocalGraphicsEnvironment(); 1209 return env.createGraphics(this); 1210 } 1211 1212 /** 1213 * Returns a subimage defined by a specified rectangular region. 1214 * The returned {@code BufferedImage} shares the same 1215 * data array as the original image. 1216 * @param x the X coordinate of the upper-left corner of the 1217 * specified rectangular region 1218 * @param y the Y coordinate of the upper-left corner of the 1219 * specified rectangular region 1220 * @param w the width of the specified rectangular region 1221 * @param h the height of the specified rectangular region 1222 * @return a {@code BufferedImage} that is the subimage of this 1223 * {@code BufferedImage}. 1224 * @exception RasterFormatException if the specified 1225 * area is not contained within this {@code BufferedImage}. 1226 */ 1227 public BufferedImage getSubimage (int x, int y, int w, int h) { 1228 return new BufferedImage (colorModel, 1229 raster.createWritableChild(x, y, w, h, 1230 0, 0, null), 1231 colorModel.isAlphaPremultiplied(), 1232 properties); 1233 } 1234 1235 /** 1236 * Returns whether or not the alpha has been premultiplied. It 1237 * returns {@code false} if there is no alpha. 1238 * @return {@code true} if the alpha has been premultiplied; 1239 * {@code false} otherwise. 1240 */ 1241 public boolean isAlphaPremultiplied() { 1242 return colorModel.isAlphaPremultiplied(); 1243 } 1244 1245 /** 1246 * Forces the data to match the state specified in the 1247 * {@code isAlphaPremultiplied} variable. It may multiply or 1248 * divide the color raster data by alpha, or do nothing if the data is 1249 * in the correct state. 1250 * @param isAlphaPremultiplied {@code true} if the alpha has been 1251 * premultiplied; {@code false} otherwise. 1252 */ 1253 public void coerceData (boolean isAlphaPremultiplied) { 1254 if (colorModel.hasAlpha() && 1255 colorModel.isAlphaPremultiplied() != isAlphaPremultiplied) { 1256 // Make the color model do the conversion 1257 colorModel = colorModel.coerceData (raster, isAlphaPremultiplied); 1258 } 1259 } 1260 1261 /** 1262 * Returns a {@code String} representation of this 1263 * {@code BufferedImage} object and its values. 1264 * @return a {@code String} representing this 1265 * {@code BufferedImage}. 1266 */ 1267 public String toString() { 1268 return "BufferedImage@"+Integer.toHexString(hashCode()) 1269 +": type = "+imageType 1270 +" "+colorModel+" "+raster; 1271 } 1272 1273 /** 1274 * Returns a {@link Vector} of {@link RenderedImage} objects that are 1275 * the immediate sources, not the sources of these immediate sources, 1276 * of image data for this {@code BufferedImage}. This 1277 * method returns {@code null} if the {@code BufferedImage} 1278 * has no information about its immediate sources. It returns an 1279 * empty {@code Vector} if the {@code BufferedImage} has no 1280 * immediate sources. 1281 * @return a {@code Vector} containing immediate sources of 1282 * this {@code BufferedImage} object's image date, or 1283 * {@code null} if this {@code BufferedImage} has 1284 * no information about its immediate sources, or an empty 1285 * {@code Vector} if this {@code BufferedImage} 1286 * has no immediate sources. 1287 */ 1288 public Vector<RenderedImage> getSources() { 1289 return null; 1290 } 1291 1292 /** 1293 * Returns an array of names recognized by 1294 * {@link #getProperty(String) getProperty(String)} 1295 * or {@code null}, if no property names are recognized. 1296 * @return a {@code String} array containing all of the property 1297 * names that {@code getProperty(String)} recognizes; 1298 * or {@code null} if no property names are recognized. 1299 */ 1300 public String[] getPropertyNames() { 1301 if (properties == null || properties.isEmpty()) { 1302 return null; 1303 } 1304 final Set<String> keys = properties.keySet(); 1305 return keys.toArray(new String[keys.size()]); 1306 } 1307 1308 /** 1309 * Returns the minimum x coordinate of this 1310 * {@code BufferedImage}. This is always zero. 1311 * @return the minimum x coordinate of this 1312 * {@code BufferedImage}. 1313 */ 1314 public int getMinX() { 1315 return raster.getMinX(); 1316 } 1317 1318 /** 1319 * Returns the minimum y coordinate of this 1320 * {@code BufferedImage}. This is always zero. 1321 * @return the minimum y coordinate of this 1322 * {@code BufferedImage}. 1323 */ 1324 public int getMinY() { 1325 return raster.getMinY(); 1326 } 1327 1328 /** 1329 * Returns the {@code SampleModel} associated with this 1330 * {@code BufferedImage}. 1331 * @return the {@code SampleModel} of this 1332 * {@code BufferedImage}. 1333 */ 1334 public SampleModel getSampleModel() { 1335 return raster.getSampleModel(); 1336 } 1337 1338 /** 1339 * Returns the number of tiles in the x direction. 1340 * This is always one. 1341 * @return the number of tiles in the x direction. 1342 */ 1343 public int getNumXTiles() { 1344 return 1; 1345 } 1346 1347 /** 1348 * Returns the number of tiles in the y direction. 1349 * This is always one. 1350 * @return the number of tiles in the y direction. 1351 */ 1352 public int getNumYTiles() { 1353 return 1; 1354 } 1355 1356 /** 1357 * Returns the minimum tile index in the x direction. 1358 * This is always zero. 1359 * @return the minimum tile index in the x direction. 1360 */ 1361 public int getMinTileX() { 1362 return 0; 1363 } 1364 1365 /** 1366 * Returns the minimum tile index in the y direction. 1367 * This is always zero. 1368 * @return the minimum tile index in the y direction. 1369 */ 1370 public int getMinTileY() { 1371 return 0; 1372 } 1373 1374 /** 1375 * Returns the tile width in pixels. 1376 * @return the tile width in pixels. 1377 */ 1378 public int getTileWidth() { 1379 return raster.getWidth(); 1380 } 1381 1382 /** 1383 * Returns the tile height in pixels. 1384 * @return the tile height in pixels. 1385 */ 1386 public int getTileHeight() { 1387 return raster.getHeight(); 1388 } 1389 1390 /** 1391 * Returns the x offset of the tile grid relative to the origin, 1392 * For example, the x coordinate of the location of tile 1393 * (0, 0). This is always zero. 1394 * @return the x offset of the tile grid. 1395 */ 1396 public int getTileGridXOffset() { 1397 return raster.getSampleModelTranslateX(); 1398 } 1399 1400 /** 1401 * Returns the y offset of the tile grid relative to the origin, 1402 * For example, the y coordinate of the location of tile 1403 * (0, 0). This is always zero. 1404 * @return the y offset of the tile grid. 1405 */ 1406 public int getTileGridYOffset() { 1407 return raster.getSampleModelTranslateY(); 1408 } 1409 1410 /** 1411 * Returns tile ({@code tileX}, {@code tileY}). Note 1412 * that {@code tileX} and {@code tileY} are indices 1413 * into the tile array, not pixel locations. The {@code Raster} 1414 * that is returned is live, which means that it is updated if the 1415 * image is changed. 1416 * @param tileX the x index of the requested tile in the tile array 1417 * @param tileY the y index of the requested tile in the tile array 1418 * @return a {@code Raster} that is the tile defined by the 1419 * arguments {@code tileX} and {@code tileY}. 1420 * @exception ArrayIndexOutOfBoundsException if both 1421 * {@code tileX} and {@code tileY} are not 1422 * equal to 0 1423 */ 1424 public Raster getTile(int tileX, int tileY) { 1425 if (tileX == 0 && tileY == 0) { 1426 return raster; 1427 } 1428 throw new ArrayIndexOutOfBoundsException("BufferedImages only have"+ 1429 " one tile with index 0,0"); 1430 } 1431 1432 /** 1433 * Returns the image as one large tile. The {@code Raster} 1434 * returned is a copy of the image data is not updated if the 1435 * image is changed. 1436 * @return a {@code Raster} that is a copy of the image data. 1437 * @see #setData(Raster) 1438 */ 1439 public Raster getData() { 1440 1441 // REMIND : this allocates a whole new tile if raster is a 1442 // subtile. (It only copies in the requested area) 1443 // We should do something smarter. 1444 int width = raster.getWidth(); 1445 int height = raster.getHeight(); 1446 int startX = raster.getMinX(); 1447 int startY = raster.getMinY(); 1448 WritableRaster wr = 1449 Raster.createWritableRaster(raster.getSampleModel(), 1450 new Point(raster.getSampleModelTranslateX(), 1451 raster.getSampleModelTranslateY())); 1452 1453 Object tdata = null; 1454 1455 for (int i = startY; i < startY+height; i++) { 1456 tdata = raster.getDataElements(startX,i,width,1,tdata); 1457 wr.setDataElements(startX,i,width,1, tdata); 1458 } 1459 return wr; 1460 } 1461 1462 /** 1463 * Computes and returns an arbitrary region of the 1464 * {@code BufferedImage}. The {@code Raster} returned is a 1465 * copy of the image data and is not updated if the image is 1466 * changed. 1467 * @param rect the region of the {@code BufferedImage} to be 1468 * returned. 1469 * @return a {@code Raster} that is a copy of the image data of 1470 * the specified region of the {@code BufferedImage} 1471 * @see #setData(Raster) 1472 */ 1473 public Raster getData(Rectangle rect) { 1474 SampleModel sm = raster.getSampleModel(); 1475 SampleModel nsm = sm.createCompatibleSampleModel(rect.width, 1476 rect.height); 1477 WritableRaster wr = Raster.createWritableRaster(nsm, 1478 rect.getLocation()); 1479 int width = rect.width; 1480 int height = rect.height; 1481 int startX = rect.x; 1482 int startY = rect.y; 1483 1484 Object tdata = null; 1485 1486 for (int i = startY; i < startY+height; i++) { 1487 tdata = raster.getDataElements(startX,i,width,1,tdata); 1488 wr.setDataElements(startX,i,width,1, tdata); 1489 } 1490 return wr; 1491 } 1492 1493 /** 1494 * Computes an arbitrary rectangular region of the 1495 * {@code BufferedImage} and copies it into a specified 1496 * {@code WritableRaster}. The region to be computed is 1497 * determined from the bounds of the specified 1498 * {@code WritableRaster}. The specified 1499 * {@code WritableRaster} must have a 1500 * {@code SampleModel} that is compatible with this image. If 1501 * {@code outRaster} is {@code null}, 1502 * an appropriate {@code WritableRaster} is created. 1503 * @param outRaster a {@code WritableRaster} to hold the returned 1504 * part of the image, or {@code null} 1505 * @return a reference to the supplied or created 1506 * {@code WritableRaster}. 1507 */ 1508 public WritableRaster copyData(WritableRaster outRaster) { 1509 if (outRaster == null) { 1510 return (WritableRaster) getData(); 1511 } 1512 int width = outRaster.getWidth(); 1513 int height = outRaster.getHeight(); 1514 int startX = outRaster.getMinX(); 1515 int startY = outRaster.getMinY(); 1516 1517 Object tdata = null; 1518 1519 for (int i = startY; i < startY+height; i++) { 1520 tdata = raster.getDataElements(startX,i,width,1,tdata); 1521 outRaster.setDataElements(startX,i,width,1, tdata); 1522 } 1523 1524 return outRaster; 1525 } 1526 1527 /** 1528 * Sets a rectangular region of the image to the contents of the 1529 * specified {@code Raster r}, which is 1530 * assumed to be in the same coordinate space as the 1531 * {@code BufferedImage}. The operation is clipped to the bounds 1532 * of the {@code BufferedImage}. 1533 * @param r the specified {@code Raster} 1534 * @see #getData 1535 * @see #getData(Rectangle) 1536 */ 1537 public void setData(Raster r) { 1538 int width = r.getWidth(); 1539 int height = r.getHeight(); 1540 int startX = r.getMinX(); 1541 int startY = r.getMinY(); 1542 1543 int[] tdata = null; 1544 1545 // Clip to the current Raster 1546 Rectangle rclip = new Rectangle(startX, startY, width, height); 1547 Rectangle bclip = new Rectangle(0, 0, raster.width, raster.height); 1548 Rectangle intersect = rclip.intersection(bclip); 1549 if (intersect.isEmpty()) { 1550 return; 1551 } 1552 width = intersect.width; 1553 height = intersect.height; 1554 startX = intersect.x; 1555 startY = intersect.y; 1556 1557 // remind use get/setDataElements for speed if Rasters are 1558 // compatible 1559 for (int i = startY; i < startY+height; i++) { 1560 tdata = r.getPixels(startX,i,width,1,tdata); 1561 raster.setPixels(startX,i,width,1, tdata); 1562 } 1563 } 1564 1565 1566 /** 1567 * Adds a tile observer. If the observer is already present, 1568 * it receives multiple notifications. 1569 * @param to the specified {@link TileObserver} 1570 */ 1571 public void addTileObserver (TileObserver to) { 1572 } 1573 1574 /** 1575 * Removes a tile observer. If the observer was not registered, 1576 * nothing happens. If the observer was registered for multiple 1577 * notifications, it is now registered for one fewer notification. 1578 * @param to the specified {@code TileObserver}. 1579 */ 1580 public void removeTileObserver (TileObserver to) { 1581 } 1582 1583 /** 1584 * Returns whether or not a tile is currently checked out for writing. 1585 * @param tileX the x index of the tile. 1586 * @param tileY the y index of the tile. 1587 * @return {@code true} if the tile specified by the specified 1588 * indices is checked out for writing; {@code false} 1589 * otherwise. 1590 * @exception ArrayIndexOutOfBoundsException if both 1591 * {@code tileX} and {@code tileY} are not equal 1592 * to 0 1593 */ 1594 public boolean isTileWritable (int tileX, int tileY) { 1595 if (tileX == 0 && tileY == 0) { 1596 return true; 1597 } 1598 throw new IllegalArgumentException("Only 1 tile in image"); 1599 } 1600 1601 /** 1602 * Returns an array of {@link Point} objects indicating which tiles 1603 * are checked out for writing. Returns {@code null} if none are 1604 * checked out. 1605 * @return a {@code Point} array that indicates the tiles that 1606 * are checked out for writing, or {@code null} if no 1607 * tiles are checked out for writing. 1608 */ 1609 public Point[] getWritableTileIndices() { 1610 Point[] p = new Point[1]; 1611 p[0] = new Point(0, 0); 1612 1613 return p; 1614 } 1615 1616 /** 1617 * Returns whether or not any tile is checked out for writing. 1618 * Semantically equivalent to 1619 * <pre> 1620 * (getWritableTileIndices() != null). 1621 * </pre> 1622 * @return {@code true} if any tile is checked out for writing; 1623 * {@code false} otherwise. 1624 */ 1625 public boolean hasTileWriters () { 1626 return true; 1627 } 1628 1629 /** 1630 * Checks out a tile for writing. All registered 1631 * {@code TileObservers} are notified when a tile goes from having 1632 * no writers to having one writer. 1633 * @param tileX the x index of the tile 1634 * @param tileY the y index of the tile 1635 * @return a {@code WritableRaster} that is the tile, indicated by 1636 * the specified indices, to be checked out for writing. 1637 */ 1638 public WritableRaster getWritableTile (int tileX, int tileY) { 1639 return raster; 1640 } 1641 1642 /** 1643 * Relinquishes permission to write to a tile. If the caller 1644 * continues to write to the tile, the results are undefined. 1645 * Calls to this method should only appear in matching pairs 1646 * with calls to {@link #getWritableTile(int, int) getWritableTile(int, int)}. Any other leads 1647 * to undefined results. All registered {@code TileObservers} 1648 * are notified when a tile goes from having one writer to having no 1649 * writers. 1650 * @param tileX the x index of the tile 1651 * @param tileY the y index of the tile 1652 */ 1653 public void releaseWritableTile (int tileX, int tileY) { 1654 } 1655 1656 /** 1657 * Returns the transparency. Returns either OPAQUE, BITMASK, 1658 * or TRANSLUCENT. 1659 * @return the transparency of this {@code BufferedImage}. 1660 * @see Transparency#OPAQUE 1661 * @see Transparency#BITMASK 1662 * @see Transparency#TRANSLUCENT 1663 * @since 1.5 1664 */ 1665 public int getTransparency() { 1666 return colorModel.getTransparency(); 1667 } 1668 }