001/* Copyright (C) 2000, 2002, 2003, 2004, 2006,  Free Software Foundation
002
003This file is part of GNU Classpath.
004
005GNU Classpath is free software; you can redistribute it and/or modify
006it under the terms of the GNU General Public License as published by
007the Free Software Foundation; either version 2, or (at your option)
008any later version.
009
010GNU Classpath is distributed in the hope that it will be useful, but
011WITHOUT ANY WARRANTY; without even the implied warranty of
012MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
013General Public License for more details.
014
015You should have received a copy of the GNU General Public License
016along with GNU Classpath; see the file COPYING.  If not, write to the
017Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
01802110-1301 USA.
019
020Linking this library statically or dynamically with other modules is
021making a combined work based on this library.  Thus, the terms and
022conditions of the GNU General Public License cover the whole
023combination.
024
025As a special exception, the copyright holders of this library give you
026permission to link this library with independent modules to produce an
027executable, regardless of the license terms of these independent
028modules, and to copy and distribute the resulting executable under
029terms of your choice, provided that you also meet, for each linked
030independent module, the terms and conditions of the license of that
031module.  An independent module is a module which is not derived from
032or based on this library.  If you modify this library, you may extend
033this exception to your version of the library, but you are not
034obligated to do so.  If you do not wish to do so, delete this
035exception statement from your version. */
036
037package java.awt.image;
038
039import java.util.Arrays;
040
041import gnu.java.awt.BitMaskExtent;
042import 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 */
051public 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}