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