Source for java.awt.image.SinglePixelPackedSampleModel

   1: /* Copyright (C) 2000, 2002, 2003, 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 java.util.Arrays;
  40: 
  41: import gnu.java.awt.BitMaskExtent;
  42: 
  43: /**
  44:  * A <code>SampleModel</code> used when all samples are stored in a single
  45:  * data element in the {@link DataBuffer}, and each data element contains 
  46:  * samples for one pixel only.
  47:  * 
  48:  * @author Rolf W. Rasmussen (rolfwr@ii.uib.no)
  49:  */
  50: public class SinglePixelPackedSampleModel extends SampleModel
  51: {
  52:   private int scanlineStride;
  53:   private int[] bitMasks;
  54:   private int[] bitOffsets;
  55:   private int[] sampleSize;
  56:   
  57:   /**
  58:    * Creates a new <code>SinglePixelPackedSampleModel</code>.
  59:    * 
  60:    * @param dataType  the data buffer type.
  61:    * @param w  the width (in pixels).
  62:    * @param h  the height (in pixels).
  63:    * @param bitMasks  an array containing the bit mask used to extract the
  64:    *     sample value for each band.
  65:    */
  66:   public SinglePixelPackedSampleModel(int dataType, int w, int h,
  67:                       int[] bitMasks)
  68:   {
  69:     this(dataType, w, h, w, bitMasks);
  70:   }
  71: 
  72:   /**
  73:    * Creates a new <code>SinglePixelPackedSampleModel</code>.
  74:    * 
  75:    * @param dataType  the data buffer type.
  76:    * @param w  the width (in pixels).
  77:    * @param h  the height (in pixels).
  78:    * @param scanlineStride  the number of data elements between a pixel on one
  79:    *     row and the corresponding pixel on the next row.
  80:    * @param bitMasks  an array containing the bit mask used to extract the
  81:    *     sample value for each band.
  82:    */
  83:   public SinglePixelPackedSampleModel(int dataType, int w, int h,
  84:                       int scanlineStride, int[] bitMasks)
  85:   {
  86:     super(dataType, w, h, bitMasks.length);
  87: 
  88:     switch (dataType)
  89:       {
  90:       case DataBuffer.TYPE_BYTE:
  91:       case DataBuffer.TYPE_USHORT:
  92:       case DataBuffer.TYPE_INT:
  93:     break;
  94:       default:
  95:         throw new IllegalArgumentException(
  96:             "SinglePixelPackedSampleModel unsupported dataType");
  97:       }
  98:     
  99:     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: }