GNU Classpath (0.20) | |
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 array of transferType containing a single pixel. The 456: * pixel should be encoded in the natural way of the color model. 457: * 458: * @return arrays of unnormalized component samples of single 459: * pixel. The scale and multiplication state of the samples are 460: * according to the color model. Each component sample is stored 461: * as a separate element in the array. 462: */ 463: public int[] getComponents(Object pixel, int[] components, int offset) 464: { 465: // subclasses has to implement this method. 466: throw new UnsupportedOperationException(); 467: } 468: 469: /** 470: * Convert normalized components to unnormalized components. 471: */ 472: public int[] getUnnormalizedComponents(float[] normComponents, 473: int normOffset, 474: int[] components, 475: int offset) 476: { 477: int numComponents = getNumComponents(); 478: if (components == null) 479: { 480: components = new int[offset + numComponents]; 481: } 482: 483: for (int i=0; i<numComponents; i++) 484: { 485: float in = normComponents[normOffset++]; 486: int out = (int) (in * ((1<<getComponentSize(i)) - 1)); 487: components[offset++] = out; 488: } 489: return components; 490: } 491: 492: /** 493: * Convert unnormalized components to normalized components. 494: */ 495: public float[] getNormalizedComponents(int[] components, 496: int offset, 497: float[] normComponents, 498: int normOffset) 499: { 500: int numComponents = getNumComponents(); 501: if (normComponents == null) 502: { 503: normComponents = new float[normOffset + numComponents]; 504: } 505: 506: for (int i=0; i<numComponents; i++) 507: { 508: float in = components[offset++]; 509: float out = in / ((1<<getComponentSize(i)) - 1); 510: normComponents[normOffset++] = out; 511: } 512: return normComponents; 513: } 514: 515: /** 516: * Convert unnormalized components to normalized components. 517: * 518: * @since 1.4 519: */ 520: public float[] getNormalizedComponents (Object pixel, 521: float[] normComponents, 522: int normOffset) 523: { 524: // subclasses has to implement this method. 525: throw new UnsupportedOperationException(); 526: } 527: 528: /** 529: * Converts the unnormalized component samples from an array to a 530: * pixel value. I.e. composes the pixel from component samples, but 531: * does not perform any color conversion or scaling of the samples. 532: * 533: * This method performs the inverse function of 534: * <code>getComponents(int pixel, int[] components, 535: * int offset)</code>. I.e. 536: * 537: * <code>(pixel == cm.getDataElement(cm.getComponents(pixel, null, 538: * 0), 0))</code>. 539: * 540: * This method is overriden in subclasses since this abstract class throws 541: * UnsupportedOperationException(). 542: * 543: * @param components Array of unnormalized component samples of single 544: * pixel. The scale and multiplication state of the samples are according 545: * to the color model. Each component sample is stored as a separate element 546: * in the array. 547: * @param offset Position of the first value of the pixel in components. 548: * 549: * @return pixel value encoded according to the color model. 550: */ 551: public int getDataElement(int[] components, int offset) 552: { 553: // subclasses have to implement this method. 554: throw new UnsupportedOperationException(); 555: } 556: 557: /** 558: * Converts the normalized component samples from an array to a pixel 559: * value. I.e. composes the pixel from component samples, but does not 560: * perform any color conversion or scaling of the samples. 561: * 562: * This method is typically overriden in subclasses to provide a 563: * more efficient implementation. The method provided by this abstract 564: * class converts the components to unnormalized form and returns 565: * getDataElement(int[], int). 566: * 567: * @param components Array of normalized component samples of single pixel. 568: * The scale and multiplication state of the samples are according to the 569: * color model. Each component sample is stored as a separate element in the 570: * array. 571: * @param offset Position of the first value of the pixel in components. 572: * 573: * @return pixel value encoded according to the color model. 574: * @since 1.4 575: */ 576: public int getDataElement (float[] components, int offset) 577: { 578: return 579: getDataElement(getUnnormalizedComponents(components, offset, null, 0), 580: 0); 581: } 582: 583: public Object getDataElements(int[] components, int offset, Object obj) 584: { 585: // subclasses have to implement this method. 586: throw new UnsupportedOperationException(); 587: } 588: 589: /** 590: * Converts the normalized component samples from an array to an array of 591: * TransferType values. I.e. composes the pixel from component samples, but 592: * does not perform any color conversion or scaling of the samples. 593: * 594: * If obj is null, a new array of TransferType is allocated and returned. 595: * Otherwise the results are stored in obj and obj is returned. If obj is 596: * not long enough, ArrayIndexOutOfBounds is thrown. If obj is not an array 597: * of primitives, ClassCastException is thrown. 598: * 599: * This method is typically overriden in subclasses to provide a 600: * more efficient implementation. The method provided by this abstract 601: * class converts the components to unnormalized form and returns 602: * getDataElement(int[], int, Object). 603: * 604: * @param components Array of normalized component samples of single pixel. 605: * The scale and multiplication state of the samples are according to the 606: * color model. Each component sample is stored as a separate element in the 607: * array. 608: * @param offset Position of the first value of the pixel in components. 609: * @param obj Array of TransferType or null. 610: * 611: * @return pixel value encoded according to the color model. 612: * @throws ArrayIndexOutOfBoundsException 613: * @throws ClassCastException 614: * @since 1.4 615: */ 616: public Object getDataElements(float[] components, int offset, Object obj) 617: { 618: return 619: getDataElements(getUnnormalizedComponents(components, offset, null, 0), 620: 0, obj); 621: } 622: 623: public boolean equals(Object obj) 624: { 625: if (!(obj instanceof ColorModel)) return false; 626: 627: ColorModel o = (ColorModel) obj; 628: return 629: (pixel_bits == o.pixel_bits) && 630: (transferType == o.transferType) && 631: (transparency == o.transparency) && 632: (hasAlpha == o.hasAlpha) && 633: (isAlphaPremultiplied == o.isAlphaPremultiplied) && 634: Arrays.equals(bits, o.bits) && 635: (cspace.equals(o.cspace)); 636: } 637: 638: public final ColorSpace getColorSpace() 639: { 640: return cspace; 641: } 642: 643: // Typically overridden 644: public ColorModel coerceData(WritableRaster raster, 645: boolean isAlphaPremultiplied) 646: { 647: if (this.isAlphaPremultiplied == isAlphaPremultiplied) 648: return this; 649: 650: int w = raster.getWidth(); 651: int h = raster.getHeight(); 652: int x = raster.getMinX(); 653: int y = raster.getMinY(); 654: int size = w*h; 655: int numColors = getNumColorComponents(); 656: int numComponents = getNumComponents(); 657: int alphaScale = (1<<getComponentSize(numColors)) - 1; 658: double[] pixels = raster.getPixels(x, y, w, h, (double[]) null); 659: 660: for (int i=0; i<size; i++) 661: { 662: double alpha = pixels[i*numComponents+numColors]*alphaScale; 663: for (int c=0; c<numColors; c++) 664: { 665: int offset = i*numComponents+c; 666: if (isAlphaPremultiplied) 667: pixels[offset] = pixels[offset]/alpha; 668: else 669: pixels[offset] = pixels[offset]*alpha; 670: } 671: } 672: 673: raster.setPixels(0, 0, w, h, pixels); 674: 675: // FIXME: what can we return? 676: return null; 677: } 678: 679: /** 680: * Checks if the given raster has a compatible data-layout (SampleModel). 681: * @param raster The Raster to test. 682: * @return true if raster is compatible. 683: */ 684: public boolean isCompatibleRaster(Raster raster) 685: { 686: SampleModel sampleModel = raster.getSampleModel(); 687: return isCompatibleSampleModel(sampleModel); 688: } 689: 690: // Typically overridden 691: public WritableRaster createCompatibleWritableRaster(int w, int h) 692: { 693: return new WritableRaster(createCompatibleSampleModel(w, h), 694: new Point(0, 0)); 695: } 696: 697: // Typically overridden 698: public SampleModel createCompatibleSampleModel(int w, int h) 699: { 700: throw new UnsupportedOperationException(); 701: } 702: 703: // Typically overridden 704: public boolean isCompatibleSampleModel(SampleModel sm) 705: { 706: return sm.getTransferType() == transferType; 707: } 708: 709: public final int getTransferType () 710: { 711: return transferType; 712: } 713: 714: /** 715: * Subclasses must override this method if it is possible for the 716: * color model to have an alpha channel. 717: * 718: * @return null, as per JDK 1.3 doc. Subclasses will only return 719: * null if no alpha raster exists. 720: */ 721: public WritableRaster getAlphaRaster(WritableRaster raster) 722: { 723: return null; 724: 725: /* It is a mystery to me why we couldn't use the following code... 726: 727: 728: if (!hasAlpha()) return null; 729: 730: SampleModel sm = raster.getSampleModel(); 731: int[] alphaBand = { sm.getNumBands() - 1 }; 732: SampleModel alphaModel = sm.createSubsetSampleModel(alphaBand); 733: DataBuffer buffer = raster.getDataBuffer(); 734: Point origin = new Point(0, 0); 735: return Raster.createWritableRaster(alphaModel, buffer, origin); 736: 737: 738: ...here, and avoided overriding the method in subclasses, 739: but the Sun docs state that this method always will return 740: null, and that overriding is required. Oh, well. 741: */ 742: } 743: 744: String stringParam() 745: { 746: return "pixel_bits=" + pixel_bits + 747: ", cspace=" + cspace + 748: ", transferType=" + transferType + 749: ", transparency=" + transparency + 750: ", hasAlpha=" + hasAlpha + 751: ", isAlphaPremultiplied=" + isAlphaPremultiplied; 752: } 753: 754: public String toString() 755: { 756: return getClass().getName() + "[" + stringParam() + "]"; 757: } 758: }
GNU Classpath (0.20) |