OSDN Git Service

libjava/ChangeLog:
[pf3gnuchains/gcc-fork.git] / libjava / classpath / java / awt / image / MultiPixelPackedSampleModel.java
1 /* Copyright (C) 2004, 2006,  Free Software Foundation
2
3 This file is part of GNU Classpath.
4
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)
8 any later version.
9
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.
14
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
18 02110-1301 USA.
19
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
23 combination.
24
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. */
36
37 package java.awt.image;
38
39 import gnu.java.awt.Buffers;
40 import gnu.java.lang.CPStringBuilder;
41
42 /**
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
45  * per data element.
46  *
47  * @author Jerry Quinn (jlquinn@optonline.net)
48  */
49 public class MultiPixelPackedSampleModel extends SampleModel
50 {
51   private int scanlineStride;
52   private int[] bitMasks;
53   private int[] bitOffsets;
54   private int[] sampleSize;
55   private int dataBitOffset;
56   private int elemBits;
57   private int numberOfBits;
58   private int numElems;
59
60   /**
61    * Creates a new <code>MultiPixelPackedSampleModel</code> with the specified
62    * data type, which should be one of:
63    * <ul>
64    *   <li>{@link DataBuffer#TYPE_BYTE};</li>
65    *   <li>{@link DataBuffer#TYPE_USHORT};</li>
66    *   <li>{@link DataBuffer#TYPE_INT};</li>
67    * </ul>
68    * 
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).
73    */
74   public MultiPixelPackedSampleModel(int dataType, int w, int h,
75                                      int numberOfBits)
76   {
77     this(dataType, w, h, numberOfBits, 0, 0);
78   }
79
80   /**
81    * Creates a new <code>MultiPixelPackedSampleModel</code> with the specified
82    * data type, which should be one of:
83    * <ul>
84    *   <li>{@link DataBuffer#TYPE_BYTE};</li>
85    *   <li>{@link DataBuffer#TYPE_USHORT};</li>
86    *   <li>{@link DataBuffer#TYPE_INT};</li>
87    * </ul>
88    * 
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.
96    */
97   public MultiPixelPackedSampleModel(int dataType, int w, int h,
98                                      int numberOfBits, int scanlineStride,
99                                      int dataBitOffset)
100   {
101     super(dataType, w, h, 1);
102
103     switch (dataType)
104       {
105       case DataBuffer.TYPE_BYTE:
106         elemBits = 8;
107         break;
108       case DataBuffer.TYPE_USHORT:
109         elemBits = 16;
110         break;
111       case DataBuffer.TYPE_INT:
112         elemBits = 32;
113         break;
114       default:
115         throw new IllegalArgumentException("MultiPixelPackedSampleModel"
116                                            + " unsupported dataType");
117       }
118
119     this.dataBitOffset = dataBitOffset;
120
121     this.numberOfBits = numberOfBits;
122     if (numberOfBits > elemBits)
123       throw new RasterFormatException("MultiPixelPackedSampleModel pixel size"
124                                       + " larger than dataType");
125     switch (numberOfBits)
126       {
127       case 1: case 2: case 4: case 8: case 16: case 32: break;
128       default:
129         throw new RasterFormatException("MultiPixelPackedSampleModel pixel"
130                                         + " size not 2^n bits");
131       }
132     numElems = elemBits / numberOfBits;
133
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;
138
139     
140     sampleSize = new int[1];
141     sampleSize[0] = numberOfBits;
142
143     bitMasks = new int[numElems];
144     bitOffsets = new int[numElems];
145     for (int i=0; i < numElems; i++)
146       {
147         bitOffsets[numElems - i- 1] = numberOfBits * i;
148         bitMasks[numElems - i - 1] = ((1 << numberOfBits) - 1) << 
149             bitOffsets[numElems - i - 1];
150       }
151   }
152
153   /**
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
156    * dimensions.
157    * 
158    * @param w  the width (in pixels).
159    * @param h  the height (in pixels).
160    * 
161    * @return The new sample model.
162    */
163   public SampleModel createCompatibleSampleModel(int w, int h)
164   {
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);
169   }
170
171   /**
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.
175    * 
176    * @return A new data buffer.
177    */
178   public DataBuffer createDataBuffer()
179   {
180     int size = scanlineStride * height;
181     if (dataBitOffset > 0)
182       size += (dataBitOffset - 1) / elemBits + 1;
183     return Buffers.createBuffer(getDataType(), size);
184   }
185
186   /**
187    * Returns the number of data elements required to transfer a pixel in the
188    * get/setDataElements() methods.
189    * 
190    * @return <code>1</code>.
191    */
192   public int getNumDataElements()
193   {
194     return 1;
195   }
196
197   /**
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>. 
201    * 
202    * @return An array containing the size (in bits) of the samples in band zero. 
203    *     
204    * @see #getSampleSize(int)
205    */
206   public int[] getSampleSize()
207   {
208     return (int[]) sampleSize.clone();
209   }
210   
211   /**
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
215    * of band zero.
216    * 
217    * @param band  the band (this parameter is ignored).
218    * 
219    * @return The size of the samples in band zero.
220    * 
221    * @see #getSampleSize()
222    */
223   public int getSampleSize(int band)
224   {
225     return sampleSize[0];
226   }
227
228   /**
229    * Returns the index in the data buffer that stores the pixel at (x, y).
230    * 
231    * @param x  the x-coordinate.
232    * @param y  the y-coordinate.
233    * 
234    * @return The index in the data buffer that stores the pixel at (x, y).
235    * 
236    * @see #getBitOffset(int)
237    */
238   public int getOffset(int x, int y)
239   {
240     return scanlineStride * y + ((dataBitOffset + x * numberOfBits) / elemBits);
241   }
242
243   /**
244    * The bit offset (within an element in the data buffer) of the pixels with 
245    * the specified x-coordinate.
246    * 
247    * @param x  the x-coordinate.
248    * 
249    * @return The bit offset.
250    */
251   public int getBitOffset(int x)
252   {
253     return (dataBitOffset + x * numberOfBits) % elemBits;
254   }
255
256   /**
257    * Returns the offset to the first data bit.
258    * 
259    * @return The offset to the first data bit.
260    */
261   public int getDataBitOffset()
262   {
263     return dataBitOffset;
264   }
265
266   /**
267    * Returns the number of data elements from a pixel in one row to the
268    * corresponding pixel in the next row.
269    * 
270    * @return The scanline stride.
271    */
272   public int getScanlineStride()
273   {
274     return scanlineStride;
275   }
276
277   /**
278    * Returns the number of bits per pixel.
279    * 
280    * @return The number of bits per pixel.
281    */
282   public int getPixelBitStride()
283   {
284     return numberOfBits;
285   }
286   
287   /**
288    * Returns the transfer type, which is one of the following (depending on
289    * the number of bits per sample for this model):
290    * <ul>
291    *   <li>{@link DataBuffer#TYPE_BYTE};</li>
292    *   <li>{@link DataBuffer#TYPE_USHORT};</li>
293    *   <li>{@link DataBuffer#TYPE_INT};</li>
294    * </ul>
295    * 
296    * @return The transfer type.
297    */
298   public int getTransferType()
299   {
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;
305   }
306
307   /**
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.
313    * 
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>).
317    *     
318    * @throws RasterFormatException if <code>bands</code> is not 
319    *     <code>null</code> and <code>bands.length != 1</code>.
320    */
321   public SampleModel createSubsetSampleModel(int[] bands)
322   {
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);
328   }
329
330   /**
331    * Extract one pixel and return in an array of transfer type.
332    *
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.
336    *
337    * @param x The x-coordinate of the pixel rectangle to store in 
338    *     <code>obj</code>.
339    * @param y The y-coordinate of the pixel rectangle to store in 
340    *     <code>obj</code>.
341    * @param obj The primitive array to store the pixels into or null to force 
342    *     creation.
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, 
346    *     DataBuffer)
347    */
348   public Object getDataElements(int x, int y, Object obj, DataBuffer data)
349   {
350     int pixel = getSample(x, y, 0, data);
351     switch (getTransferType())
352       {
353         case DataBuffer.TYPE_BYTE:
354           if (obj == null) 
355             obj = new byte[1];
356           ((byte[]) obj)[0] = (byte) pixel;
357           return obj;
358         case DataBuffer.TYPE_USHORT:
359           if (obj == null) 
360             obj = new short[1];
361           ((short[]) obj)[0] = (short) pixel;
362           return obj;
363         case DataBuffer.TYPE_INT:
364           if (obj == null) 
365             obj = new int[1];
366           ((int[]) obj)[0] = pixel;
367           return obj;
368         default:
369           // Seems like the only sensible thing to do.
370           throw new ClassCastException();
371       }
372   }
373
374   /**
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 
379    * array instance).
380    * 
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).
386    * 
387    * @return An array containing the pixel sample value.
388    * 
389    * @throws NullPointerException if <code>data</code> is <code>null</code>.
390    */
391   public int[] getPixel(int x, int y, int[] iArray, DataBuffer data)
392   {
393     if (iArray == null) 
394       iArray = new int[1];
395     iArray[0] = getSample(x, y, 0, data);
396     return iArray;
397   }
398
399   /**
400    * Returns the sample value for the pixel at (x, y) in the specified data 
401    * buffer.
402    * 
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).
408    * 
409    * @return The sample value.
410    * 
411    * @throws NullPointerException if <code>data</code> is <code>null</code>.
412    */
413   public int getSample(int x, int y, int b, DataBuffer data)
414   {
415     int pos =
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];
420   }
421   
422   /**
423    * Set the pixel at x, y to the value in the first element of the primitive
424    * array obj.
425    *
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.
430    */
431   public void setDataElements(int x, int y, Object obj, DataBuffer data)
432   {
433     int transferType = getTransferType();
434     try
435       {
436         switch (transferType)
437           {
438             case DataBuffer.TYPE_BYTE:
439               {
440                 byte[] in = (byte[]) obj;
441                 setSample(x, y, 0, in[0] & 0xFF, data);
442                 return;
443               }
444             case DataBuffer.TYPE_USHORT:
445               {
446                 short[] in = (short[]) obj;
447                 setSample(x, y, 0, in[0] & 0xFFFF, data);
448                 return;
449               }
450             case DataBuffer.TYPE_INT:
451               {
452                 int[] in = (int[]) obj;
453                 setSample(x, y, 0, in[0], data);
454                 return;
455               }
456             default:
457               throw new ClassCastException("Unsupported data type");
458           }
459       }
460     catch (ArrayIndexOutOfBoundsException aioobe)
461       {
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() +
469           ": " + aioobe;
470         throw new ArrayIndexOutOfBoundsException(msg);
471       }
472   }
473
474   /**
475    * Sets the sample value for the pixel at (x, y) in the specified data 
476    * buffer to the specified value. 
477    * 
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).
482    * 
483    * @throws NullPointerException if either <code>iArray</code> or 
484    *     <code>data</code> is <code>null</code>.
485    *     
486    * @see #setSample(int, int, int, int, DataBuffer)
487    */
488   public void setPixel(int x, int y, int[] iArray, DataBuffer data)
489   {
490     setSample(x, y, 0, iArray[0], data);
491   }
492
493   /**
494    * Sets the sample value for a band for the pixel at (x, y) in the 
495    * specified data buffer. 
496    * 
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).
503    * 
504    * @throws NullPointerException if <code>data</code> is <code>null</code>.
505    */
506   public void setSample(int x, int y, int b, int s, DataBuffer data)
507   {
508     int bitpos =
509       ((dataBitOffset + x * numberOfBits) % elemBits) / numberOfBits;
510     int offset = getOffset(x, y);
511
512     s = s << bitOffsets[bitpos];
513     s = s & bitMasks[bitpos];
514
515     int sample = data.getElem(offset);
516     sample |= s;
517     data.setElem(offset, sample);
518   }
519   
520   /**
521    * Tests this sample model for equality with an arbitrary object.  This 
522    * method returns <code>true</code> if and only if:
523    * <ul>
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:
528    *     <ul>
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>.
535    *     </ul>
536    *   </li>
537    * </ul>
538    * 
539    * @param obj  the object (<code>null</code> permitted)
540    * 
541    * @return <code>true</code> if this model is equal to <code>obj</code>, and
542    *     <code>false</code> otherwise.
543    */
544   public boolean equals(Object obj) 
545   {
546     if (this == obj) 
547       return true;
548     if (! (obj instanceof MultiPixelPackedSampleModel)) 
549       return false;
550     MultiPixelPackedSampleModel that = (MultiPixelPackedSampleModel) obj;
551     if (this.dataType != that.dataType)
552       return false;
553     if (this.width != that.width)
554       return false;
555     if (this.height != that.height)
556       return false;
557     if (this.numberOfBits != that.numberOfBits)
558       return false;
559     if (this.scanlineStride != that.scanlineStride)
560       return false;
561     if (this.dataBitOffset != that.dataBitOffset)
562       return false;
563     return true;
564   }
565   
566   /**
567    * Returns a hash code for this <code>MultiPixelPackedSampleModel</code>.
568    * 
569    * @return A hash code.
570    */
571   public int hashCode()
572   {
573     // this hash code won't match Sun's, but that shouldn't matter...
574     int result = 193;
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;
581     return result;
582   }
583   
584   /**
585    * Creates a String with some information about this SampleModel.
586    * @return A String describing this SampleModel.
587    * @see java.lang.Object#toString()
588    */
589   public String toString()
590   {
591     CPStringBuilder result = new CPStringBuilder();
592     result.append(getClass().getName());
593     result.append("[");
594     result.append("scanlineStride=").append(scanlineStride);
595     for(int i=0; i < bitMasks.length; i+=1)
596     {
597       result.append(", mask[").append(i).append("]=0x").append(Integer.toHexString(bitMasks[i]));
598     }
599     
600     result.append("]");
601     return result.toString();
602   }
603 }