001: /*
002: * Copyright 1998-2007 Sun Microsystems, Inc. All Rights Reserved.
003: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
004: *
005: * This code is free software; you can redistribute it and/or modify it
006: * under the terms of the GNU General Public License version 2 only, as
007: * published by the Free Software Foundation. Sun designates this
008: * particular file as subject to the "Classpath" exception as provided
009: * by Sun in the LICENSE file that accompanied this code.
010: *
011: * This code is distributed in the hope that it will be useful, but WITHOUT
012: * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
013: * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
014: * version 2 for more details (a copy is included in the LICENSE file that
015: * accompanied this code).
016: *
017: * You should have received a copy of the GNU General Public License version
018: * 2 along with this work; if not, write to the Free Software Foundation,
019: * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
020: *
021: * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
022: * CA 95054 USA or visit www.sun.com if you need additional information or
023: * have any questions.
024: */
025:
026: package sun.awt.image;
027:
028: import java.awt.image.Raster;
029: import java.awt.image.WritableRaster;
030: import java.awt.image.RasterFormatException;
031: import java.awt.image.SampleModel;
032: import java.awt.image.SinglePixelPackedSampleModel;
033: import java.awt.image.DataBuffer;
034: import java.awt.image.DataBufferInt;
035: import java.awt.Rectangle;
036: import java.awt.Point;
037:
038: /**
039: * This class defines a Raster with pixels consisting of one or more 32-bit
040: * data elements stored in close proximity to each other in a integer array.
041: * The bit precision per data element is that
042: * of the data type (that is, the bit precision for this raster is 32).
043: * There is only one pixel stride and one scanline stride for all
044: * bands. For a given pixel, all samples fit in N data elements and these
045: * N data elements hold samples for only one pixel. This type of Raster
046: * can be used with a PackedColorModel.
047: * <p>
048: * For example, if there is only one data element per pixel, a
049: * SinglePixelPackedSampleModel can be used to represent multiple
050: * bands with a PackedColorModel (including a DirectColorModel) for
051: * color interpretation.
052: *
053: * @version 10 Feb 1997
054: */
055: public class IntegerInterleavedRaster extends IntegerComponentRaster {
056:
057: /** A cached copy of minX + width for use in bounds checks. */
058: private int maxX;
059:
060: /** A cached copy of minY + height for use in bounds checks. */
061: private int maxY;
062:
063: /**
064: * Constructs a IntegerInterleavedRaster with the given SampleModel.
065: * The Raster's upper left corner is origin and it is the same
066: * size as the SampleModel. A DataBuffer large enough to describe the
067: * Raster is automatically created. SampleModel must be of type
068: * SinglePixelPackedSampleModel.
069: * @param sampleModel The SampleModel that specifies the layout.
070: * @param origin The Point that specified the origin.
071: */
072: public IntegerInterleavedRaster(SampleModel sampleModel,
073: Point origin) {
074: this (sampleModel, sampleModel.createDataBuffer(),
075: new Rectangle(origin.x, origin.y, sampleModel
076: .getWidth(), sampleModel.getHeight()), origin,
077: null);
078: }
079:
080: /**
081: * Constructs a IntegerInterleavedRaster with the given SampleModel
082: * and DataBuffer. The Raster's upper left corner is origin and
083: * it is the same sizes the SampleModel. The DataBuffer is not
084: * initialized and must be a DataBufferInt compatible with SampleModel.
085: * SampleModel must be of type SinglePixelPackedSampleModel.
086: * @param sampleModel The SampleModel that specifies the layout.
087: * @param dataBuffer The DataBufferInt that contains the image data.
088: * @param origin The Point that specifies the origin.
089: */
090: public IntegerInterleavedRaster(SampleModel sampleModel,
091: DataBuffer dataBuffer, Point origin) {
092: this (sampleModel, dataBuffer, new Rectangle(origin.x, origin.y,
093: sampleModel.getWidth(), sampleModel.getHeight()),
094: origin, null);
095: }
096:
097: /**
098: * Constructs a IntegerInterleavedRaster with the given SampleModel,
099: * DataBuffer, and parent. DataBuffer must be a DataBufferInt and
100: * SampleModel must be of type SinglePixelPackedSampleModel.
101: * When translated into the base Raster's
102: * coordinate system, aRegion must be contained by the base Raster.
103: * Origin is the coodinate in the new Raster's coordinate system of
104: * the origin of the base Raster. (The base Raster is the Raster's
105: * ancestor which has no parent.)
106: *
107: * Note that this constructor should generally be called by other
108: * constructors or create methods, it should not be used directly.
109: * @param sampleModel The SampleModel that specifies the layout.
110: * @param dataBuffer The DataBufferInt that contains the image data.
111: * @param aRegion The Rectangle that specifies the image area.
112: * @param origin The Point that specifies the origin.
113: * @param parent The parent (if any) of this raster.
114: */
115: public IntegerInterleavedRaster(SampleModel sampleModel,
116: DataBuffer dataBuffer, Rectangle aRegion, Point origin,
117: IntegerInterleavedRaster parent) {
118: super (sampleModel, dataBuffer, aRegion, origin, parent);
119: this .maxX = minX + width;
120: this .maxY = minY + height;
121: if (!(dataBuffer instanceof DataBufferInt)) {
122: throw new RasterFormatException(
123: "IntegerInterleavedRasters must have"
124: + "integer DataBuffers");
125: }
126: DataBufferInt dbi = (DataBufferInt) dataBuffer;
127: this .data = stealData(dbi, 0);
128:
129: if (sampleModel instanceof SinglePixelPackedSampleModel) {
130: SinglePixelPackedSampleModel sppsm = (SinglePixelPackedSampleModel) sampleModel;
131: this .scanlineStride = sppsm.getScanlineStride();
132: this .pixelStride = 1;
133: this .dataOffsets = new int[1];
134: this .dataOffsets[0] = dbi.getOffset();
135: this .bandOffset = this .dataOffsets[0];
136: int xOffset = aRegion.x - origin.x;
137: int yOffset = aRegion.y - origin.y;
138: dataOffsets[0] += xOffset + yOffset * scanlineStride;
139: this .numDataElems = sppsm.getNumDataElements();
140: } else {
141: throw new RasterFormatException(
142: "IntegerInterleavedRasters must have"
143: + " SinglePixelPackedSampleModel");
144: }
145: verify(false);
146: }
147:
148: /**
149: * Returns a copy of the data offsets array. For each band the data offset
150: * is the index into the band's data array, of the first sample of the
151: * band.
152: */
153: public int[] getDataOffsets() {
154: return (int[]) dataOffsets.clone();
155: }
156:
157: /**
158: * Returns data offset for the specified band. The data offset
159: * is the index into the data array in which the first sample
160: * of the first scanline is stored.
161: */
162: public int getDataOffset(int band) {
163: return dataOffsets[band];
164: }
165:
166: /**
167: * Returns the scanline stride -- the number of data array elements between
168: * a given sample and the sample in the same column of the next row.
169: */
170: public int getScanlineStride() {
171: return scanlineStride;
172: }
173:
174: /**
175: * Returns pixel stride -- the number of data array elements between two
176: * samples for the same band on the same scanline.
177: */
178: public int getPixelStride() {
179: return pixelStride;
180: }
181:
182: /**
183: * Returns a reference to the data array.
184: */
185: public int[] getDataStorage() {
186: return data;
187: }
188:
189: /**
190: * Returns the data elements for all bands at the specified
191: * location.
192: * An ArrayIndexOutOfBounds exception will be thrown at runtime
193: * if the pixel coordinate is out of bounds.
194: * A ClassCastException will be thrown if the input object is non null
195: * and references anything other than an array of transferType.
196: * @param x The X coordinate of the pixel location.
197: * @param y The Y coordinate of the pixel location.
198: * @param outData An object reference to an array of type defined by
199: * getTransferType() and length getNumDataElements().
200: * If null an array of appropriate type and size will be
201: * allocated.
202: * @return An object reference to an array of type defined by
203: * getTransferType() with the request pixel data.
204: */
205: public Object getDataElements(int x, int y, Object obj) {
206: if ((x < this .minX) || (y < this .minY) || (x >= this .maxX)
207: || (y >= this .maxY)) {
208: throw new ArrayIndexOutOfBoundsException(
209: "Coordinate out of bounds!");
210: }
211: int outData[];
212: if (obj == null) {
213: outData = new int[1];
214: } else {
215: outData = (int[]) obj;
216: }
217: int off = (y - minY) * scanlineStride + (x - minX)
218: + dataOffsets[0];
219: outData[0] = data[off];
220:
221: return outData;
222: }
223:
224: /**
225: * Returns an array of data elements from the specified rectangular
226: * region.
227: * An ArrayIndexOutOfBounds exception will be thrown at runtime
228: * if the pixel coordinates are out of bounds.
229: * A ClassCastException will be thrown if the input object is non null
230: * and references anything other than an array of transferType.
231: <pre>
232: * int[] bandData = (int[])raster.getDataElements(x, y, w, h, null);
233: * int numDataElements = raster.getNumDataElements();
234: * int[] pixel = new int[numDataElements];
235: * // To find a data element at location (x2, y2)
236: * System.arraycopy(bandData, ((y2-y)*w + (x2-x))*numDataElements,
237: * pixel, 0, numDataElements);
238: * </pre>
239: * @param x The X coordinate of the upper left pixel location.
240: * @param y The Y coordinate of the upper left pixel location.
241: * @param width Width of the pixel rectangle.
242: * @param height Height of the pixel rectangle.
243: * @param outData An object reference to an array of type defined by
244: * getTransferType() and length w*h*getNumDataElements().
245: * If null an array of appropriate type and size will be
246: * allocated.
247: * @return An object reference to an array of type defined by
248: * getTransferType() with the request pixel data.
249: */
250: public Object getDataElements(int x, int y, int w, int h, Object obj) {
251: if ((x < this .minX) || (y < this .minY) || (x + w > this .maxX)
252: || (y + h > this .maxY)) {
253: throw new ArrayIndexOutOfBoundsException(
254: "Coordinate out of bounds!");
255: }
256: int outData[];
257: if (obj instanceof int[]) {
258: outData = (int[]) obj;
259: } else {
260: outData = new int[w * h];
261: }
262: int yoff = (y - minY) * scanlineStride + (x - minX)
263: + dataOffsets[0];
264: int off = 0;
265:
266: for (int ystart = 0; ystart < h; ystart++) {
267: System.arraycopy(data, yoff, outData, off, w);
268: off += w;
269: yoff += scanlineStride;
270: }
271:
272: return outData;
273: }
274:
275: /**
276: * Stores the data elements for all bands at the specified location.
277: * An ArrayIndexOutOfBounds exception will be thrown at runtime
278: * if the pixel coordinate is out of bounds.
279: * A ClassCastException will be thrown if the input object is non null
280: * and references anything other than an array of transferType.
281: * @param x The X coordinate of the pixel location.
282: * @param y The Y coordinate of the pixel location.
283: * @param inData An object reference to an array of type defined by
284: * getTransferType() and length getNumDataElements()
285: * containing the pixel data to place at x,y.
286: */
287: public void setDataElements(int x, int y, Object obj) {
288: if ((x < this .minX) || (y < this .minY) || (x >= this .maxX)
289: || (y >= this .maxY)) {
290: throw new ArrayIndexOutOfBoundsException(
291: "Coordinate out of bounds!");
292: }
293: int inData[] = (int[]) obj;
294:
295: int off = (y - minY) * scanlineStride + (x - minX)
296: + dataOffsets[0];
297:
298: data[off] = inData[0];
299:
300: markDirty();
301: }
302:
303: /**
304: * Stores the Raster data at the specified location.
305: * The transferType of the inputRaster must match this raster.
306: * An ArrayIndexOutOfBoundsException will be thrown at runtime
307: * if the pixel coordinates are out of bounds.
308: * @param x The X coordinate of the pixel location.
309: * @param y The Y coordinate of the pixel location.
310: * @param inRaster Raster of data to place at x,y location.
311: */
312: public void setDataElements(int x, int y, Raster inRaster) {
313: int dstOffX = x + inRaster.getMinX();
314: int dstOffY = y + inRaster.getMinY();
315: int width = inRaster.getWidth();
316: int height = inRaster.getHeight();
317: if ((dstOffX < this .minX) || (dstOffY < this .minY)
318: || (dstOffX + width > this .maxX)
319: || (dstOffY + height > this .maxY)) {
320: throw new ArrayIndexOutOfBoundsException(
321: "Coordinate out of bounds!");
322: }
323:
324: setDataElements(dstOffX, dstOffY, width, height, inRaster);
325: }
326:
327: /**
328: * Stores the Raster data at the specified location.
329: * @param dstX The absolute X coordinate of the destination pixel
330: * that will receive a copy of the upper-left pixel of the
331: * inRaster
332: * @param dstY The absolute Y coordinate of the destination pixel
333: * that will receive a copy of the upper-left pixel of the
334: * inRaster
335: * @param width The number of pixels to store horizontally
336: * @param height The number of pixels to store vertically
337: * @param inRaster Raster of data to place at x,y location.
338: */
339: private void setDataElements(int dstX, int dstY, int width,
340: int height, Raster inRaster) {
341: // Assume bounds checking has been performed previously
342: if (width <= 0 || height <= 0) {
343: return;
344: }
345:
346: // Write inRaster (minX, minY) to (dstX, dstY)
347:
348: int srcOffX = inRaster.getMinX();
349: int srcOffY = inRaster.getMinY();
350: int tdata[] = null;
351:
352: if (inRaster instanceof IntegerInterleavedRaster) {
353: IntegerInterleavedRaster ict = (IntegerInterleavedRaster) inRaster;
354:
355: // Extract the raster parameters
356: tdata = ict.getDataStorage();
357: int tss = ict.getScanlineStride();
358: int toff = ict.getDataOffset(0);
359:
360: int srcOffset = toff;
361: int dstOffset = dataOffsets[0] + (dstY - minY)
362: * scanlineStride + (dstX - minX);
363:
364: // Fastest case. We can copy scanlines
365: // Loop through all of the scanlines and copy the data
366: for (int startY = 0; startY < height; startY++) {
367: System.arraycopy(tdata, srcOffset, data, dstOffset,
368: width);
369: srcOffset += tss;
370: dstOffset += scanlineStride;
371: }
372: markDirty();
373: return;
374: }
375:
376: Object odata = null;
377: for (int startY = 0; startY < height; startY++) {
378: // Grab one scanline at a time
379: odata = inRaster.getDataElements(srcOffX, srcOffY + startY,
380: width, 1, odata);
381: setDataElements(dstX, dstY + startY, width, 1, odata);
382: }
383: }
384:
385: /**
386: * Stores an array of data elements into the specified rectangular
387: * region.
388: * An ArrayIndexOutOfBounds exception will be thrown at runtime
389: * if the pixel coordinates are out of bounds.
390: * A ClassCastException will be thrown if the input object is non null
391: * and references anything other than an array of transferType.
392: * The data elements in the
393: * data array are assumed to be packed. That is, a data element
394: * for the nth band at location (x2, y2) would be found at:
395: * <pre>
396: * inData[((y2-y)*w + (x2-x))*numDataElements + n]
397: * </pre>
398: * @param x The X coordinate of the upper left pixel location.
399: * @param y The Y coordinate of the upper left pixel location.
400: * @param w Width of the pixel rectangle.
401: * @param h Height of the pixel rectangle.
402: * @param inData An object reference to an array of type defined by
403: * getTransferType() and length w*h*getNumDataElements()
404: * containing the pixel data to place between x,y and
405: * x+h, y+h.
406: */
407: public void setDataElements(int x, int y, int w, int h, Object obj) {
408: if ((x < this .minX) || (y < this .minY) || (x + w > this .maxX)
409: || (y + h > this .maxY)) {
410: throw new ArrayIndexOutOfBoundsException(
411: "Coordinate out of bounds!");
412: }
413: int inData[] = (int[]) obj;
414: int yoff = (y - minY) * scanlineStride + (x - minX)
415: + dataOffsets[0];
416: int off = 0;
417:
418: for (int ystart = 0; ystart < h; ystart++) {
419: System.arraycopy(inData, off, data, yoff, w);
420: off += w;
421: yoff += scanlineStride;
422: }
423:
424: markDirty();
425: }
426:
427: /**
428: * Creates a subraster given a region of the raster. The x and y
429: * coordinates specify the horizontal and vertical offsets
430: * from the upper-left corner of this raster to the upper-left corner
431: * of the subraster. A subset of the bands of the parent Raster may
432: * be specified. If this is null, then all the bands are present in the
433: * subRaster. A translation to the subRaster may also be specified.
434: * Note that the subraster will reference the same
435: * DataBuffer as the parent raster, but using different offsets.
436: * @param x X offset.
437: * @param y Y offset.
438: * @param width Width (in pixels) of the subraster.
439: * @param height Height (in pixels) of the subraster.
440: * @param x0 Translated X origin of the subraster.
441: * @param y0 Translated Y origin of the subraster.
442: * @param bandList Array of band indices.
443: * @exception RasterFormatException
444: * if the specified bounding box is outside of the parent raster.
445: */
446: public WritableRaster createWritableChild(int x, int y, int width,
447: int height, int x0, int y0, int bandList[]) {
448: if (x < this .minX) {
449: throw new RasterFormatException("x lies outside raster");
450: }
451: if (y < this .minY) {
452: throw new RasterFormatException("y lies outside raster");
453: }
454: if ((x + width < x) || (x + width > this .minX + this .width)) {
455: throw new RasterFormatException(
456: "(x + width) is outside raster");
457: }
458: if ((y + height < y) || (y + height > this .minY + this .height)) {
459: throw new RasterFormatException(
460: "(y + height) is outside raster");
461: }
462:
463: SampleModel sm;
464:
465: if (bandList != null)
466: sm = sampleModel.createSubsetSampleModel(bandList);
467: else
468: sm = sampleModel;
469:
470: int deltaX = x0 - x;
471: int deltaY = y0 - y;
472:
473: return new IntegerInterleavedRaster(sm, dataBuffer,
474: new Rectangle(x0, y0, width, height), new Point(
475: sampleModelTranslateX + deltaX,
476: sampleModelTranslateY + deltaY), this );
477: }
478:
479: /**
480: * Creates a subraster given a region of the raster. The x and y
481: * coordinates specify the horizontal and vertical offsets
482: * from the upper-left corner of this raster to the upper-left corner
483: * of the subraster. A subset of the bands of the parent raster may
484: * be specified. If this is null, then all the bands are present in the
485: * subRaster. Note that the subraster will reference the same
486: * DataBuffer as the parent raster, but using different offsets.
487: * @param x X offset.
488: * @param y Y offset.
489: * @param width Width (in pixels) of the subraster.
490: * @param height Height (in pixels) of the subraster.
491: * @param x0 Translated X origin of the subRaster.
492: * @param y0 Translated Y origin of the subRaster.
493: * @param bandList Array of band indices.
494: * @exception RasterFormatException
495: * if the specified bounding box is outside of the parent raster.
496: */
497: public Raster createChild(int x, int y, int width, int height,
498: int x0, int y0, int bandList[]) {
499: return createWritableChild(x, y, width, height, x0, y0,
500: bandList);
501: }
502:
503: /**
504: * Creates a raster with the same band layout but using a different
505: * width and height, and with new zeroed data arrays.
506: */
507: public WritableRaster createCompatibleWritableRaster(int w, int h) {
508: if (w <= 0 || h <= 0) {
509: throw new RasterFormatException("negative "
510: + ((w <= 0) ? "width" : "height"));
511: }
512:
513: SampleModel sm = sampleModel.createCompatibleSampleModel(w, h);
514:
515: return new IntegerInterleavedRaster(sm, new Point(0, 0));
516: }
517:
518: /**
519: * Creates a raster with the same data layout and the same
520: * width and height, and with new zeroed data arrays. If
521: * the raster is a subraster, this will call
522: * createCompatibleRaster(width, height).
523: */
524: public WritableRaster createCompatibleWritableRaster() {
525: return createCompatibleWritableRaster(width, height);
526: }
527:
528: /**
529: * Verify that the layout parameters are consistent with
530: * the data. If strictCheck
531: * is false, this method will check for ArrayIndexOutOfBounds conditions. If
532: * strictCheck is true, this method will check for additional error
533: * conditions such as line wraparound (width of a line greater than
534: * the scanline stride).
535: * @return String Error string, if the layout is incompatible with
536: * the data. Otherwise returns null.
537: */
538: private void verify(boolean strictCheck) {
539: int maxSize = 0;
540: int size;
541:
542: size = (height - 1) * scanlineStride + (width - 1)
543: + dataOffsets[0];
544: if (size > maxSize) {
545: maxSize = size;
546: }
547: if (data.length < maxSize) {
548: throw new RasterFormatException(
549: "Data array too small (should be " + maxSize
550: + " but is " + data.length + " )");
551: }
552: }
553:
554: public String toString() {
555: return new String("IntegerInterleavedRaster: width = " + width
556: + " height = " + height + " #Bands = " + numBands
557: + " xOff = " + sampleModelTranslateX + " yOff = "
558: + sampleModelTranslateY + " dataOffset[0] "
559: + dataOffsets[0]);
560: }
561:
562: // /**
563: // * For debugging... prints a region of a one-band IntegerInterleavedRaster
564: // */
565: // public void print(int x, int y, int w, int h) {
566: // // REMIND: Only works for 1 band!
567: // System.out.println(this);
568: // int offset = dataOffsets[0] + y*scanlineStride + x*pixelStride;
569: // int off;
570: // for (int yoff=0; yoff < h; yoff++, offset += scanlineStride) {
571: // off = offset;
572: // System.out.print("Line "+(sampleModelTranslateY+y+yoff)+": ");
573: // for (int xoff = 0; xoff < w; xoff++, off+= pixelStride) {
574: // System.out.print(Integer.toHexString(data[off])+" ");
575: // }
576: // System.out.println("");
577: // }
578: // }
579:
580: }
|