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