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