Source for java.awt.image.DirectColorModel

   1: /* DirectColorModel.java --
   2:    Copyright (C) 1999, 2000, 2002, 2004  Free Software Foundation
   3: 
   4: This file is part of GNU Classpath.
   5: 
   6: GNU Classpath is free software; you can redistribute it and/or modify
   7: it under the terms of the GNU General Public License as published by
   8: the Free Software Foundation; either version 2, or (at your option)
   9: any later version.
  10: 
  11: GNU Classpath is distributed in the hope that it will be useful, but
  12: WITHOUT ANY WARRANTY; without even the implied warranty of
  13: MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  14: General Public License for more details.
  15: 
  16: You should have received a copy of the GNU General Public License
  17: along with GNU Classpath; see the file COPYING.  If not, write to the
  18: Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
  19: 02110-1301 USA.
  20: 
  21: Linking this library statically or dynamically with other modules is
  22: making a combined work based on this library.  Thus, the terms and
  23: conditions of the GNU General Public License cover the whole
  24: combination.
  25: 
  26: As a special exception, the copyright holders of this library give you
  27: permission to link this library with independent modules to produce an
  28: executable, regardless of the license terms of these independent
  29: modules, and to copy and distribute the resulting executable under
  30: terms of your choice, provided that you also meet, for each linked
  31: independent module, the terms and conditions of the license of that
  32: module.  An independent module is a module which is not derived from
  33: or based on this library.  If you modify this library, you may extend
  34: this exception to your version of the library, but you are not
  35: obligated to do so.  If you do not wish to do so, delete this
  36: exception statement from your version. */
  37: 
  38: 
  39: package java.awt.image;
  40: 
  41: import gnu.java.awt.Buffers;
  42: 
  43: import java.awt.Point;
  44: import java.awt.Transparency;
  45: import java.awt.color.ColorSpace;
  46: 
  47: /**
  48:  * @author Rolf W. Rasmussen (rolfwr@ii.uib.no)
  49:  * @author C. Brian Jones (cbj@gnu.org)
  50:  * @author Mark Benvenuto (mcb54@columbia.edu)
  51:  */
  52: public class DirectColorModel extends PackedColorModel
  53: {
  54:   /**
  55:    * For the color model created with this constructor the pixels
  56:    * will have fully opaque alpha components with a value of 255.
  57:    * Each mask should describe a fully contiguous set of bits in the
  58:    * most likely order of alpha, red, green, blue from the most significant
  59:    * byte to the least significant byte.
  60:    * 
  61:    * @param pixelBits the number of bits wide used for bit size of pixel values
  62:    * @param rmask the bits describing the red component of a pixel
  63:    * @param gmask the bits describing the green component of a pixel
  64:    * @param bmask the bits describing the blue component of a pixel 
  65:    */
  66:   public DirectColorModel(int pixelBits, int rmask, int gmask, int bmask)
  67:   {
  68:     this(ColorSpace.getInstance(ColorSpace.CS_sRGB), pixelBits,
  69:      rmask, gmask, bmask, 0, 
  70:      false, // not alpha premultiplied
  71:      Buffers.smallestAppropriateTransferType(pixelBits) // find type
  72:      );
  73:   }
  74: 
  75:   /**
  76:    * For the color model created with this constructor the pixels
  77:    * will have fully opaque alpha components with a value of 255.
  78:    * Each mask should describe a fully contiguous set of bits in the
  79:    * most likely order of red, green, blue from the most significant
  80:    * byte to the least significant byte.
  81:    * 
  82:    * @param pixelBits the number of bits wide used for bit size of pixel values
  83:    * @param rmask the bits describing the red component of a pixel
  84:    * @param gmask the bits describing the green component of a pixel
  85:    * @param bmask the bits describing the blue component of a pixel 
  86:    * @param amask the bits describing the alpha component of a pixel 
  87:    */
  88:   public DirectColorModel(int pixelBits,
  89:               int rmask, int gmask, int bmask, int amask)
  90:   {
  91:     this(ColorSpace.getInstance(ColorSpace.CS_sRGB), pixelBits,
  92:      rmask, gmask, bmask, amask,
  93:      false, // not alpha premultiplied
  94:      Buffers.smallestAppropriateTransferType(pixelBits) // find type
  95:      );
  96:   }
  97: 
  98:   public DirectColorModel(ColorSpace cspace, int pixelBits,
  99:               int rmask, int gmask, int bmask, int amask,
 100:               boolean isAlphaPremultiplied,
 101:               int transferType)
 102:   {
 103:     super(cspace, pixelBits,
 104:       rmask, gmask, bmask, amask, isAlphaPremultiplied,
 105:       ((amask == 0) ? Transparency.OPAQUE : Transparency.TRANSLUCENT),
 106:       transferType);
 107:   }
 108:     
 109:   public final int getRedMask()
 110:   {
 111:     return getMask(0);
 112:   }
 113: 
 114:   public final int getGreenMask()
 115:   {
 116:     return getMask(1);
 117:   }
 118: 
 119:   public final int getBlueMask()
 120:   {
 121:     return getMask(2);
 122:   }
 123: 
 124:   public final int getAlphaMask()
 125:   {
 126:     return hasAlpha() ? getMask(3) : 0;
 127:   }
 128: 
 129:   /**
 130:    * Get the red component of the given pixel.
 131:    * <br>
 132:    */
 133:   public final int getRed(int pixel)
 134:   {
 135:     return extractAndNormalizeSample(pixel, 0);
 136:   }
 137: 
 138:   /**
 139:    * Get the green component of the given pixel.
 140:    * <br>
 141:    */
 142:   public final int getGreen(int pixel)
 143:   {
 144:     return extractAndNormalizeSample(pixel, 1);
 145:   }
 146:   
 147:   /**
 148:    * Get the blue component of the given pixel.
 149:    * <br>
 150:    */
 151:   public final int getBlue(int pixel)
 152:   {
 153:     return extractAndNormalizeSample(pixel, 2);
 154:   }
 155: 
 156:   /**
 157:    * Get the alpha component of the given pixel.
 158:    * <br>
 159:    */
 160:   public final int getAlpha(int pixel)
 161:   {
 162:     if (!hasAlpha())
 163:       return 255;
 164:     return extractAndScaleSample(pixel, 3);
 165:   }
 166: 
 167:   private int extractAndNormalizeSample(int pixel, int component)
 168:   {
 169:     int value = extractAndScaleSample(pixel, component);
 170:     if (hasAlpha() && isAlphaPremultiplied() && getAlpha(pixel) != 0)
 171:       value = value*255/getAlpha(pixel);
 172:     return value;
 173:   }
 174: 
 175:   private int extractAndScaleSample(int pixel, int component)
 176:   {
 177:     int field = pixel & getMask(component);
 178:     int to8BitShift =
 179:       8 - shifts[component] - getComponentSize(component);
 180:     return (to8BitShift>0) ?
 181:       (field << to8BitShift) :
 182:       (field >>> (-to8BitShift));
 183:   }
 184: 
 185:   /**
 186:    * Get the RGB color value of the given pixel using the default
 187:    * RGB color model. 
 188:    * <br>
 189:    *
 190:    * @param pixel a pixel value
 191:    */
 192:   public final int getRGB(int pixel) 
 193:   {
 194:     /* FIXME: The Sun docs show that this method is overridden, but I
 195:        don't see any way to improve on the superclass
 196:        implementation. */
 197:     return super.getRGB(pixel);
 198:   }
 199: 
 200:   public int getRed(Object inData)
 201:   {
 202:     return getRed(getPixelFromArray(inData));
 203:   }
 204: 
 205:   public int getGreen(Object inData)
 206:   {
 207:     return getGreen(getPixelFromArray(inData));
 208:   }
 209: 
 210:   public int getBlue(Object inData)
 211:   {
 212:     return getBlue(getPixelFromArray(inData));
 213:   }
 214:     
 215:   public int getAlpha(Object inData)
 216:   {
 217:     return getAlpha(getPixelFromArray(inData));
 218:   }
 219: 
 220:   public int getRGB(Object inData)
 221:   {
 222:     return getRGB(getPixelFromArray(inData));
 223:   }
 224:     
 225:   /**
 226:    * Converts a normalized pixel int value in the sRGB color
 227:    * space to an array containing a single pixel of the color space
 228:    * of the color model.
 229:    *
 230:    * <p>This method performs the inverse function of
 231:    * <code>getRGB(Object inData)</code>.
 232:    *
 233:    * @param rgb pixel as a normalized sRGB, 0xAARRGGBB value.
 234:    *  
 235:    * @param pixel to avoid needless creation of arrays, an array to
 236:    * use to return the pixel can be given. If null, a suitable array
 237:    * will be created.
 238:    *
 239:    * @return array of transferType containing a single pixel. The
 240:    * pixel should be encoded in the natural way of the color model.
 241:    *
 242:    * @see #getRGB(Object)
 243:    */
 244:   public Object getDataElements(int rgb, Object pixel)
 245:   {
 246:     // FIXME: handle alpha multiply
 247:     
 248:     int pixelValue = 0;
 249:     int a = 0;
 250:     if (hasAlpha()) {
 251:       a = (rgb >>> 24) & 0xff;
 252:       pixelValue = valueToField(a, 3, 8);
 253:     }
 254:     
 255:     if (hasAlpha() && isAlphaPremultiplied())
 256:       {
 257:     int r, g, b;
 258:     /* if r=0xff and a=0xff, then resulting
 259:        value will be (r*a)>>>8 == 0xfe... This seems wrong.
 260:        We should divide by 255 rather than shifting >>>8 after
 261:        multiplying.
 262:        
 263:        Too bad, shifting is probably less expensive.
 264:        r = ((rgb >>> 16) & 0xff)*a;
 265:        g = ((rgb >>>  8) & 0xff)*a;
 266:        b = ((rgb >>> 0) & 0xff)*a; */
 267:     /* The r, g, b values we calculate are 16 bit. This allows
 268:        us to avoid discarding the lower 8 bits obtained if
 269:        multiplying with the alpha band. */
 270:     
 271:     // using 16 bit values
 272:     r = ((rgb >>> 8) & 0xff00)*a/255;
 273:     g = ((rgb >>> 0) & 0xff00)*a/255;
 274:     b = ((rgb <<  8) & 0xff00)*a/255;
 275:     pixelValue |= 
 276:       valueToField(r, 0, 16) |  // Red
 277:       valueToField(g, 1, 16) |  // Green
 278:       valueToField(b, 2, 16);   // Blue
 279:       }
 280:     else
 281:       {
 282:     int r, g, b;
 283:     // using 8 bit values
 284:     r = (rgb >>> 16) & 0xff;
 285:     g = (rgb >>>  8) & 0xff;
 286:     b = (rgb >>>  0) & 0xff;
 287:     
 288:     pixelValue |= 
 289:       valueToField(r, 0, 8) |  // Red
 290:       valueToField(g, 1, 8) |  // Green
 291:       valueToField(b, 2, 8);   // Blue
 292:       }
 293:     
 294:     /* In this color model, the whole pixel fits in the first element
 295:        of the array. */
 296:     DataBuffer buffer = Buffers.createBuffer(transferType, pixel, 1);
 297:     buffer.setElem(0, pixelValue);
 298:     return Buffers.getData(buffer);
 299:   }
 300:     
 301:   /**
 302:    * Converts a value to the correct field bits based on the
 303:    * information derived from the field masks.
 304:    *
 305:    * @param highBit the position of the most significant bit in the
 306:    * val parameter.
 307:    */
 308:   private int valueToField(int val, int component, int highBit)
 309:   {
 310:     int toFieldShift = 
 311:       getComponentSize(component) + shifts[component] - highBit;
 312:     int ret = (toFieldShift>0) ?
 313:       (val << toFieldShift) :
 314:       (val >>> (-toFieldShift));
 315:     return ret & getMask(component);
 316:   }  
 317: 
 318:   /**
 319:    * Converts a 16 bit value to the correct field bits based on the
 320:    * information derived from the field masks.
 321:    */
 322:   private int value16ToField(int val, int component)
 323:   {
 324:     int toFieldShift = getComponentSize(component) + shifts[component] - 16;
 325:     return (toFieldShift>0) ?
 326:       (val << toFieldShift) :
 327:       (val >>> (-toFieldShift));
 328:   }
 329: 
 330:   /**
 331:    * Fills an array with the unnormalized component samples from a
 332:    * pixel value. I.e. decompose the pixel, but not perform any
 333:    * color conversion.
 334:    */
 335:   public final int[] getComponents(int pixel, int[] components, int offset)
 336:   {
 337:     int numComponents = getNumComponents();
 338:     if (components == null) components = new int[offset + numComponents];
 339:     
 340:     for (int b=0; b<numComponents; b++)
 341:       components[offset++] = (pixel&getMask(b)) >>> shifts[b];
 342:     
 343:     return components;
 344:   }
 345: 
 346:   public final int[] getComponents(Object pixel, int[] components,
 347:                    int offset)
 348:   {
 349:     return getComponents(getPixelFromArray(pixel), components, offset);
 350:   }
 351: 
 352:   /**
 353:    * Creates a <code>WriteableRaster</code> that has a <code>SampleModel</code>
 354:    * that is compatible with this <code>ColorModel</code>.
 355:    *
 356:    * @param w the width of the writeable raster to create
 357:    * @param h the height of the writeable raster to create
 358:    *
 359:    * @throws IllegalArgumentException if <code>w</code> or <code>h</code>
 360:    *         is less than or equal to zero
 361:    */
 362:   public final WritableRaster createCompatibleWritableRaster(int w, int h)
 363:   {
 364:     // Sun also makes this check here.
 365:     if(w <= 0 || h <= 0)
 366:       throw new IllegalArgumentException("width (=" + w + ") and height (="
 367:                                          + h + ") must be > 0");
 368: 
 369:     SampleModel sm = createCompatibleSampleModel(w, h);
 370:     Point origin = new Point(0, 0);
 371:     return Raster.createWritableRaster(sm, origin);    
 372:   }
 373: 
 374:   public int getDataElement(int[] components, int offset)
 375:   {
 376:     int numComponents = getNumComponents();
 377:     int pixelValue = 0;
 378:     
 379:     for (int c=0; c<numComponents; c++)
 380:       pixelValue |= (components[offset++] << shifts[c]) & getMask(c);
 381: 
 382:     return pixelValue;
 383:   }  
 384: 
 385:   public Object getDataElements(int[] components, int offset, Object obj)
 386:   {
 387:     /* In this color model, the whole pixel fits in the first element
 388:        of the array. */
 389:     int pixelValue = getDataElement(components, offset);
 390: 
 391:     DataBuffer buffer = Buffers.createBuffer(transferType, obj, 1);
 392:     buffer.setElem(0, pixelValue);
 393:     return Buffers.getData(buffer);
 394:   }
 395:     
 396:   public ColorModel coerceData (WritableRaster raster,
 397:                                 boolean isAlphaPremultiplied)
 398:   {
 399:     if (this.isAlphaPremultiplied == isAlphaPremultiplied || !hasAlpha())
 400:       return this;
 401:     
 402:     /* TODO: provide better implementation based on the
 403:        assumptions we can make due to the specific type of the
 404:        color model. */
 405:     super.coerceDataWorker(raster, isAlphaPremultiplied);
 406:     
 407:     return new DirectColorModel(cspace, pixel_bits, getRedMask(),
 408:                                 getGreenMask(), getBlueMask(), getAlphaMask(),
 409:                                 isAlphaPremultiplied, transferType);
 410:   } 
 411: 
 412:   public boolean isCompatibleRaster(Raster raster)
 413:   {
 414:     /* FIXME: the Sun docs say this method is overridden here, 
 415:        but I don't see any way to improve upon the implementation
 416:        in ColorModel. */
 417:     return super.isCompatibleRaster(raster);
 418:   }
 419: 
 420:   String stringParam()
 421:   {
 422:     return super.stringParam() +
 423:       ", redMask=" + Integer.toHexString(getRedMask()) +
 424:       ", greenMask=" + Integer.toHexString(getGreenMask()) +
 425:       ", blueMask=" + Integer.toHexString(getBlueMask()) +
 426:       ", alphaMask=" + Integer.toHexString(getAlphaMask());
 427:   }
 428: 
 429:   public String toString()
 430:   {
 431:     /* FIXME: Again, docs say override, but how do we improve upon the
 432:        superclass implementation? */
 433:     return super.toString();
 434:   }
 435: }