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