Source for java.awt.image.ComponentSampleModel

   1: /* Copyright (C) 2000, 2002, 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 gnu.java.awt.Buffers;
  40: 
  41: import java.util.Arrays;
  42: 
  43: /* FIXME: This class does not yet support data type TYPE_SHORT */
  44: 
  45: /**
  46:  * ComponentSampleModel supports a flexible organization of pixel samples in
  47:  * memory, permitting pixel samples to be interleaved by band, by scanline,
  48:  * and by pixel.
  49:  *
  50:  * A DataBuffer for this sample model has K banks of data.  Pixels have N
  51:  * samples, so there are N bands in the DataBuffer.  Each band is completely
  52:  * contained in one bank of data, but a bank may contain more than one band.
  53:  * Each pixel sample is stored in a single data element.
  54:  *
  55:  * Within a bank, each band begins at an offset stored in bandOffsets.  The
  56:  * banks containing the band is given by bankIndices.  Within the bank, there
  57:  * are three dimensions - band, pixel, and scanline.  The dimension ordering
  58:  * is controlled by bandOffset, pixelStride, and scanlineStride, which means
  59:  * that any combination of interleavings is supported.
  60:  *
  61:  * @author Rolf W. Rasmussen (rolfwr@ii.uib.no)
  62:  */
  63: public class ComponentSampleModel extends SampleModel
  64: {
  65:   /** The offsets to the first sample for each band. */
  66:   protected int[] bandOffsets;
  67:   
  68:   /** The indices of the bank used to store each band in a data buffer. */
  69:   protected int[] bankIndices;
  70:   
  71:   /** 
  72:    * The number of bands in the image.
  73:    * @specnote This field shadows the protected numBands in SampleModel.
  74:    */
  75:   protected int numBands;
  76:   
  77:   /** Used when creating data buffers. */
  78:   protected int numBanks;
  79: 
  80:   /** 
  81:    * The number of data elements between a sample in one row and the 
  82:    * corresponding sample in the next row.
  83:    */
  84:   protected int scanlineStride;
  85:   
  86:   /**
  87:    * The number of data elements between a sample for one pixel and the 
  88:    * corresponding sample for the next pixel in the same row.
  89:    */
  90:   protected int pixelStride;
  91:   
  92:   private boolean tightPixelPacking = false;
  93:   
  94:   /**
  95:    * Creates a new sample model that assumes that all bands are stored in a 
  96:    * single bank of the {@link DataBuffer}.
  97:    * <p>
  98:    * Note that the <code>bandOffsets</code> array is copied to internal storage
  99:    * to prevent subsequent changes to the array from affecting this object.
 100:    * 
 101:    * @param dataType  the data type (one of {@link DataBuffer#TYPE_BYTE},
 102:    *   {@link DataBuffer#TYPE_USHORT}, {@link DataBuffer#TYPE_SHORT},
 103:    *   {@link DataBuffer#TYPE_INT}, {@link DataBuffer#TYPE_FLOAT} or 
 104:    *   {@link DataBuffer#TYPE_DOUBLE}).
 105:    * @param w  the width in pixels.
 106:    * @param h  the height in pixels.
 107:    * @param pixelStride  the number of data elements in the step from a sample
 108:    *   in one pixel to the corresponding sample in the next pixel.
 109:    * @param scanlineStride  the number of data elements in the step from a 
 110:    *   sample in a pixel to the corresponding sample in the pixel in the next
 111:    *   row.
 112:    * @param bandOffsets  the offset to the first element for each band, with 
 113:    *   the size of the array defining the number of bands (<code>null</code>
 114:    *   not permitted).
 115:    *   
 116:    * @throws IllegalArgumentException if <code>dataType</code> is not one of
 117:    *   the specified values.
 118:    * @throws IllegalArgumentException if <code>w</code> is less than or equal
 119:    *   to zero.
 120:    * @throws IllegalArgumentException if <code>h</code> is less than or equal 
 121:    *   to zero.
 122:    * @throws IllegalArgumentException if <code>w * h</code> exceeds
 123:    *   {@link Integer#MAX_VALUE}.
 124:    * @throws IllegalArgumentException if <code>pixelStride</code> is negative.
 125:    * @throws IllegalArgumentException if <code>scanlineStride</code> is less 
 126:    *   than or equal to zero.
 127:    * @throws IllegalArgumentException if <code>bandOffsets</code> has zero 
 128:    *   length.
 129:    */
 130:   public ComponentSampleModel(int dataType,
 131:                               int w, int h,
 132:                               int pixelStride,
 133:                               int scanlineStride,
 134:                               int[] bandOffsets)
 135:   {
 136:     this(dataType, w, h, pixelStride, scanlineStride,
 137:          new int[bandOffsets.length], bandOffsets);
 138:   }
 139:     
 140:   /**
 141:    * Creates a new sample model that assumes that all bands are stored in a 
 142:    * single bank of the {@link DataBuffer}.
 143:    * 
 144:    * @param dataType  the data type (one of {@link DataBuffer#TYPE_BYTE},
 145:    *   {@link DataBuffer#TYPE_USHORT}, {@link DataBuffer#TYPE_SHORT},
 146:    *   {@link DataBuffer#TYPE_INT}, {@link DataBuffer#TYPE_FLOAT} or 
 147:    *   {@link DataBuffer#TYPE_DOUBLE}).
 148:    * @param w  the width in pixels.
 149:    * @param h  the height in pixels.
 150:    * @param pixelStride  the number of data elements in the step from a sample
 151:    *   in one pixel to the corresponding sample in the next pixel.
 152:    * @param scanlineStride  the number of data elements in the step from a 
 153:    *   sample in a pixel to the corresponding sample in the pixel in the next
 154:    *   row.
 155:    * @param bankIndices  the index of the bank in which each band is stored 
 156:    *   (<code>null</code> not permitted).  This array is copied to internal
 157:    *   storage so that subsequent updates to the array do not affect the sample 
 158:    *   model.
 159:    * @param bandOffsets  the offset to the first element for each band, with 
 160:    *   the size of the array defining the number of bands (<code>null</code>
 161:    *   not permitted).  This array is copied to internal storage so that 
 162:    *   subsequent updates to the array do not affect the sample model.
 163:    *   
 164:    * @throws IllegalArgumentException if <code>dataType</code> is not one of
 165:    *   the specified values.
 166:    * @throws IllegalArgumentException if <code>w</code> is less than or equal
 167:    *   to zero.
 168:    * @throws IllegalArgumentException if <code>h</code> is less than or equal 
 169:    *   to zero.
 170:    * @throws IllegalArgumentException if <code>w * h</code> exceeds
 171:    *   {@link Integer#MAX_VALUE}.
 172:    * @throws IllegalArgumentException if <code>pixelStride</code> is negative.
 173:    * @throws IllegalArgumentException if <code>scanlineStride</code> is less 
 174:    *   than or equal to zero.
 175:    * @throws IllegalArgumentException if <code>bandOffsets</code> has zero 
 176:    *   length.
 177:    */
 178:   public ComponentSampleModel(int dataType,
 179:                               int w, int h,
 180:                               int pixelStride,
 181:                               int scanlineStride,
 182:                               int[] bankIndices,
 183:                               int[] bandOffsets)
 184:   {
 185:     super(dataType, w, h, bandOffsets.length);
 186:     
 187:     // super permits DataBuffer.TYPE_UNDEFINED but this class doesn't...
 188:     if (dataType == DataBuffer.TYPE_UNDEFINED)
 189:       throw new IllegalArgumentException("Unsupported dataType.");
 190:     
 191:     if ((pixelStride < 0) || (scanlineStride < 0) || (bandOffsets.length < 1) 
 192:         || (bandOffsets.length != bankIndices.length))
 193:       throw new IllegalArgumentException();
 194:     
 195:     this.bandOffsets = (int[]) bandOffsets.clone();
 196:     this.bankIndices = (int[]) bankIndices.clone();
 197:     this.numBands = bandOffsets.length;
 198: 
 199:     this.numBanks = 0;
 200:     for (int b = 0; b < bankIndices.length; b++)
 201:       this.numBanks = Math.max(this.numBanks, bankIndices[b] + 1);
 202: 
 203:     this.scanlineStride = scanlineStride;
 204:     this.pixelStride = pixelStride;
 205: 
 206:     // See if we can use some speedups
 207: 
 208:     /* FIXME: May these checks should be reserved for the
 209:        PixelInterleavedSampleModel? */
 210:         
 211:     if (pixelStride == numBands)
 212:       {
 213:         tightPixelPacking = true;
 214:         for (int b = 0; b < numBands; b++) {
 215:           if ((bandOffsets[b] != b) || (bankIndices[b] != 0))
 216:             {
 217:               tightPixelPacking = false;
 218:               break;
 219:             }
 220:         }
 221:       }
 222:   }             
 223: 
 224:   /**
 225:    * Creates a new sample model that is compatible with this one, but with the
 226:    * specified dimensions.
 227:    * 
 228:    * @param w  the width (must be greater than zero).
 229:    * @param h  the height (must be greater than zero).
 230:    * 
 231:    * @return A new sample model.
 232:    */
 233:   public SampleModel createCompatibleSampleModel(int w, int h)
 234:   {
 235:     return new ComponentSampleModel(dataType, w, h, pixelStride,
 236:                                     scanlineStride, bankIndices,
 237:                                     bandOffsets);
 238:   }
 239: 
 240:   /**
 241:    * Creates a new sample model that provides access to a subset of the bands
 242:    * that this sample model supports.
 243:    * 
 244:    * @param bands  the bands (<code>null</code> not permitted).
 245:    * 
 246:    * @return The new sample model.
 247:    */
 248:   public SampleModel createSubsetSampleModel(int[] bands)
 249:   {
 250:     int numBands = bands.length;
 251:     
 252:     int[] bankIndices = new int[numBands];
 253:     int[] bandOffsets = new int[numBands];
 254:     for (int b = 0; b < numBands; b++)
 255:       {
 256:         bankIndices[b] = this.bankIndices[bands[b]];
 257:         bandOffsets[b] = this.bandOffsets[bands[b]];
 258:       }
 259: 
 260:     return new ComponentSampleModel(dataType, width, height, pixelStride,
 261:                                     scanlineStride, bankIndices,
 262:                                     bandOffsets);
 263:   }
 264: 
 265:   /**
 266:    * Creates a new data buffer that is compatible with this sample model.
 267:    * 
 268:    * @return The new data buffer.
 269:    */
 270:   public DataBuffer createDataBuffer()
 271:   {
 272:     // Maybe this value should be precalculated in the constructor?
 273:     int highestOffset = 0;
 274:     for (int b = 0; b < numBands; b++)
 275:       highestOffset = Math.max(highestOffset, bandOffsets[b]);    
 276:     int size = pixelStride * (width - 1) + scanlineStride * (height - 1) 
 277:         + highestOffset + 1;
 278:     
 279:     return Buffers.createBuffer(getDataType(), size, numBanks);
 280:   }
 281: 
 282:   /**
 283:    * Returns the offset of the sample in band 0 for the pixel at location
 284:    * <code>(x, y)</code>.  This offset can be used to read a sample value from
 285:    * a {@link DataBuffer}.
 286:    * 
 287:    * @param x  the x-coordinate.
 288:    * @param y  the y-coordinate.
 289:    * 
 290:    * @return The offset.
 291:    * 
 292:    * @see #getOffset(int, int, int)
 293:    */
 294:   public int getOffset(int x, int y)
 295:   {
 296:     return getOffset(x, y, 0);
 297:   }
 298: 
 299:   /**
 300:    * Returns the offset of the sample in band <code>b</code> for the pixel at
 301:    * location <code>(x, y)</code>.  This offset can be used to read a sample
 302:    * value from a {@link DataBuffer}.
 303:    * 
 304:    * @param x  the x-coordinate.
 305:    * @param y  the y-coordinate.
 306:    * @param b  the band index.
 307:    * 
 308:    * @return The offset.
 309:    */
 310:   public int getOffset(int x, int y, int b)
 311:   {
 312:     return bandOffsets[b] + pixelStride * x + scanlineStride * y;
 313:   }
 314: 
 315:   /**
 316:    * Returns the size in bits for each sample (one per band).  For this sample
 317:    * model, each band has the same sample size and this is determined by the
 318:    * data type for the sample model.
 319:    * 
 320:    * @return The sample sizes.
 321:    * 
 322:    * @see SampleModel#getDataType()
 323:    */
 324:   public final int[] getSampleSize()
 325:   {
 326:     int size = DataBuffer.getDataTypeSize(getDataType());
 327:     int[] sizes = new int[numBands];
 328: 
 329:     java.util.Arrays.fill(sizes, size);
 330:     return sizes;
 331:   }
 332: 
 333:   /**
 334:    * Returns the size in bits for the samples in the specified band.  In this
 335:    * class, the sample size is the same for every band and is determined from 
 336:    * the data type for the model.
 337:    * 
 338:    * @param band  the band index (ignored here).
 339:    * 
 340:    * @return The sample size in bits.
 341:    * 
 342:    * @see SampleModel#getDataType()
 343:    */
 344:   public final int getSampleSize(int band)
 345:   {
 346:     return DataBuffer.getDataTypeSize(getDataType());
 347:   }
 348: 
 349:   /**
 350:    * Returns the indices of the bank(s) in the {@link DataBuffer} used to 
 351:    * store the samples for each band.  The returned array is a copy, so that
 352:    * altering it will not impact the sample model.
 353:    * 
 354:    * @return The bank indices.
 355:    */
 356:   public final int[] getBankIndices()
 357:   {
 358:     return (int[]) bankIndices.clone();
 359:   }
 360: 
 361:   /**
 362:    * Returns the offsets to the first sample in each band.  The returned array
 363:    * is a copy, so that altering it will not impact the sample model.
 364:    * 
 365:    * @return The offsets.
 366:    */
 367:   public final int[] getBandOffsets()
 368:   {
 369:     return (int[]) bandOffsets.clone();
 370:   }
 371: 
 372:   /**
 373:    * Returns the distance (in terms of element indices) between the sample for
 374:    * one pixel and the corresponding sample for the equivalent pixel in the 
 375:    * next row.  This is used in the calculation of the element offset for
 376:    * retrieving samples from a {@link DataBuffer}.
 377:    * 
 378:    * @return The distance between pixel samples in consecutive rows.
 379:    */
 380:   public final int getScanlineStride()
 381:   {
 382:     return scanlineStride;
 383:   }
 384: 
 385:   /**
 386:    * Returns the distance (in terms of element indices) between the sample for 
 387:    * one pixel and the corresponding sample for the next pixel in a row.  This 
 388:    * is used in the calculation of the element offset for retrieving samples 
 389:    * from a {@link DataBuffer}.
 390:    * 
 391:    * @return The distance between pixel samples in the same row.
 392:    */
 393:   public final int getPixelStride()
 394:   {
 395:     return pixelStride;
 396:   }
 397: 
 398:   /**
 399:    * Returns the number of data elements used to store the samples for one 
 400:    * pixel.  In this model, this is the same as the number of bands.
 401:    * 
 402:    * @return The number of data elements used to store the samples for one 
 403:    *   pixel.
 404:    */
 405:   public final int getNumDataElements()
 406:   {
 407:     return numBands;
 408:   }
 409: 
 410:   /**
 411:    * Returns the samples for the pixel at location <code>(x, y)</code> in
 412:    * a primitive array (the array type is determined by the data type for 
 413:    * this model).  The <code>obj</code> argument provides an option to supply
 414:    * an existing array to hold the result, if this is <code>null</code> a new
 415:    * array will be allocated.
 416:    * 
 417:    * @param x  the x-coordinate.
 418:    * @param y  the y-coordinate.
 419:    * @param obj  a primitive array that, if not <code>null</code>, will be 
 420:    *   used to store and return the sample values.
 421:    * @param data  the data buffer (<code>null</code> not permitted).
 422:    * 
 423:    * @return An array of sample values for the specified pixel.
 424:    */
 425:   public Object getDataElements(int x, int y, Object obj, DataBuffer data)
 426:   {
 427:     int xyOffset = pixelStride * x + scanlineStride * y;
 428:     
 429:     int[] totalBandDataOffsets = new int[numBands];
 430:     
 431:     /* Notice that band and bank offsets are different. Band offsets
 432:        are managed by the sample model, and bank offsets are managed
 433:        by the data buffer. Both must be accounted for. */
 434:     
 435:     /* FIXME: For single pixels, it is probably easier to simple
 436:        call getElem instead of calculating the bank offset ourself.
 437:        
 438:        On the other hand, then we need to push the value through
 439:        the int type returned by the getElem method.  */
 440:     
 441:     int[] bankOffsets = data.getOffsets();
 442:     
 443:     for (int b = 0; b < numBands; b++)
 444:       {
 445:         totalBandDataOffsets[b] = bandOffsets[b] + bankOffsets[bankIndices[b]] 
 446:                                   + xyOffset;
 447:       }
 448:         
 449:     try
 450:       {
 451:         switch (getTransferType())
 452:           {
 453:           case DataBuffer.TYPE_BYTE:
 454:             DataBufferByte inByte = (DataBufferByte) data;
 455:             byte[] outByte = (byte[]) obj;
 456:             if (outByte == null) 
 457:               outByte = new byte[numBands];
 458:                 
 459:             for (int b = 0; b < numBands; b++)
 460:               {
 461:                 int dOffset = totalBandDataOffsets[b];
 462:                 outByte[b] = inByte.getData(bankIndices[b])[dOffset];
 463:               }
 464:             return outByte;
 465:                 
 466:           case DataBuffer.TYPE_USHORT:
 467:             DataBufferUShort inUShort = (DataBufferUShort) data;
 468:             short[] outUShort = (short[]) obj;
 469:             if (outUShort == null) 
 470:               outUShort = new short[numBands];
 471:                 
 472:             for (int b = 0; b < numBands; b++)
 473:               {
 474:                 int dOffset = totalBandDataOffsets[b];
 475:                 outUShort[b] = inUShort.getData(bankIndices[b])[dOffset];
 476:               }
 477:             return outUShort;
 478: 
 479:           case DataBuffer.TYPE_SHORT:
 480:             DataBufferShort inShort = (DataBufferShort) data;
 481:             short[] outShort = (short[]) obj;
 482:             if (outShort == null) 
 483:               outShort = new short[numBands];
 484:                 
 485:             for (int b = 0; b < numBands; b++)
 486:               {
 487:                 int dOffset = totalBandDataOffsets[b];
 488:                 outShort[b] = inShort.getData(bankIndices[b])[dOffset];
 489:               }
 490:             return outShort;
 491: 
 492:           case DataBuffer.TYPE_INT:
 493:             DataBufferInt inInt = (DataBufferInt) data;
 494:             int[] outInt = (int[]) obj;
 495:             if (outInt == null) 
 496:               outInt = new int[numBands];
 497:                 
 498:             for (int b = 0; b < numBands; b++)
 499:               {
 500:                 int dOffset = totalBandDataOffsets[b];
 501:                 outInt[b] = inInt.getData(bankIndices[b])[dOffset];
 502:               }
 503:             return outInt;
 504: 
 505:           case DataBuffer.TYPE_FLOAT:
 506:             DataBufferFloat inFloat = (DataBufferFloat) data;
 507:             float[] outFloat = (float[]) obj;
 508:             if (outFloat == null) 
 509:               outFloat = new float[numBands];
 510: 
 511:             for (int b = 0; b < numBands; b++)
 512:               {
 513:                 int dOffset = totalBandDataOffsets[b];
 514:                 outFloat[b] = inFloat.getData(bankIndices[b])[dOffset];
 515:               }
 516:             return outFloat;
 517:             
 518:           case DataBuffer.TYPE_DOUBLE:
 519:             DataBufferDouble inDouble = (DataBufferDouble) data;
 520:             double[] outDouble = (double[]) obj;
 521:             if (outDouble == null) 
 522:               outDouble = new double[numBands];
 523: 
 524:             for (int b = 0; b < numBands; b++)
 525:               {
 526:                 int dOffset = totalBandDataOffsets[b];
 527:                 outDouble[b] = inDouble.getData(bankIndices[b])[dOffset];
 528:               }
 529:             return outDouble;
 530:             
 531:           default:
 532:               throw new IllegalStateException("unknown transfer type " 
 533:                                               + getTransferType());
 534:           }
 535:       }
 536:     catch (ArrayIndexOutOfBoundsException aioobe)
 537:       {
 538:         String msg = "While reading data elements, " +
 539:           "x=" + x + ", y=" + y +", " + ", xyOffset=" + xyOffset +
 540:           ", data.getSize()=" + data.getSize() + ": " + aioobe;
 541:         throw new ArrayIndexOutOfBoundsException(msg);
 542:       }
 543:   }
 544: 
 545:   /**
 546:    * Returns the samples for the pixels in the region defined by 
 547:    * <code>(x, y, w, h)</code> in a primitive array (the array type is 
 548:    * determined by the data type for this model).  The <code>obj</code> 
 549:    * argument provides an option to supply an existing array to hold the 
 550:    * result, if this is <code>null</code> a new array will be allocated.
 551:    * 
 552:    * @param x  the x-coordinate.
 553:    * @param y  the y-coordinate.
 554:    * @param w  the width.
 555:    * @param h  the height.
 556:    * @param obj  a primitive array that, if not <code>null</code>, will be 
 557:    *   used to store and return the sample values.
 558:    * @param data  the data buffer (<code>null</code> not permitted).
 559:    * 
 560:    * @return An array of sample values for the specified pixels.
 561:    * 
 562:    * @see #setDataElements(int, int, int, int, Object, DataBuffer)
 563:    */
 564:   public Object getDataElements(int x, int y, int w, int h, Object obj,
 565:                                 DataBuffer data)
 566:   {
 567:     if (!tightPixelPacking)
 568:       {
 569:         return super.getDataElements(x, y, w, h, obj, data);
 570:       }
 571: 
 572:     // using get speedup
 573:     
 574:     // We can copy whole rows
 575:     int rowSize = w * numBands;
 576:     int dataSize = rowSize * h;
 577:     
 578:     DataBuffer transferBuffer = Buffers.createBuffer(getTransferType(), obj, 
 579:                                                      dataSize);
 580:     obj = Buffers.getData(transferBuffer);
 581: 
 582:     int inOffset = pixelStride * x + scanlineStride * y + data.getOffset(); 
 583:                                            // Assumes only one band is used
 584: 
 585:     /* We don't add band offsets since we assume that bands have
 586:        offsets 0, 1, 2, ... */
 587: 
 588:     // See if we can copy everything in one go
 589:     if (scanlineStride == rowSize)
 590:       {
 591:         // Collapse scan lines:
 592:         rowSize *= h;
 593:         // We ignore scanlineStride since it won't be of any use
 594:         h = 1;
 595:       }
 596: 
 597:     int outOffset = 0;
 598:     Object inArray = Buffers.getData(data);
 599:     for (int yd = 0; yd < h; yd++)
 600:       {
 601:         System.arraycopy(inArray, inOffset, obj, outOffset, rowSize);
 602:         inOffset  += scanlineStride;
 603:         outOffset += rowSize;
 604:       }
 605:     return obj;
 606:   }
 607: 
 608:   /**
 609:    * Sets the samples for the pixels in the region defined by 
 610:    * <code>(x, y, w, h)</code> from a supplied primitive array (the array type 
 611:    * must be consistent with the data type for this model).  
 612:    * 
 613:    * @param x  the x-coordinate.
 614:    * @param y  the y-coordinate.
 615:    * @param w  the width.
 616:    * @param h  the height.
 617:    * @param obj  a primitive array containing the sample values.
 618:    * @param data  the data buffer (<code>null</code> not permitted).
 619:    * 
 620:    * @see #getDataElements(int, int, int, int, Object, DataBuffer)
 621:    */
 622:   public void setDataElements(int x, int y, int w, int h,
 623:                               Object obj, DataBuffer data)
 624:   {
 625:     if (!tightPixelPacking)
 626:       {
 627:         super.setDataElements(x, y, w, h, obj, data);
 628:         return;
 629:       }
 630: 
 631:     // using set speedup, we can copy whole rows
 632:     int rowSize = w * numBands;
 633:     int dataSize = rowSize * h;
 634:     
 635:     DataBuffer transferBuffer 
 636:         = Buffers.createBufferFromData(getTransferType(), obj, dataSize);
 637: 
 638:     int[] bankOffsets = data.getOffsets();
 639: 
 640:     int outOffset = pixelStride * x + scanlineStride * y + bankOffsets[0]; 
 641:                                                 // same assumptions as in get...
 642: 
 643:     // See if we can copy everything in one go
 644:     if (scanlineStride == rowSize)
 645:       {
 646:         // Collapse scan lines:
 647:         rowSize *= h;
 648:         h = 1;
 649:       }
 650: 
 651:     int inOffset = 0;
 652:     Object outArray = Buffers.getData(data);
 653:     for (int yd = 0; yd < h; yd++)
 654:       {
 655:         System.arraycopy(obj, inOffset, outArray, outOffset, rowSize);
 656:         outOffset += scanlineStride;
 657:         inOffset += rowSize;
 658:       }
 659:   }
 660: 
 661:   /**
 662:    * Returns all the samples for the pixel at location <code>(x, y)</code>
 663:    * stored in the specified data buffer.
 664:    * 
 665:    * @param x  the x-coordinate.
 666:    * @param y  the y-coordinate.
 667:    * @param iArray  an array that will be populated with the sample values and
 668:    *   returned as the result.  The size of this array should be equal to the 
 669:    *   number of bands in the model.  If the array is <code>null</code>, a new
 670:    *   array is created.
 671:    * @param data  the data buffer (<code>null</code> not permitted).
 672:    * 
 673:    * @return The samples for the specified pixel.
 674:    * 
 675:    * @see #setPixel(int, int, int[], DataBuffer)
 676:    */
 677:   public int[] getPixel(int x, int y, int[] iArray, DataBuffer data)
 678:   {
 679:     if (x < 0 || x >= width || y < 0 || y >= height)
 680:       throw new ArrayIndexOutOfBoundsException("Pixel (" + x + ", " + y 
 681:                                                + ") is out of bounds.");
 682:     int offset = pixelStride * x + scanlineStride * y;
 683:     if (iArray == null)
 684:       iArray = new int[numBands];
 685:     for (int b = 0; b < numBands; b++)
 686:       {
 687:         iArray[b] = data.getElem(bankIndices[b], offset + bandOffsets[b]);
 688:       }
 689:     return iArray;
 690:   }
 691: 
 692:   /**
 693:    * Returns the samples for all the pixels in a rectangular region.
 694:    * 
 695:    * @param x  the x-coordinate.
 696:    * @param y  the y-coordinate.
 697:    * @param w  the width.
 698:    * @param h  the height.
 699:    * @param iArray  an array that if non-<code>null</code> will be populated 
 700:    *   with the sample values and returned as the result.
 701:    * @param data  the data buffer (<code>null</code> not permitted).
 702:    * 
 703:    * @return The samples for all the pixels in the rectangle.
 704:    */
 705:   public int[] getPixels(int x, int y, int w, int h, int[] iArray,
 706:                          DataBuffer data)
 707:   {
 708:     int offset = pixelStride * x + scanlineStride * y;
 709:     if (iArray == null) 
 710:       iArray = new int[numBands * w * h];
 711:     int outOffset = 0;
 712:     for (y = 0; y < h; y++)
 713:       {
 714:         int lineOffset = offset;
 715:         for (x = 0; x < w; x++)
 716:           {
 717:             for (int b = 0; b < numBands; b++)
 718:               {
 719:                 iArray[outOffset++] 
 720:                     = data.getElem(bankIndices[b], lineOffset+bandOffsets[b]);
 721:               }
 722:             lineOffset += pixelStride;
 723:           }
 724:         offset += scanlineStride;
 725:       }
 726:     return iArray;
 727:   }
 728:  
 729:   /**
 730:    * Returns the sample for band <code>b</code> of the pixel at 
 731:    * <code>(x, y)</code> that is stored in the specified data buffer.
 732:    * 
 733:    * @param x  the x-coordinate.
 734:    * @param y  the y-coordinate.
 735:    * @param b  the band index.
 736:    * @param data  the data buffer (<code>null</code> not permitted).
 737:    * 
 738:    * @return The sample value.
 739:    * 
 740:    * @throws ArrayIndexOutOfBoundsException if <code>(x, y)</code> is outside 
 741:    *     the bounds <code>[0, 0, width, height]</code>.
 742:    *     
 743:    * @see #setSample(int, int, int, int, DataBuffer)
 744:    */
 745:   public int getSample(int x, int y, int b, DataBuffer data)
 746:   {
 747:     if (x < 0 || x >= width || y < 0 || y >= height)
 748:       throw new ArrayIndexOutOfBoundsException("Sample (" + x + ", " + y 
 749:                                                + ") is out of bounds.");
 750:     return data.getElem(bankIndices[b], getOffset(x, y, b));
 751:   }
 752: 
 753:   /**
 754:    * Sets the samples for the pixel at location <code>(x, y)</code> from the 
 755:    * supplied primitive array (the array type must be consistent with the data 
 756:    * type for this model).
 757:    * 
 758:    * @param x  the x-coordinate.
 759:    * @param y  the y-coordinate.
 760:    * @param obj  a primitive array containing the pixel's sample values.
 761:    * @param data  the data buffer (<code>null</code> not permitted).
 762:    * 
 763:    * @see #setDataElements(int, int, Object, DataBuffer)
 764:    */
 765:   public void setDataElements(int x, int y, Object obj, DataBuffer data)
 766:   {
 767:     int offset = pixelStride * x + scanlineStride * y;
 768:     int[] totalBandDataOffsets = new int[numBands];
 769:     int[] bankOffsets = data.getOffsets();
 770:     for (int b = 0; b < numBands; b++)
 771:       totalBandDataOffsets[b] = bandOffsets[b] + bankOffsets[bankIndices[b]] 
 772:                                 + offset;
 773: 
 774:     switch (getTransferType())
 775:       {
 776:       case DataBuffer.TYPE_BYTE:
 777:         {
 778:           DataBufferByte out = (DataBufferByte) data;
 779:           byte[] in = (byte[]) obj;
 780:           
 781:           for (int b = 0; b < numBands; b++)
 782:             out.getData(bankIndices[b])[totalBandDataOffsets[b]] = in[b];
 783:           
 784:           return;
 785:         }
 786:       case DataBuffer.TYPE_USHORT:
 787:         {
 788:           DataBufferUShort out = (DataBufferUShort) data;
 789:           short[] in = (short[]) obj;
 790:           
 791:           for (int b = 0; b < numBands; b++)
 792:             out.getData(bankIndices[b])[totalBandDataOffsets[b]] = in[b];
 793:           
 794:           return;
 795:         }
 796:       case DataBuffer.TYPE_SHORT:
 797:         {
 798:           DataBufferShort out = (DataBufferShort) data;
 799:           short[] in = (short[]) obj;
 800:           
 801:           for (int b = 0; b < numBands; b++)
 802:             out.getData(bankIndices[b])[totalBandDataOffsets[b]] = in[b];
 803:           
 804:           return;
 805:         }
 806:       case DataBuffer.TYPE_INT:
 807:         {
 808:           DataBufferInt out = (DataBufferInt) data;
 809:           int[] in = (int[]) obj;
 810:           
 811:           for (int b = 0; b < numBands; b++)
 812:             out.getData(bankIndices[b])[totalBandDataOffsets[b]] = in[b];
 813:           
 814:           return;
 815:         }
 816:       case DataBuffer.TYPE_FLOAT:
 817:         {
 818:           DataBufferFloat out = (DataBufferFloat) data;
 819:           float[] in = (float[]) obj;
 820:           
 821:           for (int b = 0; b < numBands; b++)
 822:             out.getData(bankIndices[b])[totalBandDataOffsets[b]] = in[b];
 823:           
 824:           return;
 825:         }
 826:       case DataBuffer.TYPE_DOUBLE:
 827:         {
 828:           DataBufferDouble out = (DataBufferDouble) data;
 829:           double[] in = (double[]) obj;
 830:           
 831:           for (int b = 0; b < numBands; b++)
 832:             out.getData(bankIndices[b])[totalBandDataOffsets[b]] = in[b];
 833:           
 834:           return;
 835:         }
 836:       default:
 837:         throw new UnsupportedOperationException("transfer type not " +
 838:                                                 "implemented");
 839:       }
 840:   }
 841:   
 842:   /**
 843:    * Sets the sample values for the pixel at location <code>(x, y)</code>
 844:    * stored in the specified data buffer.
 845:    * 
 846:    * @param x  the x-coordinate.
 847:    * @param y  the y-coordinate.
 848:    * @param iArray  the pixel sample values (<code>null</code> not permitted).
 849:    * @param data  the data buffer (<code>null</code> not permitted).
 850:    * 
 851:    * @see #getPixel(int, int, int[], DataBuffer)
 852:    */
 853:   public void setPixel(int x, int y, int[] iArray, DataBuffer data)
 854:   {
 855:     int offset = pixelStride * x + scanlineStride * y;
 856:     for (int b = 0; b < numBands; b++)
 857:       data.setElem(bankIndices[b], offset + bandOffsets[b], iArray[b]);
 858:   }
 859:     
 860:   /**
 861:    * Sets the sample value for band <code>b</code> of the pixel at location
 862:    * <code>(x, y)</code> in the specified data buffer.
 863:    * 
 864:    * @param x  the x-coordinate.
 865:    * @param y  the y-coordinate.
 866:    * @param b  the band index.
 867:    * @param s  the sample value.
 868:    * @param data  the data buffer (<code>null</code> not permitted).
 869:    * 
 870:    * @see #getSample(int, int, int, DataBuffer)
 871:    */
 872:   public void setSample(int x, int y, int b, int s, DataBuffer data)
 873:   {
 874:     data.setElem(bankIndices[b], getOffset(x, y, b), s);
 875:   }
 876:   
 877:   /**
 878:    * Tests this sample model for equality with an arbitrary object.  Returns
 879:    * <code>true</code> if and only if:
 880:    * <ul>
 881:    * <li><code>obj</code> is not <code>null</code>;</li>
 882:    * <li><code>obj</code> is an instance of <code>ComponentSampleModel</code>;
 883:    *   </li>
 884:    * <li>both models have the same values for the <code>dataType</code>,
 885:    *   <code>width</code>, <code>height</code>, <code>pixelStride</code>,
 886:    *   <code>scanlineStride</code>, <code>bandOffsets</code> and
 887:    *   <code>bankIndices</code> fields.</li>
 888:    * </ul>
 889:    * 
 890:    * @param obj  the object to test (<code>null</code> permitted).
 891:    * 
 892:    * @return <code>true</code> if this sample model is equal to 
 893:    *   <code>obj</code>, and <code>false</code> otherwise.
 894:    */
 895:   public boolean equals(Object obj)
 896:   {
 897:     if (obj == null)
 898:       return false;
 899:     if (! (obj instanceof ComponentSampleModel))
 900:       return false;
 901:     ComponentSampleModel that = (ComponentSampleModel) obj;
 902:     if (this.dataType != that.dataType)
 903:       return false;
 904:     if (this.width != that.width)
 905:       return false;
 906:     if (this.height != that.height)
 907:       return false;
 908:     if (this.pixelStride != that.pixelStride)
 909:       return false;
 910:     if (this.scanlineStride != that.scanlineStride)
 911:       return false;
 912:     if (! Arrays.equals(this.bandOffsets, that.bandOffsets))
 913:       return false;
 914:     if (! Arrays.equals(this.bankIndices, that.bankIndices))
 915:       return false;
 916:     // couldn't find any difference, so...
 917:     return true;
 918:   }
 919:   
 920:   /**
 921:    * Returns a hash code for this sample model.
 922:    * 
 923:    * @return The hash code.
 924:    */
 925:   public int hashCode()
 926:   {
 927:     // this computation is based on the method described in Chapter 3
 928:     // of Joshua Bloch's Effective Java...
 929:     int result = 17;
 930:     result = 37 * result + dataType;
 931:     result = 37 * result + width;
 932:     result = 37 * result + height;
 933:     result = 37 * result + pixelStride;
 934:     result = 37 * result + scanlineStride;
 935:     for (int i = 0; i < bandOffsets.length; i++)
 936:       result = 37 * result + bandOffsets[i];
 937:     for (int i = 0; i < bankIndices.length; i++)
 938:       result = 37 * result + bankIndices[i];
 939:     return result;
 940:   }
 941: }