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    }