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