1 /* 2 * Copyright (c) 1998, 2018, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. Oracle designates this 8 * particular file as subject to the "Classpath" exception as provided 9 * by Oracle in the LICENSE file that accompanied this code. 10 * 11 * This code is distributed in the hope that it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 * version 2 for more details (a copy is included in the LICENSE file that 15 * accompanied this code). 16 * 17 * You should have received a copy of the GNU General Public License version 18 * 2 along with this work; if not, write to the Free Software Foundation, 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 * 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 * or visit www.oracle.com if you need additional information or have any 23 * questions. 24 */ 25 26 package sun.awt.image; 27 import java.awt.image.Raster; 28 import java.awt.image.WritableRaster; 29 import java.awt.image.RasterFormatException; 30 import java.awt.image.SampleModel; 31 import java.awt.image.SinglePixelPackedSampleModel; 32 import java.awt.image.DataBufferInt; 33 import java.awt.Rectangle; 34 import java.awt.Point; 35 36 /** 37 * This class defines a Raster with pixels consisting of one or more 32-bit 38 * data elements stored in close proximity to each other in a integer array. 39 * The bit precision per data element is that 40 * of the data type (that is, the bit precision for this raster is 32). 41 * There is only one pixel stride and one scanline stride for all 42 * bands. For a given pixel, all samples fit in N data elements and these 43 * N data elements hold samples for only one pixel. This type of Raster 44 * can be used with a PackedColorModel. 45 * <p> 46 * For example, if there is only one data element per pixel, a 47 * SinglePixelPackedSampleModel can be used to represent multiple 48 * bands with a PackedColorModel (including a DirectColorModel) for 49 * color interpretation. 50 * 51 */ 52 public class IntegerInterleavedRaster extends IntegerComponentRaster { 53 54 /** A cached copy of minX + width for use in bounds checks. */ 55 private int maxX; 56 57 /** A cached copy of minY + height for use in bounds checks. */ 58 private int maxY; 59 60 /** 61 * Constructs a IntegerInterleavedRaster with the given SampleModel. 62 * The Raster's upper left corner is origin and it is the same 63 * size as the SampleModel. A DataBuffer large enough to describe the 64 * Raster is automatically created. SampleModel must be of type 65 * SinglePixelPackedSampleModel. 66 * @param sampleModel The SampleModel that specifies the layout. 67 * @param origin The Point that specified the origin. 68 */ IntegerInterleavedRaster(SampleModel sampleModel, Point origin)69 public IntegerInterleavedRaster(SampleModel sampleModel, Point origin) { 70 this(sampleModel, 71 (DataBufferInt) sampleModel.createDataBuffer(), 72 new Rectangle(origin.x, 73 origin.y, 74 sampleModel.getWidth(), 75 sampleModel.getHeight()), 76 origin, 77 null); 78 } 79 80 /** 81 * Constructs a IntegerInterleavedRaster with the given SampleModel 82 * and DataBuffer. The Raster's upper left corner is origin and 83 * it is the same sizes the SampleModel. The DataBuffer is not 84 * initialized and must be a DataBufferInt compatible with SampleModel. 85 * SampleModel must be of type SinglePixelPackedSampleModel. 86 * @param sampleModel The SampleModel that specifies the layout. 87 * @param dataBuffer The DataBufferInt that contains the image data. 88 * @param origin The Point that specifies the origin. 89 */ IntegerInterleavedRaster(SampleModel sampleModel, DataBufferInt dataBuffer, Point origin)90 public IntegerInterleavedRaster(SampleModel sampleModel, 91 DataBufferInt dataBuffer, 92 Point origin) 93 { 94 this(sampleModel, 95 dataBuffer, 96 new Rectangle(origin.x, 97 origin.y, 98 sampleModel.getWidth(), 99 sampleModel.getHeight()), 100 origin, 101 null); 102 } 103 104 /** 105 * Constructs a IntegerInterleavedRaster with the given SampleModel, 106 * DataBuffer, and parent. DataBuffer must be a DataBufferInt and 107 * SampleModel must be of type SinglePixelPackedSampleModel. 108 * When translated into the base Raster's 109 * coordinate system, aRegion must be contained by the base Raster. 110 * Origin is the coodinate in the new Raster's coordinate system of 111 * the origin of the base Raster. (The base Raster is the Raster's 112 * ancestor which has no parent.) 113 * 114 * Note that this constructor should generally be called by other 115 * constructors or create methods, it should not be used directly. 116 * @param sampleModel The SampleModel that specifies the layout. 117 * @param dataBuffer The DataBufferInt that contains the image data. 118 * @param aRegion The Rectangle that specifies the image area. 119 * @param origin The Point that specifies the origin. 120 * @param parent The parent (if any) of this raster. 121 */ IntegerInterleavedRaster(SampleModel sampleModel, DataBufferInt dataBuffer, Rectangle aRegion, Point origin, IntegerInterleavedRaster parent)122 public IntegerInterleavedRaster(SampleModel sampleModel, 123 DataBufferInt dataBuffer, 124 Rectangle aRegion, 125 Point origin, 126 IntegerInterleavedRaster parent) 127 { 128 super(sampleModel,dataBuffer,aRegion,origin,parent); 129 this.maxX = minX + width; 130 this.maxY = minY + height; 131 132 this.data = stealData(dataBuffer, 0); 133 134 if (sampleModel instanceof SinglePixelPackedSampleModel) { 135 SinglePixelPackedSampleModel sppsm = 136 (SinglePixelPackedSampleModel)sampleModel; 137 this.scanlineStride = sppsm.getScanlineStride(); 138 this.pixelStride = 1; 139 this.dataOffsets = new int[1]; 140 this.dataOffsets[0] = dataBuffer.getOffset(); 141 this.bandOffset = this.dataOffsets[0]; 142 int xOffset = aRegion.x - origin.x; 143 int yOffset = aRegion.y - origin.y; 144 dataOffsets[0] += xOffset+yOffset*scanlineStride; 145 this.numDataElems = sppsm.getNumDataElements(); 146 } else { 147 throw new RasterFormatException("IntegerInterleavedRasters must have"+ 148 " SinglePixelPackedSampleModel"); 149 } 150 verify(); 151 } 152 153 154 /** 155 * Returns a copy of the data offsets array. For each band the data offset 156 * is the index into the band's data array, of the first sample of the 157 * band. 158 */ getDataOffsets()159 public int[] getDataOffsets() { 160 return dataOffsets.clone(); 161 } 162 163 /** 164 * Returns data offset for the specified band. The data offset 165 * is the index into the data array in which the first sample 166 * of the first scanline is stored. 167 */ getDataOffset(int band)168 public int getDataOffset(int band) { 169 return dataOffsets[band]; 170 } 171 172 173 /** 174 * Returns the scanline stride -- the number of data array elements between 175 * a given sample and the sample in the same column of the next row. 176 */ getScanlineStride()177 public int getScanlineStride() { 178 return scanlineStride; 179 } 180 181 /** 182 * Returns pixel stride -- the number of data array elements between two 183 * samples for the same band on the same scanline. 184 */ getPixelStride()185 public int getPixelStride() { 186 return pixelStride; 187 } 188 189 /** 190 * Returns a reference to the data array. 191 */ getDataStorage()192 public int[] getDataStorage() { 193 return data; 194 } 195 196 /** 197 * Returns the data elements for all bands at the specified 198 * location. 199 * An ArrayIndexOutOfBounds exception will be thrown at runtime 200 * if the pixel coordinate is out of bounds. 201 * A ClassCastException will be thrown if the input object is non null 202 * and references anything other than an array of transferType. 203 * @param x The X coordinate of the pixel location. 204 * @param y The Y coordinate of the pixel location. 205 * @param obj An object reference to an array of type defined by 206 * getTransferType() and length getNumDataElements(). 207 * If null an array of appropriate type and size will be 208 * allocated. 209 * @return An object reference to an array of type defined by 210 * getTransferType() with the request pixel data. 211 */ getDataElements(int x, int y, Object obj)212 public Object getDataElements(int x, int y, Object obj) { 213 if ((x < this.minX) || (y < this.minY) || 214 (x >= this.maxX) || (y >= this.maxY)) { 215 throw new ArrayIndexOutOfBoundsException 216 ("Coordinate out of bounds!"); 217 } 218 int[] outData; 219 if (obj == null) { 220 outData = new int[1]; 221 } else { 222 outData = (int[])obj; 223 } 224 int off = (y-minY)*scanlineStride + (x-minX) + dataOffsets[0]; 225 outData[0] = data[off]; 226 227 return outData; 228 } 229 230 231 /** 232 * Returns an array of data elements from the specified rectangular 233 * region. 234 * An ArrayIndexOutOfBounds exception will be thrown at runtime 235 * if the pixel coordinates are out of bounds. 236 * A ClassCastException will be thrown if the input object is non null 237 * and references anything other than an array of transferType. 238 <pre> 239 * int[] bandData = (int[])raster.getDataElements(x, y, w, h, null); 240 * int numDataElements = raster.getNumDataElements(); 241 * int[] pixel = new int[numDataElements]; 242 * // To find a data element at location (x2, y2) 243 * System.arraycopy(bandData, ((y2-y)*w + (x2-x))*numDataElements, 244 * pixel, 0, numDataElements); 245 * </pre> 246 * @param x The X coordinate of the upper left pixel location. 247 * @param y The Y coordinate of the upper left pixel location. 248 * @param w Width of the pixel rectangle. 249 * @param h Height of the pixel rectangle. 250 * @param obj An object reference to an array of type defined by 251 * getTransferType() and length w*h*getNumDataElements(). 252 * If null an array of appropriate type and size will be 253 * allocated. 254 * @return An object reference to an array of type defined by 255 * getTransferType() with the request pixel data. 256 */ getDataElements(int x, int y, int w, int h, Object obj)257 public Object getDataElements(int x, int y, int w, int h, Object obj) { 258 if ((x < this.minX) || (y < this.minY) || 259 (x + w > this.maxX) || (y + h > this.maxY)) { 260 throw new ArrayIndexOutOfBoundsException 261 ("Coordinate out of bounds!"); 262 } 263 int[] outData; 264 if (obj instanceof int[]) { 265 outData = (int[])obj; 266 } else { 267 outData = new int[w*h]; 268 } 269 int yoff = (y-minY)*scanlineStride + (x-minX) + dataOffsets[0]; 270 int off = 0; 271 272 for (int ystart = 0; ystart < h; ystart++) { 273 System.arraycopy(data, yoff, outData, off, w); 274 off += w; 275 yoff += scanlineStride; 276 } 277 278 return outData; 279 } 280 281 282 /** 283 * Stores the data elements for all bands at the specified location. 284 * An ArrayIndexOutOfBounds exception will be thrown at runtime 285 * if the pixel coordinate is out of bounds. 286 * A ClassCastException will be thrown if the input object is non null 287 * and references anything other than an array of transferType. 288 * @param x The X coordinate of the pixel location. 289 * @param y The Y coordinate of the pixel location. 290 * @param obj An object reference to an array of type defined by 291 * getTransferType() and length getNumDataElements() 292 * containing the pixel data to place at x,y. 293 */ setDataElements(int x, int y, Object obj)294 public void setDataElements(int x, int y, Object obj) { 295 if ((x < this.minX) || (y < this.minY) || 296 (x >= this.maxX) || (y >= this.maxY)) { 297 throw new ArrayIndexOutOfBoundsException 298 ("Coordinate out of bounds!"); 299 } 300 int[] inData = (int[])obj; 301 302 int off = (y-minY)*scanlineStride + (x-minX) + dataOffsets[0]; 303 304 data[off] = inData[0]; 305 306 markDirty(); 307 } 308 309 310 /** 311 * Stores the Raster data at the specified location. 312 * The transferType of the inputRaster must match this raster. 313 * An ArrayIndexOutOfBoundsException will be thrown at runtime 314 * if the pixel coordinates are out of bounds. 315 * @param x The X coordinate of the pixel location. 316 * @param y The Y coordinate of the pixel location. 317 * @param inRaster Raster of data to place at x,y location. 318 */ setDataElements(int x, int y, Raster inRaster)319 public void setDataElements(int x, int y, Raster inRaster) { 320 int dstOffX = x + inRaster.getMinX(); 321 int dstOffY = y + inRaster.getMinY(); 322 int width = inRaster.getWidth(); 323 int height = inRaster.getHeight(); 324 if ((dstOffX < this.minX) || (dstOffY < this.minY) || 325 (dstOffX + width > this.maxX) || (dstOffY + height > this.maxY)) { 326 throw new ArrayIndexOutOfBoundsException 327 ("Coordinate out of bounds!"); 328 } 329 330 setDataElements(dstOffX, dstOffY, width, height, inRaster); 331 } 332 333 /** 334 * Stores the Raster data at the specified location. 335 * @param dstX The absolute X coordinate of the destination pixel 336 * that will receive a copy of the upper-left pixel of the 337 * inRaster 338 * @param dstY The absolute Y coordinate of the destination pixel 339 * that will receive a copy of the upper-left pixel of the 340 * inRaster 341 * @param width The number of pixels to store horizontally 342 * @param height The number of pixels to store vertically 343 * @param inRaster Raster of data to place at x,y location. 344 */ setDataElements(int dstX, int dstY, int width, int height, Raster inRaster)345 private void setDataElements(int dstX, int dstY, 346 int width, int height, 347 Raster inRaster) { 348 // Assume bounds checking has been performed previously 349 if (width <= 0 || height <= 0) { 350 return; 351 } 352 353 // Write inRaster (minX, minY) to (dstX, dstY) 354 355 int srcOffX = inRaster.getMinX(); 356 int srcOffY = inRaster.getMinY(); 357 int[] tdata = null; 358 359 if (inRaster instanceof IntegerInterleavedRaster) { 360 IntegerInterleavedRaster ict = (IntegerInterleavedRaster) inRaster; 361 362 // Extract the raster parameters 363 tdata = ict.getDataStorage(); 364 int tss = ict.getScanlineStride(); 365 int toff = ict.getDataOffset(0); 366 367 int srcOffset = toff; 368 int dstOffset = dataOffsets[0]+(dstY-minY)*scanlineStride+ 369 (dstX-minX); 370 371 372 // Fastest case. We can copy scanlines 373 // Loop through all of the scanlines and copy the data 374 for (int startY=0; startY < height; startY++) { 375 System.arraycopy(tdata, srcOffset, data, dstOffset, width); 376 srcOffset += tss; 377 dstOffset += scanlineStride; 378 } 379 markDirty(); 380 return; 381 } 382 383 Object odata = null; 384 for (int startY=0; startY < height; startY++) { 385 // Grab one scanline at a time 386 odata = inRaster.getDataElements(srcOffX, srcOffY+startY, 387 width, 1, odata); 388 setDataElements(dstX, dstY+startY, width, 1, odata); 389 } 390 } 391 392 /** 393 * Stores an array of data elements into the specified rectangular 394 * region. 395 * An ArrayIndexOutOfBounds exception will be thrown at runtime 396 * if the pixel coordinates are out of bounds. 397 * A ClassCastException will be thrown if the input object is non null 398 * and references anything other than an array of transferType. 399 * The data elements in the 400 * data array are assumed to be packed. That is, a data element 401 * for the nth band at location (x2, y2) would be found at: 402 * <pre> 403 * inData[((y2-y)*w + (x2-x))*numDataElements + n] 404 * </pre> 405 * @param x The X coordinate of the upper left pixel location. 406 * @param y The Y coordinate of the upper left pixel location. 407 * @param w Width of the pixel rectangle. 408 * @param h Height of the pixel rectangle. 409 * @param obj An object reference to an array of type defined by 410 * getTransferType() and length w*h*getNumDataElements() 411 * containing the pixel data to place between x,y and 412 * x+h, y+h. 413 */ setDataElements(int x, int y, int w, int h, Object obj)414 public void setDataElements(int x, int y, int w, int h, Object obj) { 415 if ((x < this.minX) || (y < this.minY) || 416 (x + w > this.maxX) || (y + h > this.maxY)) { 417 throw new ArrayIndexOutOfBoundsException 418 ("Coordinate out of bounds!"); 419 } 420 int[] inData = (int[])obj; 421 int yoff = (y-minY)*scanlineStride + (x-minX) + dataOffsets[0]; 422 int off = 0; 423 424 for (int ystart = 0; ystart < h; ystart++) { 425 System.arraycopy(inData, off, data, yoff, w); 426 off += w; 427 yoff += scanlineStride; 428 } 429 430 markDirty(); 431 } 432 433 /** 434 * Creates a subraster given a region of the raster. The x and y 435 * coordinates specify the horizontal and vertical offsets 436 * from the upper-left corner of this raster to the upper-left corner 437 * of the subraster. A subset of the bands of the parent Raster may 438 * be specified. If this is null, then all the bands are present in the 439 * subRaster. A translation to the subRaster may also be specified. 440 * Note that the subraster will reference the same 441 * DataBuffer as the parent raster, but using different offsets. 442 * @param x X offset. 443 * @param y Y offset. 444 * @param width Width (in pixels) of the subraster. 445 * @param height Height (in pixels) of the subraster. 446 * @param x0 Translated X origin of the subraster. 447 * @param y0 Translated Y origin of the subraster. 448 * @param bandList Array of band indices. 449 * @exception RasterFormatException 450 * if the specified bounding box is outside of the parent raster. 451 */ createWritableChild(int x, int y, int width, int height, int x0, int y0, int[] bandList)452 public WritableRaster createWritableChild (int x, int y, 453 int width, int height, 454 int x0, int y0, 455 int[] bandList) { 456 if (x < this.minX) { 457 throw new RasterFormatException("x lies outside raster"); 458 } 459 if (y < this.minY) { 460 throw new RasterFormatException("y lies outside raster"); 461 } 462 if ((x+width < x) || (x+width > this.minX + this.width)) { 463 throw new RasterFormatException("(x + width) is outside raster"); 464 } 465 if ((y+height < y) || (y+height > this.minY + this.height)) { 466 throw new RasterFormatException("(y + height) is outside raster"); 467 } 468 469 SampleModel sm; 470 471 if (bandList != null) 472 sm = sampleModel.createSubsetSampleModel(bandList); 473 else 474 sm = sampleModel; 475 476 int deltaX = x0 - x; 477 int deltaY = y0 - y; 478 479 return new IntegerInterleavedRaster(sm, 480 (DataBufferInt) dataBuffer, 481 new Rectangle(x0,y0,width,height), 482 new Point(sampleModelTranslateX+deltaX, 483 sampleModelTranslateY+deltaY), 484 this); 485 } 486 487 488 /** 489 * Creates a subraster given a region of the raster. The x and y 490 * coordinates specify the horizontal and vertical offsets 491 * from the upper-left corner of this raster to the upper-left corner 492 * of the subraster. A subset of the bands of the parent raster may 493 * be specified. If this is null, then all the bands are present in the 494 * subRaster. Note that the subraster will reference the same 495 * DataBuffer as the parent raster, but using different offsets. 496 * @param x X offset. 497 * @param y Y offset. 498 * @param width Width (in pixels) of the subraster. 499 * @param height Height (in pixels) of the subraster. 500 * @param x0 Translated X origin of the subRaster. 501 * @param y0 Translated Y origin of the subRaster. 502 * @param bandList Array of band indices. 503 * @exception RasterFormatException 504 * if the specified bounding box is outside of the parent raster. 505 */ createChild(int x, int y, int width, int height, int x0, int y0, int[] bandList)506 public Raster createChild (int x, int y, 507 int width, int height, 508 int x0, int y0, 509 int[] bandList) { 510 return createWritableChild(x, y, width, height, x0, y0, bandList); 511 } 512 513 514 /** 515 * Creates a raster with the same band layout but using a different 516 * width and height, and with new zeroed data arrays. 517 */ createCompatibleWritableRaster(int w, int h)518 public WritableRaster createCompatibleWritableRaster(int w, int h) { 519 if (w <= 0 || h <=0) { 520 throw new RasterFormatException("negative "+ 521 ((w <= 0) ? "width" : "height")); 522 } 523 524 SampleModel sm = sampleModel.createCompatibleSampleModel(w,h); 525 526 return new IntegerInterleavedRaster(sm, new Point(0,0)); 527 } 528 529 /** 530 * Creates a raster with the same data layout and the same 531 * width and height, and with new zeroed data arrays. If 532 * the raster is a subraster, this will call 533 * createCompatibleRaster(width, height). 534 */ createCompatibleWritableRaster()535 public WritableRaster createCompatibleWritableRaster() { 536 return createCompatibleWritableRaster(width,height); 537 } 538 toString()539 public String toString() { 540 return new String ("IntegerInterleavedRaster: width = "+width 541 +" height = " + height 542 +" #Bands = " + numBands 543 +" xOff = "+sampleModelTranslateX 544 +" yOff = "+sampleModelTranslateY 545 +" dataOffset[0] "+dataOffsets[0]); 546 } 547 548 // /** 549 // * For debugging... prints a region of a one-band IntegerInterleavedRaster 550 // */ 551 // public void print(int x, int y, int w, int h) { 552 // // REMIND: Only works for 1 band! 553 // System.out.println(this); 554 // int offset = dataOffsets[0] + y*scanlineStride + x*pixelStride; 555 // int off; 556 // for (int yoff=0; yoff < h; yoff++, offset += scanlineStride) { 557 // off = offset; 558 // System.out.print("Line "+(sampleModelTranslateY+y+yoff)+": "); 559 // for (int xoff = 0; xoff < w; xoff++, off+= pixelStride) { 560 // System.out.print(Integer.toHexString(data[off])+" "); 561 // } 562 // System.out.println(""); 563 // } 564 // } 565 566 } 567