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