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