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