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