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
26package sun.awt.image;
27import java.awt.image.Raster;
28import java.awt.image.WritableRaster;
29import java.awt.image.RasterFormatException;
30import java.awt.image.SampleModel;
31import java.awt.image.BandedSampleModel;
32import java.awt.image.DataBufferUShort;
33import java.awt.Rectangle;
34import java.awt.Point;
35
36/**
37 * This class defines a Raster with pixels consisting of multiple 16-bit
38 * samples stored in separate arrays for each band.  Operations on
39 * sets of pixels are performed on a given band of each pixel
40 * in the set before moving on to the next band.  The arrays used
41 * for storage may be distinct or shared between some or all of
42 * the bands.
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. This class requires a BandedSampleModel.
46 *
47 */
48public class ShortBandedRaster extends SunWritableRaster {
49
50    /** Data offsets for each band of image data. */
51    int[]         dataOffsets;
52
53    /** Scanline stride of the image data contained in this Raster. */
54    int           scanlineStride;
55
56    /** The image data array. */
57    short[][]     data;
58
59    /** A cached copy of minX + width for use in bounds checks. */
60    private int maxX;
61
62    /** A cached copy of minY + height for use in bounds checks. */
63    private int maxY;
64
65    /**
66     * Constructs a ShortBandedRaster with the given SampleModel.
67     * The Raster's upper left corner is origin and it is the same
68     * size as the SampleModel.  A DataBuffer large enough to describe the
69     * Raster is automatically created.  SampleModel must be of type
70     * BandedSampleModel.
71     * @param sampleModel     The SampleModel that specifies the layout.
72     * @param origin          The Point that specified the origin.
73     */
74    public ShortBandedRaster(SampleModel sampleModel, Point origin) {
75        this(sampleModel,
76             (DataBufferUShort) sampleModel.createDataBuffer(),
77             new Rectangle(origin.x,
78                           origin.y,
79                           sampleModel.getWidth(),
80                           sampleModel.getHeight()),
81             origin,
82             null);
83    }
84
85    /**
86     * Constructs a ShortBandedRaster with the given SampleModel
87     * and DataBuffer.  The Raster's upper left corner is origin and
88     * it is the same size as the SampleModel.  The DataBuffer is not
89     * initialized and must be a DataBufferUShort compatible with SampleModel.
90     * SampleModel must be of type BandedSampleModel.
91     * @param sampleModel     The SampleModel that specifies the layout.
92     * @param dataBuffer      The DataBufferUShort that contains the image data.
93     * @param origin          The Point that specifies the origin.
94     */
95    public ShortBandedRaster(SampleModel sampleModel,
96                             DataBufferUShort dataBuffer,
97                             Point origin)
98    {
99        this(sampleModel, dataBuffer,
100             new Rectangle(origin.x, origin.y,
101                           sampleModel.getWidth(),
102                           sampleModel.getHeight()),
103             origin, null);
104    }
105
106    /**
107     * Constructs a ShortBandedRaster with the given SampleModel,
108     * DataBuffer, and parent.  DataBuffer must be a DataBufferUShort and
109     * SampleModel must be of type BandedSampleModel.
110     * When translated into the base Raster's
111     * coordinate system, aRegion must be contained by the base Raster.
112     * Origin is the coordinate in the new Raster's coordinate system of
113     * the origin of the base Raster.  (The base Raster is the Raster's
114     * ancestor which has no parent.)
115     *
116     * Note that this constructor should generally be called by other
117     * constructors or create methods, it should not be used directly.
118     * @param sampleModel     The SampleModel that specifies the layout.
119     * @param dataBuffer      The DataBufferUShort that contains the image data.
120     * @param aRegion         The Rectangle that specifies the image area.
121     * @param origin          The Point that specifies the origin.
122     * @param parent          The parent (if any) of this raster.
123     */
124    public ShortBandedRaster(SampleModel sampleModel,
125                             DataBufferUShort dataBuffer,
126                             Rectangle aRegion,
127                             Point origin,
128                             ShortBandedRaster parent)
129    {
130        super(sampleModel, dataBuffer, aRegion, origin, parent);
131        this.maxX = minX + width;
132        this.maxY = minY + height;
133
134        if (sampleModel instanceof BandedSampleModel) {
135            BandedSampleModel bsm = (BandedSampleModel)sampleModel;
136            this.scanlineStride = bsm.getScanlineStride();
137            int bankIndices[] = bsm.getBankIndices();
138            int bandOffsets[] = bsm.getBandOffsets();
139            int dOffsets[] = dataBuffer.getOffsets();
140            dataOffsets = new int[bankIndices.length];
141            data = new short[bankIndices.length][];
142            int xOffset = aRegion.x - origin.x;
143            int yOffset = aRegion.y - origin.y;
144            for (int i = 0; i < bankIndices.length; i++) {
145               data[i] = stealData(dataBuffer, bankIndices[i]);
146               dataOffsets[i] = dOffsets[bankIndices[i]] +
147                   xOffset + yOffset*scanlineStride + bandOffsets[i];
148            }
149        } else {
150            throw new RasterFormatException("ShortBandedRasters must have "+
151                "BandedSampleModels");
152        }
153        verify();
154    }
155
156    /**
157     * Returns a copy of the data offsets array. For each band the data offset
158     * is the index into the band's data array, of the first sample of the
159     * band.
160     */
161    public int[] getDataOffsets() {
162        return dataOffsets.clone();
163    }
164
165    /**
166     * Returns the data offset for the specified band.  The data offset
167     * is the index into the band's data array
168     * in which the first sample of the first scanline is stored.
169     * @param band The band whose offset is returned.
170     */
171    public int getDataOffset(int band) {
172        return dataOffsets[band];
173    }
174
175    /**
176     * Returns the scanline stride -- the number of data array elements between
177     * a given sample and the sample in the same column
178     * of the next row in the same band.
179     */
180    public int getScanlineStride() {
181        return scanlineStride;
182    }
183
184    /**
185     * Returns the pixel stride, which is always equal to one for
186     * a Raster with a BandedSampleModel.
187     */
188    public int getPixelStride() {
189        return 1;
190    }
191
192    /**
193     * Returns a reference to the entire data array.
194     */
195    public short[][] getDataStorage() {
196        return data;
197    }
198
199    /**
200     * Returns a reference to the specific band data array.
201     */
202    public short[] getDataStorage(int band) {
203        return data[band];
204    }
205
206    /**
207     * Returns the data elements for all bands at the specified
208     * location.
209     * An ArrayIndexOutOfBounds exception will be thrown at runtime
210     * if the pixel coordinate is out of bounds.
211     * A ClassCastException will be thrown if the input object is non null
212     * and references anything other than an array of transferType.
213     * @param x        The X coordinate of the pixel location.
214     * @param y        The Y coordinate of the pixel location.
215     * @param obj      An object reference to an array of type defined by
216     *                 getTransferType() and length getNumDataElements().
217     *                 If null an array of appropriate type and size will be
218     *                 allocated.
219     * @return         An object reference to an array of type defined by
220     *                 getTransferType() with the request pixel data.
221     */
222    public Object getDataElements(int x, int y, Object obj) {
223        if ((x < this.minX) || (y < this.minY) ||
224            (x >= this.maxX) || (y >= this.maxY)) {
225            throw new ArrayIndexOutOfBoundsException
226                ("Coordinate out of bounds!");
227        }
228        short outData[];
229        if (obj == null) {
230            outData = new short[numDataElements];
231        } else {
232            outData = (short[])obj;
233        }
234
235        int off = (y-minY)*scanlineStride + (x-minX);
236
237        for (int band = 0; band < numDataElements; band++) {
238            outData[band] = data[band][dataOffsets[band] + off];
239        }
240
241        return outData;
242    }
243
244    /**
245     * Returns an array  of data elements from the specified rectangular
246     * region.
247     * An ArrayIndexOutOfBounds exception will be thrown at runtime
248     * if the pixel coordinates are out of bounds.
249     * <pre>
250     *       short[] bandData = (short[])Raster.getDataElements(x, y, w, h, null);
251     *       int numDataElements = Raster.getnumDataElements();
252     *       short[] pixel = new short[numDataElements];
253     *       // To find a data element at location (x2, y2)
254     *       System.arraycopy(bandData, ((y2-y)*w + (x2-x))*numDataElements,
255     *                        pixel, 0, numDataElements);
256     * </pre>
257     * @param x        The X coordinate of the upper left pixel location.
258     * @param y        The Y coordinate of the upper left pixel location.
259     * @param w        Width of the pixel rectangle.
260     * @param h        Height of the pixel rectangle.
261     * @param obj      An object reference to an array of type defined by
262     *                 getTransferType() and length w*h*getNumDataElements().
263     *                 If null an array of appropriate type and size will be
264     *                 allocated.
265     * @return         An object reference to an array of type defined by
266     *                 getTransferType() with the request pixel data.
267     */
268    public Object getDataElements(int x, int y, int w, int h, Object obj) {
269        if ((x < this.minX) || (y < this.minY) ||
270            (x + w > this.maxX) || (y + h > this.maxY)) {
271            throw new ArrayIndexOutOfBoundsException
272                ("Coordinate out of bounds!");
273        }
274        short outData[];
275        if (obj == null) {
276            outData = new short[numDataElements*w*h];
277        } else {
278            outData = (short[])obj;
279        }
280        int yoff = (y-minY)*scanlineStride + (x-minX);
281
282        for (int c = 0; c < numDataElements; c++) {
283            int off = c;
284            short[] bank = data[c];
285            int dataOffset = dataOffsets[c];
286
287            int yoff2 = yoff;
288            for (int ystart=0; ystart < h; ystart++, yoff2 += scanlineStride) {
289                int xoff = dataOffset + yoff2;
290                for (int xstart=0; xstart < w; xstart++) {
291                    outData[off] = bank[xoff++];
292                    off += numDataElements;
293                }
294            }
295        }
296
297        return outData;
298    }
299
300    /**
301     * Returns a short array  of data elements from the specified rectangular
302     * region for the specified band.
303     * An ArrayIndexOutOfBounds exception will be thrown at runtime
304     * if the pixel coordinates are out of bounds.
305     * <pre>
306     *       short[] bandData = Raster.getShortData(x, y, w, h, null);
307     *       // To find the data element at location (x2, y2)
308     *       short bandElement = bandData[((y2-y)*w + (x2-x))];
309     * </pre>
310     * @param x        The X coordinate of the upper left pixel location.
311     * @param y        The Y coordinate of the upper left pixel location.
312     * @param w        Width of the pixel rectangle.
313     * @param h        Height of the pixel rectangle.
314     * @param band     The band to return.
315     * @param outData  If non-null, data elements for all bands
316     *                 at the specified location are returned in this array.
317     * @return         Data array with data elements for all bands.
318     */
319    public short[] getShortData(int x, int y, int w, int h,
320                                      int band, short[] outData) {
321        // Bounds check for 'band' will be performed automatically
322        if ((x < this.minX) || (y < this.minY) ||
323            (x + w > this.maxX) || (y + h > this.maxY)) {
324            throw new ArrayIndexOutOfBoundsException
325                ("Coordinate out of bounds!");
326        }
327        if (outData == null) {
328            outData = new short[scanlineStride*h];
329        }
330        int yoff = (y-minY)*scanlineStride + (x-minX) + dataOffsets[band];
331
332        if (scanlineStride == w) {
333            System.arraycopy(data[band], yoff, outData, 0, w*h);
334        } else {
335            int off = 0;
336            for (int ystart=0; ystart < h; ystart++, yoff += scanlineStride) {
337                System.arraycopy(data[band], yoff, outData, off, w);
338                off += w;
339            }
340        }
341
342        return outData;
343    }
344
345    /**
346     * Returns a short array  of data elements from the specified rectangular
347     * region.
348     * An ArrayIndexOutOfBounds exception will be thrown at runtime
349     * if the pixel coordinates are out of bounds.
350     * <pre>
351     *       short[] bandData = Raster.getShortData(x, y, w, h, null);
352     *       int numDataElements = Raster.getnumDataElements();
353     *       short[] pixel = new short[numDataElements];
354     *       // To find a data element at location (x2, y2)
355     *       System.arraycopy(bandData, ((y2-y)*w + (x2-x))*numDataElements,
356     *                        pixel, 0, numDataElements);
357     * </pre>
358     * @param x        The X coordinate of the upper left pixel location.
359     * @param y        The Y coordinate of the upper left pixel location.
360     * @param w        Width of the pixel rectangle.
361     * @param h        Height of the pixel rectangle.
362     * @param outData  If non-null, data elements for all bands
363     *                 at the specified location are returned in this array.
364     * @return         Data array with data elements for all bands.
365     */
366    public short[] getShortData(int x, int y, int w, int h,
367                                     short[] outData) {
368        if ((x < this.minX) || (y < this.minY) ||
369            (x + w > this.maxX) || (y + h > this.maxY)) {
370            throw new ArrayIndexOutOfBoundsException
371                ("Coordinate out of bounds!");
372        }
373        if (outData == null) {
374            outData = new short[numDataElements*scanlineStride*h];
375        }
376        int yoff = (y-minY)*scanlineStride + (x-minX);
377
378        for (int c = 0; c < numDataElements; c++) {
379            int off = c;
380            short[] bank = data[c];
381            int dataOffset = dataOffsets[c];
382
383            int yoff2 = yoff;
384            for (int ystart=0; ystart < h; ystart++, yoff2 += scanlineStride) {
385                int xoff = dataOffset + yoff2;
386                for (int xstart=0; xstart < w; xstart++) {
387                    outData[off] = bank[xoff++];
388                    off += numDataElements;
389                }
390            }
391        }
392
393        return outData;
394    }
395
396    /**
397     * Stores the data element for all bands at the specified location.
398     * An ArrayIndexOutOfBounds exception will be thrown at runtime
399     * if the pixel coordinate is out of bounds.
400     * A ClassCastException will be thrown if the input object is non null
401     * and references anything other than an array of transferType.
402     * @param x        The X coordinate of the pixel location.
403     * @param y        The Y coordinate of the pixel location.
404     * @param obj      An object reference to an array of type defined by
405     *                 getTransferType() and length getNumDataElements()
406     *                 containing the pixel data to place at x,y.
407     */
408    public void setDataElements(int x, int y, Object obj) {
409        if ((x < this.minX) || (y < this.minY) ||
410            (x >= this.maxX) || (y >= this.maxY)) {
411            throw new ArrayIndexOutOfBoundsException
412                ("Coordinate out of bounds!");
413        }
414        short inData[] = (short[])obj;
415        int off = (y-minY)*scanlineStride + (x-minX);
416        for (int i = 0; i < numDataElements; i++) {
417            data[i][dataOffsets[i] + off] = inData[i];
418        }
419
420        markDirty();
421    }
422
423    /**
424     * Stores the Raster data at the specified location.
425     * An ArrayIndexOutOfBounds exception will be thrown at runtime
426     * if the pixel coordinates are out of bounds.
427     * @param x          The X coordinate of the pixel location.
428     * @param y          The Y coordinate of the pixel location.
429     * @param inRaster   Raster of data to place at x,y location.
430     */
431    public void setDataElements(int x, int y, Raster inRaster) {
432        int dstOffX = x + inRaster.getMinX();
433        int dstOffY = y + inRaster.getMinY();
434        int width  = inRaster.getWidth();
435        int height = inRaster.getHeight();
436        if ((dstOffX < this.minX) || (dstOffY < this.minY) ||
437            (dstOffX + width > this.maxX) || (dstOffY + height > this.maxY)) {
438            throw new ArrayIndexOutOfBoundsException
439                ("Coordinate out of bounds!");
440        }
441
442        setDataElements(dstOffX, dstOffY, width, height, inRaster);
443    }
444
445    /**
446     * Stores the Raster data at the specified location.
447     * @param dstX The absolute X coordinate of the destination pixel
448     * that will receive a copy of the upper-left pixel of the
449     * inRaster
450     * @param dstY The absolute Y coordinate of the destination pixel
451     * that will receive a copy of the upper-left pixel of the
452     * inRaster
453     * @param width      The number of pixels to store horizontally
454     * @param height     The number of pixels to store vertically
455     * @param inRaster   Raster of data to place at x,y location.
456     */
457    private void setDataElements(int dstX, int dstY,
458                                 int width, int height,
459                                 Raster inRaster) {
460        // Assume bounds checking has been performed previously
461        if (width <= 0 || height <= 0) {
462            return;
463        }
464
465        // Write inRaster (minX, minY) to (dstX, dstY)
466
467        int srcOffX = inRaster.getMinX();
468        int srcOffY = inRaster.getMinY();
469        Object tdata = null;
470
471//      // REMIND: Do something faster!
472//      if (inRaster instanceof ShortBandedRaster) {
473//      }
474
475        for (int startY=0; startY < height; startY++) {
476            // Grab one scanline at a time
477            tdata = inRaster.getDataElements(srcOffX, srcOffY+startY,
478                                             width, 1, tdata);
479            setDataElements(dstX, dstY + startY, width, 1, tdata);
480        }
481    }
482
483    /**
484     * Stores an array of data elements into the specified rectangular
485     * region.
486     * An ArrayIndexOutOfBounds exception will be thrown at runtime
487     * if the pixel coordinates are out of bounds.
488     * A ClassCastException will be thrown if the input object is non null
489     * and references anything other than an array of transferType.
490     * The data elements in the
491     * data array are assumed to be packed.  That is, a data element
492     * for the nth band at location (x2, y2) would be found at:
493     * <pre>
494     *      inData[((y2-y)*w + (x2-x))*numDataElements + n]
495     * </pre>
496     * @param x        The X coordinate of the upper left pixel location.
497     * @param y        The Y coordinate of the upper left pixel location.
498     * @param w        Width of the pixel rectangle.
499     * @param h        Height of the pixel rectangle.
500     * @param obj      An object reference to an array of type defined by
501     *                 getTransferType() and length w*h*getNumDataElements()
502     *                 containing the pixel data to place between x,y and
503     *                 x+h, y+h.
504     */
505    public void setDataElements(int x, int y, int w, int h, Object obj) {
506        if ((x < this.minX) || (y < this.minY) ||
507            (x + w > this.maxX) || (y + h > this.maxY)) {
508            throw new ArrayIndexOutOfBoundsException
509                ("Coordinate out of bounds!");
510        }
511        short inData[] = (short[])obj;
512        int yoff = (y-minY)*scanlineStride + (x-minX);
513
514        for (int c = 0; c < numDataElements; c++) {
515            int off = c;
516            short[] bank = data[c];
517            int dataOffset = dataOffsets[c];
518
519            int yoff2 = yoff;
520            for (int ystart=0; ystart < h; ystart++, yoff2 += scanlineStride) {
521                int xoff = dataOffset + yoff2;
522                for (int xstart=0; xstart < w; xstart++) {
523                    bank[xoff++] = inData[off];
524                    off += numDataElements;
525                }
526            }
527        }
528
529        markDirty();
530    }
531
532    /**
533     * Stores a short array of data elements into the specified
534     * rectangular region for the specified band.
535     * An ArrayIndexOutOfBounds exception will be thrown at runtime
536     * if the pixel coordinates are out of bounds.
537     * The data elements in the
538     * data array are assumed to be packed.  That is, a data element
539     * at location (x2, y2) would be found at:
540     * <pre>
541     *      inData[((y2-y)*w + (x2-x))]
542     * </pre>
543     * @param x        The X coordinate of the upper left pixel location.
544     * @param y        The Y coordinate of the upper left pixel location.
545     * @param w        Width of the pixel rectangle.
546     * @param h        Height of the pixel rectangle.
547     * @param band     The band to set.
548     * @param inData   The data elements to be stored.
549     */
550    public void putShortData(int x, int y, int w, int h,
551                                   int band, short[] inData) {
552        // Bounds check for 'band' will be performed automatically
553        if ((x < this.minX) || (y < this.minY) ||
554            (x + w > this.maxX) || (y + h > this.maxY)) {
555            throw new ArrayIndexOutOfBoundsException
556                ("Coordinate out of bounds!");
557        }
558        int yoff = (y-minY)*scanlineStride + (x-minX) + dataOffsets[band];
559        int xoff;
560        int off = 0;
561        int xstart;
562        int ystart;
563
564        if (scanlineStride == w) {
565            System.arraycopy(inData, 0, data[band], yoff, w*h);
566        } else {
567            for (ystart=0; ystart < h; ystart++, yoff += scanlineStride) {
568                System.arraycopy(inData, off, data[band], yoff, w);
569                off += w;
570            }
571        }
572
573        markDirty();
574    }
575
576    /**
577     * Stores a short integer array of data elements into the specified
578     * rectangular region.
579     * An ArrayIndexOutOfBounds exception will be thrown at runtime
580     * if the pixel coordinates are out of bounds.
581     * The data elements in the
582     * data array are assumed to be packed.  That is, a data element
583     * for the nth band at location (x2, y2) would be found at:
584     * <pre>
585     *      inData[((y2-y)*w + (x2-x))*numDataElements + n]
586     * </pre>
587     * @param x        The X coordinate of the upper left pixel location.
588     * @param y        The Y coordinate of the upper left pixel location.
589     * @param w        Width of the pixel rectangle.
590     * @param h        Height of the pixel rectangle.
591     * @param inData   The data elements to be stored.
592     */
593    public void putShortData(int x, int y, int w, int h, short[] inData) {
594        if ((x < this.minX) || (y < this.minY) ||
595            (x + w > this.maxX) || (y + h > this.maxY)) {
596            throw new ArrayIndexOutOfBoundsException
597                ("Coordinate out of bounds!");
598        }
599        int yoff = (y-minY)*scanlineStride + (x-minX);
600
601        for (int c = 0; c < numDataElements; c++) {
602            int off = c;
603            short[] bank = data[c];
604            int dataOffset = dataOffsets[c];
605
606            int yoff2 = yoff;
607            for (int ystart=0; ystart < h; ystart++, yoff2 += scanlineStride) {
608                int xoff = dataOffset + yoff2;
609                for (int xstart=0; xstart < w; xstart++) {
610                    bank[xoff++] = inData[off];
611                    off += numDataElements;
612                }
613            }
614        }
615
616        markDirty();
617    }
618
619    /**
620     * Creates a Writable subRaster given a region of the Raster.  The x and y
621     * coordinates specify the horizontal and vertical offsets
622     * from the upper-left corner of this Raster to the upper-left corner
623     * of the subRaster.  A subset of the bands of the parent Raster may
624     * be specified.  If this is null, then all the bands are present in the
625     * subRaster. A translation to the subRaster may also be specified.
626     * Note that the subRaster will reference the same
627     * DataBuffers as the parent Raster, but using different offsets.
628     * @param x               X offset.
629     * @param y               Y offset.
630     * @param width           Width (in pixels) of the subraster.
631     * @param height          Height (in pixels) of the subraster.
632     * @param x0              Translated X origin of the subraster.
633     * @param y0              Translated Y origin of the subraster.
634     * @param bandList        Array of band indices.
635     * @exception RasterFormatException
636     *            if the specified bounding box is outside of the parent Raster.
637     */
638    public WritableRaster createWritableChild(int x, int y,
639                                              int width, int height,
640                                              int x0, int y0,
641                                              int bandList[]) {
642
643        if (x < this.minX) {
644            throw new RasterFormatException("x lies outside raster");
645        }
646        if (y < this.minY) {
647            throw new RasterFormatException("y lies outside raster");
648        }
649        if ((x+width < x) || (x+width > this.minX + this.width)) {
650            throw new RasterFormatException("(x + width) is outside of Raster");
651        }
652        if ((y+height < y) || (y+height > this.minY + this.height)) {
653            throw new RasterFormatException("(y + height) is outside of Raster");
654        }
655
656        SampleModel sm;
657
658        if (bandList != null)
659            sm = sampleModel.createSubsetSampleModel(bandList);
660        else
661            sm = sampleModel;
662
663        int deltaX = x0 - x;
664        int deltaY = y0 - y;
665
666        return new ShortBandedRaster(sm,
667                                     (DataBufferUShort) dataBuffer,
668                                     new Rectangle(x0, y0, width, height),
669                                     new Point(sampleModelTranslateX+deltaX,
670                                               sampleModelTranslateY+deltaY),
671                                     this);
672
673    }
674
675    /**
676     * Creates a subraster given a region of the raster.  The x and y
677     * coordinates specify the horizontal and vertical offsets
678     * from the upper-left corner of this raster to the upper-left corner
679     * of the subraster.  A subset of the bands of the parent Raster may
680     * be specified.  If this is null, then all the bands are present in the
681     * subRaster. A translation to the subRaster may also be specified.
682     * Note that the subraster will reference the same
683     * DataBuffers as the parent raster, but using different offsets.
684     * @param x               X offset.
685     * @param y               Y offset.
686     * @param width           Width (in pixels) of the subraster.
687     * @param height          Height (in pixels) of the subraster.
688     * @param x0              Translated X origin of the subraster.
689     * @param y0              Translated Y origin of the subraster.
690     * @param bandList        Array of band indices.
691     * @exception RasterFormatException
692     *            if the specified bounding box is outside of the parent raster.
693     */
694    public Raster createChild (int x, int y,
695                               int width, int height,
696                               int x0, int y0,
697                               int bandList[]) {
698        return createWritableChild(x, y, width, height, x0, y0, bandList);
699    }
700
701    /**
702     * Creates a Raster with the same layout but using a different
703     * width and height, and with new zeroed data arrays.
704     */
705    public WritableRaster createCompatibleWritableRaster(int w, int h) {
706        if (w <= 0 || h <=0) {
707            throw new RasterFormatException("negative "+
708                                            ((w <= 0) ? "width" : "height"));
709        }
710
711        SampleModel sm = sampleModel.createCompatibleSampleModel(w,h);
712
713        return new ShortBandedRaster(sm, new Point(0,0));
714    }
715
716    /**
717     * Creates a Raster with the same layout and the same
718     * width and height, and with new zeroed data arrays.  If
719     * the Raster is a subRaster, this will call
720     * createCompatibleRaster(width, height).
721     */
722    public WritableRaster createCompatibleWritableRaster() {
723       return createCompatibleWritableRaster(width,height);
724    }
725
726    /**
727     * Verify that the layout parameters are consistent with the data.
728     * Verifies whether the data buffer has enough data for the raster,
729     * taking into account offsets, after ensuring all offsets are >=0.
730     * @throws RasterFormatException if a problem is detected.
731     */
732    private void verify() {
733
734        /* Need to re-verify the dimensions since a sample model may be
735         * specified to the constructor
736         */
737        if (width <= 0 || height <= 0 ||
738            height > (Integer.MAX_VALUE / width))
739        {
740            throw new RasterFormatException("Invalid raster dimension");
741        }
742
743        if (scanlineStride < 0 ||
744            scanlineStride > (Integer.MAX_VALUE / height))
745        {
746            // integer overflow
747            throw new RasterFormatException("Incorrect scanline stride: "
748                    + scanlineStride);
749        }
750
751        if ((long)minX - sampleModelTranslateX < 0 ||
752            (long)minY - sampleModelTranslateY < 0) {
753
754            throw new RasterFormatException("Incorrect origin/translate: (" +
755                    minX + ", " + minY + ") / (" +
756                    sampleModelTranslateX + ", " + sampleModelTranslateY + ")");
757        }
758
759        if (height > 1 || minY - sampleModelTranslateY > 0) {
760            // buffer should contain at least one scanline
761            for (int i = 0; i < data.length; i++) {
762                if (scanlineStride > data[i].length) {
763                    throw new RasterFormatException("Incorrect scanline stride: "
764                        + scanlineStride);
765                }
766            }
767        }
768
769        // Make sure data for Raster is in a legal range
770        for (int i=0; i < dataOffsets.length; i++) {
771            if (dataOffsets[i] < 0) {
772                throw new RasterFormatException("Data offsets for band "+i+
773                                                "("+dataOffsets[i]+
774                                                ") must be >= 0");
775            }
776        }
777
778        int lastScanOffset = (height - 1) * scanlineStride;
779        if ((width - 1) > (Integer.MAX_VALUE - lastScanOffset)) {
780            throw new RasterFormatException("Invalid raster dimension");
781        }
782        int lastPixelOffset = lastScanOffset + (width - 1);
783
784        int maxIndex = 0;
785        int index;
786
787        for (int i=0; i < numDataElements; i++) {
788            if (dataOffsets[i] > (Integer.MAX_VALUE - lastPixelOffset)) {
789                throw new RasterFormatException("Invalid raster dimension");
790            }
791            index = lastPixelOffset + dataOffsets[i];
792            if (index > maxIndex) {
793                maxIndex = index;
794            }
795        }
796        for (int i=0; i < numDataElements; i++) {
797            if (data[i].length <= maxIndex) {
798                throw new RasterFormatException("Data array too small " +
799                      "(should be > "+ maxIndex+" )");
800            }
801        }
802    }
803
804    public String toString() {
805        return new String ("ShortBandedRaster: width = "+width+" height = "
806                           + height
807                           +" #numBands " + numBands
808                           +" #dataElements "+numDataElements);
809
810    }
811
812}
813