Source for java.awt.image.ComponentColorModel

   1: /* ComponentColorModel.java --
   2:    Copyright (C) 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.color.ColorSpace;
  45: 
  46: public class ComponentColorModel extends ColorModel
  47: {
  48:   private static int sum(int[] values)
  49:   {
  50:     int sum = 0;
  51:     for (int i=0; i<values.length; i++)
  52:       sum += values[i];
  53:     return sum;
  54:   }
  55: 
  56:   public ComponentColorModel(ColorSpace colorSpace, int[] bits,
  57:                  boolean hasAlpha,
  58:                  boolean isAlphaPremultiplied,
  59:                  int transparency, int transferType)
  60:   {
  61:     super(sum(bits), bits, colorSpace, hasAlpha, isAlphaPremultiplied,
  62:       transparency, transferType);
  63:   }
  64: 
  65:   /**
  66:    * Construct a new ComponentColorModel.
  67:    * 
  68:    * This constructor makes all bits of each sample significant, so for a
  69:    * transferType of DataBuffer.BYTE, the bits per sample is 8, etc.  If
  70:    * both hasAlpha and isAlphaPremultiplied are true, color samples are
  71:    * assumed to be premultiplied by the alpha component.  Transparency may be
  72:    * one of OPAQUE, BITMASK, or TRANSLUCENT. 
  73:    * 
  74:    * @param colorSpace The colorspace for this color model.
  75:    * @param hasAlpha True if there is an alpha component.
  76:    * @param isAlphaPremultiplied True if colors are already multiplied by
  77:    * alpha.
  78:    * @param transparency The type of alpha values.
  79:    * @param transferType Data type of pixel sample values.
  80:    * @since 1.4
  81:    */
  82:   public ComponentColorModel(ColorSpace colorSpace,
  83:                  boolean hasAlpha,
  84:                  boolean isAlphaPremultiplied,
  85:                  int transparency, int transferType)
  86:   {    
  87:     this(colorSpace, null, hasAlpha, isAlphaPremultiplied,
  88:          transparency, transferType);
  89:   }
  90: 
  91:   public int getRed(int pixel)
  92:   {
  93:     if (getNumComponents()>1) throw new IllegalArgumentException();
  94:     return (int) getRGBFloat(pixel)[0];
  95:   }
  96: 
  97:   public int getGreen(int pixel)
  98:   {
  99:     if (getNumComponents()>1) throw new IllegalArgumentException();
 100:     return (int) getRGBFloat(pixel)[0];
 101:   }
 102:   
 103:   public int getBlue(int pixel)
 104:   {
 105:     if (getNumComponents()>1) throw new IllegalArgumentException();
 106:     return (int) getRGBFloat(pixel)[0];
 107:   }
 108: 
 109:   public int getAlpha(int pixel)
 110:   {
 111:     if (getNumComponents()>1) throw new IllegalArgumentException();
 112:     int shift = 8 - getComponentSize(getNumColorComponents());
 113:     if (shift >= 0) return pixel << shift;
 114:     return pixel >> (-shift);
 115:   }
 116:    
 117:   public int getRGB(int pixel)
 118:   {
 119:     float[] rgb = getRGBFloat(pixel);
 120:     int ret = getRGB(rgb);
 121:     if (hasAlpha()) ret |= getAlpha(pixel) << 24;
 122:     return ret;
 123:   }
 124: 
 125: 
 126:   /* Note, it's OK to pass a to large array to toRGB(). Extra
 127:      elements are ignored. */
 128:   
 129:   private float[] getRGBFloat(int pixel)
 130:   {
 131:     float[] data = { pixel };
 132:     return cspace.toRGB(data);
 133:   }
 134: 
 135:   private float[] getRGBFloat(Object inData)
 136:   {
 137:     DataBuffer buffer =
 138:     Buffers.createBufferFromData(transferType, inData,
 139:                  getNumComponents());
 140:     int colors = getNumColorComponents();
 141:     float[] data = new float[colors];
 142:     
 143:     // FIXME: unpremultiply data that is premultiplied
 144:     for (int i=0; i<colors; i++)
 145:       {
 146:     float maxValue = (1<<getComponentSize(i))-1;
 147:     data[i] = buffer.getElemFloat(i)/maxValue; 
 148:       }
 149:     float[] rgb = cspace.toRGB(data);
 150:     return rgb;
 151:   }
 152:   
 153:   public int getRed(Object inData)
 154:   {
 155:     return (int) getRGBFloat(inData)[0]*255;
 156:   }
 157: 
 158:   public int getGreen(Object inData)
 159:   {
 160:     return (int) getRGBFloat(inData)[1]*255;
 161:   }
 162: 
 163:   public int getBlue(Object inData)
 164:   {
 165:     return (int) getRGBFloat(inData)[2]*255;
 166:   }
 167: 
 168:   public int getAlpha(Object inData)
 169:   {
 170:     DataBuffer buffer =
 171:       Buffers.createBufferFromData(transferType, inData,
 172:                    getNumComponents());
 173:     int shift = 8 - getComponentSize(getNumColorComponents());
 174:     int alpha = buffer.getElem(getNumColorComponents());
 175:     if (shift >= 0) return alpha << shift;
 176:     return alpha >> (-shift);
 177:   }
 178: 
 179:   private int getRGB(float[] rgb)
 180:   {
 181:     /* NOTE: We could cast to byte instead of int here. This would
 182:        avoid bits spilling over from one bit field to
 183:        another. But, if we assume that floats are in the [0.0,
 184:        1.0] range, this will never happen anyway. */
 185:     
 186:     /* Remember to multiply BEFORE casting to int, otherwise, decimal
 187:        point data will be lost. */
 188:     int ret =
 189:       (((int) (rgb[0]*255F)) << 16) |
 190:       (((int) (rgb[1]*255F)) <<  8) |
 191:       (((int) (rgb[2]*255F)) <<  0);
 192:     return ret;
 193:   }
 194: 
 195:   /**
 196:    * @param inData pixel data of transferType, as returned by the
 197:    * getDataElements method in SampleModel.
 198:    */
 199:   public int getRGB(Object inData)
 200:   {
 201:     float[] rgb = getRGBFloat(inData);
 202:     int ret = getRGB(rgb);
 203:     if (hasAlpha()) ret |= getAlpha(inData) << 24;
 204:     return ret;
 205:   }
 206: 
 207:   public Object getDataElements(int rgb, Object pixel)
 208:   {
 209:     // Convert rgb to [0.0, 1.0] sRGB values.
 210:     float[] rgbFloats = {
 211:       ((rgb >> 16)&0xff)/255.0F,
 212:       ((rgb >>  8)&0xff)/255.0F,
 213:       ((rgb >>  0)&0xff)/255.0F
 214:     };
 215: 
 216:     // Convert from rgb to color space components.
 217:     float[] data = cspace.fromRGB(rgbFloats);
 218:     DataBuffer buffer = Buffers.createBuffer(transferType, pixel,
 219:                          getNumComponents());
 220:     int numColors = getNumColorComponents();
 221:     
 222:     if (hasAlpha())
 223:       {
 224:     float alpha = ((rgb >> 24)&0xff)/255.0F;
 225:     
 226:     /* If color model has alpha and should be premultiplied, multiply
 227:        color space components with alpha value. */
 228:     if (isAlphaPremultiplied()) {
 229:       for (int i=0; i<numColors; i++)
 230:         data[i] *= alpha;
 231:     }
 232:     // Scale the alpha sample to the correct number of bits.
 233:     alpha *= (1<<(bits[numColors]-1));
 234:     // Arrange the alpha sample in the output array.
 235:     buffer.setElemFloat(numColors, alpha);
 236:       }
 237:     for (int i=0; i<numColors; i++)
 238:       {
 239:     // Scale the color samples to the correct number of bits.
 240:     float value = data[i]*(1<<(bits[i]-1));
 241:     // Arrange the color samples in the output array.
 242:     buffer.setElemFloat(i, value);
 243:       }
 244:     return Buffers.getData(buffer);
 245:   }
 246: 
 247:   public int[] getComponents(int pixel, int[] components, int offset)
 248:   {
 249:     if (getNumComponents()>1) throw new IllegalArgumentException();
 250:     if (components == null)
 251:     components = new int[getNumComponents() + offset];
 252:     components[offset] = pixel;
 253:     return components;
 254:   }
 255: 
 256:   public int[] getComponents(Object pixel, int[] components, int offset)
 257:   {
 258:     DataBuffer buffer = Buffers.createBuffer(transferType, pixel,
 259:                          getNumComponents());
 260:     int numComponents = getNumComponents();
 261: 
 262:     if (components == null)
 263:       components = new int[numComponents + offset];
 264: 
 265:     for (int i=0; i<numComponents; i++)
 266:       components[offset++] = buffer.getElem(i);
 267: 
 268:     return components;
 269:   }
 270: 
 271:   public int getDataElement(int[] components, int offset)
 272:   {
 273:     if (getNumComponents()>1) throw new IllegalArgumentException();
 274:     return components[offset];
 275:   }
 276: 
 277:   public Object getDataElements(int[] components, int offset, Object obj)
 278:   {
 279:     DataBuffer buffer = Buffers.createBuffer(transferType, obj,
 280:                          getNumComponents());
 281:     int numComponents = getNumComponents();
 282: 
 283:     for (int i=0; i<numComponents; i++)
 284:       buffer.setElem(i, components[offset++]);
 285: 
 286:     return Buffers.getData(buffer);
 287:   }
 288: 
 289:   public ColorModel coerceData(WritableRaster raster,
 290:                    boolean isAlphaPremultiplied) {
 291:     if (this.isAlphaPremultiplied == isAlphaPremultiplied)
 292:       return this;
 293: 
 294:     /* TODO: provide better implementation based on the
 295:        assumptions we can make due to the specific type of the
 296:        color model. */
 297:     super.coerceData(raster, isAlphaPremultiplied);
 298:     
 299:     return new ComponentColorModel(cspace, bits, hasAlpha(),
 300:                    isAlphaPremultiplied, // argument
 301:                    transparency, transferType);
 302:   }
 303: 
 304:   public boolean isCompatibleRaster(Raster raster)
 305:   {
 306:     return super.isCompatibleRaster(raster);
 307:     // FIXME: Should we test something more here? (Why override?)
 308:   }
 309: 
 310:   public WritableRaster createCompatibleWritableRaster(int w, int h)
 311:   {
 312:     SampleModel sm = createCompatibleSampleModel(w, h);
 313:     Point origin = new Point(0, 0);
 314:     return Raster.createWritableRaster(sm, origin);
 315:   }
 316: 
 317: 
 318:   /**
 319:    * Creates a <code>SampleModel</code> whose arrangement of pixel
 320:    * data is compatible to this <code>ColorModel</code>.
 321:    *
 322:    * @param w the number of pixels in the horizontal direction.
 323:    * @param h the number of pixels in the vertical direction.
 324:    */
 325:   public SampleModel createCompatibleSampleModel(int w, int h)
 326:   {
 327:     int pixelStride, scanlineStride;
 328:     int[] bandOffsets;
 329: 
 330:     pixelStride = getNumComponents();
 331:     scanlineStride = pixelStride * w;
 332: 
 333:     /* We might be able to re-use the same bandOffsets array among
 334:      * multiple calls to this method. However, this optimization does
 335:      * not seem worthwile because setting up descriptive data
 336:      * structures (such as SampleModels) is neglectible in comparision
 337:      * to shuffling around masses of pixel data.
 338:      */
 339:     bandOffsets = new int[pixelStride];
 340:     for (int i = 0; i < pixelStride; i++)
 341:       bandOffsets[i] = i;
 342: 
 343:     /* FIXME: Think about whether it would make sense to return the
 344:      * possibly more efficient PixelInterleavedSampleModel for other
 345:      * transferTypes as well. It seems unlikely that this would break
 346:      * any user applications, so the Mauve tests on this method
 347:      * might be too restrictive.
 348:      */
 349:     switch (transferType)
 350:       {
 351:       case DataBuffer.TYPE_BYTE:
 352:       case DataBuffer.TYPE_USHORT:
 353:         return new PixelInterleavedSampleModel(transferType, w, h,
 354:                                                pixelStride,
 355:                                                scanlineStride,
 356:                                                bandOffsets);
 357: 
 358:       default:
 359:         return new ComponentSampleModel(transferType, w, h,
 360:                                         pixelStride,
 361:                                         scanlineStride,
 362:                                         bandOffsets);
 363:       }
 364:   }
 365: 
 366: 
 367:   public boolean isCompatibleSampleModel(SampleModel sm)
 368:   {
 369:     return 
 370:       (sm instanceof ComponentSampleModel) &&
 371:       super.isCompatibleSampleModel(sm);
 372:   }
 373: 
 374:   public WritableRaster getAlphaRaster(WritableRaster raster)
 375:   {
 376:     if (!hasAlpha()) return null;
 377:     
 378:     SampleModel sm = raster.getSampleModel();
 379:     int[] alphaBand = { sm.getNumBands() - 1 };
 380:     SampleModel alphaModel = sm.createSubsetSampleModel(alphaBand);
 381:     DataBuffer buffer = raster.getDataBuffer();
 382:     Point origin = new Point(0, 0);
 383:     return Raster.createWritableRaster(alphaModel, buffer, origin);
 384:   }
 385:     
 386:   public boolean equals(Object obj)
 387:   {
 388:     if (!(obj instanceof ComponentColorModel)) return false;
 389:     return super.equals(obj);
 390:   }
 391: }