GNU Classpath (0.91) | |
Frames | No Frames |
1: /* ColorModel.java -- 2: Copyright (C) 1999, 2000, 2002, 2003, 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: import java.lang.reflect.Constructor; 47: import java.util.Arrays; 48: 49: /** 50: * A color model operates with colors in several formats: 51: * 52: * <ul> 53: * <li>normalized: component samples are in range [0.0, 1.0].</li> 54: * 55: * <li>color model pixel value: all the color component samples for a 56: * sigle pixel packed/encoded in a way natural for the color 57: * model.</li> 58: * 59: * <li>color model pixel int value: only makes sense if the natural 60: * encoding of a single pixel can fit in a single int value.</li> 61: * 62: * <li>array of transferType containing a single pixel: the pixel is 63: * encoded in the natural way of the color model, taking up as many 64: * array elements as needed.</li> 65: * 66: * <li>sRGB pixel int value: a pixel in sRGB color space, encoded in 67: * default 0xAARRGGBB format, assumed not alpha premultiplied.</li> 68: * 69: * <li>single [0, 255] scaled int samples from default sRGB color 70: * space. These are always assumed to be alpha non-premultiplied.</li> 71: * 72: * <li>arrays of unnormalized component samples of single pixel: these 73: * samples are scaled and multiplied according to the color model, but 74: * is otherwise not packed or encoded. Each element of the array is one 75: * separate component sample. The color model only operate on the 76: * components from one pixel at a time, but using offsets, allows 77: * manipulation of arrays that contain the components of more than one 78: * pixel.</li> 79: * 80: * </ul> 81: * 82: * @author Rolf W. Rasmussen (rolfwr@ii.uib.no) 83: * @author C. Brian Jones (cbj@gnu.org) 84: */ 85: public abstract class ColorModel implements Transparency 86: { 87: protected int pixel_bits; 88: protected int transferType; 89: 90: int[] bits; 91: ColorSpace cspace; 92: int transparency; 93: boolean hasAlpha; 94: boolean isAlphaPremultiplied; 95: 96: static int[] nArray(int value, int times) 97: { 98: int[] array = new int[times]; 99: java.util.Arrays.fill(array, value); 100: return array; 101: } 102: 103: static byte[] nArray(byte value, int times) 104: { 105: byte[] array = new byte[times]; 106: java.util.Arrays.fill(array, value); 107: return array; 108: } 109: 110: /** 111: * Constructs the default color model. The default color model 112: * can be obtained by calling <code>getRGBdefault</code> of this 113: * class. 114: * @param bits the number of bits wide used for bit size of pixel values 115: */ 116: public ColorModel(int bits) 117: { 118: this(bits * 4, // total bits, sRGB, four channels 119: nArray(bits, 4), // bits for each channel 120: ColorSpace.getInstance(ColorSpace.CS_sRGB), // sRGB 121: true, // has alpha 122: false, // not premultiplied 123: TRANSLUCENT, 124: Buffers.smallestAppropriateTransferType(bits * 4)); 125: } 126: 127: /** 128: * Constructs a ColorModel that translates pixel values to 129: * color/alpha components. 130: * 131: * @exception IllegalArgumentException If the length of the bit array is less 132: * than the number of color or alpha components in this ColorModel, or if the 133: * transparency is not a valid value, or if the sum of the number of bits in 134: * bits is less than 1 or if any of the elements in bits is less than 0. 135: */ 136: protected ColorModel(int pixel_bits, int[] bits, ColorSpace cspace, 137: boolean hasAlpha, boolean isAlphaPremultiplied, 138: int transparency, int transferType) 139: { 140: int bits_sum = 0; 141: for (int i = 0; i < bits.length; i++) 142: { 143: if (bits [i] < 0) 144: throw new IllegalArgumentException (); 145: 146: bits_sum |= bits [i]; 147: } 148: 149: if ((bits.length < cspace.getNumComponents()) 150: || (bits_sum < 1)) 151: throw new IllegalArgumentException (); 152: 153: this.pixel_bits = pixel_bits; 154: this.bits = bits; 155: this.cspace = cspace; 156: this.hasAlpha = hasAlpha; 157: this.isAlphaPremultiplied = isAlphaPremultiplied; 158: this.transparency = transparency; 159: this.transferType = transferType; 160: } 161: 162: // This is a hook for ColorConvertOp to create a colormodel with 163: // a new colorspace 164: ColorModel cloneColorModel(ColorSpace cspace) 165: { 166: Class cls = this.getClass(); 167: ColorModel cm; 168: try { 169: // This constructor will exist. 170: Constructor ctor = 171: cls.getConstructor(new Class[]{int.class, int[].class, 172: ColorSpace.class, boolean.class, 173: boolean.class, int.class, int.class}); 174: cm = (ColorModel)ctor. 175: newInstance(new Object[]{new Integer(pixel_bits), 176: bits, cspace, Boolean.valueOf(hasAlpha), 177: Boolean.valueOf(isAlphaPremultiplied), 178: new Integer(transparency), 179: new Integer(transferType)}); 180: } 181: catch (Exception e) 182: { 183: throw new IllegalArgumentException(); 184: } 185: return cm; 186: } 187: 188: public void finalize() 189: { 190: // Do nothing here. 191: } 192: 193: /** 194: * Returns the default color model which in Sun's case is an instance 195: * of <code>DirectColorModel</code>. 196: */ 197: public static ColorModel getRGBdefault() 198: { 199: return new DirectColorModel(32, 0xff0000, 0xff00, 0xff, 0xff000000); 200: } 201: 202: public final boolean hasAlpha() 203: { 204: return hasAlpha; 205: } 206: 207: public final boolean isAlphaPremultiplied() 208: { 209: return isAlphaPremultiplied; 210: } 211: 212: /** 213: * Get get number of bits wide used for the bit size of pixel values 214: */ 215: public int getPixelSize() 216: { 217: return pixel_bits; 218: } 219: 220: public int getComponentSize(int componentIdx) 221: { 222: return bits[componentIdx]; 223: } 224: 225: public int[] getComponentSize() 226: { 227: return bits; 228: } 229: 230: public int getTransparency() 231: { 232: return transparency; 233: } 234: 235: public int getNumComponents() 236: { 237: return getNumColorComponents() + (hasAlpha ? 1 : 0); 238: } 239: 240: public int getNumColorComponents() 241: { 242: return cspace.getNumComponents(); 243: } 244: 245: /** 246: * Converts pixel value to sRGB and extract red int sample scaled 247: * to range [0, 255]. 248: * 249: * @param pixel pixel value that will be interpreted according to 250: * the color model, (assumed alpha premultiplied if color model says 251: * so.) 252: * 253: * @return red sample scaled to range [0, 255], from default color 254: * space sRGB, alpha non-premultiplied. 255: */ 256: public abstract int getRed(int pixel); 257: 258: /** 259: * Converts pixel value to sRGB and extract green int sample 260: * scaled to range [0, 255]. 261: * 262: * @see #getRed(int) 263: */ 264: public abstract int getGreen(int pixel); 265: 266: /** 267: * Converts pixel value to sRGB and extract blue int sample 268: * scaled to range [0, 255]. 269: * 270: * @see #getRed(int) 271: */ 272: public abstract int getBlue(int pixel); 273: 274: /** 275: * Extract alpha int sample from pixel value, scaled to [0, 255]. 276: * 277: * @param pixel pixel value that will be interpreted according to 278: * the color model. 279: * 280: * @return alpha sample, scaled to range [0, 255]. 281: */ 282: public abstract int getAlpha(int pixel); 283: 284: /** 285: * Converts a pixel int value of the color space of the color 286: * model to a sRGB pixel int value. 287: * 288: * This method is typically overriden in subclasses to provide a 289: * more efficient implementation. 290: * 291: * @param pixel pixel value that will be interpreted according to 292: * the color model. 293: * 294: * @return a pixel in sRGB color space, encoded in default 295: * 0xAARRGGBB format. */ 296: public int getRGB(int pixel) 297: { 298: return 299: ((getAlpha(pixel) & 0xff) << 24) | 300: (( getRed(pixel) & 0xff) << 16) | 301: ((getGreen(pixel) & 0xff) << 8) | 302: (( getBlue(pixel) & 0xff) << 0); 303: } 304: 305: 306: /** 307: * In this color model we know that the whole pixel value will 308: * always be contained within the first element of the pixel 309: * array. 310: */ 311: final int getPixelFromArray(Object inData) { 312: DataBuffer data = 313: Buffers.createBufferFromData(transferType, inData, 1); 314: Object da = Buffers.getData(data); 315: 316: return data.getElem(0); 317: } 318: 319: /** 320: * Converts pixel in the given array to sRGB and extract blue int 321: * sample scaled to range [0-255]. 322: * 323: * This method is typically overriden in subclasses to provide a 324: * more efficient implementation. 325: * 326: * @param inData array of transferType containing a single pixel. The 327: * pixel should be encoded in the natural way of the color model. 328: */ 329: public int getRed(Object inData) 330: { 331: return getRed(getPixelFromArray(inData)); 332: } 333: 334: /** 335: * @see #getRed(Object) 336: */ 337: public int getGreen(Object inData) 338: { 339: return getGreen(getPixelFromArray(inData)); 340: } 341: 342: /** 343: * @see #getRed(Object) 344: */ 345: public int getBlue(Object inData) { 346: return getBlue(getPixelFromArray(inData)); 347: } 348: 349: /** 350: * @see #getRed(Object) 351: */ 352: public int getAlpha(Object inData) { 353: return getAlpha(getPixelFromArray(inData)); 354: } 355: 356: /** 357: * Converts a pixel in the given array of the color space of the 358: * color model to an sRGB pixel int value. 359: * 360: * <p>This method performs the inverse function of 361: * <code>getDataElements(int rgb, Object pixel)</code>. 362: * I.e. <code>(rgb == cm.getRGB(cm.getDataElements(rgb, 363: * null)))</code>. 364: * 365: * @param inData array of transferType containing a single pixel. The 366: * pixel should be encoded in the natural way of the color model. 367: * 368: * @return a pixel in sRGB color space, encoded in default 369: * 0xAARRGGBB format. 370: * 371: * @see #getDataElements(int, Object) 372: */ 373: public int getRGB(Object inData) 374: { 375: return 376: ((getAlpha(inData) & 0xff) << 24) | 377: (( getRed(inData) & 0xff) << 16) | 378: ((getGreen(inData) & 0xff) << 8) | 379: (( getBlue(inData) & 0xff) << 0); 380: } 381: 382: /** 383: * Converts an sRGB pixel int value to an array containing a 384: * single pixel of the color space of the color model. 385: * 386: * <p>This method performs the inverse function of 387: * <code>getRGB(Object inData)</code>. 388: * 389: * Outline of conversion process: 390: * 391: * <ol> 392: * 393: * <li>Convert rgb to normalized [0.0, 1.0] sRGB values.</li> 394: * 395: * <li>Convert to color space components using fromRGB in 396: * ColorSpace.</li> 397: * 398: * <li>If color model has alpha and should be premultiplied, 399: * multiply color space components with alpha value</li> 400: * 401: * <li>Scale the components to the correct number of bits.</li> 402: * 403: * <li>Arrange the components in the output array</li> 404: * 405: * </ol> 406: * 407: * @param rgb The color to be converted to dataElements. A pixel 408: * in sRGB color space, encoded in default 0xAARRGGBB format, 409: * assumed not alpha premultiplied. 410: * 411: * @param pixel to avoid needless creation of arrays, an array to 412: * use to return the pixel can be given. If null, a suitable array 413: * will be created. 414: * 415: * @return An array of transferType values representing the color, 416: * in the color model format. The color model defines whether the 417: * 418: * @see #getRGB(Object) 419: */ 420: public Object getDataElements(int rgb, Object pixel) 421: { 422: // subclasses has to implement this method. 423: throw new UnsupportedOperationException(); 424: } 425: 426: /** 427: * Fills an array with the unnormalized component samples from a 428: * pixel value. I.e. decompose the pixel, but not perform any 429: * color conversion. 430: * 431: * This method is typically overriden in subclasses to provide a 432: * more efficient implementation. 433: * 434: * @param pixel pixel value encoded according to the color model. 435: * 436: * @return arrays of unnormalized component samples of single 437: * pixel. The scale and multiplication state of the samples are 438: * according to the color model. Each component sample is stored 439: * as a separate element in the array. 440: */ 441: public int[] getComponents(int pixel, int[] components, int offset) 442: { 443: // subclasses has to implement this method. 444: throw new UnsupportedOperationException(); 445: } 446: 447: /** 448: * Fills an array with the unnormalized component samples from an 449: * array of transferType containing a single pixel. I.e. decompose 450: * the pixel, but not perform any color conversion. 451: * 452: * This method is typically overriden in subclasses to provide a 453: * more efficient implementation. 454: * 455: * @param pixel an array of transferType containing a single pixel. The 456: * pixel should be encoded in the natural way of the color model. If 457: * this argument is not an array, as expected, a {@link ClassCastException} 458: * will be thrown. 459: * @param components an array that will be filled with the color component 460: * of the pixel. If this is null, a new array will be allocated 461: * @param offset index into the components array at which the result 462: * will be stored 463: * 464: * @return arrays of unnormalized component samples of single 465: * pixel. The scale and multiplication state of the samples are 466: * according to the color model. Each component sample is stored 467: * as a separate element in the array. 468: */ 469: public int[] getComponents(Object pixel, int[] components, int offset) 470: { 471: // subclasses has to implement this method. 472: throw new UnsupportedOperationException(); 473: } 474: 475: /** 476: * Convert normalized components to unnormalized components. 477: */ 478: public int[] getUnnormalizedComponents(float[] normComponents, 479: int normOffset, 480: int[] components, 481: int offset) 482: { 483: int numComponents = getNumComponents(); 484: if (components == null) 485: { 486: components = new int[offset + numComponents]; 487: } 488: 489: for (int i=0; i<numComponents; i++) 490: { 491: float in = normComponents[normOffset++]; 492: int out = (int) (in * ((1<<getComponentSize(i)) - 1)); 493: components[offset++] = out; 494: } 495: return components; 496: } 497: 498: /** 499: * Convert unnormalized components to normalized components. 500: */ 501: public float[] getNormalizedComponents(int[] components, 502: int offset, 503: float[] normComponents, 504: int normOffset) 505: { 506: int numComponents = getNumComponents(); 507: if (normComponents == null) 508: { 509: normComponents = new float[normOffset + numComponents]; 510: } 511: 512: for (int i=0; i<numComponents; i++) 513: { 514: float in = components[offset++]; 515: float out = in / ((1<<getComponentSize(i)) - 1); 516: normComponents[normOffset++] = out; 517: } 518: return normComponents; 519: } 520: 521: /** 522: * Convert unnormalized components to normalized components. 523: * 524: * @since 1.4 525: */ 526: public float[] getNormalizedComponents (Object pixel, 527: float[] normComponents, 528: int normOffset) 529: { 530: int[] components = getComponents(pixel, null, 0); 531: return getNormalizedComponents(components, 0, normComponents, normOffset); 532: } 533: 534: /** 535: * Converts the unnormalized component samples from an array to a 536: * pixel value. I.e. composes the pixel from component samples, but 537: * does not perform any color conversion or scaling of the samples. 538: * 539: * This method performs the inverse function of 540: * <code>getComponents(int pixel, int[] components, 541: * int offset)</code>. I.e. 542: * 543: * <code>(pixel == cm.getDataElement(cm.getComponents(pixel, null, 544: * 0), 0))</code>. 545: * 546: * This method is overriden in subclasses since this abstract class throws 547: * UnsupportedOperationException(). 548: * 549: * @param components Array of unnormalized component samples of single 550: * pixel. The scale and multiplication state of the samples are according 551: * to the color model. Each component sample is stored as a separate element 552: * in the array. 553: * @param offset Position of the first value of the pixel in components. 554: * 555: * @return pixel value encoded according to the color model. 556: */ 557: public int getDataElement(int[] components, int offset) 558: { 559: // subclasses have to implement this method. 560: throw new UnsupportedOperationException(); 561: } 562: 563: /** 564: * Converts the normalized component samples from an array to a pixel 565: * value. I.e. composes the pixel from component samples, but does not 566: * perform any color conversion or scaling of the samples. 567: * 568: * This method is typically overriden in subclasses to provide a 569: * more efficient implementation. The method provided by this abstract 570: * class converts the components to unnormalized form and returns 571: * getDataElement(int[], int). 572: * 573: * @param components Array of normalized component samples of single pixel. 574: * The scale and multiplication state of the samples are according to the 575: * color model. Each component sample is stored as a separate element in the 576: * array. 577: * @param offset Position of the first value of the pixel in components. 578: * 579: * @return pixel value encoded according to the color model. 580: * @since 1.4 581: */ 582: public int getDataElement (float[] components, int offset) 583: { 584: return 585: getDataElement(getUnnormalizedComponents(components, offset, null, 0), 586: 0); 587: } 588: 589: public Object getDataElements(int[] components, int offset, Object obj) 590: { 591: // subclasses have to implement this method. 592: throw new UnsupportedOperationException(); 593: } 594: 595: /** 596: * Converts the normalized component samples from an array to an array of 597: * TransferType values. I.e. composes the pixel from component samples, but 598: * does not perform any color conversion or scaling of the samples. 599: * 600: * If obj is null, a new array of TransferType is allocated and returned. 601: * Otherwise the results are stored in obj and obj is returned. If obj is 602: * not long enough, ArrayIndexOutOfBounds is thrown. If obj is not an array 603: * of primitives, ClassCastException is thrown. 604: * 605: * This method is typically overriden in subclasses to provide a 606: * more efficient implementation. The method provided by this abstract 607: * class converts the components to unnormalized form and returns 608: * getDataElement(int[], int, Object). 609: * 610: * @param components Array of normalized component samples of single pixel. 611: * The scale and multiplication state of the samples are according to the 612: * color model. Each component sample is stored as a separate element in the 613: * array. 614: * @param offset Position of the first value of the pixel in components. 615: * @param obj Array of TransferType or null. 616: * 617: * @return pixel value encoded according to the color model. 618: * @throws ArrayIndexOutOfBoundsException 619: * @throws ClassCastException 620: * @since 1.4 621: */ 622: public Object getDataElements(float[] components, int offset, Object obj) 623: { 624: return 625: getDataElements(getUnnormalizedComponents(components, offset, null, 0), 626: 0, obj); 627: } 628: 629: public boolean equals(Object obj) 630: { 631: if (!(obj instanceof ColorModel)) return false; 632: 633: ColorModel o = (ColorModel) obj; 634: return 635: (pixel_bits == o.pixel_bits) && 636: (transferType == o.transferType) && 637: (transparency == o.transparency) && 638: (hasAlpha == o.hasAlpha) && 639: (isAlphaPremultiplied == o.isAlphaPremultiplied) && 640: Arrays.equals(bits, o.bits) && 641: (cspace.equals(o.cspace)); 642: } 643: 644: public final ColorSpace getColorSpace() 645: { 646: return cspace; 647: } 648: 649: // Typically overridden 650: public ColorModel coerceData(WritableRaster raster, 651: boolean isAlphaPremultiplied) 652: { 653: if (this.isAlphaPremultiplied == isAlphaPremultiplied) 654: return this; 655: 656: int w = raster.getWidth(); 657: int h = raster.getHeight(); 658: int x = raster.getMinX(); 659: int y = raster.getMinY(); 660: int size = w*h; 661: int numColors = getNumColorComponents(); 662: int numComponents = getNumComponents(); 663: int alphaScale = (1<<getComponentSize(numColors)) - 1; 664: double[] pixels = raster.getPixels(x, y, w, h, (double[]) null); 665: 666: for (int i=0; i<size; i++) 667: { 668: double alpha = pixels[i*numComponents+numColors]*alphaScale; 669: for (int c=0; c<numColors; c++) 670: { 671: int offset = i*numComponents+c; 672: if (isAlphaPremultiplied) 673: pixels[offset] = pixels[offset]/alpha; 674: else 675: pixels[offset] = pixels[offset]*alpha; 676: } 677: } 678: 679: raster.setPixels(0, 0, w, h, pixels); 680: 681: // FIXME: what can we return? 682: return null; 683: } 684: 685: /** 686: * Checks if the given raster has a compatible data-layout (SampleModel). 687: * @param raster The Raster to test. 688: * @return true if raster is compatible. 689: */ 690: public boolean isCompatibleRaster(Raster raster) 691: { 692: SampleModel sampleModel = raster.getSampleModel(); 693: return isCompatibleSampleModel(sampleModel); 694: } 695: 696: // Typically overridden 697: public WritableRaster createCompatibleWritableRaster(int w, int h) 698: { 699: return new WritableRaster(createCompatibleSampleModel(w, h), 700: new Point(0, 0)); 701: } 702: 703: // Typically overridden 704: public SampleModel createCompatibleSampleModel(int w, int h) 705: { 706: throw new UnsupportedOperationException(); 707: } 708: 709: // Typically overridden 710: public boolean isCompatibleSampleModel(SampleModel sm) 711: { 712: return sm.getTransferType() == transferType; 713: } 714: 715: public final int getTransferType () 716: { 717: return transferType; 718: } 719: 720: /** 721: * Subclasses must override this method if it is possible for the 722: * color model to have an alpha channel. 723: * 724: * @return null, as per JDK 1.3 doc. Subclasses will only return 725: * null if no alpha raster exists. 726: */ 727: public WritableRaster getAlphaRaster(WritableRaster raster) 728: { 729: return null; 730: 731: /* It is a mystery to me why we couldn't use the following code... 732: 733: 734: if (!hasAlpha()) return null; 735: 736: SampleModel sm = raster.getSampleModel(); 737: int[] alphaBand = { sm.getNumBands() - 1 }; 738: SampleModel alphaModel = sm.createSubsetSampleModel(alphaBand); 739: DataBuffer buffer = raster.getDataBuffer(); 740: Point origin = new Point(0, 0); 741: return Raster.createWritableRaster(alphaModel, buffer, origin); 742: 743: 744: ...here, and avoided overriding the method in subclasses, 745: but the Sun docs state that this method always will return 746: null, and that overriding is required. Oh, well. 747: */ 748: } 749: 750: String stringParam() 751: { 752: return "pixel_bits=" + pixel_bits + 753: ", cspace=" + cspace + 754: ", transferType=" + transferType + 755: ", transparency=" + transparency + 756: ", hasAlpha=" + hasAlpha + 757: ", isAlphaPremultiplied=" + isAlphaPremultiplied; 758: } 759: 760: public String toString() 761: { 762: return getClass().getName() + "[" + stringParam() + "]"; 763: } 764: }
GNU Classpath (0.91) |