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