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