64 * <p> 65 * Images with an IndexColorModel cannot be used. 66 * <p> 67 * If a RenderingHints object is specified in the constructor, the 68 * color rendering hint and the dithering hint may be used when color 69 * conversion is required. 70 * <p> 71 * This class allows the Source to be the same as the Destination. 72 * 73 * @see LookupTable 74 * @see java.awt.RenderingHints#KEY_COLOR_RENDERING 75 * @see java.awt.RenderingHints#KEY_DITHERING 76 */ 77 78 public class LookupOp implements BufferedImageOp, RasterOp { 79 private LookupTable ltable; 80 private int numComponents; 81 RenderingHints hints; 82 83 /** 84 * Constructs a <code>LookupOp</code> object given the lookup 85 * table and a <code>RenderingHints</code> object, which might 86 * be <code>null</code>. 87 * @param lookup the specified <code>LookupTable</code> 88 * @param hints the specified <code>RenderingHints</code>, 89 * or <code>null</code> 90 */ 91 public LookupOp(LookupTable lookup, RenderingHints hints) { 92 this.ltable = lookup; 93 this.hints = hints; 94 numComponents = ltable.getNumComponents(); 95 } 96 97 /** 98 * Returns the <code>LookupTable</code>. 99 * @return the <code>LookupTable</code> of this 100 * <code>LookupOp</code>. 101 */ 102 public final LookupTable getTable() { 103 return ltable; 104 } 105 106 /** 107 * Performs a lookup operation on a <code>BufferedImage</code>. 108 * If the color model in the source image is not the same as that 109 * in the destination image, the pixels will be converted 110 * in the destination. If the destination image is <code>null</code>, 111 * a <code>BufferedImage</code> will be created with an appropriate 112 * <code>ColorModel</code>. An <code>IllegalArgumentException</code> 113 * might be thrown if the number of arrays in the 114 * <code>LookupTable</code> does not meet the restrictions 115 * stated in the class comment above, or if the source image 116 * has an <code>IndexColorModel</code>. 117 * @param src the <code>BufferedImage</code> to be filtered 118 * @param dst the <code>BufferedImage</code> in which to 119 * store the results of the filter operation 120 * @return the filtered <code>BufferedImage</code>. 121 * @throws IllegalArgumentException if the number of arrays in the 122 * <code>LookupTable</code> does not meet the restrictions 123 * described in the class comments, or if the source image 124 * has an <code>IndexColorModel</code>. 125 */ 126 public final BufferedImage filter(BufferedImage src, BufferedImage dst) { 127 ColorModel srcCM = src.getColorModel(); 128 int numBands = srcCM.getNumColorComponents(); 129 ColorModel dstCM; 130 if (srcCM instanceof IndexColorModel) { 131 throw new 132 IllegalArgumentException("LookupOp cannot be "+ 133 "performed on an indexed image"); 134 } 135 int numComponents = ltable.getNumComponents(); 136 if (numComponents != 1 && 137 numComponents != srcCM.getNumComponents() && 138 numComponents != srcCM.getNumColorComponents()) 139 { 140 throw new IllegalArgumentException("Number of arrays in the "+ 141 " lookup table ("+ 142 numComponents+ 143 " is not compatible with the "+ 144 " src image: "+src); 215 dstRaster.getWidth(), 216 dstRaster.getHeight(), 217 minx, miny, 218 bands); 219 } 220 } 221 222 filter(srcRaster, dstRaster); 223 } 224 225 if (needToConvert) { 226 // ColorModels are not the same 227 ColorConvertOp ccop = new ColorConvertOp(hints); 228 ccop.filter(dst, origDst); 229 } 230 231 return origDst; 232 } 233 234 /** 235 * Performs a lookup operation on a <code>Raster</code>. 236 * If the destination <code>Raster</code> is <code>null</code>, 237 * a new <code>Raster</code> will be created. 238 * The <code>IllegalArgumentException</code> might be thrown 239 * if the source <code>Raster</code> and the destination 240 * <code>Raster</code> do not have the same 241 * number of bands or if the number of arrays in the 242 * <code>LookupTable</code> does not meet the 243 * restrictions stated in the class comment above. 244 * @param src the source <code>Raster</code> to filter 245 * @param dst the destination <code>WritableRaster</code> for the 246 * filtered <code>src</code> 247 * @return the filtered <code>WritableRaster</code>. 248 * @throws IllegalArgumentException if the source and destinations 249 * rasters do not have the same number of bands, or the 250 * number of arrays in the <code>LookupTable</code> does 251 * not meet the restrictions described in the class comments. 252 * 253 */ 254 public final WritableRaster filter (Raster src, WritableRaster dst) { 255 int numBands = src.getNumBands(); 256 int dstLength = dst.getNumBands(); 257 int height = src.getHeight(); 258 int width = src.getWidth(); 259 int srcPix[] = new int[numBands]; 260 261 // Create a new destination Raster, if needed 262 263 if (dst == null) { 264 dst = createCompatibleDestRaster(src); 265 } 266 else if (height != dst.getHeight() || width != dst.getWidth()) { 267 throw new 268 IllegalArgumentException ("Width or height of Rasters do not "+ 269 "match"); 270 } 313 for (int x=0; x < width; x++, sX++, dX++) { 314 // Find data for all bands at this x,y position 315 src.getPixel(sX, sY, srcPix); 316 317 // Lookup the data for all bands at this x,y position 318 ltable.lookupPixel(srcPix, srcPix); 319 320 // Put it back for all bands 321 dst.setPixel(dX, dY, srcPix); 322 } 323 } 324 } 325 326 return dst; 327 } 328 329 /** 330 * Returns the bounding box of the filtered destination image. Since 331 * this is not a geometric operation, the bounding box does not 332 * change. 333 * @param src the <code>BufferedImage</code> to be filtered 334 * @return the bounds of the filtered definition image. 335 */ 336 public final Rectangle2D getBounds2D (BufferedImage src) { 337 return getBounds2D(src.getRaster()); 338 } 339 340 /** 341 * Returns the bounding box of the filtered destination Raster. Since 342 * this is not a geometric operation, the bounding box does not 343 * change. 344 * @param src the <code>Raster</code> to be filtered 345 * @return the bounds of the filtered definition <code>Raster</code>. 346 */ 347 public final Rectangle2D getBounds2D (Raster src) { 348 return src.getBounds(); 349 350 } 351 352 /** 353 * Creates a zeroed destination image with the correct size and number of 354 * bands. If destCM is <code>null</code>, an appropriate 355 * <code>ColorModel</code> will be used. 356 * @param src Source image for the filter operation. 357 * @param destCM the destination's <code>ColorModel</code>, which 358 * can be <code>null</code>. 359 * @return a filtered destination <code>BufferedImage</code>. 360 */ 361 public BufferedImage createCompatibleDestImage (BufferedImage src, 362 ColorModel destCM) { 363 BufferedImage image; 364 int w = src.getWidth(); 365 int h = src.getHeight(); 366 int transferType = DataBuffer.TYPE_BYTE; 367 if (destCM == null) { 368 ColorModel cm = src.getColorModel(); 369 Raster raster = src.getRaster(); 370 if (cm instanceof ComponentColorModel) { 371 DataBuffer db = raster.getDataBuffer(); 372 boolean hasAlpha = cm.hasAlpha(); 373 boolean isPre = cm.isAlphaPremultiplied(); 374 int trans = cm.getTransparency(); 375 int[] nbits = null; 376 if (ltable instanceof ByteLookupTable) { 377 if (db.getDataType() == DataBuffer.TYPE_USHORT) { 378 // Dst raster should be of type byte 379 if (hasAlpha) { 416 trans, transferType); 417 } 418 } 419 image = new BufferedImage(cm, 420 cm.createCompatibleWritableRaster(w, h), 421 cm.isAlphaPremultiplied(), 422 null); 423 } 424 else { 425 image = new BufferedImage(destCM, 426 destCM.createCompatibleWritableRaster(w, 427 h), 428 destCM.isAlphaPremultiplied(), 429 null); 430 } 431 432 return image; 433 } 434 435 /** 436 * Creates a zeroed-destination <code>Raster</code> with the 437 * correct size and number of bands, given this source. 438 * @param src the <code>Raster</code> to be transformed 439 * @return the zeroed-destination <code>Raster</code>. 440 */ 441 public WritableRaster createCompatibleDestRaster (Raster src) { 442 return src.createCompatibleWritableRaster(); 443 } 444 445 /** 446 * Returns the location of the destination point given a 447 * point in the source. If <code>dstPt</code> is not 448 * <code>null</code>, it will be used to hold the return value. 449 * Since this is not a geometric operation, the <code>srcPt</code> 450 * will equal the <code>dstPt</code>. 451 * @param srcPt a <code>Point2D</code> that represents a point 452 * in the source image 453 * @param dstPt a <code>Point2D</code>that represents the location 454 * in the destination 455 * @return the <code>Point2D</code> in the destination that 456 * corresponds to the specified point in the source. 457 */ 458 public final Point2D getPoint2D (Point2D srcPt, Point2D dstPt) { 459 if (dstPt == null) { 460 dstPt = new Point2D.Float(); 461 } 462 dstPt.setLocation(srcPt.getX(), srcPt.getY()); 463 464 return dstPt; 465 } 466 467 /** 468 * Returns the rendering hints for this op. 469 * @return the <code>RenderingHints</code> object associated 470 * with this op. 471 */ 472 public final RenderingHints getRenderingHints() { 473 return hints; 474 } 475 476 private final void byteFilter(ByteLookupTable lookup, Raster src, 477 WritableRaster dst, 478 int width, int height, int numBands) { 479 int[] srcPix = null; 480 481 // Find the ref to the table and the offset 482 byte[][] table = lookup.getTable(); 483 int offset = lookup.getOffset(); 484 int tidx; 485 int step=1; 486 487 // Check if it is one lookup applied to all bands 488 if (table.length == 1) { 489 step=0; | 64 * <p> 65 * Images with an IndexColorModel cannot be used. 66 * <p> 67 * If a RenderingHints object is specified in the constructor, the 68 * color rendering hint and the dithering hint may be used when color 69 * conversion is required. 70 * <p> 71 * This class allows the Source to be the same as the Destination. 72 * 73 * @see LookupTable 74 * @see java.awt.RenderingHints#KEY_COLOR_RENDERING 75 * @see java.awt.RenderingHints#KEY_DITHERING 76 */ 77 78 public class LookupOp implements BufferedImageOp, RasterOp { 79 private LookupTable ltable; 80 private int numComponents; 81 RenderingHints hints; 82 83 /** 84 * Constructs a {@code LookupOp} object given the lookup 85 * table and a {@code RenderingHints} object, which might 86 * be {@code null}. 87 * @param lookup the specified {@code LookupTable} 88 * @param hints the specified {@code RenderingHints}, 89 * or {@code null} 90 */ 91 public LookupOp(LookupTable lookup, RenderingHints hints) { 92 this.ltable = lookup; 93 this.hints = hints; 94 numComponents = ltable.getNumComponents(); 95 } 96 97 /** 98 * Returns the {@code LookupTable}. 99 * @return the {@code LookupTable} of this 100 * {@code LookupOp}. 101 */ 102 public final LookupTable getTable() { 103 return ltable; 104 } 105 106 /** 107 * Performs a lookup operation on a {@code BufferedImage}. 108 * If the color model in the source image is not the same as that 109 * in the destination image, the pixels will be converted 110 * in the destination. If the destination image is {@code null}, 111 * a {@code BufferedImage} will be created with an appropriate 112 * {@code ColorModel}. An {@code IllegalArgumentException} 113 * might be thrown if the number of arrays in the 114 * {@code LookupTable} does not meet the restrictions 115 * stated in the class comment above, or if the source image 116 * has an {@code IndexColorModel}. 117 * @param src the {@code BufferedImage} to be filtered 118 * @param dst the {@code BufferedImage} in which to 119 * store the results of the filter operation 120 * @return the filtered {@code BufferedImage}. 121 * @throws IllegalArgumentException if the number of arrays in the 122 * {@code LookupTable} does not meet the restrictions 123 * described in the class comments, or if the source image 124 * has an {@code IndexColorModel}. 125 */ 126 public final BufferedImage filter(BufferedImage src, BufferedImage dst) { 127 ColorModel srcCM = src.getColorModel(); 128 int numBands = srcCM.getNumColorComponents(); 129 ColorModel dstCM; 130 if (srcCM instanceof IndexColorModel) { 131 throw new 132 IllegalArgumentException("LookupOp cannot be "+ 133 "performed on an indexed image"); 134 } 135 int numComponents = ltable.getNumComponents(); 136 if (numComponents != 1 && 137 numComponents != srcCM.getNumComponents() && 138 numComponents != srcCM.getNumColorComponents()) 139 { 140 throw new IllegalArgumentException("Number of arrays in the "+ 141 " lookup table ("+ 142 numComponents+ 143 " is not compatible with the "+ 144 " src image: "+src); 215 dstRaster.getWidth(), 216 dstRaster.getHeight(), 217 minx, miny, 218 bands); 219 } 220 } 221 222 filter(srcRaster, dstRaster); 223 } 224 225 if (needToConvert) { 226 // ColorModels are not the same 227 ColorConvertOp ccop = new ColorConvertOp(hints); 228 ccop.filter(dst, origDst); 229 } 230 231 return origDst; 232 } 233 234 /** 235 * Performs a lookup operation on a {@code Raster}. 236 * If the destination {@code Raster} is {@code null}, 237 * a new {@code Raster} will be created. 238 * The {@code IllegalArgumentException} might be thrown 239 * if the source {@code Raster} and the destination 240 * {@code Raster} do not have the same 241 * number of bands or if the number of arrays in the 242 * {@code LookupTable} does not meet the 243 * restrictions stated in the class comment above. 244 * @param src the source {@code Raster} to filter 245 * @param dst the destination {@code WritableRaster} for the 246 * filtered {@code src} 247 * @return the filtered {@code WritableRaster}. 248 * @throws IllegalArgumentException if the source and destinations 249 * rasters do not have the same number of bands, or the 250 * number of arrays in the {@code LookupTable} does 251 * not meet the restrictions described in the class comments. 252 * 253 */ 254 public final WritableRaster filter (Raster src, WritableRaster dst) { 255 int numBands = src.getNumBands(); 256 int dstLength = dst.getNumBands(); 257 int height = src.getHeight(); 258 int width = src.getWidth(); 259 int srcPix[] = new int[numBands]; 260 261 // Create a new destination Raster, if needed 262 263 if (dst == null) { 264 dst = createCompatibleDestRaster(src); 265 } 266 else if (height != dst.getHeight() || width != dst.getWidth()) { 267 throw new 268 IllegalArgumentException ("Width or height of Rasters do not "+ 269 "match"); 270 } 313 for (int x=0; x < width; x++, sX++, dX++) { 314 // Find data for all bands at this x,y position 315 src.getPixel(sX, sY, srcPix); 316 317 // Lookup the data for all bands at this x,y position 318 ltable.lookupPixel(srcPix, srcPix); 319 320 // Put it back for all bands 321 dst.setPixel(dX, dY, srcPix); 322 } 323 } 324 } 325 326 return dst; 327 } 328 329 /** 330 * Returns the bounding box of the filtered destination image. Since 331 * this is not a geometric operation, the bounding box does not 332 * change. 333 * @param src the {@code BufferedImage} to be filtered 334 * @return the bounds of the filtered definition image. 335 */ 336 public final Rectangle2D getBounds2D (BufferedImage src) { 337 return getBounds2D(src.getRaster()); 338 } 339 340 /** 341 * Returns the bounding box of the filtered destination Raster. Since 342 * this is not a geometric operation, the bounding box does not 343 * change. 344 * @param src the {@code Raster} to be filtered 345 * @return the bounds of the filtered definition {@code Raster}. 346 */ 347 public final Rectangle2D getBounds2D (Raster src) { 348 return src.getBounds(); 349 350 } 351 352 /** 353 * Creates a zeroed destination image with the correct size and number of 354 * bands. If destCM is {@code null}, an appropriate 355 * {@code ColorModel} will be used. 356 * @param src Source image for the filter operation. 357 * @param destCM the destination's {@code ColorModel}, which 358 * can be {@code null}. 359 * @return a filtered destination {@code BufferedImage}. 360 */ 361 public BufferedImage createCompatibleDestImage (BufferedImage src, 362 ColorModel destCM) { 363 BufferedImage image; 364 int w = src.getWidth(); 365 int h = src.getHeight(); 366 int transferType = DataBuffer.TYPE_BYTE; 367 if (destCM == null) { 368 ColorModel cm = src.getColorModel(); 369 Raster raster = src.getRaster(); 370 if (cm instanceof ComponentColorModel) { 371 DataBuffer db = raster.getDataBuffer(); 372 boolean hasAlpha = cm.hasAlpha(); 373 boolean isPre = cm.isAlphaPremultiplied(); 374 int trans = cm.getTransparency(); 375 int[] nbits = null; 376 if (ltable instanceof ByteLookupTable) { 377 if (db.getDataType() == DataBuffer.TYPE_USHORT) { 378 // Dst raster should be of type byte 379 if (hasAlpha) { 416 trans, transferType); 417 } 418 } 419 image = new BufferedImage(cm, 420 cm.createCompatibleWritableRaster(w, h), 421 cm.isAlphaPremultiplied(), 422 null); 423 } 424 else { 425 image = new BufferedImage(destCM, 426 destCM.createCompatibleWritableRaster(w, 427 h), 428 destCM.isAlphaPremultiplied(), 429 null); 430 } 431 432 return image; 433 } 434 435 /** 436 * Creates a zeroed-destination {@code Raster} with the 437 * correct size and number of bands, given this source. 438 * @param src the {@code Raster} to be transformed 439 * @return the zeroed-destination {@code Raster}. 440 */ 441 public WritableRaster createCompatibleDestRaster (Raster src) { 442 return src.createCompatibleWritableRaster(); 443 } 444 445 /** 446 * Returns the location of the destination point given a 447 * point in the source. If {@code dstPt} is not 448 * {@code null}, it will be used to hold the return value. 449 * Since this is not a geometric operation, the {@code srcPt} 450 * will equal the {@code dstPt}. 451 * @param srcPt a {@code Point2D} that represents a point 452 * in the source image 453 * @param dstPt a {@code Point2D} that represents the location 454 * in the destination 455 * @return the {@code Point2D} in the destination that 456 * corresponds to the specified point in the source. 457 */ 458 public final Point2D getPoint2D (Point2D srcPt, Point2D dstPt) { 459 if (dstPt == null) { 460 dstPt = new Point2D.Float(); 461 } 462 dstPt.setLocation(srcPt.getX(), srcPt.getY()); 463 464 return dstPt; 465 } 466 467 /** 468 * Returns the rendering hints for this op. 469 * @return the {@code RenderingHints} object associated 470 * with this op. 471 */ 472 public final RenderingHints getRenderingHints() { 473 return hints; 474 } 475 476 private final void byteFilter(ByteLookupTable lookup, Raster src, 477 WritableRaster dst, 478 int width, int height, int numBands) { 479 int[] srcPix = null; 480 481 // Find the ref to the table and the offset 482 byte[][] table = lookup.getTable(); 483 int offset = lookup.getOffset(); 484 int tidx; 485 int step=1; 486 487 // Check if it is one lookup applied to all bands 488 if (table.length == 1) { 489 step=0; |