1 /* 2 * Copyright (c) 1997, 2018, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. Oracle designates this 8 * particular file as subject to the "Classpath" exception as provided 9 * by Oracle in the LICENSE file that accompanied this code. 10 * 11 * This code is distributed in the hope that it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 * version 2 for more details (a copy is included in the LICENSE file that 15 * accompanied this code). 16 * 17 * You should have received a copy of the GNU General Public License version 18 * 2 along with this work; if not, write to the Free Software Foundation, 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 * 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 * or visit www.oracle.com if you need additional information or have any 23 * questions. 24 */ 25 26 package java.awt.image; 27 28 import java.util.Hashtable; 29 import java.awt.image.ImageConsumer; 30 import java.awt.image.ImageFilter; 31 32 /** 33 * The {@code BufferedImageFilter} class subclasses an 34 * {@code ImageFilter} to provide a simple means of 35 * using a single-source/single-destination image operator 36 * ({@link BufferedImageOp}) to filter a {@code BufferedImage} 37 * in the Image Producer/Consumer/Observer 38 * paradigm. Examples of these image operators are: {@link ConvolveOp}, 39 * {@link AffineTransformOp} and {@link LookupOp}. 40 * 41 * @see ImageFilter 42 * @see BufferedImage 43 * @see BufferedImageOp 44 */ 45 46 public class BufferedImageFilter extends ImageFilter implements Cloneable { 47 BufferedImageOp bufferedImageOp; 48 ColorModel model; 49 int width; 50 int height; 51 byte[] bytePixels; 52 int[] intPixels; 53 54 /** 55 * Constructs a {@code BufferedImageFilter} with the 56 * specified single-source/single-destination operator. 57 * @param op the specified {@code BufferedImageOp} to 58 * use to filter a {@code BufferedImage} 59 * @throws NullPointerException if op is null 60 */ 61 public BufferedImageFilter (BufferedImageOp op) { 62 super(); 63 if (op == null) { 64 throw new NullPointerException("Operation cannot be null"); 65 } 66 bufferedImageOp = op; 67 } 68 69 /** 70 * Returns the {@code BufferedImageOp}. 71 * @return the operator of this {@code BufferedImageFilter}. 72 */ 73 public BufferedImageOp getBufferedImageOp() { 74 return bufferedImageOp; 75 } 76 77 /** 78 * Filters the information provided in the 79 * {@link ImageConsumer#setDimensions(int, int) setDimensions } method 80 * of the {@link ImageConsumer} interface. 81 * <p> 82 * Note: This method is intended to be called by the 83 * {@link ImageProducer} of the {@code Image} whose pixels are 84 * being filtered. Developers using this class to retrieve pixels from 85 * an image should avoid calling this method directly since that 86 * operation could result in problems with retrieving the requested 87 * pixels. 88 * 89 * @param width the width to which to set the width of this 90 * {@code BufferedImageFilter} 91 * @param height the height to which to set the height of this 92 * {@code BufferedImageFilter} 93 * @see ImageConsumer#setDimensions 94 */ 95 public void setDimensions(int width, int height) { 96 if (width <= 0 || height <= 0) { 97 imageComplete(STATICIMAGEDONE); 98 return; 99 } 100 this.width = width; 101 this.height = height; 102 } 103 104 /** 105 * Filters the information provided in the 106 * {@link ImageConsumer#setColorModel(ColorModel) setColorModel} method 107 * of the {@code ImageConsumer} interface. 108 * <p> 109 * If {@code model} is {@code null}, this 110 * method clears the current {@code ColorModel} of this 111 * {@code BufferedImageFilter}. 112 * <p> 113 * Note: This method is intended to be called by the 114 * {@code ImageProducer} of the {@code Image} 115 * whose pixels are being filtered. Developers using this 116 * class to retrieve pixels from an image 117 * should avoid calling this method directly since that 118 * operation could result in problems with retrieving the 119 * requested pixels. 120 * @param model the {@link ColorModel} to which to set the 121 * {@code ColorModel} of this {@code BufferedImageFilter} 122 * @see ImageConsumer#setColorModel 123 */ 124 public void setColorModel(ColorModel model) { 125 this.model = model; 126 } 127 128 private void convertToRGB() { 129 int size = width * height; 130 int[] newpixels = new int[size]; 131 if (bytePixels != null) { 132 for (int i = 0; i < size; i++) { 133 newpixels[i] = this.model.getRGB(bytePixels[i] & 0xff); 134 } 135 } else if (intPixels != null) { 136 for (int i = 0; i < size; i++) { 137 newpixels[i] = this.model.getRGB(intPixels[i]); 138 } 139 } 140 bytePixels = null; 141 intPixels = newpixels; 142 this.model = ColorModel.getRGBdefault(); 143 } 144 145 /** 146 * Filters the information provided in the {@code setPixels} 147 * method of the {@code ImageConsumer} interface which takes 148 * an array of bytes. 149 * <p> 150 * Note: This method is intended to be called by the 151 * {@code ImageProducer} of the {@code Image} whose pixels 152 * are being filtered. Developers using 153 * this class to retrieve pixels from an image should avoid calling 154 * this method directly since that operation could result in problems 155 * with retrieving the requested pixels. 156 * @throws IllegalArgumentException if width or height are less than 157 * zero. 158 * @see ImageConsumer#setPixels(int, int, int, int, ColorModel, byte[], 159 int, int) 160 */ 161 public void setPixels(int x, int y, int w, int h, 162 ColorModel model, byte[] pixels, int off, 163 int scansize) { 164 // Fix 4184230 165 if (w < 0 || h < 0) { 166 throw new IllegalArgumentException("Width ("+w+ 167 ") and height ("+h+ 168 ") must be > 0"); 169 } 170 // Nothing to do 171 if (w == 0 || h == 0) { 172 return; 173 } 174 if (y < 0) { 175 int diff = -y; 176 if (diff >= h) { 177 return; 178 } 179 off += scansize * diff; 180 y += diff; 181 h -= diff; 182 } 183 if (y + h > height) { 184 h = height - y; 185 if (h <= 0) { 186 return; 187 } 188 } 189 if (x < 0) { 190 int diff = -x; 191 if (diff >= w) { 192 return; 193 } 194 off += diff; 195 x += diff; 196 w -= diff; 197 } 198 if (x + w > width) { 199 w = width - x; 200 if (w <= 0) { 201 return; 202 } 203 } 204 int dstPtr = y*width + x; 205 if (intPixels == null) { 206 if (bytePixels == null) { 207 bytePixels = new byte[width*height]; 208 this.model = model; 209 } else if (this.model != model) { 210 convertToRGB(); 211 } 212 if (bytePixels != null) { 213 for (int sh = h; sh > 0; sh--) { 214 System.arraycopy(pixels, off, bytePixels, dstPtr, w); 215 off += scansize; 216 dstPtr += width; 217 } 218 } 219 } 220 if (intPixels != null) { 221 int dstRem = width - w; 222 int srcRem = scansize - w; 223 for (int sh = h; sh > 0; sh--) { 224 for (int sw = w; sw > 0; sw--) { 225 intPixels[dstPtr++] = model.getRGB(pixels[off++]&0xff); 226 } 227 off += srcRem; 228 dstPtr += dstRem; 229 } 230 } 231 } 232 /** 233 * Filters the information provided in the {@code setPixels} 234 * method of the {@code ImageConsumer} interface which takes 235 * an array of integers. 236 * <p> 237 * Note: This method is intended to be called by the 238 * {@code ImageProducer} of the {@code Image} whose 239 * pixels are being filtered. Developers using this class to 240 * retrieve pixels from an image should avoid calling this method 241 * directly since that operation could result in problems 242 * with retrieving the requested pixels. 243 * @throws IllegalArgumentException if width or height are less than 244 * zero. 245 * @see ImageConsumer#setPixels(int, int, int, int, ColorModel, int[], 246 int, int) 247 */ 248 public void setPixels(int x, int y, int w, int h, 249 ColorModel model, int[] pixels, int off, 250 int scansize) { 251 // Fix 4184230 252 if (w < 0 || h < 0) { 253 throw new IllegalArgumentException("Width ("+w+ 254 ") and height ("+h+ 255 ") must be > 0"); 256 } 257 // Nothing to do 258 if (w == 0 || h == 0) { 259 return; 260 } 261 if (y < 0) { 262 int diff = -y; 263 if (diff >= h) { 264 return; 265 } 266 off += scansize * diff; 267 y += diff; 268 h -= diff; 269 } 270 if (y + h > height) { 271 h = height - y; 272 if (h <= 0) { 273 return; 274 } 275 } 276 if (x < 0) { 277 int diff = -x; 278 if (diff >= w) { 279 return; 280 } 281 off += diff; 282 x += diff; 283 w -= diff; 284 } 285 if (x + w > width) { 286 w = width - x; 287 if (w <= 0) { 288 return; 289 } 290 } 291 292 if (intPixels == null) { 293 if (bytePixels == null) { 294 intPixels = new int[width * height]; 295 this.model = model; 296 } else { 297 convertToRGB(); 298 } 299 } 300 int dstPtr = y*width + x; 301 if (this.model == model) { 302 for (int sh = h; sh > 0; sh--) { 303 System.arraycopy(pixels, off, intPixels, dstPtr, w); 304 off += scansize; 305 dstPtr += width; 306 } 307 } else { 308 if (this.model != ColorModel.getRGBdefault()) { 309 convertToRGB(); 310 } 311 int dstRem = width - w; 312 int srcRem = scansize - w; 313 for (int sh = h; sh > 0; sh--) { 314 for (int sw = w; sw > 0; sw--) { 315 intPixels[dstPtr++] = model.getRGB(pixels[off++]); 316 } 317 off += srcRem; 318 dstPtr += dstRem; 319 } 320 } 321 } 322 323 /** 324 * Filters the information provided in the {@code imageComplete} 325 * method of the {@code ImageConsumer} interface. 326 * <p> 327 * Note: This method is intended to be called by the 328 * {@code ImageProducer} of the {@code Image} whose pixels 329 * are being filtered. Developers using 330 * this class to retrieve pixels from an image should avoid calling 331 * this method directly since that operation could result in problems 332 * with retrieving the requested pixels. 333 * @param status the status of image loading 334 * @throws ImagingOpException if there was a problem calling the filter 335 * method of the {@code BufferedImageOp} associated with this 336 * instance. 337 * @see ImageConsumer#imageComplete 338 */ 339 public void imageComplete(int status) { 340 WritableRaster wr; 341 switch(status) { 342 case IMAGEERROR: 343 case IMAGEABORTED: 344 // reinitialize the params 345 model = null; 346 width = -1; 347 height = -1; 348 intPixels = null; 349 bytePixels = null; 350 break; 351 352 case SINGLEFRAMEDONE: 353 case STATICIMAGEDONE: 354 if (width <= 0 || height <= 0) break; 355 if (model instanceof DirectColorModel) { 356 if (intPixels == null) break; 357 wr = createDCMraster(); 358 } 359 else if (model instanceof IndexColorModel) { 360 int[] bandOffsets = {0}; 361 if (bytePixels == null) break; 362 DataBufferByte db = new DataBufferByte(bytePixels, 363 width*height); 364 wr = Raster.createInterleavedRaster(db, width, height, width, 365 1, bandOffsets, null); 366 } 367 else { 368 convertToRGB(); 369 if (intPixels == null) break; 370 wr = createDCMraster(); 371 } 372 BufferedImage bi = new BufferedImage(model, wr, 373 model.isAlphaPremultiplied(), 374 null); 375 bi = bufferedImageOp.filter(bi, null); 376 WritableRaster r = bi.getRaster(); 377 ColorModel cm = bi.getColorModel(); 378 int w = r.getWidth(); 379 int h = r.getHeight(); 380 consumer.setDimensions(w, h); 381 consumer.setColorModel(cm); 382 if (cm instanceof DirectColorModel) { 383 DataBufferInt db = (DataBufferInt) r.getDataBuffer(); 384 consumer.setPixels(0, 0, w, h, 385 cm, db.getData(), 0, w); 386 } 387 else if (cm instanceof IndexColorModel) { 388 DataBufferByte db = (DataBufferByte) r.getDataBuffer(); 389 consumer.setPixels(0, 0, w, h, 390 cm, db.getData(), 0, w); 391 } 392 else { 393 throw new InternalError("Unknown color model "+cm); 394 } 395 break; 396 } 397 consumer.imageComplete(status); 398 } 399 400 private WritableRaster createDCMraster() { 401 WritableRaster wr; 402 DirectColorModel dcm = (DirectColorModel) model; 403 boolean hasAlpha = model.hasAlpha(); 404 int[] bandMasks = new int[3+(hasAlpha ? 1 : 0)]; 405 bandMasks[0] = dcm.getRedMask(); 406 bandMasks[1] = dcm.getGreenMask(); 407 bandMasks[2] = dcm.getBlueMask(); 408 if (hasAlpha) { 409 bandMasks[3] = dcm.getAlphaMask(); 410 } 411 DataBufferInt db = new DataBufferInt(intPixels, width*height); 412 wr = Raster.createPackedRaster(db, width, height, width, 413 bandMasks, null); 414 return wr; 415 } 416 417 }