1 /* 2 * Copyright (c) 1997, 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.ComponentSampleModel; 32 import java.awt.image.SinglePixelPackedSampleModel; 33 import java.awt.image.DataBufferByte; 34 import java.awt.Rectangle; 35 import java.awt.Point; 36 37 /** 38 * This class defines a Raster with pixels consisting of one or more 8-bit 39 * data elements stored in close proximity to each other in a single byte 40 * array. 41 * The bit precision per data element is that 42 * of the data type (that is, the bit precision for this Raster is 8). 43 * There is only one pixel stride and one scanline stride for all 44 * bands. This type of Raster can be used with a 45 * ComponentColorModel if there are multiple bands, or an 46 * IndexColorModel if there is only one band. 47 * <p> 48 * For example, 3-3-2 RGB image data can be represented by a 49 * ByteComponentRaster using a SinglePixelPackedSampleModel and 50 * a ComponentColorModel. 51 * 52 */ 53 public class ByteComponentRaster extends SunWritableRaster { 54 55 /** private band offset for use by native code */ 56 protected int bandOffset; 57 58 /** Data offsets for each band of image data. */ 59 protected int[] dataOffsets; 60 61 /** Scanline stride of the image data contained in this Raster. */ 62 protected int scanlineStride; 63 64 /** Pixel stride of the image data contained in this Raster. */ 65 protected int pixelStride; 66 67 /** The image data array. */ 68 protected byte[] data; 69 70 int type; 71 72 /** A cached copy of minX + width for use in bounds checks. */ 73 private int maxX; 74 75 /** A cached copy of minY + height for use in bounds checks. */ 76 private int maxY; 77 78 private static native void initIDs(); 79 static { 80 /* ensure that the necessary native libraries are loaded */ 81 NativeLibLoader.loadLibraries(); 82 initIDs(); 83 } 84 85 /** 86 * Constructs a ByteComponentRaster with the given SampleModel. 87 * The Raster's upper left corner is origin and it is the same 88 * size as the SampleModel. A DataBuffer large enough to describe the 89 * Raster is automatically created. SampleModel must be of type 90 * SinglePixelPackedSampleModel or ComponentSampleModel. 91 * @param sampleModel The SampleModel that specifies the layout. 92 * @param origin The Point that specified the origin. 93 */ 94 public ByteComponentRaster(SampleModel sampleModel, Point origin) { 95 this(sampleModel, 96 (DataBufferByte) sampleModel.createDataBuffer(), 97 new Rectangle(origin.x, 98 origin.y, 99 sampleModel.getWidth(), 100 sampleModel.getHeight()), 101 origin, 102 null); 103 } 104 105 /** 106 * Constructs a ByteComponentRaster with the given SampleModel 107 * and DataBuffer. The Raster's upper left corner is origin and 108 * it is the same size as the SampleModel. The DataBuffer is not 109 * initialized and must be a DataBufferByte compatible with SampleModel. 110 * SampleModel must be of type SinglePixelPackedSampleModel 111 * or ComponentSampleModel. 112 * @param sampleModel The SampleModel that specifies the layout. 113 * @param dataBuffer The DataBufferByte that contains the image data. 114 * @param origin The Point that specifies the origin. 115 */ 116 public ByteComponentRaster(SampleModel sampleModel, 117 DataBufferByte dataBuffer, 118 Point origin) 119 { 120 this(sampleModel, 121 dataBuffer, 122 new Rectangle(origin.x, 123 origin.y, 124 sampleModel.getWidth(), 125 sampleModel.getHeight()), 126 origin, 127 null); 128 } 129 130 /** 131 * Constructs a ByteComponentRaster with the given SampleModel, 132 * DataBuffer, and parent. DataBuffer must be a DataBufferByte and 133 * SampleModel must be of type SinglePixelPackedSampleModel 134 * or ComponentSampleModel. 135 * When translated into the base Raster's 136 * coordinate system, aRegion must be contained by the base Raster. 137 * Origin is the coordinate in the new Raster's coordinate system of 138 * the origin of the base Raster. (The base Raster is the Raster's 139 * ancestor which has no parent.) 140 * 141 * Note that this constructor should generally be called by other 142 * constructors or create methods, it should not be used directly. 143 * @param sampleModel The SampleModel that specifies the layout. 144 * @param dataBuffer The DataBufferByte that contains the image data. 145 * @param aRegion The Rectangle that specifies the image area. 146 * @param origin The Point that specifies the origin. 147 * @param parent The parent (if any) of this raster. 148 */ 149 public ByteComponentRaster(SampleModel sampleModel, 150 DataBufferByte dataBuffer, 151 Rectangle aRegion, 152 Point origin, 153 ByteComponentRaster parent) 154 { 155 super(sampleModel, dataBuffer, aRegion, origin, parent); 156 this.maxX = minX + width; 157 this.maxY = minY + height; 158 159 this.data = stealData(dataBuffer, 0); 160 if (dataBuffer.getNumBanks() != 1) { 161 throw new 162 RasterFormatException("DataBuffer for ByteComponentRasters"+ 163 " must only have 1 bank."); 164 } 165 int dbOffset = dataBuffer.getOffset(); 166 167 if (sampleModel instanceof ComponentSampleModel) { 168 ComponentSampleModel ism = (ComponentSampleModel)sampleModel; 169 this.type = IntegerComponentRaster.TYPE_BYTE_SAMPLES; 170 this.scanlineStride = ism.getScanlineStride(); 171 this.pixelStride = ism.getPixelStride(); 172 this.dataOffsets = ism.getBandOffsets(); 173 int xOffset = aRegion.x - origin.x; 174 int yOffset = aRegion.y - origin.y; 175 for (int i = 0; i < getNumDataElements(); i++) { 176 dataOffsets[i] += dbOffset + 177 xOffset*pixelStride+yOffset*scanlineStride; 178 } 179 } else if (sampleModel instanceof SinglePixelPackedSampleModel) { 180 SinglePixelPackedSampleModel sppsm = 181 (SinglePixelPackedSampleModel)sampleModel; 182 this.type = IntegerComponentRaster.TYPE_BYTE_PACKED_SAMPLES; 183 this.scanlineStride = sppsm.getScanlineStride(); 184 this.pixelStride = 1; 185 this.dataOffsets = new int[1]; 186 this.dataOffsets[0] = dbOffset; 187 int xOffset = aRegion.x - origin.x; 188 int yOffset = aRegion.y - origin.y; 189 dataOffsets[0] += xOffset*pixelStride+yOffset*scanlineStride; 190 } else { 191 throw new RasterFormatException("IntegerComponentRasters must " + 192 "have ComponentSampleModel or SinglePixelPackedSampleModel"); 193 } 194 this.bandOffset = this.dataOffsets[0]; 195 196 verify(); 197 } 198 199 /** 200 * Returns a copy of the data offsets array. For each band the data offset 201 * is the index into the band's data array, of the first sample of the 202 * band. 203 */ 204 public int[] getDataOffsets() { 205 return dataOffsets.clone(); 206 } 207 208 /** 209 * Returns the data offset for the specified band. The data offset 210 * is the index into the data array 211 * in which the first sample of the first scanline is stored. 212 * @param band The band whose offset is returned. 213 */ 214 public int getDataOffset(int band) { 215 return dataOffsets[band]; 216 } 217 218 /** 219 * Returns the scanline stride -- the number of data array elements between 220 * a given sample and the sample in the same column of the next row in the 221 * same band. 222 */ 223 public int getScanlineStride() { 224 return scanlineStride; 225 } 226 227 /** 228 * Returns pixel stride -- the number of data array elements between two 229 * samples for the same band on the same scanline. 230 */ 231 public int getPixelStride() { 232 return pixelStride; 233 } 234 235 /** 236 * Returns a reference to the data array. 237 */ 238 public byte[] getDataStorage() { 239 return data; 240 } 241 242 /** 243 * Returns the data elements for all bands at the specified 244 * location. 245 * An ArrayIndexOutOfBounds exception will be thrown at runtime 246 * if the pixel coordinate is out of bounds. 247 * A ClassCastException will be thrown if the input object is non null 248 * and references anything other than an array of transferType. 249 * @param x The X coordinate of the pixel location. 250 * @param y The Y coordinate of the pixel location. 251 * @param obj An object reference to an array of type defined by 252 * getTransferType() and length getNumDataElements(). 253 * If null an array of appropriate type and size will be 254 * allocated. 255 * @return An object reference to an array of type defined by 256 * getTransferType() with the request pixel data. 257 */ 258 public Object getDataElements(int x, int y, Object obj) { 259 if ((x < this.minX) || (y < this.minY) || 260 (x >= this.maxX) || (y >= this.maxY)) { 261 throw new ArrayIndexOutOfBoundsException 262 ("Coordinate out of bounds!"); 263 } 264 byte[] outData; 265 if (obj == null) { 266 outData = new byte[numDataElements]; 267 } else { 268 outData = (byte[])obj; 269 } 270 int off = (y-minY)*scanlineStride + 271 (x-minX)*pixelStride; 272 273 for (int band = 0; band < numDataElements; band++) { 274 outData[band] = data[dataOffsets[band] + off]; 275 } 276 277 return outData; 278 } 279 280 /** 281 * Returns an array of data elements from the specified rectangular 282 * region. 283 * An ArrayIndexOutOfBounds exception will be thrown at runtime 284 * if the pixel coordinates are out of bounds. 285 * A ClassCastException will be thrown if the input object is non null 286 * and references anything other than an array of transferType. 287 * <pre> 288 * byte[] bandData = (byte[])raster.getDataElements(x, y, w, h, null); 289 * int numDataElements = raster.getNumDataElements(); 290 * byte[] pixel = new byte[numDataElements]; 291 * // To find a data element at location (x2, y2) 292 * System.arraycopy(bandData, ((y2-y)*w + (x2-x))*numDataElements, 293 * pixel, 0, numDataElements); 294 * </pre> 295 * @param x The X coordinate of the upper left pixel location. 296 * @param y The Y coordinate of the upper left pixel location. 297 * @param w Width of the pixel rectangle. 298 * @param h Height of the pixel rectangle. 299 * @param obj An object reference to an array of type defined by 300 * getTransferType() and length w*h*getNumDataElements(). 301 * If null an array of appropriate type and size will be 302 * allocated. 303 * @return An object reference to an array of type defined by 304 * getTransferType() with the request pixel data. 305 */ 306 public Object getDataElements(int x, int y, int w, int h, Object obj) { 307 if ((x < this.minX) || (y < this.minY) || 308 (x + w > this.maxX) || (y + h > this.maxY)) { 309 throw new ArrayIndexOutOfBoundsException 310 ("Coordinate out of bounds!"); 311 } 312 byte[] outData; 313 if (obj == null) { 314 outData = new byte[w*h*numDataElements]; 315 } else { 316 outData = (byte[])obj; 317 } 318 319 int yoff = (y-minY)*scanlineStride + 320 (x-minX)*pixelStride; 321 int xoff; 322 int off = 0; 323 int xstart; 324 int ystart; 325 326 for (ystart=0; ystart < h; ystart++, yoff += scanlineStride) { 327 xoff = yoff; 328 for (xstart=0; xstart < w; xstart++, xoff += pixelStride) { 329 for (int c = 0; c < numDataElements; c++) { 330 outData[off++] = data[dataOffsets[c] + xoff]; 331 } 332 } 333 } 334 335 return outData; 336 } 337 338 /** 339 * Returns a byte array of data elements from the specified rectangular 340 * region for the specified band. 341 * An ArrayIndexOutOfBounds exception will be thrown at runtime 342 * if the pixel coordinates are out of bounds. 343 * <pre> 344 * byte[] bandData = raster.getByteData(x, y, w, h, null); 345 * // To find the data element at location (x2, y2) 346 * byte bandElement = bandData[((y2-y)*w + (x2-x))]; 347 * </pre> 348 * @param x The X coordinate of the upper left pixel location. 349 * @param y The Y coordinate of the upper left pixel location. 350 * @param w Width of the pixel rectangle. 351 * @param h Height of the pixel rectangle. 352 * @param band The band to return. 353 * @param outData If non-null, data elements for all bands 354 * at the specified location are returned in this array. 355 * @return Data array with data elements for all bands. 356 */ 357 public byte[] getByteData(int x, int y, int w, int h, 358 int band, byte[] outData) { 359 // Bounds check for 'band' will be performed automatically 360 if ((x < this.minX) || (y < this.minY) || 361 (x + w > this.maxX) || (y + h > this.maxY)) { 362 throw new ArrayIndexOutOfBoundsException 363 ("Coordinate out of bounds!"); 364 } 365 if (outData == null) { 366 outData = new byte[scanlineStride*h]; 367 } 368 int yoff = (y-minY)*scanlineStride + 369 (x-minX)*pixelStride + dataOffsets[band]; 370 int xoff; 371 int off = 0; 372 int xstart; 373 int ystart; 374 375 if (pixelStride == 1) { 376 if (scanlineStride == w) { 377 System.arraycopy(data, yoff, outData, 0, w*h); 378 } 379 else { 380 for (ystart=0; ystart < h; ystart++, yoff += scanlineStride) { 381 System.arraycopy(data, yoff, outData, off, w); 382 off += w; 383 } 384 } 385 } 386 else { 387 for (ystart=0; ystart < h; ystart++, yoff += scanlineStride) { 388 xoff = yoff; 389 for (xstart=0; xstart < w; xstart++, xoff += pixelStride) { 390 outData[off++] = data[xoff]; 391 } 392 } 393 } 394 395 return outData; 396 } 397 398 /** 399 * Returns a byte array of data elements from the specified rectangular 400 * region. 401 * An ArrayIndexOutOfBounds exception will be thrown at runtime 402 * if the pixel coordinates are out of bounds. 403 * <pre> 404 * byte[] bandData = raster.getByteData(x, y, w, h, null); 405 * int numDataElements = raster.getnumDataElements(); 406 * byte[] pixel = new byte[numDataElements]; 407 * // To find a data element at location (x2, y2) 408 * System.arraycopy(bandData, ((y2-y)*w + (x2-x))*numDataElements, 409 * pixel, 0, numDataElements); 410 * </pre> 411 * @param x The X coordinate of the upper left pixel location. 412 * @param y The Y coordinate of the upper left pixel location. 413 * @param w Width of the pixel rectangle. 414 * @param h Height of the pixel rectangle. 415 * @param outData If non-null, data elements for all bands 416 * at the specified location are returned in this array. 417 * @return Data array with data elements for all bands. 418 */ 419 public byte[] getByteData(int x, int y, int w, int h, byte[] outData) { 420 if ((x < this.minX) || (y < this.minY) || 421 (x + w > this.maxX) || (y + h > this.maxY)) { 422 throw new ArrayIndexOutOfBoundsException 423 ("Coordinate out of bounds!"); 424 } 425 if (outData == null) { 426 outData = new byte[numDataElements*scanlineStride*h]; 427 } 428 int yoff = (y-minY)*scanlineStride + 429 (x-minX)*pixelStride; 430 int xoff; 431 int off = 0; 432 int xstart; 433 int ystart; 434 435 // REMIND: Should keep track if dataOffsets are in a nice order 436 for (ystart=0; ystart < h; ystart++, yoff += scanlineStride) { 437 xoff = yoff; 438 for (xstart=0; xstart < w; xstart++, xoff += pixelStride) { 439 for (int c = 0; c < numDataElements; c++) { 440 outData[off++] = data[dataOffsets[c] + xoff]; 441 } 442 } 443 } 444 445 return outData; 446 } 447 448 /** 449 * Stores the data elements for all bands at the specified location. 450 * An ArrayIndexOutOfBounds exception will be thrown at runtime 451 * if the pixel coordinate is out of bounds. 452 * A ClassCastException will be thrown if the input object is non null 453 * and references anything other than an array of transferType. 454 * @param x The X coordinate of the pixel location. 455 * @param y The Y coordinate of the pixel location. 456 * @param obj An object reference to an array of type defined by 457 * getTransferType() and length getNumDataElements() 458 * containing the pixel data to place at x,y. 459 */ 460 public void setDataElements(int x, int y, Object obj) { 461 if ((x < this.minX) || (y < this.minY) || 462 (x >= this.maxX) || (y >= this.maxY)) { 463 throw new ArrayIndexOutOfBoundsException 464 ("Coordinate out of bounds!"); 465 } 466 byte[] inData = (byte[])obj; 467 int off = (y-minY)*scanlineStride + 468 (x-minX)*pixelStride; 469 470 for (int i = 0; i < numDataElements; i++) { 471 data[dataOffsets[i] + off] = inData[i]; 472 } 473 474 markDirty(); 475 } 476 477 /** 478 * Stores the Raster data at the specified location. 479 * An ArrayIndexOutOfBounds exception will be thrown at runtime 480 * if the pixel coordinates are out of bounds. 481 * @param x The X coordinate of the pixel location. 482 * @param y The Y coordinate of the pixel location. 483 * @param inRaster Raster of data to place at x,y location. 484 */ 485 public void setDataElements(int x, int y, Raster inRaster) { 486 int dstOffX = inRaster.getMinX() + x; 487 int dstOffY = inRaster.getMinY() + y; 488 int width = inRaster.getWidth(); 489 int height = inRaster.getHeight(); 490 if ((dstOffX < this.minX) || (dstOffY < this.minY) || 491 (dstOffX + width > this.maxX) || (dstOffY + height > this.maxY)) { 492 throw new ArrayIndexOutOfBoundsException 493 ("Coordinate out of bounds!"); 494 } 495 496 setDataElements(dstOffX, dstOffY, width, height, inRaster); 497 } 498 499 /** 500 * Stores the Raster data at the specified location. 501 * @param dstX The absolute X coordinate of the destination pixel 502 * that will receive a copy of the upper-left pixel of the 503 * inRaster 504 * @param dstY The absolute Y coordinate of the destination pixel 505 * that will receive a copy of the upper-left pixel of the 506 * inRaster 507 * @param width The number of pixels to store horizontally 508 * @param height The number of pixels to store vertically 509 * @param inRaster Raster of data to place at x,y location. 510 */ 511 private void setDataElements(int dstX, int dstY, 512 int width, int height, 513 Raster inRaster) { 514 // Assume bounds checking has been performed previously 515 if (width <= 0 || height <= 0) { 516 return; 517 } 518 519 int srcOffX = inRaster.getMinX(); 520 int srcOffY = inRaster.getMinY(); 521 Object tdata = null; 522 523 if (inRaster instanceof ByteComponentRaster) { 524 ByteComponentRaster bct = (ByteComponentRaster) inRaster; 525 byte[] bdata = bct.getDataStorage(); 526 // REMIND: Do something faster! 527 if (numDataElements == 1) { 528 int toff = bct.getDataOffset(0); 529 int tss = bct.getScanlineStride(); 530 531 int srcOffset = toff; 532 int dstOffset = dataOffsets[0]+(dstY-minY)*scanlineStride+ 533 (dstX-minX); 534 535 536 if (pixelStride == bct.getPixelStride()) { 537 width *= pixelStride; 538 for (int tmpY=0; tmpY < height; tmpY++) { 539 System.arraycopy(bdata, srcOffset, 540 data, dstOffset, width); 541 srcOffset += tss; 542 dstOffset += scanlineStride; 543 } 544 markDirty(); 545 return; 546 } 547 } 548 } 549 550 for (int startY=0; startY < height; startY++) { 551 // Grab one scanline at a time 552 tdata = inRaster.getDataElements(srcOffX, srcOffY+startY, 553 width, 1, tdata); 554 setDataElements(dstX, dstY+startY, width, 1, tdata); 555 } 556 } 557 558 /** 559 * Stores an array of data elements into the specified rectangular 560 * region. 561 * An ArrayIndexOutOfBounds exception will be thrown at runtime 562 * if the pixel coordinates are out of bounds. 563 * A ClassCastException will be thrown if the input object is non null 564 * and references anything other than an array of transferType. 565 * The data elements in the 566 * data array are assumed to be packed. That is, a data element 567 * for the nth band at location (x2, y2) would be found at: 568 * <pre> 569 * inData[((y2-y)*w + (x2-x))*numDataElements + n] 570 * </pre> 571 * @param x The X coordinate of the upper left pixel location. 572 * @param y The Y coordinate of the upper left pixel location. 573 * @param w Width of the pixel rectangle. 574 * @param h Height of the pixel rectangle. 575 * @param obj An object reference to an array of type defined by 576 * getTransferType() and length w*h*getNumDataElements() 577 * containing the pixel data to place between x,y and 578 * x+h, y+h. 579 */ 580 public void setDataElements(int x, int y, int w, int h, Object obj) { 581 if ((x < this.minX) || (y < this.minY) || 582 (x + w > this.maxX) || (y + h > this.maxY)) { 583 throw new ArrayIndexOutOfBoundsException 584 ("Coordinate out of bounds!"); 585 } 586 byte[] inData = (byte[])obj; 587 int yoff = (y-minY)*scanlineStride + 588 (x-minX)*pixelStride; 589 int xoff; 590 int off = 0; 591 int xstart; 592 int ystart; 593 594 if (numDataElements == 1) { 595 int srcOffset = 0; 596 int dstOffset = yoff + dataOffsets[0]; 597 for (ystart=0; ystart < h; ystart++) { 598 xoff = yoff; 599 System.arraycopy(inData, srcOffset, 600 data, dstOffset, w); 601 602 srcOffset += w; 603 dstOffset += scanlineStride; 604 } 605 markDirty(); 606 return; 607 } 608 609 for (ystart=0; ystart < h; ystart++, yoff += scanlineStride) { 610 xoff = yoff; 611 for (xstart=0; xstart < w; xstart++, xoff += pixelStride) { 612 for (int c = 0; c < numDataElements; c++) { 613 data[dataOffsets[c] + xoff] = inData[off++]; 614 } 615 } 616 } 617 618 markDirty(); 619 } 620 621 /** 622 * Stores a byte array of data elements into the specified rectangular 623 * region for the specified band. 624 * An ArrayIndexOutOfBounds exception will be thrown at runtime 625 * if the pixel coordinates are out of bounds. 626 * The data elements in the 627 * data array are assumed to be packed. That is, a data element 628 * at location (x2, y2) would be found at: 629 * <pre> 630 * inData[((y2-y)*w + (x2-x)) + n] 631 * </pre> 632 * @param x The X coordinate of the upper left pixel location. 633 * @param y The Y coordinate of the upper left pixel location. 634 * @param w Width of the pixel rectangle. 635 * @param h Height of the pixel rectangle. 636 * @param band The band to set. 637 * @param inData The data elements to be stored. 638 */ 639 public void putByteData(int x, int y, int w, int h, 640 int band, byte[] inData) { 641 // Bounds check for 'band' will be performed automatically 642 if ((x < this.minX) || (y < this.minY) || 643 (x + w > this.maxX) || (y + h > this.maxY)) { 644 throw new ArrayIndexOutOfBoundsException 645 ("Coordinate out of bounds!"); 646 } 647 int yoff = (y-minY)*scanlineStride + 648 (x-minX)*pixelStride + dataOffsets[band]; 649 int xoff; 650 int off = 0; 651 int xstart; 652 int ystart; 653 654 if (pixelStride == 1) { 655 if (scanlineStride == w) { 656 System.arraycopy(inData, 0, data, yoff, w*h); 657 } 658 else { 659 for (ystart=0; ystart < h; ystart++, yoff += scanlineStride) { 660 System.arraycopy(inData, off, data, yoff, w); 661 off += w; 662 } 663 } 664 } 665 else { 666 for (ystart=0; ystart < h; ystart++, yoff += scanlineStride) { 667 xoff = yoff; 668 for (xstart=0; xstart < w; xstart++, xoff += pixelStride) { 669 data[xoff] = inData[off++]; 670 } 671 } 672 } 673 674 markDirty(); 675 } 676 677 /** 678 * Stores a byte array of data elements into the specified rectangular 679 * region. 680 * An ArrayIndexOutOfBounds exception will be thrown at runtime 681 * if the pixel coordinates are out of bounds. 682 * The data elements in the 683 * data array are assumed to be packed. That is, a data element 684 * for the nth band at location (x2, y2) would be found at: 685 * <pre> 686 * inData[((y2-y)*w + (x2-x))*numDataElements + n] 687 * </pre> 688 * @param x The X coordinate of the upper left pixel location. 689 * @param y The Y coordinate of the upper left pixel location. 690 * @param w Width of the pixel rectangle. 691 * @param h Height of the pixel rectangle. 692 * @param inData The data elements to be stored. 693 */ 694 public void putByteData(int x, int y, int w, int h, byte[] inData) { 695 if ((x < this.minX) || (y < this.minY) || 696 (x + w > this.maxX) || (y + h > this.maxY)) { 697 throw new ArrayIndexOutOfBoundsException 698 ("Coordinate out of bounds!"); 699 } 700 int yoff = (y-minY)*scanlineStride + 701 (x-minX)*pixelStride; 702 703 int xoff; 704 int off = 0; 705 int xstart; 706 int ystart; 707 708 if (numDataElements == 1) { 709 yoff += dataOffsets[0]; 710 if (pixelStride == 1) { 711 if (scanlineStride == w) { 712 System.arraycopy(inData, 0, data, yoff, w*h); 713 } 714 else { 715 for (ystart=0; ystart < h; ystart++) { 716 System.arraycopy(inData, off, data, yoff, w); 717 off += w; 718 yoff += scanlineStride; 719 } 720 } 721 } 722 else { 723 for (ystart=0; ystart < h; ystart++, yoff += scanlineStride) { 724 xoff = yoff; 725 for (xstart=0; xstart < w; xstart++, xoff += pixelStride) { 726 data[xoff] = inData[off++]; 727 } 728 } 729 } 730 } 731 else { 732 for (ystart=0; ystart < h; ystart++, yoff += scanlineStride) { 733 xoff = yoff; 734 for (xstart=0; xstart < w; xstart++, xoff += pixelStride) { 735 for (int c = 0; c < numDataElements; c++) { 736 data[dataOffsets[c] + xoff] = inData[off++]; 737 } 738 } 739 } 740 } 741 742 markDirty(); 743 } 744 745 /** 746 * Creates a subraster given a region of the raster. The x and y 747 * coordinates specify the horizontal and vertical offsets 748 * from the upper-left corner of this raster to the upper-left corner 749 * of the subraster. A subset of the bands of the parent Raster may 750 * be specified. If this is null, then all the bands are present in the 751 * subRaster. A translation to the subRaster may also be specified. 752 * Note that the subraster will reference the same 753 * DataBuffer as the parent raster, but using different offsets. 754 * @param x X offset. 755 * @param y Y offset. 756 * @param width Width (in pixels) of the subraster. 757 * @param height Height (in pixels) of the subraster. 758 * @param x0 Translated X origin of the subraster. 759 * @param y0 Translated Y origin of the subraster. 760 * @param bandList Array of band indices. 761 * @exception RasterFormatException 762 * if the specified bounding box is outside of the parent raster. 763 */ 764 public Raster createChild(int x, int y, 765 int width, int height, 766 int x0, int y0, int[] bandList) { 767 WritableRaster newRaster = createWritableChild(x, y, 768 width, height, 769 x0, y0, 770 bandList); 771 return (Raster) newRaster; 772 } 773 774 /** 775 * Creates a Writable subRaster given a region of the Raster. The x and y 776 * coordinates specify the horizontal and vertical offsets 777 * from the upper-left corner of this Raster to the upper-left corner 778 * of the subRaster. A subset of the bands of the parent Raster may 779 * be specified. If this is null, then all the bands are present in the 780 * subRaster. A translation to the subRaster may also be specified. 781 * Note that the subRaster will reference the same 782 * DataBuffer as the parent Raster, but using different offsets. 783 * @param x X offset. 784 * @param y Y offset. 785 * @param width Width (in pixels) of the subraster. 786 * @param height Height (in pixels) of the subraster. 787 * @param x0 Translated X origin of the subraster. 788 * @param y0 Translated Y origin of the subraster. 789 * @param bandList Array of band indices. 790 * @exception RasterFormatException 791 * if the specified bounding box is outside of the parent Raster. 792 */ 793 public WritableRaster createWritableChild(int x, int y, 794 int width, int height, 795 int x0, int y0, 796 int[] bandList) { 797 if (x < this.minX) { 798 throw new RasterFormatException("x lies outside the raster"); 799 } 800 if (y < this.minY) { 801 throw new RasterFormatException("y lies outside the raster"); 802 } 803 if ((x+width < x) || (x+width > this.minX + this.width)) { 804 throw new RasterFormatException("(x + width) is outside of Raster"); 805 } 806 if ((y+height < y) || (y+height > this.minY + this.height)) { 807 throw new RasterFormatException("(y + height) is outside of Raster"); 808 } 809 810 SampleModel sm; 811 812 if (bandList != null) 813 sm = sampleModel.createSubsetSampleModel(bandList); 814 else 815 sm = sampleModel; 816 817 int deltaX = x0 - x; 818 int deltaY = y0 - y; 819 820 return new ByteComponentRaster(sm, 821 (DataBufferByte) dataBuffer, 822 new Rectangle(x0, y0, width, height), 823 new Point(sampleModelTranslateX+deltaX, 824 sampleModelTranslateY+deltaY), 825 this); 826 } 827 828 /** 829 * Creates a Raster with the same layout but using a different 830 * width and height, and with new zeroed data arrays. 831 */ 832 public WritableRaster createCompatibleWritableRaster(int w, int h) { 833 if (w <= 0 || h <=0) { 834 throw new RasterFormatException("negative "+ 835 ((w <= 0) ? "width" : "height")); 836 } 837 838 SampleModel sm = sampleModel.createCompatibleSampleModel(w, h); 839 840 return new ByteComponentRaster(sm , new Point(0,0)); 841 842 } 843 844 /** 845 * Creates a Raster with the same layout and the same 846 * width and height, and with new zeroed data arrays. If 847 * the Raster is a subRaster, this will call 848 * createCompatibleRaster(width, height). 849 */ 850 public WritableRaster createCompatibleWritableRaster() { 851 return createCompatibleWritableRaster(width,height); 852 } 853 854 /** 855 * Verify that the layout parameters are consistent with the data. 856 * 857 * The method verifies whether scanline stride and pixel stride do not 858 * cause an integer overflow during calculation of a position of the pixel 859 * in data buffer. It also verifies whether the data buffer has enough data 860 * to correspond the raster layout attributes. 861 * 862 * @throws RasterFormatException if an integer overflow is detected, 863 * or if data buffer has not enough capacity. 864 */ 865 protected final void verify() { 866 /* Need to re-verify the dimensions since a sample model may be 867 * specified to the constructor 868 */ 869 if (width <= 0 || height <= 0 || 870 height > (Integer.MAX_VALUE / width)) 871 { 872 throw new RasterFormatException("Invalid raster dimension"); 873 } 874 875 for (int i = 0; i < dataOffsets.length; i++) { 876 if (dataOffsets[i] < 0) { 877 throw new RasterFormatException("Data offsets for band " + i 878 + "(" + dataOffsets[i] 879 + ") must be >= 0"); 880 } 881 } 882 883 if ((long)minX - sampleModelTranslateX < 0 || 884 (long)minY - sampleModelTranslateY < 0) { 885 886 throw new RasterFormatException("Incorrect origin/translate: (" + 887 minX + ", " + minY + ") / (" + 888 sampleModelTranslateX + ", " + sampleModelTranslateY + ")"); 889 } 890 891 // we can be sure that width and height are greater than 0 892 if (scanlineStride < 0 || 893 scanlineStride > (Integer.MAX_VALUE / height)) 894 { 895 // integer overflow 896 throw new RasterFormatException("Incorrect scanline stride: " 897 + scanlineStride); 898 } 899 900 if (height > 1 || minY - sampleModelTranslateY > 0) { 901 // buffer should contain at least one scanline 902 if (scanlineStride > data.length) { 903 throw new RasterFormatException("Incorrect scanline stride: " 904 + scanlineStride); 905 } 906 } 907 908 int lastScanOffset = (height - 1) * scanlineStride; 909 910 if (pixelStride < 0 || 911 pixelStride > (Integer.MAX_VALUE / width) || 912 pixelStride > data.length) 913 { 914 // integer overflow 915 throw new RasterFormatException("Incorrect pixel stride: " 916 + pixelStride); 917 } 918 int lastPixelOffset = (width - 1) * pixelStride; 919 920 if (lastPixelOffset > (Integer.MAX_VALUE - lastScanOffset)) { 921 // integer overflow 922 throw new RasterFormatException("Incorrect raster attributes"); 923 } 924 lastPixelOffset += lastScanOffset; 925 926 int index; 927 int maxIndex = 0; 928 for (int i = 0; i < numDataElements; i++) { 929 if (dataOffsets[i] > (Integer.MAX_VALUE - lastPixelOffset)) { 930 throw new RasterFormatException("Incorrect band offset: " 931 + dataOffsets[i]); 932 933 } 934 935 index = lastPixelOffset + dataOffsets[i]; 936 937 if (index > maxIndex) { 938 maxIndex = index; 939 } 940 } 941 if (data.length <= maxIndex) { 942 throw new RasterFormatException("Data array too small (should be > " 943 + maxIndex + " )"); 944 } 945 } 946 947 public String toString() { 948 return new String ("ByteComponentRaster: width = "+width+" height = " 949 + height 950 +" #numDataElements "+numDataElements 951 // +" xOff = "+xOffset+" yOff = "+yOffset 952 +" dataOff[0] = "+dataOffsets[0]); 953 } 954 955 // /** 956 // * For debugging... prints a region of a one-band ByteComponentRaster 957 // */ 958 // public void print(int x, int y, int w, int h) { 959 // // REMIND: Only works for 1 band! 960 // System.out.println(this); 961 // int offset = dataOffsets[0] + y*scanlineStride + x*pixelStride; 962 // int off; 963 // for (int yoff=0; yoff < h; yoff++, offset += scanlineStride) { 964 // off = offset; 965 // System.out.print("Line "+(y+yoff)+": "); 966 // for (int xoff = 0; xoff < w; xoff++, off+= pixelStride) { 967 // String s = Integer.toHexString(data[off]); 968 // if (s.length() == 8) { 969 // s = s.substring(6,8); 970 // } 971 // System.out.print(s+" "); 972 // } 973 // System.out.println(""); 974 // } 975 // } 976 977 978 }