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