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.BandedSampleModel;
  32 import java.awt.image.DataBuffer;
  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 multiple
  39  * 8-bit samples stored in possibly separate arrays for each band.
  40  * Operations on sets of pixels are performed on a given band in the
  41  * Raster before moving on to the next band.  The arrays used for
  42  * storage may be distinct or shared between some or all of the bands.
  43  * Each band additionally has an offset that is added to determine the
  44  * DataBuffer location of each pixel.
  45  *
  46  * There is only one scanline stride for all bands.  The pixel stride
  47  * is always equal to one.  This type of raster can be used with a
  48  * ComponentColorModel. This class requires a BandedSampleModel.
  49  *
  50  */
  51 public class ByteBandedRaster extends SunWritableRaster {
  52 
  53     /** Data offsets for each band of image data. */
  54     int[]         dataOffsets;
  55 
  56     /** Scanline stride of the image data contained in this Raster. */
  57     int           scanlineStride;
  58 
  59     /** The image data array. */
  60     byte[][]      data;
  61 
  62     /** A cached copy of minX + width for use in bounds checks. */
  63     private int maxX;
  64 
  65     /** A cached copy of minY + height for use in bounds checks. */
  66     private int maxY;
  67 
  68     /**
  69      *  Constructs a ByteBandedRaster with the given sampleModel. The
  70      *  Raster's upper left corner is origin and it is the same
  71      *  size as the SampleModel.  A dataBuffer large
  72      *  enough to describe the Raster is automatically created. SampleModel
  73      *  must be of type BandedSampleModel.
  74      *  @param sampleModel     The SampleModel that specifies the layout.
  75      *  @param origin          The Point that specifies the origin.
  76      */
  77     public ByteBandedRaster(SampleModel sampleModel,
  78                                Point origin) {
  79         this(sampleModel,
  80              sampleModel.createDataBuffer(),
  81              new Rectangle(origin.x,
  82                            origin.y,
  83                            sampleModel.getWidth(),
  84                            sampleModel.getHeight()),
  85              origin,
  86              null);
  87     }
  88 
  89     /**
  90      *  Constructs a ByteBanded Raster with the given sampleModel
  91      *  and DataBuffer. The Raster's upper left corner is origin and
  92      *  it is the same size as the SampleModel.  The DataBuffer is not
  93      *  initialized and must be a DataBufferShort compatible with SampleModel.
  94      *  SampleModel must be of type BandedSampleModel.
  95      *  @param sampleModel     The SampleModel that specifies the layout.
  96      *  @param dataBuffer      The DataBufferShort that contains the image data.
  97      *  @param origin          The Point that specifies the origin.
  98      */
  99     public ByteBandedRaster(SampleModel sampleModel,
 100                                DataBuffer dataBuffer,
 101                                Point origin) {
 102         this(sampleModel, dataBuffer,
 103              new Rectangle(origin.x , origin.y,
 104                            sampleModel.getWidth(),
 105                            sampleModel.getHeight()),
 106              origin, null);
 107     }
 108 
 109     /**
 110      *  Constructs a ByteBandedRaster with the given sampleModel,
 111      *  DataBuffer, and parent. DataBuffer must be a DataBufferShort and
 112      *  SampleModel must be of type BandedSampleModel.
 113      *  When translated into the base Raster's
 114      *  coordinate system, aRegion must be contained by the base Raster.
 115      *  Origin is the coordinate in the new Raster's coordinate system of
 116      *  the origin of the base Raster.  (The base Raster is the Raster's
 117      *  ancestor which has no parent.)
 118      *
 119      *  Note that this constructor should generally be called by other
 120      *  constructors or create methods, it should not be used directly.
 121      *  @param sampleModel     The SampleModel that specifies the layout.
 122      *  @param dataBuffer      The DataBufferShort that contains the image data.
 123      *  @param aRegion         The Rectangle that specifies the image area.
 124      *  @param origin          The Point that specifies the origin.
 125      *  @param parent          The parent (if any) of this raster.
 126      */
 127     public ByteBandedRaster(SampleModel sampleModel,
 128                             DataBuffer dataBuffer,
 129                             Rectangle aRegion,
 130                             Point origin,
 131                             ByteBandedRaster parent) {
 132 
 133         super(sampleModel, dataBuffer, aRegion, origin, parent);
 134         this.maxX = minX + width;
 135         this.maxY = minY + height;
 136 
 137         if (!(dataBuffer instanceof DataBufferByte)) {
 138            throw new RasterFormatException("ByteBandedRaster must have" +
 139                 "byte DataBuffers");
 140         }
 141         DataBufferByte dbb = (DataBufferByte)dataBuffer;
 142 
 143         if (sampleModel instanceof BandedSampleModel) {
 144             BandedSampleModel bsm = (BandedSampleModel)sampleModel;
 145             this.scanlineStride = bsm.getScanlineStride();
 146             int bankIndices[] = bsm.getBankIndices();
 147             int bandOffsets[] = bsm.getBandOffsets();
 148             int dOffsets[] = dbb.getOffsets();
 149             dataOffsets = new int[bankIndices.length];
 150             data = new byte[bankIndices.length][];
 151             int xOffset = aRegion.x - origin.x;
 152             int yOffset = aRegion.y - origin.y;
 153             for (int i = 0; i < bankIndices.length; i++) {
 154                data[i] = stealData(dbb, bankIndices[i]);
 155                dataOffsets[i] = dOffsets[bankIndices[i]] +
 156                    xOffset + yOffset*scanlineStride + bandOffsets[i];
 157             }
 158         } else {
 159             throw new RasterFormatException("ByteBandedRasters must have"+
 160                 "BandedSampleModels");
 161         }
 162         verify();
 163     }
 164 
 165 
 166     /**
 167      * Returns a copy of the data offsets array. For each band the data
 168      * offset is the index into the band's data array, of the first sample
 169      * of the band.
 170      */
 171     public int[] getDataOffsets() {
 172         return dataOffsets.clone();
 173     }
 174 
 175     /**
 176      * Returns data offset for the specified band.  The data offset
 177      * is the index into the band's data array
 178      * in which the first sample of the first scanline is stored.
 179      * @param The band whose offset is returned.
 180      */
 181     public int getDataOffset(int band) {
 182         return dataOffsets[band];
 183     }
 184 
 185     /**
 186      * Returns the scanline stride -- the number of data array elements
 187      * between a given sample and the sample in the same column
 188      * of the next row in the same band.
 189      */
 190     public int getScanlineStride() {
 191         return scanlineStride;
 192     }
 193 
 194     /**
 195      * Returns the pixel stride, which is always equal to one for
 196      * a Raster with a BandedSampleModel.
 197      */
 198     public int getPixelStride() {
 199         return 1;
 200     }
 201 
 202     /**
 203      * Returns a reference to the entire data array.
 204      */
 205     public byte[][] getDataStorage() {
 206         return data;
 207     }
 208 
 209     /**
 210      * Returns a reference to the specific band data array.
 211      */
 212     public byte[] getDataStorage(int band) {
 213         return data[band];
 214     }
 215 
 216     /**
 217      * Returns the data elements for all bands at the specified
 218      * location.
 219      * An ArrayIndexOutOfBounds exception will be thrown at runtime
 220      * if the pixel coordinate is out of bounds.
 221      * A ClassCastException will be thrown if the input object is non null
 222      * and references anything other than an array of transferType.
 223      * @param x        The X coordinate of the pixel location.
 224      * @param y        The Y coordinate of the pixel location.
 225      * @param outData  An object reference to an array of type defined by
 226      *                 getTransferType() and length getNumDataElements().
 227      *                 If null an array of appropriate type and size will be
 228      *                 allocated.
 229      * @return         An object reference to an array of type defined by
 230      *                 getTransferType() with the request pixel data.
 231      */
 232     public Object getDataElements(int x, int y, Object obj) {
 233         if ((x < this.minX) || (y < this.minY) ||
 234             (x >= this.maxX) || (y >= this.maxY)) {
 235             throw new ArrayIndexOutOfBoundsException
 236                 ("Coordinate out of bounds!");
 237         }
 238         byte outData[];
 239         if (obj == null) {
 240             outData = new byte[numDataElements];
 241         } else {
 242             outData = (byte[])obj;
 243         }
 244         int off = (y-minY)*scanlineStride + (x-minX);
 245 
 246         for (int band = 0; band < numDataElements; band++) {
 247             outData[band] = data[band][dataOffsets[band] + off];
 248         }
 249 
 250         return outData;
 251     }
 252 
 253     /**
 254      * Returns an  array  of data elements from the specified
 255      * rectangular region.
 256      * An ArrayIndexOutOfBounds exception will be thrown at runtime
 257      * if the pixel coordinates are out of bounds.
 258      * A ClassCastException will be thrown if the input object is non null
 259      * and references anything other than an array of transferType.
 260      * <pre>
 261      *       byte[] bandData = (byte[])raster.getDataElement(x, y, w, h, null);
 262      *       int numDataElements = raster.getNumDataElements();
 263      *       byte[] pixel = new byte[numDataElements];
 264      *       // To find a data element at location (x2, y2)
 265      *       System.arraycopy(bandData, ((y2-y)*w + (x2-x))*numDataElements,
 266      *                        pixel, 0, numDataElements);
 267      * </pre>
 268      * @param x        The X coordinate of the upper left pixel location.
 269      * @param y        The Y coordinate of the upper left pixel location.
 270      * @param width    Width of the pixel rectangle.
 271      * @param height   Height of the pixel rectangle.
 272      * @param outData  An object reference to an array of type defined by
 273      *                 getTransferType() and length w*h*getNumDataElements().
 274      *                 If null an array of appropriate type and size will be
 275      *                 allocated.
 276      * @return         An object reference to an array of type defined by
 277      *                 getTransferType() with the request pixel data.
 278      */
 279     public Object getDataElements(int x, int y, int w, int h, Object obj) {
 280         if ((x < this.minX) || (y < this.minY) ||
 281             (x + w > this.maxX) || (y + h > this.maxY)) {
 282             throw new ArrayIndexOutOfBoundsException
 283                 ("Coordinate out of bounds!");
 284         }
 285         byte outData[];
 286         if (obj == null) {
 287             outData = new byte[numDataElements*w*h];
 288         } else {
 289             outData = (byte[])obj;
 290         }
 291         int yoff = (y-minY)*scanlineStride + (x-minX);
 292 
 293         for (int c = 0; c < numDataElements; c++) {
 294             int off = c;
 295             byte[] bank = data[c];
 296             int dataOffset = dataOffsets[c];
 297 
 298             int yoff2 = yoff;
 299             for (int ystart=0; ystart < h; ystart++, yoff2 += scanlineStride) {
 300                 int xoff = dataOffset + yoff2;
 301                 for (int xstart=0; xstart < w; xstart++) {
 302                     outData[off] = bank[xoff++];
 303                     off += numDataElements;
 304                 }
 305             }
 306         }
 307 
 308         return outData;
 309     }
 310 
 311     /**
 312      * Returns a byte array  of data elements from the specified rectangular
 313      * region for the specified band.
 314      * An ArrayIndexOutOfBounds exception will be thrown at runtime
 315      * if the pixel coordinates are out of bounds.
 316      * <pre>
 317      *       byte[] bandData = raster.getByteData(x, y, w, h, null);
 318      *       // To find the data element at location (x2, y2)
 319      *       byte bandElement = bandData[((y2-y)*w + (x2-x))];
 320      * </pre>
 321      * @param x        The X coordinate of the upper left pixel location.
 322      * @param y        The Y coordinate of the upper left pixel location.
 323      * @param width    Width of the pixel rectangle.
 324      * @param height   Height of the pixel rectangle.
 325      * @param band     The band to return.
 326      * @param outData  If non-null, data elements for all bands
 327      *                 at the specified location are returned in this array.
 328      * @return         Data array with data elements for all bands.
 329      */
 330     public byte[] getByteData(int x, int y, int w, int h,
 331                               int band, byte[] outData) {
 332         // Bounds check for 'band' will be performed automatically
 333         if ((x < this.minX) || (y < this.minY) ||
 334             (x + w > this.maxX) || (y + h > this.maxY)) {
 335             throw new ArrayIndexOutOfBoundsException
 336                 ("Coordinate out of bounds!");
 337         }
 338         if (outData == null) {
 339             outData = new byte[scanlineStride*h];
 340         }
 341         int yoff = (y-minY)*scanlineStride + (x-minX) + dataOffsets[band];
 342 
 343         if (scanlineStride == w) {
 344             System.arraycopy(data[band], yoff, outData, 0, w*h);
 345         } else {
 346             int off = 0;
 347             for (int ystart=0; ystart < h; ystart++, yoff += scanlineStride) {
 348                 System.arraycopy(data[band], yoff, outData, off, w);
 349                 off += w;
 350             }
 351         }
 352 
 353         return outData;
 354     }
 355 
 356     /**
 357      * Returns a byte array of data elements from the specified rectangular
 358      * region.
 359      * An ArrayIndexOutOfBounds exception will be thrown at runtime
 360      * if the pixel coordinates are out of bounds.
 361      * <pre>
 362      *       byte[] bandData = raster.getByteData(x, y, w, h, null);
 363      *       int numDataElements = raster.getNumDataElements();
 364      *       byte[] pixel = new byte[numDataElements];
 365      *       // To find a data element at location (x2, y2)
 366      *       System.arraycopy(bandData, ((y2-y)*w + (x2-x))*numDataElements,
 367      *                        pixel, 0, numDataElements);
 368      * </pre>
 369      * @param x        The X coordinate of the upper left pixel location.
 370      * @param y        The Y coordinate of the upper left pixel location.
 371      * @param width    Width of the pixel rectangle.
 372      * @param height   Height of the pixel rectangle.
 373      * @param outData  If non-null, data elements for all bands
 374      *                 at the specified location are returned in this array.
 375      * @return         Data array with data elements for all bands.
 376      */
 377     public byte[] getByteData(int x, int y, int w, int h, byte[] outData) {
 378         if ((x < this.minX) || (y < this.minY) ||
 379             (x + w > this.maxX) || (y + h > this.maxY)) {
 380             throw new ArrayIndexOutOfBoundsException
 381                 ("Coordinate out of bounds!");
 382         }
 383         if (outData == null) {
 384             outData = new byte[numDataElements*scanlineStride*h];
 385         }
 386         int yoff = (y-minY)*scanlineStride + (x-minX);
 387 
 388         for (int c = 0; c < numDataElements; c++) {
 389             int off = c;
 390             byte[] bank = data[c];
 391             int dataOffset = dataOffsets[c];
 392 
 393             // REMIND: Should keep track if dataoffsets are in a nice order
 394             int yoff2 = yoff;
 395             for (int ystart=0; ystart < h; ystart++, yoff2 += scanlineStride) {
 396                 int xoff = dataOffset + yoff2;
 397                 for (int xstart=0; xstart < w; xstart++) {
 398                     outData[off] = bank[xoff++];
 399                     off += numDataElements;
 400                 }
 401             }
 402         }
 403 
 404         return outData;
 405     }
 406 
 407     /**
 408      * Stores the data elements for all bands at the specified location.
 409      * An ArrayIndexOutOfBounds exception will be thrown at runtime
 410      * if the pixel coordinate is out of bounds.
 411      * A ClassCastException will be thrown if the input object is non null
 412      * and references anything other than an array of transferType.
 413      * @param x        The X coordinate of the pixel location.
 414      * @param y        The Y coordinate of the pixel location.
 415      * @param inData   An object reference to an array of type defined by
 416      *                 getTransferType() and length getNumDataElements()
 417      *                 containing the pixel data to place at x,y.
 418      */
 419     public void setDataElements(int x, int y, Object obj) {
 420         if ((x < this.minX) || (y < this.minY) ||
 421             (x >= this.maxX) || (y >= this.maxY)) {
 422             throw new ArrayIndexOutOfBoundsException
 423                 ("Coordinate out of bounds!");
 424         }
 425         byte inData[] = (byte[])obj;
 426         int off = (y-minY)*scanlineStride + (x-minX);
 427         for (int i = 0; i < numDataElements; i++) {
 428             data[i][dataOffsets[i] + off] = inData[i];
 429         }
 430         markDirty();
 431     }
 432 
 433     /**
 434      * Stores the Raster data at the specified location.
 435      * An ArrayIndexOutOfBounds exception will be thrown at runtime
 436      * if the pixel coordinate is out of bounds.
 437      * @param x          The X coordinate of the pixel location.
 438      * @param y          The Y coordinate of the pixel location.
 439      * @param inRaster   Raster of data to place at x,y location.
 440      */
 441     public void setDataElements(int x, int y, Raster inRaster) {
 442         int dstOffX = inRaster.getMinX() + x;
 443         int dstOffY = inRaster.getMinY() + y;
 444         int width  = inRaster.getWidth();
 445         int height = inRaster.getHeight();
 446         if ((dstOffX < this.minX) || (dstOffY < this.minY) ||
 447             (dstOffX + width > this.maxX) || (dstOffY + height > this.maxY)) {
 448             throw new ArrayIndexOutOfBoundsException
 449                 ("Coordinate out of bounds!");
 450         }
 451 
 452         setDataElements(dstOffX, dstOffY, width, height, inRaster);
 453     }
 454 
 455    /**
 456      * Stores the Raster data at the specified location.
 457      * @param dstX The absolute X coordinate of the destination pixel
 458      * that will receive a copy of the upper-left pixel of the
 459      * inRaster
 460      * @param dstY The absolute Y coordinate of the destination pixel
 461      * that will receive a copy of the upper-left pixel of the
 462      * inRaster
 463      * @param width      The number of pixels to store horizontally
 464      * @param height     The number of pixels to store vertically
 465      * @param inRaster   Raster of data to place at x,y location.
 466      */
 467     private void setDataElements(int dstX, int dstY,
 468                                  int width, int height,
 469                                  Raster inRaster) {
 470         // Assume bounds checking has been performed previously
 471         if (width <= 0 || height <= 0) {
 472             return;
 473         }
 474 
 475         int srcOffX = inRaster.getMinX();
 476         int srcOffY = inRaster.getMinY();
 477         Object tdata = null;
 478 
 479 //      // REMIND: Do something faster!
 480 //      if (inRaster instanceof ByteBandedRaster) {
 481 //      }
 482 
 483         for (int startY=0; startY < height; startY++) {
 484             // Grab one scanline at a time
 485             tdata = inRaster.getDataElements(srcOffX, srcOffY+startY,
 486                                              width, 1, tdata);
 487             setDataElements(dstX, dstY+startY, width, 1, tdata);
 488         }
 489     }
 490 
 491     /**
 492      * Stores an array of data elements into the specified rectangular
 493      * region.
 494      * An ArrayIndexOutOfBounds exception will be thrown at runtime
 495      * if the pixel coordinates are out of bounds.
 496      * A ClassCastException will be thrown if the input object is non null
 497      * and references anything other than an array of transferType.
 498      * The data elements in the
 499      * data array are assumed to be packed.  That is, a data element
 500      * for the nth band at location (x2, y2) would be found at:
 501      * <pre>
 502      *      inData[((y2-y)*w + (x2-x))*numDataElements + n]
 503      * </pre>
 504      * @param x        The X coordinate of the upper left pixel location.
 505      * @param y        The Y coordinate of the upper left pixel location.
 506      * @param w        Width of the pixel rectangle.
 507      * @param h        Height of the pixel rectangle.
 508      * @param inData   An object reference to an array of type defined by
 509      *                 getTransferType() and length w*h*getNumDataElements()
 510      *                 containing the pixel data to place between x,y and
 511      *                 x+h, y+h.
 512      */
 513     public void setDataElements(int x, int y, int w, int h, Object obj) {
 514         if ((x < this.minX) || (y < this.minY) ||
 515             (x + w > this.maxX) || (y + h > this.maxY)) {
 516             throw new ArrayIndexOutOfBoundsException
 517                 ("Coordinate out of bounds!");
 518         }
 519         byte inData[] = (byte[])obj;
 520         int yoff = (y-minY)*scanlineStride + (x-minX);
 521 
 522         for (int c = 0; c < numDataElements; c++) {
 523             int off = c;
 524             byte[] bank = data[c];
 525             int dataOffset = dataOffsets[c];
 526 
 527             int yoff2 = yoff;
 528             for (int ystart=0; ystart < h; ystart++, yoff2 += scanlineStride) {
 529                 int xoff = dataOffset + yoff2;
 530                 for (int xstart=0; xstart < w; xstart++) {
 531                     bank[xoff++] = inData[off];
 532                     off += numDataElements;
 533                 }
 534             }
 535         }
 536 
 537         markDirty();
 538     }
 539 
 540     /**
 541      * Stores a byte array of data elements into the specified rectangular
 542      * region.
 543      * An ArrayIndexOutOfBounds exception will be thrown at runtime
 544      * if the pixel coordinates are out of bounds.
 545      * The data elements in the
 546      * data array are assumed to be packed.  That is, a data element
 547      * for the nth band at location (x2, y2) would be found at:
 548      * <pre>
 549      *      inData[((y2-y)*w + (x2-x))*numDataElements + n]
 550      * </pre>
 551      * @param x        The X coordinate of the upper left pixel location.
 552      * @param y        The Y coordinate of the upper left pixel location.
 553      * @param w        Width of the pixel rectangle.
 554      * @param h        Height of the pixel rectangle.
 555      * @param band     The band to set.
 556      * @param inData   The data elements to be stored.
 557      */
 558     public void putByteData(int x, int y, int w, int h,
 559                             int band, byte[] inData) {
 560         // Bounds check for 'band' will be performed automatically
 561         if ((x < this.minX) || (y < this.minY) ||
 562             (x + w > this.maxX) || (y + h > this.maxY)) {
 563             throw new ArrayIndexOutOfBoundsException
 564                 ("Coordinate out of bounds!");
 565         }
 566         int yoff = (y-minY)*scanlineStride + (x-minX) + dataOffsets[band];
 567         int xoff;
 568         int off = 0;
 569         int xstart;
 570         int ystart;
 571 
 572         if (scanlineStride == w) {
 573             System.arraycopy(inData, 0, data[band], yoff, w*h);
 574         } else {
 575             for (ystart=0; ystart < h; ystart++, yoff += scanlineStride) {
 576                 System.arraycopy(inData, off, data[band], yoff, w);
 577                 off += w;
 578             }
 579         }
 580 
 581         markDirty();
 582     }
 583 
 584     /**
 585      * Stores a byte array of data elements into the specified rectangular
 586      * region.
 587      * An ArrayIndexOutOfBounds exception will be thrown at runtime
 588      * if the pixel coordinates are out of bounds.
 589      * The data elements in the
 590      * data array are assumed to be packed.  That is, a data element
 591      * for the nth band at location (x2, y2) would be found at:
 592      * <pre>
 593      *      inData[((y2-y)*w + (x2-x))*numDataElements + n]
 594      * </pre>
 595      * @param x        The X coordinate of the upper left pixel location.
 596      * @param y        The Y coordinate of the upper left pixel location.
 597      * @param w        Width of the pixel rectangle.
 598      * @param h        Height of the pixel rectangle.
 599      * @param inData   The data elements to be stored.
 600      */
 601     public void putByteData(int x, int y, int w, int h, byte[] inData) {
 602         if ((x < this.minX) || (y < this.minY) ||
 603             (x + w > this.maxX) || (y + h > this.maxY)) {
 604             throw new ArrayIndexOutOfBoundsException
 605                 ("Coordinate out of bounds!");
 606         }
 607         int yoff = (y-minY)*scanlineStride + (x-minX);
 608 
 609         for (int c = 0; c < numDataElements; c++) {
 610             int off = c;
 611             byte[] bank = data[c];
 612             int dataOffset = dataOffsets[c];
 613 
 614             int yoff2 = yoff;
 615             for (int ystart=0; ystart < h; ystart++, yoff2 += scanlineStride) {
 616                 int xoff = dataOffset + yoff2;
 617                 for (int xstart=0; xstart < w; xstart++) {
 618                     bank[xoff++] = inData[off];
 619                     off += numDataElements;
 620                 }
 621             }
 622         }
 623 
 624         markDirty();
 625     }
 626 
 627     /**
 628      * Creates a Writable subraster given a region of the raster.  The x and y
 629      * coordinates specify the horizontal and vertical offsets
 630      * from the upper-left corner of this raster to the upper-left corner
 631      * of the subraster.  A subset of the bands of the parent Raster may
 632      * be specified.  If this is null, then all the bands are present in the
 633      * subRaster. A translation to the subRaster may also be specified.
 634      * Note that the subraster will reference the same
 635      * DataBuffers as the parent raster, but using different offsets.
 636      * @param x               X offset.
 637      * @param y               Y offset.
 638      * @param width           Width of the subraster.
 639      * @param height          Height of the subraster.
 640      * @param x0              Translated X origin of the subraster.
 641      * @param y0              Translated Y origin of the subraster.
 642      * @param bandList        Array of band indices.
 643      * @exception RasterFormatException
 644      *            if the specified bounding box is outside of the parent raster.
 645      */
 646     public WritableRaster createWritableChild (int x, int y,
 647                                                int width, int height,
 648                                                int x0, int y0,
 649                                                int bandList[]) {
 650 
 651         if (x < this.minX) {
 652             throw new RasterFormatException("x lies outside raster");
 653         }
 654         if (y < this.minY) {
 655             throw new RasterFormatException("y lies outside raster");
 656         }
 657         if ((x+width < x) || (x+width > this.width + this.minX)) {
 658             throw new RasterFormatException("(x + width) is outside raster") ;
 659         }
 660         if ((y+height < y) || (y+height > this.height + this.minY)) {
 661             throw new RasterFormatException("(y + height) is outside raster");
 662         }
 663 
 664         SampleModel sm;
 665 
 666         if (bandList != null)
 667             sm = sampleModel.createSubsetSampleModel(bandList);
 668         else
 669             sm = sampleModel;
 670 
 671         int deltaX = x0 - x;
 672         int deltaY = y0 - y;
 673 
 674         return new ByteBandedRaster(sm,
 675                                     dataBuffer,
 676                                     new Rectangle(x0,y0,width,height),
 677                                     new Point(sampleModelTranslateX+deltaX,
 678                                               sampleModelTranslateY+deltaY),
 679                                     this);
 680     }
 681 
 682     /**
 683      * Creates a subraster given a region of the raster.  The x and y
 684      * coordinates specify the horizontal and vertical offsets
 685      * from the upper-left corner of this raster to the upper-left corner
 686      * of the subraster.  A subset of the bands of the parent Raster may
 687      * be specified.  If this is null, then all the bands are present in the
 688      * subRaster. A translation to the subRaster may also be specified.
 689      * Note that the subraster will reference the same
 690      * DataBuffers as the parent raster, but using different offsets.
 691      * @param x               X offset.
 692      * @param y               Y offset.
 693      * @param width           Width (in pixels) of the subraster.
 694      * @param height          Height (in pixels) of the subraster.
 695      * @param x0              Translated X origin of the subraster.
 696      * @param y0              Translated Y origin of the subraster.
 697      * @param bandList        Array of band indices.
 698      * @exception RasterFormatException
 699      *            if the specified bounding box is outside of the parent raster.
 700      */
 701     public Raster createChild (int x, int y,
 702                                    int width, int height,
 703                                    int x0, int y0,
 704                                    int bandList[]) {
 705         return createWritableChild(x, y, width, height, x0, y0, bandList);
 706     }
 707 
 708     /**
 709      * Creates a Raster with the same layout but using a different
 710      * width and height, and with new zeroed data arrays.
 711      */
 712     public WritableRaster createCompatibleWritableRaster(int w, int h) {
 713         if (w <= 0 || h <=0) {
 714             throw new RasterFormatException("negative "+
 715                                           ((w <= 0) ? "width" : "height"));
 716         }
 717 
 718         SampleModel sm = sampleModel.createCompatibleSampleModel(w,h);
 719 
 720         return new ByteBandedRaster(sm, new Point(0,0));
 721     }
 722 
 723     /**
 724      * Creates a Raster with the same layout and the same
 725      * width and height, and with new zeroed data arrays.  If
 726      * the Raster is a subRaster, this will call
 727      * createCompatibleRaster(width, height).
 728      */
 729     public WritableRaster createCompatibleWritableRaster() {
 730         return createCompatibleWritableRaster(width, height);
 731     }
 732 
 733     /**
 734      * Verify that the layout parameters are consistent with the data.
 735      * Verifies whether the data buffer has enough data for the raster,
 736      * taking into account offsets, after ensuring all offsets are >=0.
 737      * @throws RasterFormatException if a problem is detected.
 738      */
 739     private void verify() {
 740 
 741         /* Need to re-verify the dimensions since a sample model may be
 742          * specified to the constructor
 743          */
 744         if (width <= 0 || height <= 0 ||
 745             height > (Integer.MAX_VALUE / width))
 746         {
 747             throw new RasterFormatException("Invalid raster dimension");
 748         }
 749 
 750         if (scanlineStride < 0 ||
 751             scanlineStride > (Integer.MAX_VALUE / height))
 752         {
 753             // integer overflow
 754             throw new RasterFormatException("Incorrect scanline stride: "
 755                     + scanlineStride);
 756         }
 757 
 758         for (int i = 0; i < data.length; i++) {
 759             if (scanlineStride > data[i].length) {
 760                 throw new RasterFormatException("Incorrect scanline stride: "
 761                     + scanlineStride);
 762             }
 763         }
 764 
 765         // Make sure data for Raster is in a legal range
 766         for (int i=0; i < dataOffsets.length; i++) {
 767             if (dataOffsets[i] < 0) {
 768                 throw new RasterFormatException("Data offsets for band "+i+
 769                                                 "("+dataOffsets[i]+
 770                                                 ") must be >= 0");
 771             }
 772         }
 773 
 774         int lastScanOffset = (height - 1) * scanlineStride;
 775 
 776         if ((width - 1) > (Integer.MAX_VALUE - lastScanOffset)) {
 777             throw new RasterFormatException("Invalid raster dimension");
 778         }
 779         int lastPixelOffset = lastScanOffset + (width-1);
 780 
 781         int maxIndex = 0;
 782         int index;
 783 
 784         for (int i=0; i < numDataElements; i++) {
 785             if (dataOffsets[i] > (Integer.MAX_VALUE - lastPixelOffset)) {
 786                 throw new RasterFormatException("Invalid raster dimension");
 787             }
 788             index = lastPixelOffset + dataOffsets[i];
 789             if (index > maxIndex) {
 790                 maxIndex = index;
 791             }
 792         }
 793 
 794         if (data.length == 1) {
 795             if (data[0].length <= maxIndex*numDataElements) {
 796                 throw new RasterFormatException("Data array too small "+
 797                                                 "(it is "+data[0].length+
 798                                                 " and should be > "+
 799                                                 (maxIndex*numDataElements)+
 800                                                 " )");
 801             }
 802         }
 803         else {
 804             for (int i=0; i < numDataElements; i++) {
 805                 if (data[i].length <= maxIndex) {
 806                     throw new RasterFormatException("Data array too small "+
 807                                                     "(it is "+data[i].length+
 808                                                     " and should be > "+
 809                                                     maxIndex+" )");
 810                 }
 811             }
 812         }
 813     }
 814 
 815     public String toString() {
 816         return new String ("ByteBandedRaster: width = "+width+" height = "
 817                            + height
 818                            +" #bands "+numDataElements
 819                            +" minX = "+minX+" minY = "+minY);
 820     }
 821 
 822 }