Source for java.awt.image.MultiPixelPackedSampleModel

   1: /* Copyright (C) 2004  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: /**
  42:  * MultiPixelPackedSampleModel provides a single band model that supports
  43:  * multiple pixels in a single unit.  Pixels have 2^n bits and 2^k pixels fit
  44:  * per data element.
  45:  *
  46:  * @author Jerry Quinn (jlquinn@optonline.net)
  47:  */
  48: public class MultiPixelPackedSampleModel extends SampleModel
  49: {
  50:   private int scanlineStride;
  51:   private int[] bitMasks;
  52:   private int[] bitOffsets;
  53:   private int[] sampleSize;
  54:   private int dataBitOffset;
  55:   private int elemBits;
  56:   private int numberOfBits;
  57:   private int numElems;
  58: 
  59:   public MultiPixelPackedSampleModel(int dataType, int w, int h,
  60:                      int numberOfBits)
  61:   {
  62:     this(dataType, w, h, numberOfBits, 0, 0);
  63:   }
  64: 
  65:   public MultiPixelPackedSampleModel(int dataType, int w, int h,
  66:                      int numberOfBits, int scanlineStride,
  67:                      int dataBitOffset)
  68:   {
  69:     super(dataType, w, h, 1);
  70: 
  71:     switch (dataType)
  72:       {
  73:       case DataBuffer.TYPE_BYTE:
  74:     elemBits = 8;
  75:     break;
  76:       case DataBuffer.TYPE_USHORT:
  77:     elemBits = 16;
  78:     break;
  79:       case DataBuffer.TYPE_INT:
  80:     elemBits = 32;
  81:     break;
  82:       default:
  83:     throw new IllegalArgumentException("MultiPixelPackedSampleModel"
  84:                        + " unsupported dataType");
  85:       }
  86: 
  87:     this.dataBitOffset = dataBitOffset;
  88: 
  89:     this.numberOfBits = numberOfBits;
  90:     if (numberOfBits > elemBits)
  91:       throw new RasterFormatException("MultiPixelPackedSampleModel pixel size"
  92:                       + " larger than dataType");
  93:     switch (numberOfBits)
  94:       {
  95:       case 1: case 2: case 4: case 8: case 16: case 32: break;
  96:       default:
  97:     throw new RasterFormatException("MultiPixelPackedSampleModel pixel"
  98:                     + " size not 2^n bits");
  99:       }
 100:     numElems = elemBits / numberOfBits;
 101: 
 102:     // Compute scan line large enough for w pixels.
 103:     if (scanlineStride == 0)
 104:       scanlineStride = ((dataBitOffset + w * numberOfBits) / elemBits);
 105:     this.scanlineStride = scanlineStride;
 106: 
 107:     
 108:     sampleSize = new int[1];
 109:     sampleSize[0] = numberOfBits;
 110: 
 111:     bitMasks = new int[numElems];
 112:     bitOffsets = new int[numElems];
 113:     for (int i=0; i < numElems; i++)
 114:       {
 115:     bitOffsets[numElems - i- 1] = numberOfBits * i;
 116:     bitMasks[numElems - i - 1] = ((1 << numberOfBits) - 1) << 
 117:         bitOffsets[numElems - i - 1];
 118:       }
 119:   }
 120: 
 121:   public SampleModel createCompatibleSampleModel(int w, int h)
 122:   {
 123:     /* FIXME: We can avoid recalculation of bit offsets and sample
 124:        sizes here by passing these from the current instance to a
 125:        special private constructor. */
 126:     return new MultiPixelPackedSampleModel(dataType, w, h, numberOfBits);
 127:   }
 128: 
 129: 
 130:   /**
 131:    * Creates a DataBuffer for holding pixel data in the format and
 132:    * layout described by this SampleModel. The returned buffer will
 133:    * consist of one single bank.
 134:    */
 135:   public DataBuffer createDataBuffer()
 136:   {
 137:     int size;
 138: 
 139:     // FIXME:  The comment refers to SinglePixelPackedSampleModel.  See if the
 140:     // same can be done for MultiPixelPackedSampleModel.
 141:     // We can save (scanlineStride - width) pixels at the very end of
 142:     // the buffer. The Sun reference implementation (J2SE 1.3.1 and
 143:     // 1.4.1_01) seems to do this; tested with Mauve test code.
 144:     size = scanlineStride * height;
 145: 
 146:     return Buffers.createBuffer(getDataType(), size);
 147:   }
 148: 
 149: 
 150:   public int getNumDataElements()
 151:   {
 152:     return 1;
 153:   }
 154: 
 155:   public int[] getSampleSize()
 156:   {
 157:     return sampleSize;
 158:   }
 159:   
 160:   public int getSampleSize(int band)
 161:   {
 162:     return sampleSize[0];
 163:   }
 164: 
 165:   public int getOffset(int x, int y)
 166:   {
 167:     return scanlineStride * y + ((dataBitOffset + x*numberOfBits) / elemBits);
 168:   }
 169: 
 170:   public int getBitOffset(int x)
 171:   {
 172:     return (dataBitOffset + x*numberOfBits) % elemBits;
 173:   }
 174: 
 175:   public int getDataBitOffset()
 176:   {
 177:     return dataBitOffset;
 178:   }
 179: 
 180:   public int getScanlineStride()
 181:   {
 182:     return scanlineStride;
 183:   }
 184: 
 185:   public int getPixelBitStride()
 186:   {
 187:     return numberOfBits;
 188:   }
 189: 
 190: 
 191:   public SampleModel createSubsetSampleModel(int[] bands)
 192:   {
 193:     int numBands = bands.length;
 194:     if (numBands != 1)
 195:       throw new RasterFormatException("MultiPixelPackedSampleModel only"
 196:                       + " supports one band");
 197:     
 198:     return new MultiPixelPackedSampleModel(dataType, width, height,
 199:                        numberOfBits, scanlineStride,
 200:                        dataBitOffset);
 201:   }
 202: 
 203:   /**
 204:    * Extract one pixel and return in an array of transfer type.
 205:    *
 206:    * Extracts the pixel at x, y from data and stores into the 0th index of the
 207:    * array obj, since there is only one band.  If obj is null, a new array of
 208:    * getTransferType() is created.
 209:    *
 210:    * @param x The x-coordinate of the pixel rectangle to store in <code>obj</code>.
 211:    * @param y The y-coordinate of the pixel rectangle to store in <code>obj</code>.
 212:    * @param obj The primitive array to store the pixels into or null to force creation.
 213:    * @param data The DataBuffer that is the source of the pixel data.
 214:    * @return The primitive array containing the pixel data.
 215:    * @see java.awt.image.SampleModel#getDataElements(int, int, java.lang.Object, java.awt.image.DataBuffer)
 216:    */
 217:   public Object getDataElements(int x, int y, Object obj,
 218:                 DataBuffer data)
 219:   {
 220:     int pixel = getSample(x, y, 0, data);
 221:     switch (getTransferType())
 222:     {
 223:     case DataBuffer.TYPE_BYTE:
 224:       if (obj == null) obj = new byte[1];
 225:       ((byte[])obj)[0] = (byte)pixel;
 226:       return obj;
 227:     case DataBuffer.TYPE_USHORT:
 228:       if (obj == null) obj = new short[1];
 229:       ((short[])obj)[0] = (short)pixel;
 230:       return obj;
 231:     case DataBuffer.TYPE_INT:
 232:       if (obj == null) obj = new int[1];
 233:       ((int[])obj)[0] = pixel;
 234:       return obj;
 235:     default:
 236:       // Seems like the only sensible thing to do.
 237:       throw new ClassCastException();
 238:     }
 239:   }
 240: 
 241:   public int[] getPixel(int x, int y, int[] iArray, DataBuffer data)
 242:   {
 243:     if (iArray == null) iArray = new int[1];
 244:     iArray[0] = getSample(x, y, 0, data);
 245:     
 246:     return iArray;
 247:   }
 248: 
 249:   public int[] getPixels(int x, int y, int w, int h, int[] iArray,
 250:              DataBuffer data)
 251:   {
 252:     int offset = getOffset(x, y);
 253:     if (iArray == null) iArray = new int[w*h];
 254:     int outOffset = 0;
 255:     for (y=0; y<h; y++)
 256:       {
 257:     int lineOffset = offset;
 258:     for (x=0; x<w;)
 259:       {
 260:         int samples = data.getElem(lineOffset++);
 261:         for (int b=0; b<numElems && x<w; b++)
 262:           {
 263:         iArray[outOffset++] = (samples & bitMasks[b]) >>> bitOffsets[b];
 264:         x++;
 265:           }
 266:       }
 267:     offset += scanlineStride;
 268:       }
 269:     return iArray;    
 270:   }
 271: 
 272:   public int getSample(int x, int y, int b, DataBuffer data)
 273:   {
 274:     int pos =
 275:       ((dataBitOffset + x * numberOfBits) % elemBits) / numberOfBits;
 276:     int offset = getOffset(x, y);
 277:     int samples = data.getElem(offset);
 278:     return (samples & bitMasks[pos]) >>> bitOffsets[pos];
 279:   }
 280:   
 281:   /**
 282:    * Set the pixel at x, y to the value in the first element of the primitive
 283:    * array obj.
 284:    *
 285:    * @param x The x-coordinate of the data elements in <code>obj</code>.
 286:    * @param y The y-coordinate of the data elements in <code>obj</code>.
 287:    * @param obj The primitive array containing the data elements to set.
 288:    * @param data The DataBuffer to store the data elements into.
 289:    * @see java.awt.image.SampleModel#setDataElements(int, int, int, int, java.lang.Object, java.awt.image.DataBuffer)
 290:    */
 291:   public void setDataElements(int x, int y, Object obj, DataBuffer data)
 292:   {
 293:     int transferType = getTransferType();
 294:     if (getTransferType() != data.getDataType())
 295:       {
 296:     throw new IllegalArgumentException("transfer type ("+
 297:                        getTransferType()+"), "+
 298:                        "does not match data "+
 299:                        "buffer type (" +
 300:                        data.getDataType() +
 301:                        ").");
 302:       }
 303: 
 304:     int offset = getOffset(x, y);
 305:     
 306:     try
 307:       {
 308:     switch (transferType)
 309:       {
 310:       case DataBuffer.TYPE_BYTE:
 311:         {
 312:           DataBufferByte out = (DataBufferByte) data;
 313:           byte[] in = (byte[]) obj;
 314:           out.getData()[offset] = in[0];
 315:           return;
 316:         }
 317:       case DataBuffer.TYPE_USHORT:
 318:         {
 319:           DataBufferUShort out = (DataBufferUShort) data;
 320:           short[] in = (short[]) obj;
 321:           out.getData()[offset] = in[0];
 322:           return;
 323:         }
 324:       case DataBuffer.TYPE_INT:
 325:         {
 326:           DataBufferInt out = (DataBufferInt) data;
 327:           int[] in = (int[]) obj;
 328:           out.getData()[offset] = in[0];
 329:           return;
 330:         }
 331:       default:
 332:         throw new ClassCastException("Unsupported data type");
 333:       }
 334:       }
 335:     catch (ArrayIndexOutOfBoundsException aioobe)
 336:       {
 337:     String msg = "While writing data elements" +
 338:       ", x="+x+", y="+y+
 339:       ", width="+width+", height="+height+
 340:       ", scanlineStride="+scanlineStride+
 341:       ", offset="+offset+
 342:       ", data.getSize()="+data.getSize()+
 343:       ", data.getOffset()="+data.getOffset()+
 344:       ": " +
 345:       aioobe;
 346:     throw new ArrayIndexOutOfBoundsException(msg);
 347:       }
 348:     }
 349: 
 350:   public void setPixel(int x, int y, int[] iArray, DataBuffer data)
 351:   {
 352:     setSample(x, y, 0, iArray[0], data);
 353:   }
 354: 
 355:   public void setSample(int x, int y, int b, int s, DataBuffer data)
 356:   {
 357:     int bitpos =
 358:       ((dataBitOffset + x * numberOfBits) % elemBits) / numberOfBits;
 359:     int offset = getOffset(x, y);
 360: 
 361:     s = s << bitOffsets[bitpos];
 362:     s = s & bitMasks[bitpos];
 363: 
 364:     int sample = data.getElem(offset);
 365:     sample |= s;
 366:     data.setElem(offset, sample);
 367:   }
 368:   
 369:   /**
 370:    * Creates a String with some information about this SampleModel.
 371:    * @return A String describing this SampleModel.
 372:    * @see java.lang.Object#toString()
 373:    */
 374:   public String toString()
 375:   {
 376:     StringBuffer result = new StringBuffer();
 377:     result.append(getClass().getName());
 378:     result.append("[");
 379:     result.append("scanlineStride=").append(scanlineStride);
 380:     for(int i=0; i < bitMasks.length; i+=1)
 381:     {
 382:       result.append(", mask[").append(i).append("]=0x").append(Integer.toHexString(bitMasks[i]));
 383:     }
 384:     
 385:     result.append("]");
 386:     return result.toString();
 387:   }
 388: }