001/* ColorModel.java -- 002 Copyright (C) 1999, 2000, 2002, 2003, 2004, 2006 Free Software Foundation 003 004This file is part of GNU Classpath. 005 006GNU Classpath is free software; you can redistribute it and/or modify 007it under the terms of the GNU General Public License as published by 008the Free Software Foundation; either version 2, or (at your option) 009any later version. 010 011GNU Classpath is distributed in the hope that it will be useful, but 012WITHOUT ANY WARRANTY; without even the implied warranty of 013MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 014General Public License for more details. 015 016You should have received a copy of the GNU General Public License 017along with GNU Classpath; see the file COPYING. If not, write to the 018Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 01902110-1301 USA. 020 021Linking this library statically or dynamically with other modules is 022making a combined work based on this library. Thus, the terms and 023conditions of the GNU General Public License cover the whole 024combination. 025 026As a special exception, the copyright holders of this library give you 027permission to link this library with independent modules to produce an 028executable, regardless of the license terms of these independent 029modules, and to copy and distribute the resulting executable under 030terms of your choice, provided that you also meet, for each linked 031independent module, the terms and conditions of the license of that 032module. An independent module is a module which is not derived from 033or based on this library. If you modify this library, you may extend 034this exception to your version of the library, but you are not 035obligated to do so. If you do not wish to do so, delete this 036exception statement from your version. */ 037 038 039package java.awt.image; 040 041import gnu.java.awt.Buffers; 042 043import java.awt.Point; 044import java.awt.Transparency; 045import java.awt.color.ColorSpace; 046import java.util.Arrays; 047 048/** 049 * A color model operates with colors in several formats: 050 * 051 * <ul> 052 * <li>normalized: component samples are in range [0.0, 1.0].</li> 053 * 054 * <li>color model pixel value: all the color component samples for a 055 * sigle pixel packed/encoded in a way natural for the color 056 * model.</li> 057 * 058 * <li>color model pixel int value: only makes sense if the natural 059 * encoding of a single pixel can fit in a single int value.</li> 060 * 061 * <li>array of transferType containing a single pixel: the pixel is 062 * encoded in the natural way of the color model, taking up as many 063 * array elements as needed.</li> 064 * 065 * <li>sRGB pixel int value: a pixel in sRGB color space, encoded in 066 * default 0xAARRGGBB format, assumed not alpha premultiplied.</li> 067 * 068 * <li>single [0, 255] scaled int samples from default sRGB color 069 * space. These are always assumed to be alpha non-premultiplied.</li> 070 * 071 * <li>arrays of unnormalized component samples of single pixel: these 072 * samples are scaled and multiplied according to the color model, but 073 * is otherwise not packed or encoded. Each element of the array is one 074 * separate component sample. The color model only operate on the 075 * components from one pixel at a time, but using offsets, allows 076 * manipulation of arrays that contain the components of more than one 077 * pixel.</li> 078 * 079 * </ul> 080 * 081 * @author Rolf W. Rasmussen (rolfwr@ii.uib.no) 082 * @author C. Brian Jones (cbj@gnu.org) 083 */ 084public abstract class ColorModel implements Transparency 085{ 086 protected int pixel_bits; 087 protected int transferType; 088 089 int[] bits; 090 ColorSpace cspace; 091 int transparency; 092 boolean hasAlpha; 093 boolean isAlphaPremultiplied; 094 095 /** 096 * The standard color model for the common sRGB. 097 */ 098 private static final ColorModel S_RGB_MODEL = new SRGBColorModel(); 099 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}