001 /* Copyright (C) 2004, 2006, Free Software Foundation
002
003 This file is part of GNU Classpath.
004
005 GNU Classpath is free software; you can redistribute it and/or modify
006 it under the terms of the GNU General Public License as published by
007 the Free Software Foundation; either version 2, or (at your option)
008 any later version.
009
010 GNU Classpath is distributed in the hope that it will be useful, but
011 WITHOUT ANY WARRANTY; without even the implied warranty of
012 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
013 General Public License for more details.
014
015 You should have received a copy of the GNU General Public License
016 along with GNU Classpath; see the file COPYING. If not, write to the
017 Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
018 02110-1301 USA.
019
020 Linking this library statically or dynamically with other modules is
021 making a combined work based on this library. Thus, the terms and
022 conditions of the GNU General Public License cover the whole
023 combination.
024
025 As a special exception, the copyright holders of this library give you
026 permission to link this library with independent modules to produce an
027 executable, regardless of the license terms of these independent
028 modules, and to copy and distribute the resulting executable under
029 terms of your choice, provided that you also meet, for each linked
030 independent module, the terms and conditions of the license of that
031 module. An independent module is a module which is not derived from
032 or based on this library. If you modify this library, you may extend
033 this exception to your version of the library, but you are not
034 obligated to do so. If you do not wish to do so, delete this
035 exception statement from your version. */
036
037 package java.awt.image;
038
039 import gnu.java.awt.Buffers;
040
041 /**
042 * MultiPixelPackedSampleModel provides a single band model that supports
043 * multiple pixels in a single unit. Pixels have 2^n bits and 2^k pixels fit
044 * per data element.
045 *
046 * @author Jerry Quinn (jlquinn@optonline.net)
047 */
048 public class MultiPixelPackedSampleModel extends SampleModel
049 {
050 private int scanlineStride;
051 private int[] bitMasks;
052 private int[] bitOffsets;
053 private int[] sampleSize;
054 private int dataBitOffset;
055 private int elemBits;
056 private int numberOfBits;
057 private int numElems;
058
059 /**
060 * Creates a new <code>MultiPixelPackedSampleModel</code> with the specified
061 * data type, which should be one of:
062 * <ul>
063 * <li>{@link DataBuffer#TYPE_BYTE};</li>
064 * <li>{@link DataBuffer#TYPE_USHORT};</li>
065 * <li>{@link DataBuffer#TYPE_INT};</li>
066 * </ul>
067 *
068 * @param dataType the data type.
069 * @param w the width (in pixels).
070 * @param h the height (in pixels).
071 * @param numberOfBits the number of bits per pixel (must be a power of 2).
072 */
073 public MultiPixelPackedSampleModel(int dataType, int w, int h,
074 int numberOfBits)
075 {
076 this(dataType, w, h, numberOfBits, 0, 0);
077 }
078
079 /**
080 * Creates a new <code>MultiPixelPackedSampleModel</code> with the specified
081 * data type, which should be one of:
082 * <ul>
083 * <li>{@link DataBuffer#TYPE_BYTE};</li>
084 * <li>{@link DataBuffer#TYPE_USHORT};</li>
085 * <li>{@link DataBuffer#TYPE_INT};</li>
086 * </ul>
087 *
088 * @param dataType the data type.
089 * @param w the width (in pixels).
090 * @param h the height (in pixels).
091 * @param numberOfBits the number of bits per pixel (must be a power of 2).
092 * @param scanlineStride the number of data elements from a pixel on one
093 * row to the corresponding pixel in the next row.
094 * @param dataBitOffset the offset to the first data bit.
095 */
096 public MultiPixelPackedSampleModel(int dataType, int w, int h,
097 int numberOfBits, int scanlineStride,
098 int dataBitOffset)
099 {
100 super(dataType, w, h, 1);
101
102 switch (dataType)
103 {
104 case DataBuffer.TYPE_BYTE:
105 elemBits = 8;
106 break;
107 case DataBuffer.TYPE_USHORT:
108 elemBits = 16;
109 break;
110 case DataBuffer.TYPE_INT:
111 elemBits = 32;
112 break;
113 default:
114 throw new IllegalArgumentException("MultiPixelPackedSampleModel"
115 + " unsupported dataType");
116 }
117
118 this.dataBitOffset = dataBitOffset;
119
120 this.numberOfBits = numberOfBits;
121 if (numberOfBits > elemBits)
122 throw new RasterFormatException("MultiPixelPackedSampleModel pixel size"
123 + " larger than dataType");
124 switch (numberOfBits)
125 {
126 case 1: case 2: case 4: case 8: case 16: case 32: break;
127 default:
128 throw new RasterFormatException("MultiPixelPackedSampleModel pixel"
129 + " size not 2^n bits");
130 }
131 numElems = elemBits / numberOfBits;
132
133 // Compute scan line large enough for w pixels.
134 if (scanlineStride == 0)
135 scanlineStride = ((dataBitOffset + w * numberOfBits) - 1) / elemBits + 1;
136 this.scanlineStride = scanlineStride;
137
138
139 sampleSize = new int[1];
140 sampleSize[0] = numberOfBits;
141
142 bitMasks = new int[numElems];
143 bitOffsets = new int[numElems];
144 for (int i=0; i < numElems; i++)
145 {
146 bitOffsets[numElems - i- 1] = numberOfBits * i;
147 bitMasks[numElems - i - 1] = ((1 << numberOfBits) - 1) <<
148 bitOffsets[numElems - i - 1];
149 }
150 }
151
152 /**
153 * Creates a new <code>MultiPixelPackedSample</code> model with the same
154 * data type and bits per pixel as this model, but with the specified
155 * dimensions.
156 *
157 * @param w the width (in pixels).
158 * @param h the height (in pixels).
159 *
160 * @return The new sample model.
161 */
162 public SampleModel createCompatibleSampleModel(int w, int h)
163 {
164 /* FIXME: We can avoid recalculation of bit offsets and sample
165 sizes here by passing these from the current instance to a
166 special private constructor. */
167 return new MultiPixelPackedSampleModel(dataType, w, h, numberOfBits);
168 }
169
170 /**
171 * Creates a DataBuffer for holding pixel data in the format and
172 * layout described by this SampleModel. The returned buffer will
173 * consist of one single bank.
174 *
175 * @return A new data buffer.
176 */
177 public DataBuffer createDataBuffer()
178 {
179 int size = scanlineStride * height;
180 if (dataBitOffset > 0)
181 size += (dataBitOffset - 1) / elemBits + 1;
182 return Buffers.createBuffer(getDataType(), size);
183 }
184
185 /**
186 * Returns the number of data elements required to transfer a pixel in the
187 * get/setDataElements() methods.
188 *
189 * @return <code>1</code>.
190 */
191 public int getNumDataElements()
192 {
193 return 1;
194 }
195
196 /**
197 * Returns an array containing the size (in bits) of the samples in each
198 * band. The <code>MultiPixelPackedSampleModel</code> class supports only
199 * one band, so this method returns an array with length <code>1</code>.
200 *
201 * @return An array containing the size (in bits) of the samples in band zero.
202 *
203 * @see #getSampleSize(int)
204 */
205 public int[] getSampleSize()
206 {
207 return (int[]) sampleSize.clone();
208 }
209
210 /**
211 * Returns the size of the samples in the specified band. Note that the
212 * <code>MultiPixelPackedSampleModel</code> supports only one band -- this
213 * method ignored the <code>band</code> argument, and always returns the size
214 * of band zero.
215 *
216 * @param band the band (this parameter is ignored).
217 *
218 * @return The size of the samples in band zero.
219 *
220 * @see #getSampleSize()
221 */
222 public int getSampleSize(int band)
223 {
224 return sampleSize[0];
225 }
226
227 /**
228 * Returns the index in the data buffer that stores the pixel at (x, y).
229 *
230 * @param x the x-coordinate.
231 * @param y the y-coordinate.
232 *
233 * @return The index in the data buffer that stores the pixel at (x, y).
234 *
235 * @see #getBitOffset(int)
236 */
237 public int getOffset(int x, int y)
238 {
239 return scanlineStride * y + ((dataBitOffset + x * numberOfBits) / elemBits);
240 }
241
242 /**
243 * The bit offset (within an element in the data buffer) of the pixels with
244 * the specified x-coordinate.
245 *
246 * @param x the x-coordinate.
247 *
248 * @return The bit offset.
249 */
250 public int getBitOffset(int x)
251 {
252 return (dataBitOffset + x * numberOfBits) % elemBits;
253 }
254
255 /**
256 * Returns the offset to the first data bit.
257 *
258 * @return The offset to the first data bit.
259 */
260 public int getDataBitOffset()
261 {
262 return dataBitOffset;
263 }
264
265 /**
266 * Returns the number of data elements from a pixel in one row to the
267 * corresponding pixel in the next row.
268 *
269 * @return The scanline stride.
270 */
271 public int getScanlineStride()
272 {
273 return scanlineStride;
274 }
275
276 /**
277 * Returns the number of bits per pixel.
278 *
279 * @return The number of bits per pixel.
280 */
281 public int getPixelBitStride()
282 {
283 return numberOfBits;
284 }
285
286 /**
287 * Returns the transfer type, which is one of the following (depending on
288 * the number of bits per sample for this model):
289 * <ul>
290 * <li>{@link DataBuffer#TYPE_BYTE};</li>
291 * <li>{@link DataBuffer#TYPE_USHORT};</li>
292 * <li>{@link DataBuffer#TYPE_INT};</li>
293 * </ul>
294 *
295 * @return The transfer type.
296 */
297 public int getTransferType()
298 {
299 if (numberOfBits <= DataBuffer.getDataTypeSize(DataBuffer.TYPE_BYTE))
300 return DataBuffer.TYPE_BYTE;
301 else if (numberOfBits <= DataBuffer.getDataTypeSize(DataBuffer.TYPE_USHORT))
302 return DataBuffer.TYPE_USHORT;
303 return DataBuffer.TYPE_INT;
304 }
305
306 /**
307 * Normally this method returns a sample model for accessing a subset of
308 * bands of image data, but since <code>MultiPixelPackedSampleModel</code>
309 * only supports a single band, this overridden implementation just returns
310 * a new instance of <code>MultiPixelPackedSampleModel</code>, with the same
311 * attributes as this instance.
312 *
313 * @param bands the bands to include in the subset (this is ignored, except
314 * that if it is non-<code>null</code> a check is made to ensure that the
315 * array length is equal to <code>1</code>).
316 *
317 * @throws RasterFormatException if <code>bands</code> is not
318 * <code>null</code> and <code>bands.length != 1</code>.
319 */
320 public SampleModel createSubsetSampleModel(int[] bands)
321 {
322 if (bands != null && bands.length != 1)
323 throw new RasterFormatException("MultiPixelPackedSampleModel only"
324 + " supports one band");
325 return new MultiPixelPackedSampleModel(dataType, width, height,
326 numberOfBits, scanlineStride, dataBitOffset);
327 }
328
329 /**
330 * Extract one pixel and return in an array of transfer type.
331 *
332 * Extracts the pixel at x, y from data and stores into the 0th index of the
333 * array obj, since there is only one band. If obj is null, a new array of
334 * getTransferType() is created.
335 *
336 * @param x The x-coordinate of the pixel rectangle to store in
337 * <code>obj</code>.
338 * @param y The y-coordinate of the pixel rectangle to store in
339 * <code>obj</code>.
340 * @param obj The primitive array to store the pixels into or null to force
341 * creation.
342 * @param data The DataBuffer that is the source of the pixel data.
343 * @return The primitive array containing the pixel data.
344 * @see java.awt.image.SampleModel#getDataElements(int, int, Object,
345 * DataBuffer)
346 */
347 public Object getDataElements(int x, int y, Object obj, DataBuffer data)
348 {
349 int pixel = getSample(x, y, 0, data);
350 switch (getTransferType())
351 {
352 case DataBuffer.TYPE_BYTE:
353 if (obj == null)
354 obj = new byte[1];
355 ((byte[]) obj)[0] = (byte) pixel;
356 return obj;
357 case DataBuffer.TYPE_USHORT:
358 if (obj == null)
359 obj = new short[1];
360 ((short[]) obj)[0] = (short) pixel;
361 return obj;
362 case DataBuffer.TYPE_INT:
363 if (obj == null)
364 obj = new int[1];
365 ((int[]) obj)[0] = pixel;
366 return obj;
367 default:
368 // Seems like the only sensible thing to do.
369 throw new ClassCastException();
370 }
371 }
372
373 /**
374 * Returns an array (of length 1) containing the sample for the pixel at
375 * (x, y) in the specified data buffer. If <code>iArray</code> is not
376 * <code>null</code>, it will be populated with the sample value and
377 * returned as the result of this function (this avoids allocating a new
378 * array instance).
379 *
380 * @param x the x-coordinate of the pixel.
381 * @param y the y-coordinate of the pixel.
382 * @param iArray an array to populate with the sample values and return as
383 * the result (if <code>null</code>, a new array will be allocated).
384 * @param data the data buffer (<code>null</code> not permitted).
385 *
386 * @return An array containing the pixel sample value.
387 *
388 * @throws NullPointerException if <code>data</code> is <code>null</code>.
389 */
390 public int[] getPixel(int x, int y, int[] iArray, DataBuffer data)
391 {
392 if (iArray == null)
393 iArray = new int[1];
394 iArray[0] = getSample(x, y, 0, data);
395 return iArray;
396 }
397
398 /**
399 * Returns the sample value for the pixel at (x, y) in the specified data
400 * buffer.
401 *
402 * @param x the x-coordinate of the pixel.
403 * @param y the y-coordinate of the pixel.
404 * @param b the band (in the range <code>0</code> to
405 * <code>getNumBands() - 1</code>).
406 * @param data the data buffer (<code>null</code> not permitted).
407 *
408 * @return The sample value.
409 *
410 * @throws NullPointerException if <code>data</code> is <code>null</code>.
411 */
412 public int getSample(int x, int y, int b, DataBuffer data)
413 {
414 int pos =
415 ((dataBitOffset + x * numberOfBits) % elemBits) / numberOfBits;
416 int offset = getOffset(x, y);
417 int samples = data.getElem(offset);
418 return (samples & bitMasks[pos]) >>> bitOffsets[pos];
419 }
420
421 /**
422 * Set the pixel at x, y to the value in the first element of the primitive
423 * array obj.
424 *
425 * @param x The x-coordinate of the data elements in <code>obj</code>.
426 * @param y The y-coordinate of the data elements in <code>obj</code>.
427 * @param obj The primitive array containing the data elements to set.
428 * @param data The DataBuffer to store the data elements into.
429 */
430 public void setDataElements(int x, int y, Object obj, DataBuffer data)
431 {
432 int transferType = getTransferType();
433 try
434 {
435 switch (transferType)
436 {
437 case DataBuffer.TYPE_BYTE:
438 {
439 byte[] in = (byte[]) obj;
440 setSample(x, y, 0, in[0] & 0xFF, data);
441 return;
442 }
443 case DataBuffer.TYPE_USHORT:
444 {
445 short[] in = (short[]) obj;
446 setSample(x, y, 0, in[0] & 0xFFFF, data);
447 return;
448 }
449 case DataBuffer.TYPE_INT:
450 {
451 int[] in = (int[]) obj;
452 setSample(x, y, 0, in[0], data);
453 return;
454 }
455 default:
456 throw new ClassCastException("Unsupported data type");
457 }
458 }
459 catch (ArrayIndexOutOfBoundsException aioobe)
460 {
461 String msg = "While writing data elements" +
462 ", x=" + x + ", y=" + y +
463 ", width=" + width + ", height=" + height +
464 ", scanlineStride=" + scanlineStride +
465 ", offset=" + getOffset(x, y) +
466 ", data.getSize()=" + data.getSize() +
467 ", data.getOffset()=" + data.getOffset() +
468 ": " + aioobe;
469 throw new ArrayIndexOutOfBoundsException(msg);
470 }
471 }
472
473 /**
474 * Sets the sample value for the pixel at (x, y) in the specified data
475 * buffer to the specified value.
476 *
477 * @param x the x-coordinate of the pixel.
478 * @param y the y-coordinate of the pixel.
479 * @param iArray the sample value (<code>null</code> not permitted).
480 * @param data the data buffer (<code>null</code> not permitted).
481 *
482 * @throws NullPointerException if either <code>iArray</code> or
483 * <code>data</code> is <code>null</code>.
484 *
485 * @see #setSample(int, int, int, int, DataBuffer)
486 */
487 public void setPixel(int x, int y, int[] iArray, DataBuffer data)
488 {
489 setSample(x, y, 0, iArray[0], data);
490 }
491
492 /**
493 * Sets the sample value for a band for the pixel at (x, y) in the
494 * specified data buffer.
495 *
496 * @param x the x-coordinate of the pixel.
497 * @param y the y-coordinate of the pixel.
498 * @param b the band (in the range <code>0</code> to
499 * <code>getNumBands() - 1</code>).
500 * @param s the sample value.
501 * @param data the data buffer (<code>null</code> not permitted).
502 *
503 * @throws NullPointerException if <code>data</code> is <code>null</code>.
504 */
505 public void setSample(int x, int y, int b, int s, DataBuffer data)
506 {
507 int bitpos =
508 ((dataBitOffset + x * numberOfBits) % elemBits) / numberOfBits;
509 int offset = getOffset(x, y);
510
511 s = s << bitOffsets[bitpos];
512 s = s & bitMasks[bitpos];
513
514 int sample = data.getElem(offset);
515 sample |= s;
516 data.setElem(offset, sample);
517 }
518
519 /**
520 * Tests this sample model for equality with an arbitrary object. This
521 * method returns <code>true</code> if and only if:
522 * <ul>
523 * <li><code>obj</code> is not <code>null</code>;
524 * <li><code>obj</code> is an instance of
525 * <code>MultiPixelPackedSampleModel</code>;
526 * <li>both models have the same:
527 * <ul>
528 * <li><code>dataType</code>;
529 * <li><code>width</code>;
530 * <li><code>height</code>;
531 * <li><code>numberOfBits</code>;
532 * <li><code>scanlineStride</code>;
533 * <li><code>dataBitOffsets</code>.
534 * </ul>
535 * </li>
536 * </ul>
537 *
538 * @param obj the object (<code>null</code> permitted)
539 *
540 * @return <code>true</code> if this model is equal to <code>obj</code>, and
541 * <code>false</code> otherwise.
542 */
543 public boolean equals(Object obj)
544 {
545 if (this == obj)
546 return true;
547 if (! (obj instanceof MultiPixelPackedSampleModel))
548 return false;
549 MultiPixelPackedSampleModel that = (MultiPixelPackedSampleModel) obj;
550 if (this.dataType != that.dataType)
551 return false;
552 if (this.width != that.width)
553 return false;
554 if (this.height != that.height)
555 return false;
556 if (this.numberOfBits != that.numberOfBits)
557 return false;
558 if (this.scanlineStride != that.scanlineStride)
559 return false;
560 if (this.dataBitOffset != that.dataBitOffset)
561 return false;
562 return true;
563 }
564
565 /**
566 * Returns a hash code for this <code>MultiPixelPackedSampleModel</code>.
567 *
568 * @return A hash code.
569 */
570 public int hashCode()
571 {
572 // this hash code won't match Sun's, but that shouldn't matter...
573 int result = 193;
574 result = 37 * result + dataType;
575 result = 37 * result + width;
576 result = 37 * result + height;
577 result = 37 * result + numberOfBits;
578 result = 37 * result + scanlineStride;
579 result = 37 * result + dataBitOffset;
580 return result;
581 }
582
583 /**
584 * Creates a String with some information about this SampleModel.
585 * @return A String describing this SampleModel.
586 * @see java.lang.Object#toString()
587 */
588 public String toString()
589 {
590 StringBuffer result = new StringBuffer();
591 result.append(getClass().getName());
592 result.append("[");
593 result.append("scanlineStride=").append(scanlineStride);
594 for(int i=0; i < bitMasks.length; i+=1)
595 {
596 result.append(", mask[").append(i).append("]=0x").append(Integer.toHexString(bitMasks[i]));
597 }
598
599 result.append("]");
600 return result.toString();
601 }
602 }