1 /* Copyright (C) 2004, 2006, Free Software Foundation
3 This file is part of GNU Classpath.
5 GNU Classpath is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation; either version 2, or (at your option)
10 GNU Classpath is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 General Public License for more details.
15 You should have received a copy of the GNU General Public License
16 along with GNU Classpath; see the file COPYING. If not, write to the
17 Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
20 Linking this library statically or dynamically with other modules is
21 making a combined work based on this library. Thus, the terms and
22 conditions of the GNU General Public License cover the whole
25 As a special exception, the copyright holders of this library give you
26 permission to link this library with independent modules to produce an
27 executable, regardless of the license terms of these independent
28 modules, and to copy and distribute the resulting executable under
29 terms of your choice, provided that you also meet, for each linked
30 independent module, the terms and conditions of the license of that
31 module. An independent module is a module which is not derived from
32 or based on this library. If you modify this library, you may extend
33 this exception to your version of the library, but you are not
34 obligated to do so. If you do not wish to do so, delete this
35 exception statement from your version. */
37 package java.awt.image;
39 import gnu.java.awt.Buffers;
40 import gnu.java.lang.CPStringBuilder;
43 * MultiPixelPackedSampleModel provides a single band model that supports
44 * multiple pixels in a single unit. Pixels have 2^n bits and 2^k pixels fit
47 * @author Jerry Quinn (jlquinn@optonline.net)
49 public class MultiPixelPackedSampleModel extends SampleModel
51 private int scanlineStride;
52 private int[] bitMasks;
53 private int[] bitOffsets;
54 private int[] sampleSize;
55 private int dataBitOffset;
57 private int numberOfBits;
61 * Creates a new <code>MultiPixelPackedSampleModel</code> with the specified
62 * data type, which should be one of:
64 * <li>{@link DataBuffer#TYPE_BYTE};</li>
65 * <li>{@link DataBuffer#TYPE_USHORT};</li>
66 * <li>{@link DataBuffer#TYPE_INT};</li>
69 * @param dataType the data type.
70 * @param w the width (in pixels).
71 * @param h the height (in pixels).
72 * @param numberOfBits the number of bits per pixel (must be a power of 2).
74 public MultiPixelPackedSampleModel(int dataType, int w, int h,
77 this(dataType, w, h, numberOfBits, 0, 0);
81 * Creates a new <code>MultiPixelPackedSampleModel</code> with the specified
82 * data type, which should be one of:
84 * <li>{@link DataBuffer#TYPE_BYTE};</li>
85 * <li>{@link DataBuffer#TYPE_USHORT};</li>
86 * <li>{@link DataBuffer#TYPE_INT};</li>
89 * @param dataType the data type.
90 * @param w the width (in pixels).
91 * @param h the height (in pixels).
92 * @param numberOfBits the number of bits per pixel (must be a power of 2).
93 * @param scanlineStride the number of data elements from a pixel on one
94 * row to the corresponding pixel in the next row.
95 * @param dataBitOffset the offset to the first data bit.
97 public MultiPixelPackedSampleModel(int dataType, int w, int h,
98 int numberOfBits, int scanlineStride,
101 super(dataType, w, h, 1);
105 case DataBuffer.TYPE_BYTE:
108 case DataBuffer.TYPE_USHORT:
111 case DataBuffer.TYPE_INT:
115 throw new IllegalArgumentException("MultiPixelPackedSampleModel"
116 + " unsupported dataType");
119 this.dataBitOffset = dataBitOffset;
121 this.numberOfBits = numberOfBits;
122 if (numberOfBits > elemBits)
123 throw new RasterFormatException("MultiPixelPackedSampleModel pixel size"
124 + " larger than dataType");
125 switch (numberOfBits)
127 case 1: case 2: case 4: case 8: case 16: case 32: break;
129 throw new RasterFormatException("MultiPixelPackedSampleModel pixel"
130 + " size not 2^n bits");
132 numElems = elemBits / numberOfBits;
134 // Compute scan line large enough for w pixels.
135 if (scanlineStride == 0)
136 scanlineStride = ((dataBitOffset + w * numberOfBits) - 1) / elemBits + 1;
137 this.scanlineStride = scanlineStride;
140 sampleSize = new int[1];
141 sampleSize[0] = numberOfBits;
143 bitMasks = new int[numElems];
144 bitOffsets = new int[numElems];
145 for (int i=0; i < numElems; i++)
147 bitOffsets[numElems - i- 1] = numberOfBits * i;
148 bitMasks[numElems - i - 1] = ((1 << numberOfBits) - 1) <<
149 bitOffsets[numElems - i - 1];
154 * Creates a new <code>MultiPixelPackedSample</code> model with the same
155 * data type and bits per pixel as this model, but with the specified
158 * @param w the width (in pixels).
159 * @param h the height (in pixels).
161 * @return The new sample model.
163 public SampleModel createCompatibleSampleModel(int w, int h)
165 /* FIXME: We can avoid recalculation of bit offsets and sample
166 sizes here by passing these from the current instance to a
167 special private constructor. */
168 return new MultiPixelPackedSampleModel(dataType, w, h, numberOfBits);
172 * Creates a DataBuffer for holding pixel data in the format and
173 * layout described by this SampleModel. The returned buffer will
174 * consist of one single bank.
176 * @return A new data buffer.
178 public DataBuffer createDataBuffer()
180 int size = scanlineStride * height;
181 if (dataBitOffset > 0)
182 size += (dataBitOffset - 1) / elemBits + 1;
183 return Buffers.createBuffer(getDataType(), size);
187 * Returns the number of data elements required to transfer a pixel in the
188 * get/setDataElements() methods.
190 * @return <code>1</code>.
192 public int getNumDataElements()
198 * Returns an array containing the size (in bits) of the samples in each
199 * band. The <code>MultiPixelPackedSampleModel</code> class supports only
200 * one band, so this method returns an array with length <code>1</code>.
202 * @return An array containing the size (in bits) of the samples in band zero.
204 * @see #getSampleSize(int)
206 public int[] getSampleSize()
208 return (int[]) sampleSize.clone();
212 * Returns the size of the samples in the specified band. Note that the
213 * <code>MultiPixelPackedSampleModel</code> supports only one band -- this
214 * method ignored the <code>band</code> argument, and always returns the size
217 * @param band the band (this parameter is ignored).
219 * @return The size of the samples in band zero.
221 * @see #getSampleSize()
223 public int getSampleSize(int band)
225 return sampleSize[0];
229 * Returns the index in the data buffer that stores the pixel at (x, y).
231 * @param x the x-coordinate.
232 * @param y the y-coordinate.
234 * @return The index in the data buffer that stores the pixel at (x, y).
236 * @see #getBitOffset(int)
238 public int getOffset(int x, int y)
240 return scanlineStride * y + ((dataBitOffset + x * numberOfBits) / elemBits);
244 * The bit offset (within an element in the data buffer) of the pixels with
245 * the specified x-coordinate.
247 * @param x the x-coordinate.
249 * @return The bit offset.
251 public int getBitOffset(int x)
253 return (dataBitOffset + x * numberOfBits) % elemBits;
257 * Returns the offset to the first data bit.
259 * @return The offset to the first data bit.
261 public int getDataBitOffset()
263 return dataBitOffset;
267 * Returns the number of data elements from a pixel in one row to the
268 * corresponding pixel in the next row.
270 * @return The scanline stride.
272 public int getScanlineStride()
274 return scanlineStride;
278 * Returns the number of bits per pixel.
280 * @return The number of bits per pixel.
282 public int getPixelBitStride()
288 * Returns the transfer type, which is one of the following (depending on
289 * the number of bits per sample for this model):
291 * <li>{@link DataBuffer#TYPE_BYTE};</li>
292 * <li>{@link DataBuffer#TYPE_USHORT};</li>
293 * <li>{@link DataBuffer#TYPE_INT};</li>
296 * @return The transfer type.
298 public int getTransferType()
300 if (numberOfBits <= DataBuffer.getDataTypeSize(DataBuffer.TYPE_BYTE))
301 return DataBuffer.TYPE_BYTE;
302 else if (numberOfBits <= DataBuffer.getDataTypeSize(DataBuffer.TYPE_USHORT))
303 return DataBuffer.TYPE_USHORT;
304 return DataBuffer.TYPE_INT;
308 * Normally this method returns a sample model for accessing a subset of
309 * bands of image data, but since <code>MultiPixelPackedSampleModel</code>
310 * only supports a single band, this overridden implementation just returns
311 * a new instance of <code>MultiPixelPackedSampleModel</code>, with the same
312 * attributes as this instance.
314 * @param bands the bands to include in the subset (this is ignored, except
315 * that if it is non-<code>null</code> a check is made to ensure that the
316 * array length is equal to <code>1</code>).
318 * @throws RasterFormatException if <code>bands</code> is not
319 * <code>null</code> and <code>bands.length != 1</code>.
321 public SampleModel createSubsetSampleModel(int[] bands)
323 if (bands != null && bands.length != 1)
324 throw new RasterFormatException("MultiPixelPackedSampleModel only"
325 + " supports one band");
326 return new MultiPixelPackedSampleModel(dataType, width, height,
327 numberOfBits, scanlineStride, dataBitOffset);
331 * Extract one pixel and return in an array of transfer type.
333 * Extracts the pixel at x, y from data and stores into the 0th index of the
334 * array obj, since there is only one band. If obj is null, a new array of
335 * getTransferType() is created.
337 * @param x The x-coordinate of the pixel rectangle to store in
339 * @param y The y-coordinate of the pixel rectangle to store in
341 * @param obj The primitive array to store the pixels into or null to force
343 * @param data The DataBuffer that is the source of the pixel data.
344 * @return The primitive array containing the pixel data.
345 * @see java.awt.image.SampleModel#getDataElements(int, int, Object,
348 public Object getDataElements(int x, int y, Object obj, DataBuffer data)
350 int pixel = getSample(x, y, 0, data);
351 switch (getTransferType())
353 case DataBuffer.TYPE_BYTE:
356 ((byte[]) obj)[0] = (byte) pixel;
358 case DataBuffer.TYPE_USHORT:
361 ((short[]) obj)[0] = (short) pixel;
363 case DataBuffer.TYPE_INT:
366 ((int[]) obj)[0] = pixel;
369 // Seems like the only sensible thing to do.
370 throw new ClassCastException();
375 * Returns an array (of length 1) containing the sample for the pixel at
376 * (x, y) in the specified data buffer. If <code>iArray</code> is not
377 * <code>null</code>, it will be populated with the sample value and
378 * returned as the result of this function (this avoids allocating a new
381 * @param x the x-coordinate of the pixel.
382 * @param y the y-coordinate of the pixel.
383 * @param iArray an array to populate with the sample values and return as
384 * the result (if <code>null</code>, a new array will be allocated).
385 * @param data the data buffer (<code>null</code> not permitted).
387 * @return An array containing the pixel sample value.
389 * @throws NullPointerException if <code>data</code> is <code>null</code>.
391 public int[] getPixel(int x, int y, int[] iArray, DataBuffer data)
395 iArray[0] = getSample(x, y, 0, data);
400 * Returns the sample value for the pixel at (x, y) in the specified data
403 * @param x the x-coordinate of the pixel.
404 * @param y the y-coordinate of the pixel.
405 * @param b the band (in the range <code>0</code> to
406 * <code>getNumBands() - 1</code>).
407 * @param data the data buffer (<code>null</code> not permitted).
409 * @return The sample value.
411 * @throws NullPointerException if <code>data</code> is <code>null</code>.
413 public int getSample(int x, int y, int b, DataBuffer data)
416 ((dataBitOffset + x * numberOfBits) % elemBits) / numberOfBits;
417 int offset = getOffset(x, y);
418 int samples = data.getElem(offset);
419 return (samples & bitMasks[pos]) >>> bitOffsets[pos];
423 * Set the pixel at x, y to the value in the first element of the primitive
426 * @param x The x-coordinate of the data elements in <code>obj</code>.
427 * @param y The y-coordinate of the data elements in <code>obj</code>.
428 * @param obj The primitive array containing the data elements to set.
429 * @param data The DataBuffer to store the data elements into.
431 public void setDataElements(int x, int y, Object obj, DataBuffer data)
433 int transferType = getTransferType();
436 switch (transferType)
438 case DataBuffer.TYPE_BYTE:
440 byte[] in = (byte[]) obj;
441 setSample(x, y, 0, in[0] & 0xFF, data);
444 case DataBuffer.TYPE_USHORT:
446 short[] in = (short[]) obj;
447 setSample(x, y, 0, in[0] & 0xFFFF, data);
450 case DataBuffer.TYPE_INT:
452 int[] in = (int[]) obj;
453 setSample(x, y, 0, in[0], data);
457 throw new ClassCastException("Unsupported data type");
460 catch (ArrayIndexOutOfBoundsException aioobe)
462 String msg = "While writing data elements" +
463 ", x=" + x + ", y=" + y +
464 ", width=" + width + ", height=" + height +
465 ", scanlineStride=" + scanlineStride +
466 ", offset=" + getOffset(x, y) +
467 ", data.getSize()=" + data.getSize() +
468 ", data.getOffset()=" + data.getOffset() +
470 throw new ArrayIndexOutOfBoundsException(msg);
475 * Sets the sample value for the pixel at (x, y) in the specified data
476 * buffer to the specified value.
478 * @param x the x-coordinate of the pixel.
479 * @param y the y-coordinate of the pixel.
480 * @param iArray the sample value (<code>null</code> not permitted).
481 * @param data the data buffer (<code>null</code> not permitted).
483 * @throws NullPointerException if either <code>iArray</code> or
484 * <code>data</code> is <code>null</code>.
486 * @see #setSample(int, int, int, int, DataBuffer)
488 public void setPixel(int x, int y, int[] iArray, DataBuffer data)
490 setSample(x, y, 0, iArray[0], data);
494 * Sets the sample value for a band for the pixel at (x, y) in the
495 * specified data buffer.
497 * @param x the x-coordinate of the pixel.
498 * @param y the y-coordinate of the pixel.
499 * @param b the band (in the range <code>0</code> to
500 * <code>getNumBands() - 1</code>).
501 * @param s the sample value.
502 * @param data the data buffer (<code>null</code> not permitted).
504 * @throws NullPointerException if <code>data</code> is <code>null</code>.
506 public void setSample(int x, int y, int b, int s, DataBuffer data)
509 ((dataBitOffset + x * numberOfBits) % elemBits) / numberOfBits;
510 int offset = getOffset(x, y);
512 s = s << bitOffsets[bitpos];
513 s = s & bitMasks[bitpos];
515 int sample = data.getElem(offset);
517 data.setElem(offset, sample);
521 * Tests this sample model for equality with an arbitrary object. This
522 * method returns <code>true</code> if and only if:
524 * <li><code>obj</code> is not <code>null</code>;
525 * <li><code>obj</code> is an instance of
526 * <code>MultiPixelPackedSampleModel</code>;
527 * <li>both models have the same:
529 * <li><code>dataType</code>;
530 * <li><code>width</code>;
531 * <li><code>height</code>;
532 * <li><code>numberOfBits</code>;
533 * <li><code>scanlineStride</code>;
534 * <li><code>dataBitOffsets</code>.
539 * @param obj the object (<code>null</code> permitted)
541 * @return <code>true</code> if this model is equal to <code>obj</code>, and
542 * <code>false</code> otherwise.
544 public boolean equals(Object obj)
548 if (! (obj instanceof MultiPixelPackedSampleModel))
550 MultiPixelPackedSampleModel that = (MultiPixelPackedSampleModel) obj;
551 if (this.dataType != that.dataType)
553 if (this.width != that.width)
555 if (this.height != that.height)
557 if (this.numberOfBits != that.numberOfBits)
559 if (this.scanlineStride != that.scanlineStride)
561 if (this.dataBitOffset != that.dataBitOffset)
567 * Returns a hash code for this <code>MultiPixelPackedSampleModel</code>.
569 * @return A hash code.
571 public int hashCode()
573 // this hash code won't match Sun's, but that shouldn't matter...
575 result = 37 * result + dataType;
576 result = 37 * result + width;
577 result = 37 * result + height;
578 result = 37 * result + numberOfBits;
579 result = 37 * result + scanlineStride;
580 result = 37 * result + dataBitOffset;
585 * Creates a String with some information about this SampleModel.
586 * @return A String describing this SampleModel.
587 * @see java.lang.Object#toString()
589 public String toString()
591 CPStringBuilder result = new CPStringBuilder();
592 result.append(getClass().getName());
594 result.append("scanlineStride=").append(scanlineStride);
595 for(int i=0; i < bitMasks.length; i+=1)
597 result.append(", mask[").append(i).append("]=0x").append(Integer.toHexString(bitMasks[i]));
601 return result.toString();