75 ICC_Profile thisSrcProfile, thisDestProfile; 76 RenderingHints hints; 77 boolean gotProfiles; 78 float[] srcMinVals, srcMaxVals, dstMinVals, dstMaxVals; 79 80 /* the class initializer */ 81 static { 82 if (ProfileDeferralMgr.deferring) { 83 ProfileDeferralMgr.activateProfiles(); 84 } 85 } 86 87 /** 88 * Constructs a new ColorConvertOp which will convert 89 * from a source color space to a destination color space. 90 * The RenderingHints argument may be null. 91 * This Op can be used only with BufferedImages, and will convert 92 * directly from the ColorSpace of the source image to that of the 93 * destination. The destination argument of the filter method 94 * cannot be specified as null. 95 * @param hints the <code>RenderingHints</code> object used to control 96 * the color conversion, or <code>null</code> 97 */ 98 public ColorConvertOp (RenderingHints hints) 99 { 100 profileList = new ICC_Profile [0]; /* 0 length list */ 101 this.hints = hints; 102 } 103 104 /** 105 * Constructs a new ColorConvertOp from a ColorSpace object. 106 * The RenderingHints argument may be null. This 107 * Op can be used only with BufferedImages, and is primarily useful 108 * when the {@link #filter(BufferedImage, BufferedImage) filter} 109 * method is invoked with a destination argument of null. 110 * In that case, the ColorSpace defines the destination color space 111 * for the destination created by the filter method. Otherwise, the 112 * ColorSpace defines an intermediate space to which the source is 113 * converted before being converted to the destination space. 114 * @param cspace defines the destination <code>ColorSpace</code> or an 115 * intermediate <code>ColorSpace</code> 116 * @param hints the <code>RenderingHints</code> object used to control 117 * the color conversion, or <code>null</code> 118 * @throws NullPointerException if cspace is null 119 */ 120 public ColorConvertOp (ColorSpace cspace, RenderingHints hints) 121 { 122 if (cspace == null) { 123 throw new NullPointerException("ColorSpace cannot be null"); 124 } 125 if (cspace instanceof ICC_ColorSpace) { 126 profileList = new ICC_Profile [1]; /* 1 profile in the list */ 127 128 profileList [0] = ((ICC_ColorSpace) cspace).getProfile(); 129 } 130 else { 131 CSList = new ColorSpace[1]; /* non-ICC case: 1 ColorSpace in list */ 132 CSList[0] = cspace; 133 } 134 this.hints = hints; 135 } 136 137 138 /** 139 * Constructs a new ColorConvertOp from two ColorSpace objects. 140 * The RenderingHints argument may be null. 141 * This Op is primarily useful for calling the filter method on 142 * Rasters, in which case the two ColorSpaces define the operation 143 * to be performed on the Rasters. In that case, the number of bands 144 * in the source Raster must match the number of components in 145 * srcCspace, and the number of bands in the destination Raster 146 * must match the number of components in dstCspace. For BufferedImages, 147 * the two ColorSpaces define intermediate spaces through which the 148 * source is converted before being converted to the destination space. 149 * @param srcCspace the source <code>ColorSpace</code> 150 * @param dstCspace the destination <code>ColorSpace</code> 151 * @param hints the <code>RenderingHints</code> object used to control 152 * the color conversion, or <code>null</code> 153 * @throws NullPointerException if either srcCspace or dstCspace is null 154 */ 155 public ColorConvertOp(ColorSpace srcCspace, ColorSpace dstCspace, 156 RenderingHints hints) 157 { 158 if ((srcCspace == null) || (dstCspace == null)) { 159 throw new NullPointerException("ColorSpaces cannot be null"); 160 } 161 if ((srcCspace instanceof ICC_ColorSpace) && 162 (dstCspace instanceof ICC_ColorSpace)) { 163 profileList = new ICC_Profile [2]; /* 2 profiles in the list */ 164 165 profileList [0] = ((ICC_ColorSpace) srcCspace).getProfile(); 166 profileList [1] = ((ICC_ColorSpace) dstCspace).getProfile(); 167 168 getMinMaxValsFromColorSpaces(srcCspace, dstCspace); 169 } else { 170 /* non-ICC case: 2 ColorSpaces in list */ 171 CSList = new ColorSpace[2]; 172 CSList[0] = srcCspace; 179 /** 180 * Constructs a new ColorConvertOp from an array of ICC_Profiles. 181 * The RenderingHints argument may be null. 182 * The sequence of profiles may include profiles that represent color 183 * spaces, profiles that represent effects, etc. If the whole sequence 184 * does not represent a well-defined color conversion, an exception is 185 * thrown. 186 * <p>For BufferedImages, if the ColorSpace 187 * of the source BufferedImage does not match the requirements of the 188 * first profile in the array, 189 * the first conversion is to an appropriate ColorSpace. 190 * If the requirements of the last profile in the array are not met 191 * by the ColorSpace of the destination BufferedImage, 192 * the last conversion is to the destination's ColorSpace. 193 * <p>For Rasters, the number of bands in the source Raster must match 194 * the requirements of the first profile in the array, and the 195 * number of bands in the destination Raster must match the requirements 196 * of the last profile in the array. The array must have at least two 197 * elements or calling the filter method for Rasters will throw an 198 * IllegalArgumentException. 199 * @param profiles the array of <code>ICC_Profile</code> objects 200 * @param hints the <code>RenderingHints</code> object used to control 201 * the color conversion, or <code>null</code> 202 * @exception IllegalArgumentException when the profile sequence does not 203 * specify a well-defined color conversion 204 * @exception NullPointerException if profiles is null 205 */ 206 public ColorConvertOp (ICC_Profile[] profiles, RenderingHints hints) 207 { 208 if (profiles == null) { 209 throw new NullPointerException("Profiles cannot be null"); 210 } 211 gotProfiles = true; 212 profileList = new ICC_Profile[profiles.length]; 213 for (int i1 = 0; i1 < profiles.length; i1++) { 214 profileList[i1] = profiles[i1]; 215 } 216 this.hints = hints; 217 } 218 219 220 /** 221 * Returns the array of ICC_Profiles used to construct this ColorConvertOp. 222 * Returns null if the ColorConvertOp was not constructed from such an 223 * array. 224 * @return the array of <code>ICC_Profile</code> objects of this 225 * <code>ColorConvertOp</code>, or <code>null</code> if this 226 * <code>ColorConvertOp</code> was not constructed with an 227 * array of <code>ICC_Profile</code> objects. 228 */ 229 public final ICC_Profile[] getICC_Profiles() { 230 if (gotProfiles) { 231 ICC_Profile[] profiles = new ICC_Profile[profileList.length]; 232 for (int i1 = 0; i1 < profileList.length; i1++) { 233 profiles[i1] = profileList[i1]; 234 } 235 return profiles; 236 } 237 return null; 238 } 239 240 /** 241 * ColorConverts the source BufferedImage. 242 * If the destination image is null, 243 * a BufferedImage will be created with an appropriate ColorModel. 244 * @param src the source <code>BufferedImage</code> to be converted 245 * @param dest the destination <code>BufferedImage</code>, 246 * or <code>null</code> 247 * @return <code>dest</code> color converted from <code>src</code> 248 * or a new, converted <code>BufferedImage</code> 249 * if <code>dest</code> is <code>null</code> 250 * @exception IllegalArgumentException if dest is null and this op was 251 * constructed using the constructor which takes only a 252 * RenderingHints argument, since the operation is ill defined. 253 */ 254 public final BufferedImage filter(BufferedImage src, BufferedImage dest) { 255 ColorSpace srcColorSpace, destColorSpace; 256 BufferedImage savdest = null; 257 258 if (src.getColorModel() instanceof IndexColorModel) { 259 IndexColorModel icm = (IndexColorModel) src.getColorModel(); 260 src = icm.convertToIntDiscrete(src.getRaster(), true); 261 } 262 srcColorSpace = src.getColorModel().getColorSpace(); 263 if (dest != null) { 264 if (dest.getColorModel() instanceof IndexColorModel) { 265 savdest = dest; 266 dest = null; 267 destColorSpace = null; 268 } else { 269 destColorSpace = dest.getColorModel().getColorSpace(); 438 /* "middle" profiles use simulation transform */ 439 whichTrans = ColorTransform.Simulation; 440 } 441 442 /* make the net transform */ 443 thisTransform = mdl.createTransform(theTransforms); 444 445 /* update corresponding source and dest profiles */ 446 thisSrcProfile = srcProfile; 447 thisDestProfile = destProfile; 448 } 449 450 /** 451 * ColorConverts the image data in the source Raster. 452 * If the destination Raster is null, a new Raster will be created. 453 * The number of bands in the source and destination Rasters must 454 * meet the requirements explained above. The constructor used to 455 * create this ColorConvertOp must have provided enough information 456 * to define both source and destination color spaces. See above. 457 * Otherwise, an exception is thrown. 458 * @param src the source <code>Raster</code> to be converted 459 * @param dest the destination <code>WritableRaster</code>, 460 * or <code>null</code> 461 * @return <code>dest</code> color converted from <code>src</code> 462 * or a new, converted <code>WritableRaster</code> 463 * if <code>dest</code> is <code>null</code> 464 * @exception IllegalArgumentException if the number of source or 465 * destination bands is incorrect, the source or destination 466 * color spaces are undefined, or this op was constructed 467 * with one of the constructors that applies only to 468 * operations on BufferedImages. 469 */ 470 public final WritableRaster filter (Raster src, WritableRaster dest) { 471 472 if (CSList != null) { 473 /* non-ICC case */ 474 return nonICCRasterFilter(src, dest); 475 } 476 int nProfiles = profileList.length; 477 if (nProfiles < 2) { 478 throw new IllegalArgumentException( 479 "Source or Destination ColorSpace is undefined"); 480 } 481 if (src.getNumBands() != profileList[0].getNumComponents()) { 482 throw new IllegalArgumentException( 483 "Numbers of source Raster bands and source color space " + 562 getMinMaxValsFromProfiles(profileList[0], 563 profileList[nProfiles-1]); 564 } 565 /* color convert the raster */ 566 thisRasterTransform.colorConvert(src, dest, 567 srcMinVals, srcMaxVals, 568 dstMinVals, dstMaxVals); 569 } else { 570 /* color convert the raster */ 571 thisRasterTransform.colorConvert(src, dest); 572 } 573 574 575 return dest; 576 } 577 578 /** 579 * Returns the bounding box of the destination, given this source. 580 * Note that this will be the same as the bounding box of the 581 * source. 582 * @param src the source <code>BufferedImage</code> 583 * @return a <code>Rectangle2D</code> that is the bounding box 584 * of the destination, given the specified <code>src</code> 585 */ 586 public final Rectangle2D getBounds2D (BufferedImage src) { 587 return getBounds2D(src.getRaster()); 588 } 589 590 /** 591 * Returns the bounding box of the destination, given this source. 592 * Note that this will be the same as the bounding box of the 593 * source. 594 * @param src the source <code>Raster</code> 595 * @return a <code>Rectangle2D</code> that is the bounding box 596 * of the destination, given the specified <code>src</code> 597 */ 598 public final Rectangle2D getBounds2D (Raster src) { 599 /* return new Rectangle (src.getXOffset(), 600 src.getYOffset(), 601 src.getWidth(), src.getHeight()); */ 602 return src.getBounds(); 603 } 604 605 /** 606 * Creates a zeroed destination image with the correct size and number of 607 * bands, given this source. 608 * @param src Source image for the filter operation. 609 * @param destCM ColorModel of the destination. If null, an 610 * appropriate ColorModel will be used. 611 * @return a <code>BufferedImage</code> with the correct size and 612 * number of bands from the specified <code>src</code>. 613 * @throws IllegalArgumentException if <code>destCM</code> is 614 * <code>null</code> and this <code>ColorConvertOp</code> was 615 * created without any <code>ICC_Profile</code> or 616 * <code>ColorSpace</code> defined for the destination 617 */ 618 public BufferedImage createCompatibleDestImage (BufferedImage src, 619 ColorModel destCM) { 620 ColorSpace cs = null;; 621 if (destCM == null) { 622 if (CSList == null) { 623 /* ICC case */ 624 int nProfiles = profileList.length; 625 if (nProfiles == 0) { 626 throw new IllegalArgumentException( 627 "Destination ColorSpace is undefined"); 628 } 629 ICC_Profile destProfile = profileList[nProfiles - 1]; 630 cs = new ICC_ColorSpace(destProfile); 631 } else { 632 /* non-ICC case */ 633 int nSpaces = CSList.length; 634 cs = CSList[nSpaces - 1]; 635 } 636 } 652 for (int i = 0; i < nbands; i++) { 653 nbits[i] = 8; 654 } 655 destCM = new ComponentColorModel(destCS, nbits, hasAlpha, 656 srcCM.isAlphaPremultiplied(), 657 srcCM.getTransparency(), 658 DataBuffer.TYPE_BYTE); 659 } 660 int w = src.getWidth(); 661 int h = src.getHeight(); 662 image = new BufferedImage(destCM, 663 destCM.createCompatibleWritableRaster(w, h), 664 destCM.isAlphaPremultiplied(), null); 665 return image; 666 } 667 668 669 /** 670 * Creates a zeroed destination Raster with the correct size and number of 671 * bands, given this source. 672 * @param src the specified <code>Raster</code> 673 * @return a <code>WritableRaster</code> with the correct size and number 674 * of bands from the specified <code>src</code> 675 * @throws IllegalArgumentException if this <code>ColorConvertOp</code> 676 * was created without sufficient information to define the 677 * <code>dst</code> and <code>src</code> color spaces 678 */ 679 public WritableRaster createCompatibleDestRaster (Raster src) { 680 int ncomponents; 681 682 if (CSList != null) { 683 /* non-ICC case */ 684 if (CSList.length != 2) { 685 throw new IllegalArgumentException( 686 "Destination ColorSpace is undefined"); 687 } 688 ncomponents = CSList[1].getNumComponents(); 689 } else { 690 /* ICC case */ 691 int nProfiles = profileList.length; 692 if (nProfiles < 2) { 693 throw new IllegalArgumentException( 694 "Destination ColorSpace is undefined"); 695 } 696 ncomponents = profileList[nProfiles-1].getNumComponents(); 697 } 698 699 WritableRaster dest = 700 Raster.createInterleavedRaster(DataBuffer.TYPE_BYTE, 701 src.getWidth(), 702 src.getHeight(), 703 ncomponents, 704 new Point(src.getMinX(), src.getMinY())); 705 return dest; 706 } 707 708 /** 709 * Returns the location of the destination point given a 710 * point in the source. If <code>dstPt</code> is non-null, 711 * it will be used to hold the return value. Note that 712 * for this class, the destination point will be the same 713 * as the source point. 714 * @param srcPt the specified source <code>Point2D</code> 715 * @param dstPt the destination <code>Point2D</code> 716 * @return <code>dstPt</code> after setting its location to be 717 * the same as <code>srcPt</code> 718 */ 719 public final Point2D getPoint2D (Point2D srcPt, Point2D dstPt) { 720 if (dstPt == null) { 721 dstPt = new Point2D.Float(); 722 } 723 dstPt.setLocation(srcPt.getX(), srcPt.getY()); 724 725 return dstPt; 726 } 727 728 729 /** 730 * Returns the RenderingIntent from the specified ICC Profile. 731 */ 732 private int getRenderingIntent (ICC_Profile profile) { 733 byte[] header = profile.getData(ICC_Profile.icSigHead); 734 int index = ICC_Profile.icHdrRenderingIntent; 735 736 /* According to ICC spec, only the least-significant 16 bits shall be 737 * used to encode the rendering intent. The most significant 16 bits 738 * shall be set to zero. Thus, we are ignoring two most significant 739 * bytes here. 740 * 741 * See http://www.color.org/ICC1v42_2006-05.pdf, section 7.2.15. 742 */ 743 return ((header[index+2] & 0xff) << 8) | 744 (header[index+3] & 0xff); 745 } 746 747 /** 748 * Returns the rendering hints used by this op. 749 * @return the <code>RenderingHints</code> object of this 750 * <code>ColorConvertOp</code> 751 */ 752 public final RenderingHints getRenderingHints() { 753 return hints; 754 } 755 756 private final BufferedImage nonICCBIFilter(BufferedImage src, 757 ColorSpace srcColorSpace, 758 BufferedImage dst, 759 ColorSpace dstColorSpace) { 760 761 int w = src.getWidth(); 762 int h = src.getHeight(); 763 ICC_ColorSpace ciespace = 764 (ICC_ColorSpace) ColorSpace.getInstance(ColorSpace.CS_CIEXYZ); 765 if (dst == null) { 766 dst = createCompatibleDestImage(src, null); 767 dstColorSpace = dst.getColorModel().getColorSpace(); 768 } else { 769 if ((h != dst.getHeight()) || (w != dst.getWidth())) { 770 throw new IllegalArgumentException( | 75 ICC_Profile thisSrcProfile, thisDestProfile; 76 RenderingHints hints; 77 boolean gotProfiles; 78 float[] srcMinVals, srcMaxVals, dstMinVals, dstMaxVals; 79 80 /* the class initializer */ 81 static { 82 if (ProfileDeferralMgr.deferring) { 83 ProfileDeferralMgr.activateProfiles(); 84 } 85 } 86 87 /** 88 * Constructs a new ColorConvertOp which will convert 89 * from a source color space to a destination color space. 90 * The RenderingHints argument may be null. 91 * This Op can be used only with BufferedImages, and will convert 92 * directly from the ColorSpace of the source image to that of the 93 * destination. The destination argument of the filter method 94 * cannot be specified as null. 95 * @param hints the {@code RenderingHints} object used to control 96 * the color conversion, or {@code null} 97 */ 98 public ColorConvertOp (RenderingHints hints) 99 { 100 profileList = new ICC_Profile [0]; /* 0 length list */ 101 this.hints = hints; 102 } 103 104 /** 105 * Constructs a new ColorConvertOp from a ColorSpace object. 106 * The RenderingHints argument may be null. This 107 * Op can be used only with BufferedImages, and is primarily useful 108 * when the {@link #filter(BufferedImage, BufferedImage) filter} 109 * method is invoked with a destination argument of null. 110 * In that case, the ColorSpace defines the destination color space 111 * for the destination created by the filter method. Otherwise, the 112 * ColorSpace defines an intermediate space to which the source is 113 * converted before being converted to the destination space. 114 * @param cspace defines the destination {@code ColorSpace} or an 115 * intermediate {@code ColorSpace} 116 * @param hints the {@code RenderingHints} object used to control 117 * the color conversion, or {@code null} 118 * @throws NullPointerException if cspace is null 119 */ 120 public ColorConvertOp (ColorSpace cspace, RenderingHints hints) 121 { 122 if (cspace == null) { 123 throw new NullPointerException("ColorSpace cannot be null"); 124 } 125 if (cspace instanceof ICC_ColorSpace) { 126 profileList = new ICC_Profile [1]; /* 1 profile in the list */ 127 128 profileList [0] = ((ICC_ColorSpace) cspace).getProfile(); 129 } 130 else { 131 CSList = new ColorSpace[1]; /* non-ICC case: 1 ColorSpace in list */ 132 CSList[0] = cspace; 133 } 134 this.hints = hints; 135 } 136 137 138 /** 139 * Constructs a new ColorConvertOp from two ColorSpace objects. 140 * The RenderingHints argument may be null. 141 * This Op is primarily useful for calling the filter method on 142 * Rasters, in which case the two ColorSpaces define the operation 143 * to be performed on the Rasters. In that case, the number of bands 144 * in the source Raster must match the number of components in 145 * srcCspace, and the number of bands in the destination Raster 146 * must match the number of components in dstCspace. For BufferedImages, 147 * the two ColorSpaces define intermediate spaces through which the 148 * source is converted before being converted to the destination space. 149 * @param srcCspace the source {@code ColorSpace} 150 * @param dstCspace the destination {@code ColorSpace} 151 * @param hints the {@code RenderingHints} object used to control 152 * the color conversion, or {@code null} 153 * @throws NullPointerException if either srcCspace or dstCspace is null 154 */ 155 public ColorConvertOp(ColorSpace srcCspace, ColorSpace dstCspace, 156 RenderingHints hints) 157 { 158 if ((srcCspace == null) || (dstCspace == null)) { 159 throw new NullPointerException("ColorSpaces cannot be null"); 160 } 161 if ((srcCspace instanceof ICC_ColorSpace) && 162 (dstCspace instanceof ICC_ColorSpace)) { 163 profileList = new ICC_Profile [2]; /* 2 profiles in the list */ 164 165 profileList [0] = ((ICC_ColorSpace) srcCspace).getProfile(); 166 profileList [1] = ((ICC_ColorSpace) dstCspace).getProfile(); 167 168 getMinMaxValsFromColorSpaces(srcCspace, dstCspace); 169 } else { 170 /* non-ICC case: 2 ColorSpaces in list */ 171 CSList = new ColorSpace[2]; 172 CSList[0] = srcCspace; 179 /** 180 * Constructs a new ColorConvertOp from an array of ICC_Profiles. 181 * The RenderingHints argument may be null. 182 * The sequence of profiles may include profiles that represent color 183 * spaces, profiles that represent effects, etc. If the whole sequence 184 * does not represent a well-defined color conversion, an exception is 185 * thrown. 186 * <p>For BufferedImages, if the ColorSpace 187 * of the source BufferedImage does not match the requirements of the 188 * first profile in the array, 189 * the first conversion is to an appropriate ColorSpace. 190 * If the requirements of the last profile in the array are not met 191 * by the ColorSpace of the destination BufferedImage, 192 * the last conversion is to the destination's ColorSpace. 193 * <p>For Rasters, the number of bands in the source Raster must match 194 * the requirements of the first profile in the array, and the 195 * number of bands in the destination Raster must match the requirements 196 * of the last profile in the array. The array must have at least two 197 * elements or calling the filter method for Rasters will throw an 198 * IllegalArgumentException. 199 * @param profiles the array of {@code ICC_Profile} objects 200 * @param hints the {@code RenderingHints} object used to control 201 * the color conversion, or {@code null} 202 * @exception IllegalArgumentException when the profile sequence does not 203 * specify a well-defined color conversion 204 * @exception NullPointerException if profiles is null 205 */ 206 public ColorConvertOp (ICC_Profile[] profiles, RenderingHints hints) 207 { 208 if (profiles == null) { 209 throw new NullPointerException("Profiles cannot be null"); 210 } 211 gotProfiles = true; 212 profileList = new ICC_Profile[profiles.length]; 213 for (int i1 = 0; i1 < profiles.length; i1++) { 214 profileList[i1] = profiles[i1]; 215 } 216 this.hints = hints; 217 } 218 219 220 /** 221 * Returns the array of ICC_Profiles used to construct this ColorConvertOp. 222 * Returns null if the ColorConvertOp was not constructed from such an 223 * array. 224 * @return the array of {@code ICC_Profile} objects of this 225 * {@code ColorConvertOp}, or {@code null} if this 226 * {@code ColorConvertOp} was not constructed with an 227 * array of {@code ICC_Profile} objects. 228 */ 229 public final ICC_Profile[] getICC_Profiles() { 230 if (gotProfiles) { 231 ICC_Profile[] profiles = new ICC_Profile[profileList.length]; 232 for (int i1 = 0; i1 < profileList.length; i1++) { 233 profiles[i1] = profileList[i1]; 234 } 235 return profiles; 236 } 237 return null; 238 } 239 240 /** 241 * ColorConverts the source BufferedImage. 242 * If the destination image is null, 243 * a BufferedImage will be created with an appropriate ColorModel. 244 * @param src the source {@code BufferedImage} to be converted 245 * @param dest the destination {@code BufferedImage}, 246 * or {@code null} 247 * @return {@code dest} color converted from {@code src} 248 * or a new, converted {@code BufferedImage} 249 * if {@code dest} is {@code null} 250 * @exception IllegalArgumentException if dest is null and this op was 251 * constructed using the constructor which takes only a 252 * RenderingHints argument, since the operation is ill defined. 253 */ 254 public final BufferedImage filter(BufferedImage src, BufferedImage dest) { 255 ColorSpace srcColorSpace, destColorSpace; 256 BufferedImage savdest = null; 257 258 if (src.getColorModel() instanceof IndexColorModel) { 259 IndexColorModel icm = (IndexColorModel) src.getColorModel(); 260 src = icm.convertToIntDiscrete(src.getRaster(), true); 261 } 262 srcColorSpace = src.getColorModel().getColorSpace(); 263 if (dest != null) { 264 if (dest.getColorModel() instanceof IndexColorModel) { 265 savdest = dest; 266 dest = null; 267 destColorSpace = null; 268 } else { 269 destColorSpace = dest.getColorModel().getColorSpace(); 438 /* "middle" profiles use simulation transform */ 439 whichTrans = ColorTransform.Simulation; 440 } 441 442 /* make the net transform */ 443 thisTransform = mdl.createTransform(theTransforms); 444 445 /* update corresponding source and dest profiles */ 446 thisSrcProfile = srcProfile; 447 thisDestProfile = destProfile; 448 } 449 450 /** 451 * ColorConverts the image data in the source Raster. 452 * If the destination Raster is null, a new Raster will be created. 453 * The number of bands in the source and destination Rasters must 454 * meet the requirements explained above. The constructor used to 455 * create this ColorConvertOp must have provided enough information 456 * to define both source and destination color spaces. See above. 457 * Otherwise, an exception is thrown. 458 * @param src the source {@code Raster} to be converted 459 * @param dest the destination {@code WritableRaster}, 460 * or {@code null} 461 * @return {@code dest} color converted from {@code src} 462 * or a new, converted {@code WritableRaster} 463 * if {@code dest} is {@code null} 464 * @exception IllegalArgumentException if the number of source or 465 * destination bands is incorrect, the source or destination 466 * color spaces are undefined, or this op was constructed 467 * with one of the constructors that applies only to 468 * operations on BufferedImages. 469 */ 470 public final WritableRaster filter (Raster src, WritableRaster dest) { 471 472 if (CSList != null) { 473 /* non-ICC case */ 474 return nonICCRasterFilter(src, dest); 475 } 476 int nProfiles = profileList.length; 477 if (nProfiles < 2) { 478 throw new IllegalArgumentException( 479 "Source or Destination ColorSpace is undefined"); 480 } 481 if (src.getNumBands() != profileList[0].getNumComponents()) { 482 throw new IllegalArgumentException( 483 "Numbers of source Raster bands and source color space " + 562 getMinMaxValsFromProfiles(profileList[0], 563 profileList[nProfiles-1]); 564 } 565 /* color convert the raster */ 566 thisRasterTransform.colorConvert(src, dest, 567 srcMinVals, srcMaxVals, 568 dstMinVals, dstMaxVals); 569 } else { 570 /* color convert the raster */ 571 thisRasterTransform.colorConvert(src, dest); 572 } 573 574 575 return dest; 576 } 577 578 /** 579 * Returns the bounding box of the destination, given this source. 580 * Note that this will be the same as the bounding box of the 581 * source. 582 * @param src the source {@code BufferedImage} 583 * @return a {@code Rectangle2D} that is the bounding box 584 * of the destination, given the specified {@code src} 585 */ 586 public final Rectangle2D getBounds2D (BufferedImage src) { 587 return getBounds2D(src.getRaster()); 588 } 589 590 /** 591 * Returns the bounding box of the destination, given this source. 592 * Note that this will be the same as the bounding box of the 593 * source. 594 * @param src the source {@code Raster} 595 * @return a {@code Rectangle2D} that is the bounding box 596 * of the destination, given the specified {@code src} 597 */ 598 public final Rectangle2D getBounds2D (Raster src) { 599 /* return new Rectangle (src.getXOffset(), 600 src.getYOffset(), 601 src.getWidth(), src.getHeight()); */ 602 return src.getBounds(); 603 } 604 605 /** 606 * Creates a zeroed destination image with the correct size and number of 607 * bands, given this source. 608 * @param src Source image for the filter operation. 609 * @param destCM ColorModel of the destination. If null, an 610 * appropriate ColorModel will be used. 611 * @return a {@code BufferedImage} with the correct size and 612 * number of bands from the specified {@code src}. 613 * @throws IllegalArgumentException if {@code destCM} is 614 * {@code null} and this {@code ColorConvertOp} was 615 * created without any {@code ICC_Profile} or 616 * {@code ColorSpace} defined for the destination 617 */ 618 public BufferedImage createCompatibleDestImage (BufferedImage src, 619 ColorModel destCM) { 620 ColorSpace cs = null;; 621 if (destCM == null) { 622 if (CSList == null) { 623 /* ICC case */ 624 int nProfiles = profileList.length; 625 if (nProfiles == 0) { 626 throw new IllegalArgumentException( 627 "Destination ColorSpace is undefined"); 628 } 629 ICC_Profile destProfile = profileList[nProfiles - 1]; 630 cs = new ICC_ColorSpace(destProfile); 631 } else { 632 /* non-ICC case */ 633 int nSpaces = CSList.length; 634 cs = CSList[nSpaces - 1]; 635 } 636 } 652 for (int i = 0; i < nbands; i++) { 653 nbits[i] = 8; 654 } 655 destCM = new ComponentColorModel(destCS, nbits, hasAlpha, 656 srcCM.isAlphaPremultiplied(), 657 srcCM.getTransparency(), 658 DataBuffer.TYPE_BYTE); 659 } 660 int w = src.getWidth(); 661 int h = src.getHeight(); 662 image = new BufferedImage(destCM, 663 destCM.createCompatibleWritableRaster(w, h), 664 destCM.isAlphaPremultiplied(), null); 665 return image; 666 } 667 668 669 /** 670 * Creates a zeroed destination Raster with the correct size and number of 671 * bands, given this source. 672 * @param src the specified {@code Raster} 673 * @return a {@code WritableRaster} with the correct size and number 674 * of bands from the specified {@code src} 675 * @throws IllegalArgumentException if this {@code ColorConvertOp} 676 * was created without sufficient information to define the 677 * {@code dst} and {@code src} color spaces 678 */ 679 public WritableRaster createCompatibleDestRaster (Raster src) { 680 int ncomponents; 681 682 if (CSList != null) { 683 /* non-ICC case */ 684 if (CSList.length != 2) { 685 throw new IllegalArgumentException( 686 "Destination ColorSpace is undefined"); 687 } 688 ncomponents = CSList[1].getNumComponents(); 689 } else { 690 /* ICC case */ 691 int nProfiles = profileList.length; 692 if (nProfiles < 2) { 693 throw new IllegalArgumentException( 694 "Destination ColorSpace is undefined"); 695 } 696 ncomponents = profileList[nProfiles-1].getNumComponents(); 697 } 698 699 WritableRaster dest = 700 Raster.createInterleavedRaster(DataBuffer.TYPE_BYTE, 701 src.getWidth(), 702 src.getHeight(), 703 ncomponents, 704 new Point(src.getMinX(), src.getMinY())); 705 return dest; 706 } 707 708 /** 709 * Returns the location of the destination point given a 710 * point in the source. If {@code dstPt} is non-null, 711 * it will be used to hold the return value. Note that 712 * for this class, the destination point will be the same 713 * as the source point. 714 * @param srcPt the specified source {@code Point2D} 715 * @param dstPt the destination {@code Point2D} 716 * @return {@code dstPt} after setting its location to be 717 * the same as {@code srcPt} 718 */ 719 public final Point2D getPoint2D (Point2D srcPt, Point2D dstPt) { 720 if (dstPt == null) { 721 dstPt = new Point2D.Float(); 722 } 723 dstPt.setLocation(srcPt.getX(), srcPt.getY()); 724 725 return dstPt; 726 } 727 728 729 /** 730 * Returns the RenderingIntent from the specified ICC Profile. 731 */ 732 private int getRenderingIntent (ICC_Profile profile) { 733 byte[] header = profile.getData(ICC_Profile.icSigHead); 734 int index = ICC_Profile.icHdrRenderingIntent; 735 736 /* According to ICC spec, only the least-significant 16 bits shall be 737 * used to encode the rendering intent. The most significant 16 bits 738 * shall be set to zero. Thus, we are ignoring two most significant 739 * bytes here. 740 * 741 * See http://www.color.org/ICC1v42_2006-05.pdf, section 7.2.15. 742 */ 743 return ((header[index+2] & 0xff) << 8) | 744 (header[index+3] & 0xff); 745 } 746 747 /** 748 * Returns the rendering hints used by this op. 749 * @return the {@code RenderingHints} object of this 750 * {@code ColorConvertOp} 751 */ 752 public final RenderingHints getRenderingHints() { 753 return hints; 754 } 755 756 private final BufferedImage nonICCBIFilter(BufferedImage src, 757 ColorSpace srcColorSpace, 758 BufferedImage dst, 759 ColorSpace dstColorSpace) { 760 761 int w = src.getWidth(); 762 int h = src.getHeight(); 763 ICC_ColorSpace ciespace = 764 (ICC_ColorSpace) ColorSpace.getInstance(ColorSpace.CS_CIEXYZ); 765 if (dst == null) { 766 dst = createCompatibleDestImage(src, null); 767 dstColorSpace = dst.getColorModel().getColorSpace(); 768 } else { 769 if ((h != dst.getHeight()) || (w != dst.getWidth())) { 770 throw new IllegalArgumentException( |