blob: 5bbde1ab94ef0198a9ee37139430b1fa487d70af [file] [log] [blame]
J. Duke319a3b92007-12-01 00:00:00 +00001/*
2 * Copyright 1998-2007 Sun Microsystems, Inc. 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. Sun designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Sun 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
22 * CA 95054 USA or visit www.sun.com if you need additional information or
23 * have any 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.SinglePixelPackedSampleModel;
32import java.awt.image.DataBuffer;
33import java.awt.image.DataBufferInt;
34import java.awt.Rectangle;
35import 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 */
53public 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(false);
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 /**
544 * Verify that the layout parameters are consistent with
545 * the data. If strictCheck
546 * is false, this method will check for ArrayIndexOutOfBounds conditions. If
547 * strictCheck is true, this method will check for additional error
548 * conditions such as line wraparound (width of a line greater than
549 * the scanline stride).
550 * @return String Error string, if the layout is incompatible with
551 * the data. Otherwise returns null.
552 */
553 private void verify (boolean strictCheck) {
554 int maxSize = 0;
555 int size;
556
557 size = (height-1)*scanlineStride + (width-1) + dataOffsets[0];
558 if (size > maxSize) {
559 maxSize = size;
560 }
561 if (data.length < maxSize) {
562 throw new RasterFormatException("Data array too small (should be "+
563 maxSize
564 +" but is "+data.length+" )");
565 }
566 }
567
568 public String toString() {
569 return new String ("IntegerInterleavedRaster: width = "+width
570 +" height = " + height
571 +" #Bands = " + numBands
572 +" xOff = "+sampleModelTranslateX
573 +" yOff = "+sampleModelTranslateY
574 +" dataOffset[0] "+dataOffsets[0]);
575 }
576
577// /**
578// * For debugging... prints a region of a one-band IntegerInterleavedRaster
579// */
580// public void print(int x, int y, int w, int h) {
581// // REMIND: Only works for 1 band!
582// System.out.println(this);
583// int offset = dataOffsets[0] + y*scanlineStride + x*pixelStride;
584// int off;
585// for (int yoff=0; yoff < h; yoff++, offset += scanlineStride) {
586// off = offset;
587// System.out.print("Line "+(sampleModelTranslateY+y+yoff)+": ");
588// for (int xoff = 0; xoff < w; xoff++, off+= pixelStride) {
589// System.out.print(Integer.toHexString(data[off])+" ");
590// }
591// System.out.println("");
592// }
593// }
594
595}