1 /*
   2  * Copyright (c) 1998, 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.SinglePixelPackedSampleModel;
  32 import java.awt.image.DataBufferInt;
  33 import java.awt.Rectangle;
  34 import java.awt.Point;
  35 
  36 /**
  37  * This class defines a Raster with pixels consisting of one or more 32-bit
  38  * data elements stored in close proximity to each other in a integer array.
  39  * The bit precision per data element is that
  40  * of the data type (that is, the bit precision for this raster is 32).
  41  * There is only one pixel stride and one scanline stride for all
  42  * bands.  For a given pixel, all samples fit in N data elements and these
  43  * N data elements hold samples for only one pixel.  This type of Raster
  44  * can be used with a PackedColorModel.
  45  * <p>
  46  * For example, if there is only one data element per pixel, a
  47  * SinglePixelPackedSampleModel can be used to represent multiple
  48  * bands with a PackedColorModel (including a DirectColorModel) for
  49  * color interpretation.
  50  *
  51  */
  52 public class IntegerInterleavedRaster extends IntegerComponentRaster {
  53 
  54     /** A cached copy of minX + width for use in bounds checks. */
  55     private int maxX;
  56 
  57     /** A cached copy of minY + height for use in bounds checks. */
  58     private int maxY;
  59 
  60     /**
  61      *  Constructs a IntegerInterleavedRaster with the given SampleModel.
  62      *  The Raster's upper left corner is origin and it is the same
  63      *  size as the SampleModel.  A DataBuffer large enough to describe the
  64      *  Raster is automatically created.  SampleModel must be of type
  65      *  SinglePixelPackedSampleModel.
  66      *  @param sampleModel     The SampleModel that specifies the layout.
  67      *  @param origin          The Point that specified the origin.
  68      */
  69     public IntegerInterleavedRaster(SampleModel sampleModel, Point origin) {
  70         this(sampleModel,
  71              (DataBufferInt) sampleModel.createDataBuffer(),
  72              new Rectangle(origin.x,
  73                            origin.y,
  74                            sampleModel.getWidth(),
  75                            sampleModel.getHeight()),
  76              origin,
  77              null);
  78     }
  79 
  80     /**
  81      * Constructs a IntegerInterleavedRaster with the given SampleModel
  82      * and DataBuffer.  The Raster's upper left corner is origin and
  83      * it is the same sizes the SampleModel.  The DataBuffer is not
  84      * initialized and must be a DataBufferInt compatible with SampleModel.
  85      * SampleModel must be of type SinglePixelPackedSampleModel.
  86      * @param sampleModel     The SampleModel that specifies the layout.
  87      * @param dataBuffer      The DataBufferInt that contains the image data.
  88      * @param origin          The Point that specifies the origin.
  89      */
  90     public IntegerInterleavedRaster(SampleModel sampleModel,
  91                                     DataBufferInt dataBuffer,
  92                                     Point origin)
  93     {
  94         this(sampleModel,
  95              dataBuffer,
  96              new Rectangle(origin.x,
  97                            origin.y,
  98                            sampleModel.getWidth(),
  99                            sampleModel.getHeight()),
 100              origin,
 101              null);
 102     }
 103 
 104    /**
 105      * Constructs a IntegerInterleavedRaster with the given SampleModel,
 106      * DataBuffer, and parent.  DataBuffer must be a DataBufferInt and
 107      * SampleModel must be of type SinglePixelPackedSampleModel.
 108      * When translated into the base Raster's
 109      * coordinate system, aRegion must be contained by the base Raster.
 110      * Origin is the coodinate in the new Raster's coordinate system of
 111      * the origin of the base Raster.  (The base Raster is the Raster's
 112      * ancestor which has no parent.)
 113      *
 114      * Note that this constructor should generally be called by other
 115      * constructors or create methods, it should not be used directly.
 116      * @param sampleModel     The SampleModel that specifies the layout.
 117      * @param dataBuffer      The DataBufferInt that contains the image data.
 118      * @param aRegion         The Rectangle that specifies the image area.
 119      * @param origin          The Point that specifies the origin.
 120      * @param parent          The parent (if any) of this raster.
 121      */
 122     public IntegerInterleavedRaster(SampleModel sampleModel,
 123                                     DataBufferInt dataBuffer,
 124                                     Rectangle aRegion,
 125                                     Point origin,
 126                                     IntegerInterleavedRaster parent)
 127     {
 128         super(sampleModel,dataBuffer,aRegion,origin,parent);
 129         this.maxX = minX + width;
 130         this.maxY = minY + height;
 131 
 132         this.data = stealData(dataBuffer, 0);
 133 
 134         if (sampleModel instanceof SinglePixelPackedSampleModel) {
 135             SinglePixelPackedSampleModel sppsm =
 136                     (SinglePixelPackedSampleModel)sampleModel;
 137             this.scanlineStride = sppsm.getScanlineStride();
 138             this.pixelStride    = 1;
 139             this.dataOffsets = new int[1];
 140             this.dataOffsets[0] = dataBuffer.getOffset();
 141             this.bandOffset = this.dataOffsets[0];
 142             int xOffset = aRegion.x - origin.x;
 143             int yOffset = aRegion.y - origin.y;
 144             dataOffsets[0] += xOffset+yOffset*scanlineStride;
 145             this.numDataElems = sppsm.getNumDataElements();
 146         } else {
 147             throw new RasterFormatException("IntegerInterleavedRasters must have"+
 148                                             " SinglePixelPackedSampleModel");
 149         }
 150         verify();
 151     }
 152 
 153 
 154     /**
 155      * Returns a copy of the data offsets array. For each band the data offset
 156      * is the index into the band's data array, of the first sample of the
 157      * band.
 158      */
 159     public int[] getDataOffsets() {
 160         return dataOffsets.clone();
 161     }
 162 
 163     /**
 164      * Returns data offset for the specified band.  The data offset
 165      * is the index into the data array in which the first sample
 166      * of the first scanline is stored.
 167      */
 168     public int getDataOffset(int band) {
 169         return dataOffsets[band];
 170     }
 171 
 172 
 173     /**
 174      * Returns the scanline stride -- the number of data array elements between
 175      * a given sample and the sample in the same column of the next row.
 176      */
 177     public int getScanlineStride() {
 178         return scanlineStride;
 179     }
 180 
 181     /**
 182      * Returns pixel stride -- the number of data array elements  between two
 183      * samples for the same band on the same scanline.
 184      */
 185     public int getPixelStride() {
 186         return pixelStride;
 187     }
 188 
 189     /**
 190      * Returns a reference to the data array.
 191      */
 192     public int[] getDataStorage() {
 193         return data;
 194     }
 195 
 196     /**
 197      * Returns the data elements for all bands at the specified
 198      * location.
 199      * An ArrayIndexOutOfBounds exception will be thrown at runtime
 200      * if the pixel coordinate is out of bounds.
 201      * A ClassCastException will be thrown if the input object is non null
 202      * and references anything other than an array of transferType.
 203      * @param x        The X coordinate of the pixel location.
 204      * @param y        The Y coordinate of the pixel location.
 205      * @param obj      An object reference to an array of type defined by
 206      *                 getTransferType() and length getNumDataElements().
 207      *                 If null an array of appropriate type and size will be
 208      *                 allocated.
 209      * @return         An object reference to an array of type defined by
 210      *                 getTransferType() with the request pixel data.
 211      */
 212     public Object getDataElements(int x, int y, Object obj) {
 213         if ((x < this.minX) || (y < this.minY) ||
 214             (x >= this.maxX) || (y >= this.maxY)) {
 215             throw new ArrayIndexOutOfBoundsException
 216                 ("Coordinate out of bounds!");
 217         }
 218         int outData[];
 219         if (obj == null) {
 220             outData = new int[1];
 221         } else {
 222             outData = (int[])obj;
 223         }
 224         int off = (y-minY)*scanlineStride + (x-minX) + dataOffsets[0];
 225         outData[0] = data[off];
 226 
 227         return outData;
 228     }
 229 
 230 
 231     /**
 232      * Returns an array  of data elements from the specified rectangular
 233      * region.
 234      * An ArrayIndexOutOfBounds exception will be thrown at runtime
 235      * if the pixel coordinates are out of bounds.
 236      * A ClassCastException will be thrown if the input object is non null
 237      * and references anything other than an array of transferType.
 238      <pre>
 239      *       int[] bandData = (int[])raster.getDataElements(x, y, w, h, null);
 240      *       int numDataElements = raster.getNumDataElements();
 241      *       int[] pixel = new int[numDataElements];
 242      *       // To find a data element at location (x2, y2)
 243      *       System.arraycopy(bandData, ((y2-y)*w + (x2-x))*numDataElements,
 244      *                        pixel, 0, numDataElements);
 245      * </pre>
 246      * @param x        The X coordinate of the upper left pixel location.
 247      * @param y        The Y coordinate of the upper left pixel location.
 248      * @param w        Width of the pixel rectangle.
 249      * @param h        Height of the pixel rectangle.
 250      * @param obj      An object reference to an array of type defined by
 251      *                 getTransferType() and length w*h*getNumDataElements().
 252      *                 If null an array of appropriate type and size will be
 253      *                 allocated.
 254      * @return         An object reference to an array of type defined by
 255      *                 getTransferType() with the request pixel data.
 256      */
 257     public Object getDataElements(int x, int y, int w, int h, Object obj) {
 258         if ((x < this.minX) || (y < this.minY) ||
 259             (x + w > this.maxX) || (y + h > this.maxY)) {
 260             throw new ArrayIndexOutOfBoundsException
 261                 ("Coordinate out of bounds!");
 262         }
 263         int outData[];
 264         if (obj instanceof int[]) {
 265             outData = (int[])obj;
 266         } else {
 267             outData = new int[w*h];
 268         }
 269         int yoff = (y-minY)*scanlineStride + (x-minX) + dataOffsets[0];
 270         int off = 0;
 271 
 272         for (int ystart = 0; ystart < h; ystart++) {
 273             System.arraycopy(data, yoff, outData, off, w);
 274             off += w;
 275             yoff += scanlineStride;
 276         }
 277 
 278         return outData;
 279     }
 280 
 281 
 282     /**
 283      * Stores the data elements for all bands at the specified location.
 284      * An ArrayIndexOutOfBounds exception will be thrown at runtime
 285      * if the pixel coordinate is out of bounds.
 286      * A ClassCastException will be thrown if the input object is non null
 287      * and references anything other than an array of transferType.
 288      * @param x        The X coordinate of the pixel location.
 289      * @param y        The Y coordinate of the pixel location.
 290      * @param obj      An object reference to an array of type defined by
 291      *                 getTransferType() and length getNumDataElements()
 292      *                 containing the pixel data to place at x,y.
 293      */
 294     public void setDataElements(int x, int y, Object obj) {
 295         if ((x < this.minX) || (y < this.minY) ||
 296             (x >= this.maxX) || (y >= this.maxY)) {
 297             throw new ArrayIndexOutOfBoundsException
 298                 ("Coordinate out of bounds!");
 299         }
 300         int inData[] = (int[])obj;
 301 
 302         int off = (y-minY)*scanlineStride + (x-minX) + dataOffsets[0];
 303 
 304         data[off] = inData[0];
 305 
 306         markDirty();
 307     }
 308 
 309 
 310     /**
 311      * Stores the Raster data at the specified location.
 312      * The transferType of the inputRaster must match this raster.
 313      * An ArrayIndexOutOfBoundsException will be thrown at runtime
 314      * if the pixel coordinates are out of bounds.
 315      * @param x          The X coordinate of the pixel location.
 316      * @param y          The Y coordinate of the pixel location.
 317      * @param inRaster   Raster of data to place at x,y location.
 318      */
 319     public void setDataElements(int x, int y, Raster inRaster) {
 320         int dstOffX = x + inRaster.getMinX();
 321         int dstOffY = y + inRaster.getMinY();
 322         int width  = inRaster.getWidth();
 323         int height = inRaster.getHeight();
 324         if ((dstOffX < this.minX) || (dstOffY < this.minY) ||
 325             (dstOffX + width > this.maxX) || (dstOffY + height > this.maxY)) {
 326             throw new ArrayIndexOutOfBoundsException
 327                 ("Coordinate out of bounds!");
 328         }
 329 
 330         setDataElements(dstOffX, dstOffY, width, height, inRaster);
 331     }
 332 
 333     /**
 334      * Stores the Raster data at the specified location.
 335      * @param dstX The absolute X coordinate of the destination pixel
 336      * that will receive a copy of the upper-left pixel of the
 337      * inRaster
 338      * @param dstY The absolute Y coordinate of the destination pixel
 339      * that will receive a copy of the upper-left pixel of the
 340      * inRaster
 341      * @param width      The number of pixels to store horizontally
 342      * @param height     The number of pixels to store vertically
 343      * @param inRaster   Raster of data to place at x,y location.
 344      */
 345     private void setDataElements(int dstX, int dstY,
 346                                  int width, int height,
 347                                  Raster inRaster) {
 348         // Assume bounds checking has been performed previously
 349         if (width <= 0 || height <= 0) {
 350             return;
 351         }
 352 
 353         // Write inRaster (minX, minY) to (dstX, dstY)
 354 
 355         int srcOffX = inRaster.getMinX();
 356         int srcOffY = inRaster.getMinY();
 357         int tdata[] = null;
 358 
 359         if (inRaster instanceof IntegerInterleavedRaster) {
 360             IntegerInterleavedRaster ict = (IntegerInterleavedRaster) inRaster;
 361 
 362             // Extract the raster parameters
 363             tdata    = ict.getDataStorage();
 364             int tss  = ict.getScanlineStride();
 365             int toff = ict.getDataOffset(0);
 366 
 367             int srcOffset = toff;
 368             int dstOffset = dataOffsets[0]+(dstY-minY)*scanlineStride+
 369                                            (dstX-minX);
 370 
 371 
 372             // Fastest case.  We can copy scanlines
 373             // Loop through all of the scanlines and copy the data
 374             for (int startY=0; startY < height; startY++) {
 375                 System.arraycopy(tdata, srcOffset, data, dstOffset, width);
 376                 srcOffset += tss;
 377                 dstOffset += scanlineStride;
 378             }
 379             markDirty();
 380             return;
 381         }
 382 
 383         Object odata = null;
 384         for (int startY=0; startY < height; startY++) {
 385             // Grab one scanline at a time
 386             odata = inRaster.getDataElements(srcOffX, srcOffY+startY,
 387                                              width, 1, odata);
 388             setDataElements(dstX, dstY+startY, width, 1, odata);
 389         }
 390     }
 391 
 392     /**
 393      * Stores an array of data elements into the specified rectangular
 394      * region.
 395      * An ArrayIndexOutOfBounds exception will be thrown at runtime
 396      * if the pixel coordinates are out of bounds.
 397      * A ClassCastException will be thrown if the input object is non null
 398      * and references anything other than an array of transferType.
 399      * The data elements in the
 400      * data array are assumed to be packed.  That is, a data element
 401      * for the nth band at location (x2, y2) would be found at:
 402      * <pre>
 403      *      inData[((y2-y)*w + (x2-x))*numDataElements + n]
 404      * </pre>
 405      * @param x        The X coordinate of the upper left pixel location.
 406      * @param y        The Y coordinate of the upper left pixel location.
 407      * @param w        Width of the pixel rectangle.
 408      * @param h        Height of the pixel rectangle.
 409      * @param obj      An object reference to an array of type defined by
 410      *                 getTransferType() and length w*h*getNumDataElements()
 411      *                 containing the pixel data to place between x,y and
 412      *                 x+h, y+h.
 413      */
 414     public void setDataElements(int x, int y, int w, int h, Object obj) {
 415         if ((x < this.minX) || (y < this.minY) ||
 416             (x + w > this.maxX) || (y + h > this.maxY)) {
 417             throw new ArrayIndexOutOfBoundsException
 418                 ("Coordinate out of bounds!");
 419         }
 420         int inData[] = (int[])obj;
 421         int yoff = (y-minY)*scanlineStride + (x-minX) + dataOffsets[0];
 422         int off = 0;
 423 
 424         for (int ystart = 0; ystart < h; ystart++) {
 425             System.arraycopy(inData, off, data, yoff, w);
 426             off += w;
 427             yoff += scanlineStride;
 428         }
 429 
 430         markDirty();
 431     }
 432 
 433     /**
 434      * Creates a subraster given a region of the raster.  The x and y
 435      * coordinates specify the horizontal and vertical offsets
 436      * from the upper-left corner of this raster to the upper-left corner
 437      * of the subraster.  A subset of the bands of the parent Raster may
 438      * be specified.  If this is null, then all the bands are present in the
 439      * subRaster. A translation to the subRaster may also be specified.
 440      * Note that the subraster will reference the same
 441      * DataBuffer as the parent raster, but using different offsets.
 442      * @param x               X offset.
 443      * @param y               Y offset.
 444      * @param width           Width (in pixels) of the subraster.
 445      * @param height          Height (in pixels) of the subraster.
 446      * @param x0              Translated X origin of the subraster.
 447      * @param y0              Translated Y origin of the subraster.
 448      * @param bandList        Array of band indices.
 449      * @exception RasterFormatException
 450      *            if the specified bounding box is outside of the parent raster.
 451      */
 452     public WritableRaster createWritableChild (int x, int y,
 453                                                int width, int height,
 454                                                int x0, int y0,
 455                                                int bandList[]) {
 456         if (x < this.minX) {
 457             throw new RasterFormatException("x lies outside raster");
 458         }
 459         if (y < this.minY) {
 460             throw new RasterFormatException("y lies outside raster");
 461         }
 462         if ((x+width < x) || (x+width > this.minX + this.width)) {
 463             throw new RasterFormatException("(x + width) is outside raster");
 464         }
 465         if ((y+height < y) || (y+height > this.minY + this.height)) {
 466             throw new RasterFormatException("(y + height) is outside raster");
 467         }
 468 
 469         SampleModel sm;
 470 
 471         if (bandList != null)
 472             sm = sampleModel.createSubsetSampleModel(bandList);
 473         else
 474             sm = sampleModel;
 475 
 476         int deltaX = x0 - x;
 477         int deltaY = y0 - y;
 478 
 479         return new IntegerInterleavedRaster(sm,
 480                                           (DataBufferInt) dataBuffer,
 481                                           new Rectangle(x0,y0,width,height),
 482                                           new Point(sampleModelTranslateX+deltaX,
 483                                                     sampleModelTranslateY+deltaY),
 484                                           this);
 485     }
 486 
 487 
 488     /**
 489      * Creates a subraster given a region of the raster.  The x and y
 490      * coordinates specify the horizontal and vertical offsets
 491      * from the upper-left corner of this raster to the upper-left corner
 492      * of the subraster.  A subset of the bands of the parent raster may
 493      * be specified. If this is null, then all the bands are present in the
 494      * subRaster. Note that the subraster will reference the same
 495      * DataBuffer as the parent raster, but using different offsets.
 496      * @param x               X offset.
 497      * @param y               Y offset.
 498      * @param width           Width (in pixels) of the subraster.
 499      * @param height          Height (in pixels) of the subraster.
 500      * @param x0              Translated X origin of the subRaster.
 501      * @param y0              Translated Y origin of the subRaster.
 502      * @param bandList        Array of band indices.
 503      * @exception RasterFormatException
 504      *            if the specified bounding box is outside of the parent raster.
 505      */
 506     public Raster createChild (int x, int y,
 507                                    int width, int height,
 508                                    int x0, int y0,
 509                                    int bandList[]) {
 510         return createWritableChild(x, y, width, height, x0, y0, bandList);
 511     }
 512 
 513 
 514     /**
 515      * Creates a raster with the same band layout but using a different
 516      * width and height, and with new zeroed data arrays.
 517      */
 518     public WritableRaster createCompatibleWritableRaster(int w, int h) {
 519         if (w <= 0 || h <=0) {
 520             throw new RasterFormatException("negative "+
 521                                           ((w <= 0) ? "width" : "height"));
 522         }
 523 
 524         SampleModel sm = sampleModel.createCompatibleSampleModel(w,h);
 525 
 526         return new IntegerInterleavedRaster(sm, new Point(0,0));
 527     }
 528 
 529     /**
 530      * Creates a raster with the same data layout and the same
 531      * width and height, and with new zeroed data arrays.  If
 532      * the raster is a subraster, this will call
 533      * createCompatibleRaster(width, height).
 534      */
 535     public WritableRaster createCompatibleWritableRaster() {
 536         return createCompatibleWritableRaster(width,height);
 537     }
 538 
 539     public String toString() {
 540         return new String ("IntegerInterleavedRaster: width = "+width
 541                            +" height = " + height
 542                            +" #Bands = " + numBands
 543                            +" xOff = "+sampleModelTranslateX
 544                            +" yOff = "+sampleModelTranslateY
 545                            +" dataOffset[0] "+dataOffsets[0]);
 546     }
 547 
 548 //    /**
 549 //     * For debugging...  prints a region of a one-band IntegerInterleavedRaster
 550 //     */
 551 //    public void print(int x, int y, int w, int h) {
 552 //        // REMIND:  Only works for 1 band!
 553 //        System.out.println(this);
 554 //        int offset = dataOffsets[0] + y*scanlineStride + x*pixelStride;
 555 //        int off;
 556 //        for (int yoff=0; yoff < h; yoff++, offset += scanlineStride) {
 557 //            off = offset;
 558 //            System.out.print("Line "+(sampleModelTranslateY+y+yoff)+": ");
 559 //            for (int xoff = 0; xoff < w; xoff++, off+= pixelStride) {
 560 //                System.out.print(Integer.toHexString(data[off])+" ");
 561 //            }
 562 //            System.out.println("");
 563 //        }
 564 //    }
 565 
 566 }