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