001 /* Copyright (C) 2000, 2002, 2003, 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 java.util.Arrays;
040
041 import gnu.java.awt.BitMaskExtent;
042
043 /**
044 * A <code>SampleModel</code> used when all samples are stored in a single
045 * data element in the {@link DataBuffer}, and each data element contains
046 * samples for one pixel only.
047 *
048 * @author Rolf W. Rasmussen (rolfwr@ii.uib.no)
049 */
050 public class SinglePixelPackedSampleModel extends SampleModel
051 {
052 private int scanlineStride;
053 private int[] bitMasks;
054 private int[] bitOffsets;
055 private int[] sampleSize;
056
057 /**
058 * Creates a new <code>SinglePixelPackedSampleModel</code>.
059 *
060 * @param dataType the data buffer type.
061 * @param w the width (in pixels).
062 * @param h the height (in pixels).
063 * @param bitMasks an array containing the bit mask used to extract the
064 * sample value for each band.
065 */
066 public SinglePixelPackedSampleModel(int dataType, int w, int h,
067 int[] bitMasks)
068 {
069 this(dataType, w, h, w, bitMasks);
070 }
071
072 /**
073 * Creates a new <code>SinglePixelPackedSampleModel</code>.
074 *
075 * @param dataType the data buffer type.
076 * @param w the width (in pixels).
077 * @param h the height (in pixels).
078 * @param scanlineStride the number of data elements between a pixel on one
079 * row and the corresponding pixel on the next row.
080 * @param bitMasks an array containing the bit mask used to extract the
081 * sample value for each band.
082 */
083 public SinglePixelPackedSampleModel(int dataType, int w, int h,
084 int scanlineStride, int[] bitMasks)
085 {
086 super(dataType, w, h, bitMasks.length);
087
088 switch (dataType)
089 {
090 case DataBuffer.TYPE_BYTE:
091 case DataBuffer.TYPE_USHORT:
092 case DataBuffer.TYPE_INT:
093 break;
094 default:
095 throw new IllegalArgumentException(
096 "SinglePixelPackedSampleModel unsupported dataType");
097 }
098
099 this.scanlineStride = scanlineStride;
100 this.bitMasks = bitMasks;
101
102 bitOffsets = new int[numBands];
103 sampleSize = new int[numBands];
104
105 BitMaskExtent extent = new BitMaskExtent();
106 for (int b = 0; b < numBands; b++)
107 {
108 // the mask is an unsigned integer
109 long mask = bitMasks[b] & 0xFFFFFFFFL;
110 extent.setMask(mask);
111 sampleSize[b] = extent.bitWidth;
112 bitOffsets[b] = extent.leastSignificantBit;
113 }
114 }
115
116 /**
117 * Returns the number of data elements.
118 *
119 * @return <code>1</code>.
120 */
121 public int getNumDataElements()
122 {
123 return 1;
124 }
125
126 /**
127 * Creates a new <code>SampleModel</code> that is compatible with this
128 * model and has the specified width and height.
129 *
130 * @param w the width (in pixels).
131 * @param h the height (in pixels).
132 *
133 * @return The new sample model.
134 */
135 public SampleModel createCompatibleSampleModel(int w, int h)
136 {
137 /* FIXME: We can avoid recalculation of bit offsets and sample
138 sizes here by passing these from the current instance to a
139 special private constructor. */
140 return new SinglePixelPackedSampleModel(dataType, w, h, bitMasks);
141 }
142
143
144 /**
145 * Creates a DataBuffer for holding pixel data in the format and
146 * layout described by this SampleModel. The returned buffer will
147 * consist of one single bank.
148 *
149 * @return The data buffer.
150 */
151 public DataBuffer createDataBuffer()
152 {
153 // We can save (scanlineStride - width) pixels at the very end of
154 // the buffer. The Sun reference implementation (J2SE 1.3.1 and
155 // 1.4.1_01) seems to do this; tested with Mauve test code.
156 int size = scanlineStride * (height - 1) + width;
157
158 DataBuffer buffer = null;
159 switch (getTransferType())
160 {
161 case DataBuffer.TYPE_BYTE:
162 buffer = new DataBufferByte(size);
163 break;
164 case DataBuffer.TYPE_USHORT:
165 buffer = new DataBufferUShort(size);
166 break;
167 case DataBuffer.TYPE_INT:
168 buffer = new DataBufferInt(size);
169 break;
170 }
171 return buffer;
172 }
173
174 /**
175 * Returns an array containing the size (in bits) for each band accessed by
176 * the <code>SampleModel</code>.
177 *
178 * @return An array.
179 *
180 * @see #getSampleSize(int)
181 */
182 public int[] getSampleSize()
183 {
184 return (int[]) sampleSize.clone();
185 }
186
187 /**
188 * Returns the size (in bits) of the samples for the specified band.
189 *
190 * @param band the band (in the range <code>0</code> to
191 * <code>getNumBands() - 1</code>).
192 *
193 * @return The sample size (in bits).
194 */
195 public int getSampleSize(int band)
196 {
197 return sampleSize[band];
198 }
199
200 /**
201 * Returns the index in the data buffer that stores the pixel at (x, y).
202 *
203 * @param x the x-coordinate.
204 * @param y the y-coordinate.
205 *
206 * @return The index in the data buffer that stores the pixel at (x, y).
207 */
208 public int getOffset(int x, int y)
209 {
210 return scanlineStride*y + x;
211 }
212
213 public int[] getBitOffsets()
214 {
215 return bitOffsets;
216 }
217
218 public int[] getBitMasks()
219 {
220 return bitMasks;
221 }
222
223 /**
224 * Returns the number of data elements from a pixel in one row to the
225 * corresponding pixel in the next row.
226 *
227 * @return The scanline stride.
228 */
229 public int getScanlineStride()
230 {
231 return scanlineStride;
232 }
233
234 /**
235 * Creates a new <code>SinglePixelPackedSampleModel</code> that accesses
236 * the specified subset of bands.
237 *
238 * @param bands an array containing band indices (<code>null</code> not
239 * permitted).
240 *
241 * @return A new sample model.
242 *
243 * @throws NullPointerException if <code>bands</code> is <code>null</code>.
244 * @throws RasterFormatException if <code>bands.length</code> is greater
245 * than the number of bands in this model.
246 */
247 public SampleModel createSubsetSampleModel(int[] bands)
248 {
249 if (bands.length > numBands)
250 throw new RasterFormatException("Too many bands.");
251
252 int numBands = bands.length;
253
254 int[] bitMasks = new int[numBands];
255
256 for (int b = 0; b < numBands; b++)
257 bitMasks[b] = this.bitMasks[bands[b]];
258
259 return new SinglePixelPackedSampleModel(dataType, width, height,
260 scanlineStride, bitMasks);
261 }
262
263 public Object getDataElements(int x, int y, Object obj,
264 DataBuffer data)
265 {
266 int type = getTransferType();
267 Object ret = null;
268 switch (type)
269 {
270 case DataBuffer.TYPE_BYTE:
271 {
272 byte[] in = (byte[]) obj;
273 if (in == null)
274 in = new byte[1];
275 in[0] = (byte) data.getElem(x + y * scanlineStride);
276 ret = in;
277 }
278 break;
279 case DataBuffer.TYPE_USHORT:
280 {
281 short[] in = (short[]) obj;
282 if (in == null)
283 in = new short[1];
284 in[0] = (short) data.getElem(x + y * scanlineStride);
285 ret = in;
286 }
287 break;
288 case DataBuffer.TYPE_INT:
289 {
290 int[] in = (int[]) obj;
291 if (in == null)
292 in = new int[1];
293 in[0] = data.getElem(x + y * scanlineStride);
294 ret = in;
295 }
296 break;
297 }
298 return ret;
299 }
300
301 /**
302 * Returns an array containing the samples for the pixel at (x, y) in the
303 * specified data buffer. If <code>iArray</code> is not <code>null</code>,
304 * it will be populated with the sample values and returned as the result of
305 * this function (this avoids allocating a new array instance).
306 *
307 * @param x the x-coordinate of the pixel.
308 * @param y the y-coordinate of the pixel.
309 * @param iArray an array to populate with the sample values and return as
310 * the result (if <code>null</code>, a new array will be allocated).
311 * @param data the data buffer (<code>null</code> not permitted).
312 *
313 * @return The pixel sample values.
314 *
315 * @throws NullPointerException if <code>data</code> is <code>null</code>.
316 */
317 public int[] getPixel(int x, int y, int[] iArray, DataBuffer data)
318 {
319 int offset = scanlineStride*y + x;
320 if (iArray == null) iArray = new int[numBands];
321 int samples = data.getElem(offset);
322
323 for (int b = 0; b < numBands; b++)
324 iArray[b] = (samples & bitMasks[b]) >>> bitOffsets[b];
325
326 return iArray;
327 }
328
329 /**
330 * Returns an array containing the samples for the pixels in the region
331 * specified by (x, y, w, h) in the specified data buffer. The array is
332 * ordered by pixels (that is, all the samples for the first pixel are
333 * grouped together, followed by all the samples for the second pixel, and so
334 * on). If <code>iArray</code> is not <code>null</code>, it will be
335 * populated with the sample values and returned as the result of this
336 * function (this avoids allocating a new array instance).
337 *
338 * @param x the x-coordinate of the top-left pixel.
339 * @param y the y-coordinate of the top-left pixel.
340 * @param w the width of the region of pixels.
341 * @param h the height of the region of pixels.
342 * @param iArray an array to populate with the sample values and return as
343 * the result (if <code>null</code>, a new array will be allocated).
344 * @param data the data buffer (<code>null</code> not permitted).
345 *
346 * @return The pixel sample values.
347 *
348 * @throws NullPointerException if <code>data</code> is <code>null</code>.
349 */
350 public int[] getPixels(int x, int y, int w, int h, int[] iArray,
351 DataBuffer data)
352 {
353 int offset = scanlineStride*y + x;
354 if (iArray == null) iArray = new int[numBands*w*h];
355 int outOffset = 0;
356 for (y = 0; y < h; y++)
357 {
358 int lineOffset = offset;
359 for (x = 0; x < w; x++)
360 {
361 int samples = data.getElem(lineOffset++);
362 for (int b = 0; b < numBands; b++)
363 iArray[outOffset++] = (samples & bitMasks[b]) >>> bitOffsets[b];
364 }
365 offset += scanlineStride;
366 }
367 return iArray;
368 }
369
370 /**
371 * Returns the sample value for the pixel at (x, y) in the specified data
372 * buffer.
373 *
374 * @param x the x-coordinate of the pixel.
375 * @param y the y-coordinate of the pixel.
376 * @param b the band (in the range <code>0</code> to
377 * <code>getNumBands() - 1</code>).
378 * @param data the data buffer (<code>null</code> not permitted).
379 *
380 * @return The sample value.
381 *
382 * @throws NullPointerException if <code>data</code> is <code>null</code>.
383 */
384 public int getSample(int x, int y, int b, DataBuffer data)
385 {
386 int offset = scanlineStride*y + x;
387 int samples = data.getElem(offset);
388 return (samples & bitMasks[b]) >>> bitOffsets[b];
389 }
390
391 public void setDataElements(int x, int y, Object obj, DataBuffer data)
392 {
393 int transferType = getTransferType();
394 switch (transferType)
395 {
396 case DataBuffer.TYPE_BYTE:
397 {
398 byte[] in = (byte[]) obj;
399 data.setElem(y * scanlineStride + x, ((int) in[0]) & 0xff);
400 }
401 break;
402 case DataBuffer.TYPE_USHORT:
403 {
404 short[] in = (short[]) obj;
405 data.setElem(y * scanlineStride + x, ((int) in[0]) & 0xffff);
406 }
407 break;
408 case DataBuffer.TYPE_INT:
409 {
410 int[] in = (int[]) obj;
411 data.setElem(y * scanlineStride + x, in[0]);
412 break;
413 }
414 }
415 }
416
417 /**
418 * Sets the samples for the pixel at (x, y) in the specified data buffer to
419 * the specified values.
420 *
421 * @param x the x-coordinate of the pixel.
422 * @param y the y-coordinate of the pixel.
423 * @param iArray the sample values (<code>null</code> not permitted).
424 * @param data the data buffer (<code>null</code> not permitted).
425 *
426 * @throws NullPointerException if either <code>iArray</code> or
427 * <code>data</code> is <code>null</code>.
428 */
429 public void setPixel(int x, int y, int[] iArray, DataBuffer data)
430 {
431 int offset = scanlineStride*y + x;
432
433 int samples = 0;
434 for (int b = 0; b < numBands; b++)
435 samples |= (iArray[b] << bitOffsets[b]) & bitMasks[b];
436
437 data.setElem(offset, samples);
438 }
439
440 /**
441 * This method implements a more efficient way to set pixels than the default
442 * implementation of the super class. It copies the pixel components directly
443 * from the input array instead of creating a intermediate buffer.
444 * @param x The x-coordinate of the pixel rectangle in <code>obj</code>.
445 * @param y The y-coordinate of the pixel rectangle in <code>obj</code>.
446 * @param w The width of the pixel rectangle in <code>obj</code>.
447 * @param h The height of the pixel rectangle in <code>obj</code>.
448 * @param iArray The primitive array containing the pixels to set.
449 * @param data The DataBuffer to store the pixels into.
450 * @see java.awt.image.SampleModel#setPixels(int, int, int, int, int[],
451 * java.awt.image.DataBuffer)
452 */
453 public void setPixels(int x, int y, int w, int h, int[] iArray,
454 DataBuffer data)
455 {
456 int inOffset = 0;
457 for (int yy=y; yy<(y+h); yy++)
458 {
459 int offset = scanlineStride*yy + x;
460 for (int xx=x; xx<(x+w); xx++)
461 {
462 int samples = 0;
463 for (int b = 0; b < numBands; b++)
464 samples |= (iArray[inOffset+b] << bitOffsets[b]) & bitMasks[b];
465 data.setElem(0, offset, samples);
466 inOffset += numBands;
467 offset += 1;
468 }
469 }
470 }
471
472 /**
473 * Sets the sample value for a band for the pixel at (x, y) in the
474 * specified data buffer.
475 *
476 * @param x the x-coordinate of the pixel.
477 * @param y the y-coordinate of the pixel.
478 * @param b the band (in the range <code>0</code> to
479 * <code>getNumBands() - 1</code>).
480 * @param s the sample value.
481 * @param data the data buffer (<code>null</code> not permitted).
482 *
483 * @throws NullPointerException if <code>data</code> is <code>null</code>.
484 */
485 public void setSample(int x, int y, int b, int s, DataBuffer data)
486 {
487 int offset = scanlineStride*y + x;
488 int samples = data.getElem(offset);
489 int bitMask = bitMasks[b];
490 samples &= ~bitMask;
491 samples |= (s << bitOffsets[b]) & bitMask;
492 data.setElem(offset, samples);
493 }
494
495 /**
496 * Tests this sample model for equality with an arbitrary object. This
497 * method returns <code>true</code> if and only if:
498 * <ul>
499 * <li><code>obj</code> is not <code>null</code>;
500 * <li><code>obj</code> is an instance of
501 * <code>SinglePixelPackedSampleModel</code>;
502 * <li>both models have the same:
503 * <ul>
504 * <li><code>dataType</code>;
505 * <li><code>width</code>;
506 * <li><code>height</code>;
507 * <li><code>numBands</code>;
508 * <li><code>scanlineStride</code>;
509 * <li><code>bitMasks</code>;
510 * <li><code>bitOffsets</code>.
511 * </ul>
512 * </li>
513 * </ul>
514 *
515 * @param obj the object (<code>null</code> permitted)
516 *
517 * @return <code>true</code> if this model is equal to <code>obj</code>, and
518 * <code>false</code> otherwise.
519 */
520 public boolean equals(Object obj)
521 {
522 if (this == obj)
523 return true;
524 if (! (obj instanceof SinglePixelPackedSampleModel))
525 return false;
526 SinglePixelPackedSampleModel that = (SinglePixelPackedSampleModel) obj;
527 if (this.dataType != that.dataType)
528 return false;
529 if (this.width != that.width)
530 return false;
531 if (this.height != that.height)
532 return false;
533 if (this.numBands != that.numBands)
534 return false;
535 if (this.scanlineStride != that.scanlineStride)
536 return false;
537 if (!Arrays.equals(this.bitMasks, that.bitMasks))
538 return false;
539 if (!Arrays.equals(this.bitOffsets, that.bitOffsets))
540 return false;
541 return true;
542 }
543
544 /**
545 * Returns a hash code for this <code>SinglePixelPackedSampleModel</code>.
546 *
547 * @return A hash code.
548 */
549 public int hashCode()
550 {
551 // this hash code won't match Sun's, but that shouldn't matter...
552 int result = 193;
553 result = 37 * result + dataType;
554 result = 37 * result + width;
555 result = 37 * result + height;
556 result = 37 * result + numBands;
557 result = 37 * result + scanlineStride;
558 for (int i = 0; i < bitMasks.length; i++)
559 result = 37 * result + bitMasks[i];
560 for (int i = 0; i < bitOffsets.length; i++)
561 result = 37 * result + bitOffsets[i];
562 return result;
563 }
564
565 /**
566 * Creates a String with some information about this SampleModel.
567 * @return A String describing this SampleModel.
568 * @see java.lang.Object#toString()
569 */
570 public String toString()
571 {
572 StringBuffer result = new StringBuffer();
573 result.append(getClass().getName());
574 result.append("[");
575 result.append("scanlineStride=").append(scanlineStride);
576 for(int i = 0; i < bitMasks.length; i+=1)
577 {
578 result.append(", mask[").append(i).append("]=0x").append(
579 Integer.toHexString(bitMasks[i]));
580 }
581
582 result.append("]");
583 return result.toString();
584 }
585 }