1 /*
   2  * Copyright (c) 1997, 2008, Oracle and/or its affiliates. All rights reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.  Oracle designates this
   8  * particular file as subject to the "Classpath" exception as provided
   9  * by Oracle in the LICENSE file that accompanied this code.
  10  *
  11  * This code is distributed in the hope that it will be useful, but WITHOUT
  12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  14  * version 2 for more details (a copy is included in the LICENSE file that
  15  * accompanied this code).
  16  *
  17  * You should have received a copy of the GNU General Public License version
  18  * 2 along with this work; if not, write to the Free Software Foundation,
  19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  20  *
  21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  22  * or visit www.oracle.com if you need additional information or have any
  23  * questions.
  24  */
  25 
  26 /* ****************************************************************
  27  ******************************************************************
  28  ******************************************************************
  29  *** COPYRIGHT (c) Eastman Kodak Company, 1997
  30  *** As  an unpublished  work pursuant to Title 17 of the United
  31  *** States Code.  All rights reserved.
  32  ******************************************************************
  33  ******************************************************************
  34  ******************************************************************/
  35 
  36 package java.awt.image;
  37 
  38 import java.util.Arrays;
  39 
  40 /**
  41  *  This class represents image data which is stored such that each sample
  42  *  of a pixel occupies one data element of the DataBuffer.  It stores the
  43  *  N samples which make up a pixel in N separate data array elements.
  44  *  Different bands may be in different banks of the DataBuffer.
  45  *  Accessor methods are provided so that image data can be manipulated
  46  *  directly. This class can support different kinds of interleaving, e.g.
  47  *  band interleaving, scanline interleaving, and pixel interleaving.
  48  *  Pixel stride is the number of data array elements between two samples
  49  *  for the same band on the same scanline. Scanline stride is the number
  50  *  of data array elements between a given sample and the corresponding sample
  51  *  in the same column of the next scanline.  Band offsets denote the number
  52  *  of data array elements from the first data array element of the bank
  53  *  of the DataBuffer holding each band to the first sample of the band.
  54  *  The bands are numbered from 0 to N-1.  This class can represent image
  55  *  data for which each sample is an unsigned integral number which can be
  56  *  stored in 8, 16, or 32 bits (using <code>DataBuffer.TYPE_BYTE</code>,
  57  *  <code>DataBuffer.TYPE_USHORT</code>, or <code>DataBuffer.TYPE_INT</code>,
  58  *  respectively), data for which each sample is a signed integral number
  59  *  which can be stored in 16 bits (using <code>DataBuffer.TYPE_SHORT</code>),
  60  *  or data for which each sample is a signed float or double quantity
  61  *  (using <code>DataBuffer.TYPE_FLOAT</code> or
  62  *  <code>DataBuffer.TYPE_DOUBLE</code>, respectively).
  63  *  All samples of a given ComponentSampleModel
  64  *  are stored with the same precision.  All strides and offsets must be
  65  *  non-negative.  This class supports
  66  *  {@link DataBuffer#TYPE_BYTE TYPE_BYTE},
  67  *  {@link DataBuffer#TYPE_USHORT TYPE_USHORT},
  68  *  {@link DataBuffer#TYPE_SHORT TYPE_SHORT},
  69  *  {@link DataBuffer#TYPE_INT TYPE_INT},
  70  *  {@link DataBuffer#TYPE_FLOAT TYPE_FLOAT},
  71  *  {@link DataBuffer#TYPE_DOUBLE TYPE_DOUBLE},
  72  *  @see java.awt.image.PixelInterleavedSampleModel
  73  *  @see java.awt.image.BandedSampleModel
  74  */
  75 
  76 public class ComponentSampleModel extends SampleModel
  77 {
  78     /** Offsets for all bands in data array elements. */
  79     protected int bandOffsets[];
  80 
  81     /** Index for each bank storing a band of image data. */
  82     protected int[] bankIndices;
  83 
  84     /**
  85      * The number of bands in this
  86      * <code>ComponentSampleModel</code>.
  87      */
  88     protected int numBands = 1;
  89 
  90     /**
  91      * The number of banks in this
  92      * <code>ComponentSampleModel</code>.
  93      */
  94     protected int numBanks = 1;
  95 
  96     /**
  97      *  Line stride (in data array elements) of the region of image
  98      *  data described by this ComponentSampleModel.
  99      */
 100     protected int scanlineStride;
 101 
 102     /** Pixel stride (in data array elements) of the region of image
 103      *  data described by this ComponentSampleModel.
 104      */
 105     protected int pixelStride;
 106 
 107     static private native void initIDs();
 108     static {
 109         ColorModel.loadLibraries();
 110         initIDs();
 111     }
 112 
 113     /**
 114      * Constructs a ComponentSampleModel with the specified parameters.
 115      * The number of bands will be given by the length of the bandOffsets array.
 116      * All bands will be stored in the first bank of the DataBuffer.
 117      * @param dataType  the data type for storing samples
 118      * @param w         the width (in pixels) of the region of
 119      *     image data described
 120      * @param h         the height (in pixels) of the region of
 121      *     image data described
 122      * @param pixelStride the pixel stride of the region of image
 123      *     data described
 124      * @param scanlineStride the line stride of the region of image
 125      *     data described
 126      * @param bandOffsets the offsets of all bands
 127      * @throws IllegalArgumentException if <code>w</code> or
 128      *         <code>h</code> is not greater than 0
 129      * @throws IllegalArgumentException if <code>pixelStride</code>
 130      *         is less than 0
 131      * @throws IllegalArgumentException if <code>scanlineStride</code>
 132      *         is less than 0
 133      * @throws IllegalArgumentException if <code>numBands</code>
 134      *         is less than 1
 135      * @throws IllegalArgumentException if the product of <code>w</code>
 136      *         and <code>h</code> is greater than
 137      *         <code>Integer.MAX_VALUE</code>
 138      * @throws IllegalArgumentException if <code>dataType</code> is not
 139      *         one of the supported data types
 140      */
 141     public ComponentSampleModel(int dataType,
 142                                 int w, int h,
 143                                 int pixelStride,
 144                                 int scanlineStride,
 145                                 int bandOffsets[]) {
 146         super(dataType, w, h, bandOffsets.length);
 147         this.dataType = dataType;
 148         this.pixelStride = pixelStride;
 149         this.scanlineStride  = scanlineStride;
 150         this.bandOffsets = (int[])bandOffsets.clone();
 151         numBands = bandOffsets.length;
 152         if (pixelStride < 0) {
 153             throw new IllegalArgumentException("Pixel stride must be >= 0");
 154         }
 155         // TODO - bug 4296691 - remove this check
 156         if (scanlineStride < 0) {
 157             throw new IllegalArgumentException("Scanline stride must be >= 0");
 158         }
 159         if (numBands < 1) {
 160             throw new IllegalArgumentException("Must have at least one band.");
 161         }
 162         if ((dataType < DataBuffer.TYPE_BYTE) ||
 163             (dataType > DataBuffer.TYPE_DOUBLE)) {
 164             throw new IllegalArgumentException("Unsupported dataType.");
 165         }
 166         bankIndices = new int[numBands];
 167         for (int i=0; i<numBands; i++) {
 168             bankIndices[i] = 0;
 169         }
 170     }
 171 
 172 
 173     /**
 174      * Constructs a ComponentSampleModel with the specified parameters.
 175      * The number of bands will be given by the length of the bandOffsets array.
 176      * Different bands may be stored in different banks of the DataBuffer.
 177      *
 178      * @param dataType  the data type for storing samples
 179      * @param w         the width (in pixels) of the region of
 180      *     image data described
 181      * @param h         the height (in pixels) of the region of
 182      *     image data described
 183      * @param pixelStride the pixel stride of the region of image
 184      *     data described
 185      * @param scanlineStride The line stride of the region of image
 186      *     data described
 187      * @param bankIndices the bank indices of all bands
 188      * @param bandOffsets the band offsets of all bands
 189      * @throws IllegalArgumentException if <code>w</code> or
 190      *         <code>h</code> is not greater than 0
 191      * @throws IllegalArgumentException if <code>pixelStride</code>
 192      *         is less than 0
 193      * @throws IllegalArgumentException if <code>scanlineStride</code>
 194      *         is less than 0
 195      * @throws IllegalArgumentException if the length of
 196      *         <code>bankIndices</code> does not equal the length of
 197      *         <code>bankOffsets</code>
 198      * @throws IllegalArgumentException if any of the bank indices
 199      *         of <code>bandIndices</code> is less than 0
 200      * @throws IllegalArgumentException if <code>dataType</code> is not
 201      *         one of the supported data types
 202      */
 203     public ComponentSampleModel(int dataType,
 204                                 int w, int h,
 205                                 int pixelStride,
 206                                 int scanlineStride,
 207                                 int bankIndices[],
 208                                 int bandOffsets[]) {
 209         super(dataType, w, h, bandOffsets.length);
 210         this.dataType = dataType;
 211         this.pixelStride = pixelStride;
 212         this.scanlineStride  = scanlineStride;
 213         this.bandOffsets = (int[])bandOffsets.clone();
 214         this.bankIndices = (int[]) bankIndices.clone();
 215         if (pixelStride < 0) {
 216             throw new IllegalArgumentException("Pixel stride must be >= 0");
 217         }
 218         // TODO - bug 4296691 - remove this check
 219         if (scanlineStride < 0) {
 220             throw new IllegalArgumentException("Scanline stride must be >= 0");
 221         }
 222         if ((dataType < DataBuffer.TYPE_BYTE) ||
 223             (dataType > DataBuffer.TYPE_DOUBLE)) {
 224             throw new IllegalArgumentException("Unsupported dataType.");
 225         }
 226         int maxBank = bankIndices[0];
 227         if (maxBank < 0) {
 228             throw new IllegalArgumentException("Index of bank 0 is less than "+
 229                                                "0 ("+maxBank+")");
 230         }
 231         for (int i=1; i < bankIndices.length; i++) {
 232             if (bankIndices[i] > maxBank) {
 233                 maxBank = bankIndices[i];
 234             }
 235             else if (bankIndices[i] < 0) {
 236                 throw new IllegalArgumentException("Index of bank "+i+
 237                                                    " is less than 0 ("+
 238                                                    maxBank+")");
 239             }
 240         }
 241         numBanks         = maxBank+1;
 242         numBands         = bandOffsets.length;
 243         if (bandOffsets.length != bankIndices.length) {
 244             throw new IllegalArgumentException("Length of bandOffsets must "+
 245                                                "equal length of bankIndices.");
 246         }
 247     }
 248 
 249     /**
 250      * Returns the size of the data buffer (in data elements) needed
 251      * for a data buffer that matches this ComponentSampleModel.
 252      */
 253      private long getBufferSize() {
 254          int maxBandOff=bandOffsets[0];
 255          for (int i=1; i<bandOffsets.length; i++)
 256              maxBandOff = Math.max(maxBandOff,bandOffsets[i]);
 257 
 258          long size = 0;
 259          if (maxBandOff >= 0)
 260              size += maxBandOff+1;
 261          if (pixelStride > 0)
 262              size += pixelStride * (width-1);
 263          if (scanlineStride > 0)
 264              size += scanlineStride*(height-1);
 265          return size;
 266      }
 267 
 268      /**
 269       * Preserves band ordering with new step factor...
 270       */
 271     int []orderBands(int orig[], int step) {
 272         int map[] = new int[orig.length];
 273         int ret[] = new int[orig.length];
 274 
 275         for (int i=0; i<map.length; i++) map[i] = i;
 276 
 277         for (int i = 0; i < ret.length; i++) {
 278             int index = i;
 279             for (int j = i+1; j < ret.length; j++) {
 280                 if (orig[map[index]] > orig[map[j]]) {
 281                     index = j;
 282                 }
 283             }
 284             ret[map[index]] = i*step;
 285             map[index]  = map[i];
 286         }
 287         return ret;
 288     }
 289 
 290     /**
 291      * Creates a new <code>ComponentSampleModel</code> with the specified
 292      * width and height.  The new <code>SampleModel</code> will have the same
 293      * number of bands, storage data type, interleaving scheme, and
 294      * pixel stride as this <code>SampleModel</code>.
 295      * @param w the width of the resulting <code>SampleModel</code>
 296      * @param h the height of the resulting <code>SampleModel</code>
 297      * @return a new <code>ComponentSampleModel</code> with the specified size
 298      * @throws IllegalArgumentException if <code>w</code> or
 299      *         <code>h</code> is not greater than 0
 300      */
 301     public SampleModel createCompatibleSampleModel(int w, int h) {
 302         SampleModel ret=null;
 303         long size;
 304         int minBandOff=bandOffsets[0];
 305         int maxBandOff=bandOffsets[0];
 306         for (int i=1; i<bandOffsets.length; i++) {
 307             minBandOff = Math.min(minBandOff,bandOffsets[i]);
 308             maxBandOff = Math.max(maxBandOff,bandOffsets[i]);
 309         }
 310         maxBandOff -= minBandOff;
 311 
 312         int bands   = bandOffsets.length;
 313         int bandOff[];
 314         int pStride = Math.abs(pixelStride);
 315         int lStride = Math.abs(scanlineStride);
 316         int bStride = Math.abs(maxBandOff);
 317 
 318         if (pStride > lStride) {
 319             if (pStride > bStride) {
 320                 if (lStride > bStride) { // pix > line > band
 321                     bandOff = new int[bandOffsets.length];
 322                     for (int i=0; i<bands; i++)
 323                         bandOff[i] = bandOffsets[i]-minBandOff;
 324                     lStride = bStride+1;
 325                     pStride = lStride*h;
 326                 } else { // pix > band > line
 327                     bandOff = orderBands(bandOffsets,lStride*h);
 328                     pStride = bands*lStride*h;
 329                 }
 330             } else { // band > pix > line
 331                 pStride = lStride*h;
 332                 bandOff = orderBands(bandOffsets,pStride*w);
 333             }
 334         } else {
 335             if (pStride > bStride) { // line > pix > band
 336                 bandOff = new int[bandOffsets.length];
 337                 for (int i=0; i<bands; i++)
 338                     bandOff[i] = bandOffsets[i]-minBandOff;
 339                 pStride = bStride+1;
 340                 lStride = pStride*w;
 341             } else {
 342                 if (lStride > bStride) { // line > band > pix
 343                     bandOff = orderBands(bandOffsets,pStride*w);
 344                     lStride = bands*pStride*w;
 345                 } else { // band > line > pix
 346                     lStride = pStride*w;
 347                     bandOff = orderBands(bandOffsets,lStride*h);
 348                 }
 349             }
 350         }
 351 
 352         // make sure we make room for negative offsets...
 353         int base = 0;
 354         if (scanlineStride < 0) {
 355             base += lStride*h;
 356             lStride *= -1;
 357         }
 358         if (pixelStride    < 0) {
 359             base += pStride*w;
 360             pStride *= -1;
 361         }
 362 
 363         for (int i=0; i<bands; i++)
 364             bandOff[i] += base;
 365         return new ComponentSampleModel(dataType, w, h, pStride,
 366                                         lStride, bankIndices, bandOff);
 367     }
 368 
 369     /**
 370      * Creates a new ComponentSampleModel with a subset of the bands
 371      * of this ComponentSampleModel.  The new ComponentSampleModel can be
 372      * used with any DataBuffer that the existing ComponentSampleModel
 373      * can be used with.  The new ComponentSampleModel/DataBuffer
 374      * combination will represent an image with a subset of the bands
 375      * of the original ComponentSampleModel/DataBuffer combination.
 376      * @param bands a subset of bands from this
 377      *              <code>ComponentSampleModel</code>
 378      * @return a <code>ComponentSampleModel</code> created with a subset
 379      *          of bands from this <code>ComponentSampleModel</code>.
 380      */
 381     public SampleModel createSubsetSampleModel(int bands[]) {
 382        if (bands.length > bankIndices.length)
 383             throw new RasterFormatException("There are only " +
 384                                             bankIndices.length +
 385                                             " bands");
 386         int newBankIndices[] = new int[bands.length];
 387         int newBandOffsets[] = new int[bands.length];
 388 
 389         for (int i=0; i<bands.length; i++) {
 390             newBankIndices[i] = bankIndices[bands[i]];
 391             newBandOffsets[i] = bandOffsets[bands[i]];
 392         }
 393 
 394         return new ComponentSampleModel(this.dataType, width, height,
 395                                         this.pixelStride,
 396                                         this.scanlineStride,
 397                                         newBankIndices, newBandOffsets);
 398     }
 399 
 400     /**
 401      * Creates a <code>DataBuffer</code> that corresponds to this
 402      * <code>ComponentSampleModel</code>.
 403      * The <code>DataBuffer</code> object's data type, number of banks,
 404      * and size are be consistent with this <code>ComponentSampleModel</code>.
 405      * @return a <code>DataBuffer</code> whose data type, number of banks
 406      *         and size are consistent with this
 407      *         <code>ComponentSampleModel</code>.
 408      */
 409     public DataBuffer createDataBuffer() {
 410         DataBuffer dataBuffer = null;
 411 
 412         int size = (int)getBufferSize();
 413         switch (dataType) {
 414         case DataBuffer.TYPE_BYTE:
 415             dataBuffer = new DataBufferByte(size, numBanks);
 416             break;
 417         case DataBuffer.TYPE_USHORT:
 418             dataBuffer = new DataBufferUShort(size, numBanks);
 419             break;
 420         case DataBuffer.TYPE_SHORT:
 421             dataBuffer = new DataBufferShort(size, numBanks);
 422             break;
 423         case DataBuffer.TYPE_INT:
 424             dataBuffer = new DataBufferInt(size, numBanks);
 425             break;
 426         case DataBuffer.TYPE_FLOAT:
 427             dataBuffer = new DataBufferFloat(size, numBanks);
 428             break;
 429         case DataBuffer.TYPE_DOUBLE:
 430             dataBuffer = new DataBufferDouble(size, numBanks);
 431             break;
 432         }
 433 
 434         return dataBuffer;
 435     }
 436 
 437 
 438     /** Gets the offset for the first band of pixel (x,y).
 439      *  A sample of the first band can be retrieved from a
 440      * <code>DataBuffer</code>
 441      *  <code>data</code> with a <code>ComponentSampleModel</code>
 442      * <code>csm</code> as
 443      * <pre>
 444      *        data.getElem(csm.getOffset(x, y));
 445      * </pre>
 446      * @param x the X location of the pixel
 447      * @param y the Y location of the pixel
 448      * @return the offset for the first band of the specified pixel.
 449      */
 450     public int getOffset(int x, int y) {
 451         int offset = y*scanlineStride + x*pixelStride + bandOffsets[0];
 452         return offset;
 453     }
 454 
 455     /** Gets the offset for band b of pixel (x,y).
 456      *  A sample of band <code>b</code> can be retrieved from a
 457      *  <code>DataBuffer</code> <code>data</code>
 458      *  with a <code>ComponentSampleModel</code> <code>csm</code> as
 459      * <pre>
 460      *       data.getElem(csm.getOffset(x, y, b));
 461      * </pre>
 462      * @param x the X location of the specified pixel
 463      * @param y the Y location of the specified pixel
 464      * @param b the specified band
 465      * @return the offset for the specified band of the specified pixel.
 466      */
 467     public int getOffset(int x, int y, int b) {
 468         int offset = y*scanlineStride + x*pixelStride + bandOffsets[b];
 469         return offset;
 470     }
 471 
 472     /** Returns the number of bits per sample for all bands.
 473      *  @return an array containing the number of bits per sample
 474      *          for all bands, where each element in the array
 475      *          represents a band.
 476      */
 477     public final int[] getSampleSize() {
 478         int sampleSize[] = new int [numBands];
 479         int sizeInBits = getSampleSize(0);
 480 
 481         for (int i=0; i<numBands; i++)
 482             sampleSize[i] = sizeInBits;
 483 
 484         return sampleSize;
 485     }
 486 
 487     /** Returns the number of bits per sample for the specified band.
 488      *  @param band the specified band
 489      *  @return the number of bits per sample for the specified band.
 490      */
 491     public final int getSampleSize(int band) {
 492         return DataBuffer.getDataTypeSize(dataType);
 493     }
 494 
 495     /** Returns the bank indices for all bands.
 496      *  @return the bank indices for all bands.
 497      */
 498     public final int [] getBankIndices() {
 499         return (int[]) bankIndices.clone();
 500     }
 501 
 502     /** Returns the band offset for all bands.
 503      *  @return the band offsets for all bands.
 504      */
 505     public final int [] getBandOffsets() {
 506         return (int[])bandOffsets.clone();
 507     }
 508 
 509     /** Returns the scanline stride of this ComponentSampleModel.
 510      *  @return the scanline stride of this <code>ComponentSampleModel</code>.
 511      */
 512     public final int getScanlineStride() {
 513         return scanlineStride;
 514     }
 515 
 516     /** Returns the pixel stride of this ComponentSampleModel.
 517      *  @return the pixel stride of this <code>ComponentSampleModel</code>.
 518      */
 519     public final int getPixelStride() {
 520         return pixelStride;
 521     }
 522 
 523     /**
 524      * Returns the number of data elements needed to transfer a pixel
 525      * with the
 526      * {@link #getDataElements(int, int, Object, DataBuffer) } and
 527      * {@link #setDataElements(int, int, Object, DataBuffer) }
 528      * methods.
 529      * For a <code>ComponentSampleModel</code>, this is identical to the
 530      * number of bands.
 531      * @return the number of data elements needed to transfer a pixel with
 532      *         the <code>getDataElements</code> and
 533      *         <code>setDataElements</code> methods.
 534      * @see java.awt.image.SampleModel#getNumDataElements
 535      * @see #getNumBands
 536      */
 537     public final int getNumDataElements() {
 538         return getNumBands();
 539     }
 540 
 541     /**
 542      * Returns data for a single pixel in a primitive array of type
 543      * <code>TransferType</code>.  For a <code>ComponentSampleModel</code>,
 544      * this is the same as the data type, and samples are returned
 545      * one per array element.  Generally, <code>obj</code> should
 546      * be passed in as <code>null</code>, so that the <code>Object</code>
 547      * is created automatically and is the right primitive data type.
 548      * <p>
 549      * The following code illustrates transferring data for one pixel from
 550      * <code>DataBuffer</code> <code>db1</code>, whose storage layout is
 551      * described by <code>ComponentSampleModel</code> <code>csm1</code>,
 552      * to <code>DataBuffer</code> <code>db2</code>, whose storage layout
 553      * is described by <code>ComponentSampleModel</code> <code>csm2</code>.
 554      * The transfer is usually more efficient than using
 555      * <code>getPixel</code> and <code>setPixel</code>.
 556      * <pre>
 557      *       ComponentSampleModel csm1, csm2;
 558      *       DataBufferInt db1, db2;
 559      *       csm2.setDataElements(x, y,
 560      *                            csm1.getDataElements(x, y, null, db1), db2);
 561      * </pre>
 562      *
 563      * Using <code>getDataElements</code> and <code>setDataElements</code>
 564      * to transfer between two <code>DataBuffer/SampleModel</code>
 565      * pairs is legitimate if the <code>SampleModel</code> objects have
 566      * the same number of bands, corresponding bands have the same number of
 567      * bits per sample, and the <code>TransferType</code>s are the same.
 568      * <p>
 569      * If <code>obj</code> is not <code>null</code>, it should be a
 570      * primitive array of type <code>TransferType</code>.
 571      * Otherwise, a <code>ClassCastException</code> is thrown.  An
 572      * <code>ArrayIndexOutOfBoundsException</code> might be thrown if the
 573      * coordinates are not in bounds, or if <code>obj</code> is not
 574      * <code>null</code> and is not large enough to hold
 575      * the pixel data.
 576      *
 577      * @param x         the X coordinate of the pixel location
 578      * @param y         the Y coordinate of the pixel location
 579      * @param obj       if non-<code>null</code>, a primitive array
 580      *                  in which to return the pixel data
 581      * @param data      the <code>DataBuffer</code> containing the image data
 582      * @return the data of the specified pixel
 583      * @see #setDataElements(int, int, Object, DataBuffer)
 584      *
 585      * @throws NullPointerException if data is null.
 586      * @throws ArrayIndexOutOfBoundsException if the coordinates are
 587      * not in bounds, or if obj is too small to hold the ouput.
 588      */
 589     public Object getDataElements(int x, int y, Object obj, DataBuffer data) {
 590         if ((x < 0) || (y < 0) || (x >= width) || (y >= height)) {
 591             throw new ArrayIndexOutOfBoundsException
 592                 ("Coordinate out of bounds!");
 593         }
 594 
 595         int type = getTransferType();
 596         int numDataElems = getNumDataElements();
 597         int pixelOffset = y*scanlineStride + x*pixelStride;
 598 
 599         switch(type) {
 600 
 601         case DataBuffer.TYPE_BYTE:
 602 
 603             byte[] bdata;
 604 
 605             if (obj == null)
 606                 bdata = new byte[numDataElems];
 607             else
 608                 bdata = (byte[])obj;
 609 
 610             for (int i=0; i<numDataElems; i++) {
 611                 bdata[i] = (byte)data.getElem(bankIndices[i],
 612                                               pixelOffset + bandOffsets[i]);
 613             }
 614 
 615             obj = (Object)bdata;
 616             break;
 617 
 618         case DataBuffer.TYPE_USHORT:
 619         case DataBuffer.TYPE_SHORT:
 620 
 621             short[] sdata;
 622 
 623             if (obj == null)
 624                 sdata = new short[numDataElems];
 625             else
 626                 sdata = (short[])obj;
 627 
 628             for (int i=0; i<numDataElems; i++) {
 629                 sdata[i] = (short)data.getElem(bankIndices[i],
 630                                                pixelOffset + bandOffsets[i]);
 631             }
 632 
 633             obj = (Object)sdata;
 634             break;
 635 
 636         case DataBuffer.TYPE_INT:
 637 
 638             int[] idata;
 639 
 640             if (obj == null)
 641                 idata = new int[numDataElems];
 642             else
 643                 idata = (int[])obj;
 644 
 645             for (int i=0; i<numDataElems; i++) {
 646                 idata[i] = data.getElem(bankIndices[i],
 647                                         pixelOffset + bandOffsets[i]);
 648             }
 649 
 650             obj = (Object)idata;
 651             break;
 652 
 653         case DataBuffer.TYPE_FLOAT:
 654 
 655             float[] fdata;
 656 
 657             if (obj == null)
 658                 fdata = new float[numDataElems];
 659             else
 660                 fdata = (float[])obj;
 661 
 662             for (int i=0; i<numDataElems; i++) {
 663                 fdata[i] = data.getElemFloat(bankIndices[i],
 664                                              pixelOffset + bandOffsets[i]);
 665             }
 666 
 667             obj = (Object)fdata;
 668             break;
 669 
 670         case DataBuffer.TYPE_DOUBLE:
 671 
 672             double[] ddata;
 673 
 674             if (obj == null)
 675                 ddata = new double[numDataElems];
 676             else
 677                 ddata = (double[])obj;
 678 
 679             for (int i=0; i<numDataElems; i++) {
 680                 ddata[i] = data.getElemDouble(bankIndices[i],
 681                                               pixelOffset + bandOffsets[i]);
 682             }
 683 
 684             obj = (Object)ddata;
 685             break;
 686         }
 687 
 688         return obj;
 689     }
 690 
 691     /**
 692      * Returns all samples for the specified pixel in an int array,
 693      * one sample per array element.
 694      * An <code>ArrayIndexOutOfBoundsException</code> might be thrown if
 695      * the coordinates are not in bounds.
 696      * @param x         the X coordinate of the pixel location
 697      * @param y         the Y coordinate of the pixel location
 698      * @param iArray    If non-null, returns the samples in this array
 699      * @param data      The DataBuffer containing the image data
 700      * @return the samples of the specified pixel.
 701      * @see #setPixel(int, int, int[], DataBuffer)
 702      *
 703      * @throws NullPointerException if data is null.
 704      * @throws ArrayIndexOutOfBoundsException if the coordinates are
 705      * not in bounds, or if iArray is too small to hold the output.
 706      */
 707     public int[] getPixel(int x, int y, int iArray[], DataBuffer data) {
 708         if ((x < 0) || (y < 0) || (x >= width) || (y >= height)) {
 709             throw new ArrayIndexOutOfBoundsException
 710                 ("Coordinate out of bounds!");
 711         }
 712         int pixels[];
 713         if (iArray != null) {
 714            pixels = iArray;
 715         } else {
 716            pixels = new int [numBands];
 717         }
 718         int pixelOffset = y*scanlineStride + x*pixelStride;
 719         for (int i=0; i<numBands; i++) {
 720             pixels[i] = data.getElem(bankIndices[i],
 721                                      pixelOffset + bandOffsets[i]);
 722         }
 723         return pixels;
 724     }
 725 
 726     /**
 727      * Returns all samples for the specified rectangle of pixels in
 728      * an int array, one sample per array element.
 729      * An <code>ArrayIndexOutOfBoundsException</code> might be thrown if
 730      * the coordinates are not in bounds.
 731      * @param x         The X coordinate of the upper left pixel location
 732      * @param y         The Y coordinate of the upper left pixel location
 733      * @param w         The width of the pixel rectangle
 734      * @param h         The height of the pixel rectangle
 735      * @param iArray    If non-null, returns the samples in this array
 736      * @param data      The DataBuffer containing the image data
 737      * @return the samples of the pixels within the specified region.
 738      * @see #setPixels(int, int, int, int, int[], DataBuffer)
 739      */
 740     public int[] getPixels(int x, int y, int w, int h,
 741                            int iArray[], DataBuffer data) {
 742         int x1 = x + w;
 743         int y1 = y + h;
 744 
 745         if (x < 0 || x >= width || w > width || x1 < 0 || x1 > width ||
 746             y < 0 || y >= height || y > height || y1 < 0 || y1 >  height)
 747         {
 748             throw new ArrayIndexOutOfBoundsException
 749                 ("Coordinate out of bounds!");
 750         }
 751         int pixels[];
 752         if (iArray != null) {
 753            pixels = iArray;
 754         } else {
 755            pixels = new int [w*h*numBands];
 756         }
 757         int lineOffset = y*scanlineStride + x*pixelStride;
 758         int srcOffset = 0;
 759 
 760         for (int i = 0; i < h; i++) {
 761            int pixelOffset = lineOffset;
 762            for (int j = 0; j < w; j++) {
 763               for (int k=0; k < numBands; k++) {
 764                  pixels[srcOffset++] =
 765                     data.getElem(bankIndices[k], pixelOffset + bandOffsets[k]);
 766               }
 767               pixelOffset += pixelStride;
 768            }
 769            lineOffset += scanlineStride;
 770         }
 771         return pixels;
 772     }
 773 
 774     /**
 775      * Returns as int the sample in a specified band for the pixel
 776      * located at (x,y).
 777      * An <code>ArrayIndexOutOfBoundsException</code> might be thrown if
 778      * the coordinates are not in bounds.
 779      * @param x         the X coordinate of the pixel location
 780      * @param y         the Y coordinate of the pixel location
 781      * @param b         the band to return
 782      * @param data      the <code>DataBuffer</code> containing the image data
 783      * @return the sample in a specified band for the specified pixel
 784      * @see #setSample(int, int, int, int, DataBuffer)
 785      */
 786     public int getSample(int x, int y, int b, DataBuffer data) {
 787         // Bounds check for 'b' will be performed automatically
 788         if ((x < 0) || (y < 0) || (x >= width) || (y >= height)) {
 789             throw new ArrayIndexOutOfBoundsException
 790                 ("Coordinate out of bounds!");
 791         }
 792         int sample = data.getElem(bankIndices[b],
 793                                   y*scanlineStride + x*pixelStride +
 794                                   bandOffsets[b]);
 795         return sample;
 796     }
 797 
 798     /**
 799      * Returns the sample in a specified band
 800      * for the pixel located at (x,y) as a float.
 801      * An <code>ArrayIndexOutOfBoundsException</code> might be
 802      * thrown if the coordinates are not in bounds.
 803      * @param x         The X coordinate of the pixel location
 804      * @param y         The Y coordinate of the pixel location
 805      * @param b         The band to return
 806      * @param data      The DataBuffer containing the image data
 807      * @return a float value representing the sample in the specified
 808      * band for the specified pixel.
 809      */
 810     public float getSampleFloat(int x, int y, int b, DataBuffer data) {
 811         // Bounds check for 'b' will be performed automatically
 812         if ((x < 0) || (y < 0) || (x >= width) || (y >= height)) {
 813             throw new ArrayIndexOutOfBoundsException
 814                 ("Coordinate out of bounds!");
 815         }
 816 
 817         float sample = data.getElemFloat(bankIndices[b],
 818                                          y*scanlineStride + x*pixelStride +
 819                                          bandOffsets[b]);
 820         return sample;
 821     }
 822 
 823     /**
 824      * Returns the sample in a specified band
 825      * for a pixel located at (x,y) as a double.
 826      * An <code>ArrayIndexOutOfBoundsException</code> might be
 827      * thrown if the coordinates are not in bounds.
 828      * @param x         The X coordinate of the pixel location
 829      * @param y         The Y coordinate of the pixel location
 830      * @param b         The band to return
 831      * @param data      The DataBuffer containing the image data
 832      * @return a double value representing the sample in the specified
 833      * band for the specified pixel.
 834      */
 835     public double getSampleDouble(int x, int y, int b, DataBuffer data) {
 836         // Bounds check for 'b' will be performed automatically
 837         if ((x < 0) || (y < 0) || (x >= width) || (y >= height)) {
 838             throw new ArrayIndexOutOfBoundsException
 839                 ("Coordinate out of bounds!");
 840         }
 841 
 842         double sample = data.getElemDouble(bankIndices[b],
 843                                            y*scanlineStride + x*pixelStride +
 844                                            bandOffsets[b]);
 845         return sample;
 846     }
 847 
 848     /**
 849      * Returns the samples in a specified band for the specified rectangle
 850      * of pixels in an int array, one sample per data array element.
 851      * An <code>ArrayIndexOutOfBoundsException</code> might be thrown if
 852      * the coordinates are not in bounds.
 853      * @param x         The X coordinate of the upper left pixel location
 854      * @param y         The Y coordinate of the upper left pixel location
 855      * @param w         the width of the pixel rectangle
 856      * @param h         the height of the pixel rectangle
 857      * @param b         the band to return
 858      * @param iArray    if non-<code>null</code>, returns the samples
 859      *                  in this array
 860      * @param data      the <code>DataBuffer</code> containing the image data
 861      * @return the samples in the specified band of the specified pixel
 862      * @see #setSamples(int, int, int, int, int, int[], DataBuffer)
 863      */
 864     public int[] getSamples(int x, int y, int w, int h, int b,
 865                             int iArray[], DataBuffer data) {
 866         // Bounds check for 'b' will be performed automatically
 867         if ((x < 0) || (y < 0) || (x + w > width) || (y + h > height)) {
 868             throw new ArrayIndexOutOfBoundsException
 869                 ("Coordinate out of bounds!");
 870         }
 871         int samples[];
 872         if (iArray != null) {
 873            samples = iArray;
 874         } else {
 875            samples = new int [w*h];
 876         }
 877         int lineOffset = y*scanlineStride + x*pixelStride +  bandOffsets[b];
 878         int srcOffset = 0;
 879 
 880         for (int i = 0; i < h; i++) {
 881            int sampleOffset = lineOffset;
 882            for (int j = 0; j < w; j++) {
 883               samples[srcOffset++] = data.getElem(bankIndices[b],
 884                                                   sampleOffset);
 885               sampleOffset += pixelStride;
 886            }
 887            lineOffset += scanlineStride;
 888         }
 889         return samples;
 890     }
 891 
 892     /**
 893      * Sets the data for a single pixel in the specified
 894      * <code>DataBuffer</code> from a primitive array of type
 895      * <code>TransferType</code>.  For a <code>ComponentSampleModel</code>,
 896      * this is the same as the data type, and samples are transferred
 897      * one per array element.
 898      * <p>
 899      * The following code illustrates transferring data for one pixel from
 900      * <code>DataBuffer</code> <code>db1</code>, whose storage layout is
 901      * described by <code>ComponentSampleModel</code> <code>csm1</code>,
 902      * to <code>DataBuffer</code> <code>db2</code>, whose storage layout
 903      * is described by <code>ComponentSampleModel</code> <code>csm2</code>.
 904      * The transfer is usually more efficient than using
 905      * <code>getPixel</code> and <code>setPixel</code>.
 906      * <pre>
 907      *       ComponentSampleModel csm1, csm2;
 908      *       DataBufferInt db1, db2;
 909      *       csm2.setDataElements(x, y, csm1.getDataElements(x, y, null, db1),
 910      *                            db2);
 911      * </pre>
 912      * Using <code>getDataElements</code> and <code>setDataElements</code>
 913      * to transfer between two <code>DataBuffer/SampleModel</code> pairs
 914      * is legitimate if the <code>SampleModel</code> objects have
 915      * the same number of bands, corresponding bands have the same number of
 916      * bits per sample, and the <code>TransferType</code>s are the same.
 917      * <p>
 918      * A <code>ClassCastException</code> is thrown if <code>obj</code> is not
 919      * a primitive array of type <code>TransferType</code>.
 920      * An <code>ArrayIndexOutOfBoundsException</code> might be thrown if
 921      * the coordinates are not in bounds, or if <code>obj</code> is not large
 922      * enough to hold the pixel data.
 923      * @param x         the X coordinate of the pixel location
 924      * @param y         the Y coordinate of the pixel location
 925      * @param obj       a primitive array containing pixel data
 926      * @param data      the DataBuffer containing the image data
 927      * @see #getDataElements(int, int, Object, DataBuffer)
 928      */
 929     public void setDataElements(int x, int y, Object obj, DataBuffer data) {
 930         if ((x < 0) || (y < 0) || (x >= width) || (y >= height)) {
 931             throw new ArrayIndexOutOfBoundsException
 932                 ("Coordinate out of bounds!");
 933         }
 934 
 935         int type = getTransferType();
 936         int numDataElems = getNumDataElements();
 937         int pixelOffset = y*scanlineStride + x*pixelStride;
 938 
 939         switch(type) {
 940 
 941         case DataBuffer.TYPE_BYTE:
 942 
 943             byte[] barray = (byte[])obj;
 944 
 945             for (int i=0; i<numDataElems; i++) {
 946                 data.setElem(bankIndices[i], pixelOffset + bandOffsets[i],
 947                            ((int)barray[i])&0xff);
 948             }
 949             break;
 950 
 951         case DataBuffer.TYPE_USHORT:
 952         case DataBuffer.TYPE_SHORT:
 953 
 954             short[] sarray = (short[])obj;
 955 
 956             for (int i=0; i<numDataElems; i++) {
 957                 data.setElem(bankIndices[i], pixelOffset + bandOffsets[i],
 958                            ((int)sarray[i])&0xffff);
 959             }
 960             break;
 961 
 962         case DataBuffer.TYPE_INT:
 963 
 964             int[] iarray = (int[])obj;
 965 
 966             for (int i=0; i<numDataElems; i++) {
 967                 data.setElem(bankIndices[i],
 968                              pixelOffset + bandOffsets[i], iarray[i]);
 969             }
 970             break;
 971 
 972         case DataBuffer.TYPE_FLOAT:
 973 
 974             float[] farray = (float[])obj;
 975 
 976             for (int i=0; i<numDataElems; i++) {
 977                 data.setElemFloat(bankIndices[i],
 978                              pixelOffset + bandOffsets[i], farray[i]);
 979             }
 980             break;
 981 
 982         case DataBuffer.TYPE_DOUBLE:
 983 
 984             double[] darray = (double[])obj;
 985 
 986             for (int i=0; i<numDataElems; i++) {
 987                 data.setElemDouble(bankIndices[i],
 988                              pixelOffset + bandOffsets[i], darray[i]);
 989             }
 990             break;
 991 
 992         }
 993     }
 994 
 995     /**
 996      * Sets a pixel in the <code>DataBuffer</code> using an int array of
 997      * samples for input.  An <code>ArrayIndexOutOfBoundsException</code>
 998      * might be thrown if the coordinates are
 999      * not in bounds.
1000      * @param x         The X coordinate of the pixel location
1001      * @param y         The Y coordinate of the pixel location
1002      * @param iArray    The input samples in an int array
1003      * @param data      The DataBuffer containing the image data
1004      * @see #getPixel(int, int, int[], DataBuffer)
1005      */
1006     public void setPixel(int x, int y, int iArray[], DataBuffer data) {
1007         if ((x < 0) || (y < 0) || (x >= width) || (y >= height)) {
1008             throw new ArrayIndexOutOfBoundsException
1009                 ("Coordinate out of bounds!");
1010         }
1011        int pixelOffset = y*scanlineStride + x*pixelStride;
1012        for (int i=0; i<numBands; i++) {
1013            data.setElem(bankIndices[i],
1014                         pixelOffset + bandOffsets[i],iArray[i]);
1015        }
1016     }
1017 
1018     /**
1019      * Sets all samples for a rectangle of pixels from an int array containing
1020      * one sample per array element.
1021      * An <code>ArrayIndexOutOfBoundsException</code> might be thrown if the
1022      * coordinates are not in bounds.
1023      * @param x         The X coordinate of the upper left pixel location
1024      * @param y         The Y coordinate of the upper left pixel location
1025      * @param w         The width of the pixel rectangle
1026      * @param h         The height of the pixel rectangle
1027      * @param iArray    The input samples in an int array
1028      * @param data      The DataBuffer containing the image data
1029      * @see #getPixels(int, int, int, int, int[], DataBuffer)
1030      */
1031     public void setPixels(int x, int y, int w, int h,
1032                           int iArray[], DataBuffer data) {
1033         int x1 = x + w;
1034         int y1 = y + h;
1035 
1036         if (x < 0 || x >= width || w > width || x1 < 0 || x1 > width ||
1037             y < 0 || y >= height || h > height || y1 < 0 || y1 >  height)
1038         {
1039             throw new ArrayIndexOutOfBoundsException
1040                 ("Coordinate out of bounds!");
1041         }
1042 
1043         int lineOffset = y*scanlineStride + x*pixelStride;
1044         int srcOffset = 0;
1045 
1046         for (int i = 0; i < h; i++) {
1047            int pixelOffset = lineOffset;
1048            for (int j = 0; j < w; j++) {
1049               for (int k=0; k < numBands; k++) {
1050                  data.setElem(bankIndices[k], pixelOffset + bandOffsets[k],
1051                               iArray[srcOffset++]);
1052               }
1053               pixelOffset += pixelStride;
1054            }
1055            lineOffset += scanlineStride;
1056         }
1057     }
1058 
1059     /**
1060      * Sets a sample in the specified band for the pixel located at (x,y)
1061      * in the <code>DataBuffer</code> using an int for input.
1062      * An <code>ArrayIndexOutOfBoundsException</code> might be thrown if the
1063      * coordinates are not in bounds.
1064      * @param x         The X coordinate of the pixel location
1065      * @param y         The Y coordinate of the pixel location
1066      * @param b         the band to set
1067      * @param s         the input sample as an int
1068      * @param data      the DataBuffer containing the image data
1069      * @see #getSample(int, int, int, DataBuffer)
1070      */
1071     public void setSample(int x, int y, int b, int s,
1072                           DataBuffer data) {
1073         // Bounds check for 'b' will be performed automatically
1074         if ((x < 0) || (y < 0) || (x >= width) || (y >= height)) {
1075             throw new ArrayIndexOutOfBoundsException
1076                 ("Coordinate out of bounds!");
1077         }
1078         data.setElem(bankIndices[b],
1079                      y*scanlineStride + x*pixelStride + bandOffsets[b], s);
1080     }
1081 
1082     /**
1083      * Sets a sample in the specified band for the pixel located at (x,y)
1084      * in the <code>DataBuffer</code> using a float for input.
1085      * An <code>ArrayIndexOutOfBoundsException</code> might be thrown if
1086      * the coordinates are not in bounds.
1087      * @param x         The X coordinate of the pixel location
1088      * @param y         The Y coordinate of the pixel location
1089      * @param b         The band to set
1090      * @param s         The input sample as a float
1091      * @param data      The DataBuffer containing the image data
1092      * @see #getSample(int, int, int, DataBuffer)
1093      */
1094     public void setSample(int x, int y, int b,
1095                           float s ,
1096                           DataBuffer data) {
1097         // Bounds check for 'b' will be performed automatically
1098         if ((x < 0) || (y < 0) || (x >= width) || (y >= height)) {
1099             throw new ArrayIndexOutOfBoundsException
1100                 ("Coordinate out of bounds!");
1101         }
1102         data.setElemFloat(bankIndices[b],
1103                           y*scanlineStride + x*pixelStride + bandOffsets[b],
1104                           s);
1105     }
1106 
1107     /**
1108      * Sets a sample in the specified band for the pixel located at (x,y)
1109      * in the <code>DataBuffer</code> using a double for input.
1110      * An <code>ArrayIndexOutOfBoundsException</code> might be thrown if
1111      * the coordinates are not in bounds.
1112      * @param x         The X coordinate of the pixel location
1113      * @param y         The Y coordinate of the pixel location
1114      * @param b         The band to set
1115      * @param s         The input sample as a double
1116      * @param data      The DataBuffer containing the image data
1117      * @see #getSample(int, int, int, DataBuffer)
1118      */
1119     public void setSample(int x, int y, int b,
1120                           double s,
1121                           DataBuffer data) {
1122         // Bounds check for 'b' will be performed automatically
1123         if ((x < 0) || (y < 0) || (x >= width) || (y >= height)) {
1124             throw new ArrayIndexOutOfBoundsException
1125                 ("Coordinate out of bounds!");
1126         }
1127         data.setElemDouble(bankIndices[b],
1128                           y*scanlineStride + x*pixelStride + bandOffsets[b],
1129                           s);
1130     }
1131 
1132     /**
1133      * Sets the samples in the specified band for the specified rectangle
1134      * of pixels from an int array containing one sample per data array element.
1135      * An <code>ArrayIndexOutOfBoundsException</code> might be thrown if the
1136      * coordinates are not in bounds.
1137      * @param x         The X coordinate of the upper left pixel location
1138      * @param y         The Y coordinate of the upper left pixel location
1139      * @param w         The width of the pixel rectangle
1140      * @param h         The height of the pixel rectangle
1141      * @param b         The band to set
1142      * @param iArray    The input samples in an int array
1143      * @param data      The DataBuffer containing the image data
1144      * @see #getSamples(int, int, int, int, int, int[], DataBuffer)
1145      */
1146     public void setSamples(int x, int y, int w, int h, int b,
1147                            int iArray[], DataBuffer data) {
1148         // Bounds check for 'b' will be performed automatically
1149         if ((x < 0) || (y < 0) || (x + w > width) || (y + h > height)) {
1150             throw new ArrayIndexOutOfBoundsException
1151                 ("Coordinate out of bounds!");
1152         }
1153         int lineOffset = y*scanlineStride + x*pixelStride + bandOffsets[b];
1154         int srcOffset = 0;
1155 
1156         for (int i = 0; i < h; i++) {
1157            int sampleOffset = lineOffset;
1158            for (int j = 0; j < w; j++) {
1159               data.setElem(bankIndices[b], sampleOffset, iArray[srcOffset++]);
1160               sampleOffset += pixelStride;
1161            }
1162            lineOffset += scanlineStride;
1163         }
1164     }
1165 
1166     public boolean equals(Object o) {
1167         if ((o == null) || !(o instanceof ComponentSampleModel)) {
1168             return false;
1169         }
1170 
1171         ComponentSampleModel that = (ComponentSampleModel)o;
1172         return this.width == that.width &&
1173             this.height == that.height &&
1174             this.numBands == that.numBands &&
1175             this.dataType == that.dataType &&
1176             Arrays.equals(this.bandOffsets, that.bandOffsets) &&
1177             Arrays.equals(this.bankIndices, that.bankIndices) &&
1178             this.numBands == that.numBands &&
1179             this.numBanks == that.numBanks &&
1180             this.scanlineStride == that.scanlineStride &&
1181             this.pixelStride == that.pixelStride;
1182     }
1183 
1184     // If we implement equals() we must also implement hashCode
1185     public int hashCode() {
1186         int hash = 0;
1187         hash = width;
1188         hash <<= 8;
1189         hash ^= height;
1190         hash <<= 8;
1191         hash ^= numBands;
1192         hash <<= 8;
1193         hash ^= dataType;
1194         hash <<= 8;
1195         for (int i = 0; i < bandOffsets.length; i++) {
1196             hash ^= bandOffsets[i];
1197             hash <<= 8;
1198         }
1199         for (int i = 0; i < bankIndices.length; i++) {
1200             hash ^= bankIndices[i];
1201             hash <<= 8;
1202         }
1203         hash ^= numBands;
1204         hash <<= 8;
1205         hash ^= numBanks;
1206         hash <<= 8;
1207         hash ^= scanlineStride;
1208         hash <<= 8;
1209         hash ^= pixelStride;
1210         return hash;
1211     }
1212 }