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 final 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:     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: }