1/* 2 * Licensed to the Apache Software Foundation (ASF) under one 3 * or more contributor license agreements. See the NOTICE file 4 * distributed with this work for additional information 5 * regarding copyright ownership. The ASF licenses this file 6 * to you under the Apache License, Version 2.0 (the 7 * "License"); you may not use this file except in compliance 8 * with the License. You may obtain a copy of the License at 9 * 10 * http://www.apache.org/licenses/LICENSE-2.0 11 * 12 * Unless required by applicable law or agreed to in writing, 13 * software distributed under the License is distributed on an 14 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 15 * KIND, either express or implied. See the License for the 16 * specific language governing permissions and limitations 17 * under the License. 18 */ 19/** 20 * @author Oleg V. Khaschansky 21 * @version $Revision$ 22 */ 23 24package java.awt.image; 25 26import org.apache.harmony.awt.gl.AwtImageBackdoorAccessor; 27import org.apache.harmony.awt.internal.nls.Messages; 28 29/** 30 * The BufferedImageFilter class provides filtering operations to the 31 * BufferedImage objects using operators which implement BufferedImageOp 32 * interface. 33 * 34 * @since Android 1.0 35 */ 36public class BufferedImageFilter extends ImageFilter implements Cloneable { 37 38 /** 39 * The Constant accessor. 40 */ 41 private static final AwtImageBackdoorAccessor accessor = AwtImageBackdoorAccessor.getInstance(); 42 43 /** 44 * The op. 45 */ 46 private BufferedImageOp op; 47 48 /** 49 * The raster. 50 */ 51 private WritableRaster raster; 52 53 /** 54 * The i data. 55 */ 56 private int iData[]; 57 58 /** 59 * The b data. 60 */ 61 private byte bData[]; 62 63 /** 64 * The width. 65 */ 66 private int width; 67 68 /** 69 * The height. 70 */ 71 private int height; 72 73 /** 74 * The cm. 75 */ 76 private ColorModel cm; 77 78 /** 79 * The forced rgb. 80 */ 81 private boolean forcedRGB = false; 82 83 /** 84 * The transfer type. 85 */ 86 private int transferType = DataBuffer.TYPE_UNDEFINED; 87 88 /** 89 * Instantiates a new BufferedImageFilter with the specified BufferedImageOp 90 * operator. 91 * 92 * @param op 93 * the specified BufferedImageOp operator. 94 * @throws NullPointerException 95 * if BufferedImageOp is null. 96 */ 97 public BufferedImageFilter(BufferedImageOp op) { 98 if (op == null) { 99 throw new NullPointerException(Messages.getString("awt.05")); //$NON-NLS-1$ 100 } 101 this.op = op; 102 } 103 104 /** 105 * Gets the BufferedImageOp operator associated with this 106 * BufferedImageFilter object. 107 * 108 * @return the BufferedImageOp associated with this BufferedImageFilter 109 * object. 110 */ 111 public BufferedImageOp getBufferedImageOp() { 112 return op; 113 } 114 115 @Override 116 public void setDimensions(int width, int height) { 117 this.width = width; 118 this.height = height; 119 // Stop image consuming if no pixels expected. 120 if (width <= 0 || height <= 0) { 121 consumer.imageComplete(ImageConsumer.STATICIMAGEDONE); 122 reset(); 123 } 124 } 125 126 @Override 127 public void setColorModel(ColorModel model) { 128 if (this.cm != null && this.cm != model && raster != null) { 129 forceRGB(); 130 } else { 131 this.cm = model; 132 } 133 } 134 135 @Override 136 public void setPixels(int x, int y, int w, int h, ColorModel model, byte[] pixels, int off, 137 int scansize) { 138 setPixels(x, y, w, h, model, pixels, off, scansize, true); 139 } 140 141 @Override 142 public void setPixels(int x, int y, int w, int h, ColorModel model, int[] pixels, int off, 143 int scansize) { 144 setPixels(x, y, w, h, model, pixels, off, scansize, false); 145 } 146 147 @Override 148 public void imageComplete(int status) { 149 if (status == STATICIMAGEDONE || status == SINGLEFRAMEDONE) { 150 BufferedImage bim = new BufferedImage(cm, raster, cm.isAlphaPremultiplied, null); 151 bim = op.filter(bim, null); 152 DataBuffer dstDb = bim.getRaster().getDataBuffer(); 153 ColorModel dstCm = bim.getColorModel(); 154 int dstW = bim.getWidth(); 155 int dstH = bim.getHeight(); 156 157 consumer.setDimensions(dstW, dstH); 158 159 if (dstDb.getDataType() == DataBuffer.TYPE_INT) { 160 consumer.setColorModel(dstCm); 161 consumer.setPixels(0, 0, dstW, dstH, dstCm, accessor.getDataInt(dstDb), 0, dstW); 162 } else if (dstDb.getDataType() == DataBuffer.TYPE_BYTE) { 163 consumer.setColorModel(dstCm); 164 consumer.setPixels(0, 0, dstW, dstH, dstCm, accessor.getDataByte(dstDb), 0, dstW); 165 } else { 166 int dstData[] = bim.getRGB(0, 0, dstW, dstH, null, 0, dstW); 167 dstCm = ColorModel.getRGBdefault(); 168 consumer.setColorModel(dstCm); 169 consumer.setPixels(0, 0, dstW, dstH, dstCm, dstData, 0, dstW); 170 } 171 } else if (status == IMAGEERROR || status == IMAGEABORTED) { 172 reset(); 173 } 174 175 consumer.imageComplete(status); 176 } 177 178 /** 179 * Sets the pixels. 180 * 181 * @param x 182 * the x. 183 * @param y 184 * the y. 185 * @param w 186 * the w. 187 * @param h 188 * the h. 189 * @param model 190 * the model. 191 * @param pixels 192 * the pixels. 193 * @param off 194 * the off. 195 * @param scansize 196 * the scansize. 197 * @param isByteData 198 * the is byte data. 199 */ 200 private void setPixels(int x, int y, int w, int h, ColorModel model, Object pixels, int off, 201 int scansize, boolean isByteData) { 202 // Check bounds 203 // Need to copy only the pixels that will fit into the destination area 204 if (x < 0) { 205 w -= x; 206 off += x; 207 x = 0; 208 } 209 210 if (y < 0) { 211 h -= y; 212 off += y * scansize; 213 y = 0; 214 } 215 216 if (x + w > width) { 217 w = width - x; 218 } 219 220 if (y + h > height) { 221 h = height - y; 222 } 223 224 if (w <= 0 || h <= 0) { 225 return; 226 } 227 228 // Check model 229 if (this.cm == null) { 230 setColorModel(model); 231 } else if (model == null) { 232 model = this.cm; 233 } else if (!model.equals(this.cm)) { 234 forceRGB(); 235 } 236 237 boolean canArraycopy; 238 // Process pixels 239 switch (transferType) { 240 case DataBuffer.TYPE_UNDEFINED: { 241 if (isByteData) { 242 transferType = DataBuffer.TYPE_BYTE; 243 createRaster(transferType); 244 // bData = new byte[width*height]; 245 canArraycopy = !forcedRGB; 246 break; 247 } 248 transferType = DataBuffer.TYPE_INT; 249 createRaster(transferType); 250 // iData = new int[width*height]; 251 canArraycopy = !forcedRGB || model.equals(ColorModel.getRGBdefault()); 252 break; 253 } // And proceed to copy the pixels 254 case DataBuffer.TYPE_INT: { 255 if (isByteData) { // There are int data already but the new data 256 // are bytes 257 forceRGB(); 258 canArraycopy = false; 259 break; 260 } else if (!forcedRGB || model.equals(ColorModel.getRGBdefault())) { 261 canArraycopy = true; 262 break; 263 } // Else fallback to the RGB conversion 264 } 265 case DataBuffer.TYPE_BYTE: { 266 if (isByteData && !forcedRGB) { 267 canArraycopy = true; 268 break; 269 } 270 271 // RGB conversion 272 canArraycopy = false; 273 break; 274 } 275 default: { 276 throw new IllegalStateException(Messages.getString("awt.06")); //$NON-NLS-1$ 277 } 278 } 279 280 off += x; 281 int maxOffset = off + h * scansize; 282 int dstOffset = x + y * width; 283 284 if (canArraycopy) { 285 Object dstArray = isByteData ? (Object)bData : (Object)iData; 286 for (; off < maxOffset; off += scansize, dstOffset += width) { 287 System.arraycopy(pixels, off, dstArray, dstOffset, w); 288 } 289 } else { 290 // RGB conversion 291 for (; off < maxOffset; off += scansize, dstOffset += width) { 292 int srcPos = off; 293 int dstPos = dstOffset; 294 int maxDstPos = dstOffset + w; 295 for (; dstPos < maxDstPos; dstPos++, srcPos++) { 296 iData[dstPos] = model.getRGB(isByteData ? ((byte[])pixels)[srcPos] 297 : ((int[])pixels)[srcPos]); 298 } 299 } 300 } 301 } 302 303 /** 304 * Force rgb. 305 */ 306 private void forceRGB() { 307 if (!forcedRGB) { 308 forcedRGB = true; 309 int size = width * height; 310 int rgbData[] = new int[size]; 311 312 if (bData != null) { 313 for (int i = 0; i < size; i++) { 314 rgbData[i] = cm.getRGB(bData[i]); 315 } 316 } else if (iData != null) { 317 for (int i = 0; i < size; i++) { 318 rgbData[i] = cm.getRGB(iData[i]); 319 } 320 } 321 322 cm = ColorModel.getRGBdefault(); 323 DataBufferInt db = new DataBufferInt(rgbData, size); 324 int masks[] = new int[] { 325 0x00ff0000, 0x0000ff00, 0x000000ff, 0xff000000 326 }; 327 raster = Raster.createPackedRaster(db, width, height, width, masks, null); 328 iData = accessor.getDataInt(db); 329 bData = null; 330 transferType = DataBuffer.TYPE_INT; 331 } 332 } 333 334 /** 335 * Reset. 336 */ 337 private void reset() { 338 width = 0; 339 height = 0; 340 forcedRGB = false; 341 cm = null; 342 iData = null; 343 bData = null; 344 transferType = DataBuffer.TYPE_UNDEFINED; 345 raster = null; 346 } 347 348 /** 349 * Creates the raster. 350 * 351 * @param dataType 352 * the data type. 353 */ 354 private void createRaster(int dataType) { 355 boolean createdValidBuffer = false; 356 try { 357 raster = cm.createCompatibleWritableRaster(width, height); 358 int rasterType = raster.getDataBuffer().getDataType(); 359 if (rasterType == dataType) { 360 switch (rasterType) { 361 case DataBuffer.TYPE_INT: { 362 iData = accessor.getDataInt(raster.getDataBuffer()); 363 if (iData != null) { 364 createdValidBuffer = true; 365 } 366 break; 367 } 368 case DataBuffer.TYPE_BYTE: { 369 bData = accessor.getDataByte(raster.getDataBuffer()); 370 if (bData != null) { 371 createdValidBuffer = true; 372 } 373 break; 374 } 375 default: 376 createdValidBuffer = false; 377 } 378 379 if (cm == ColorModel.getRGBdefault()) { 380 forcedRGB = true; 381 } 382 } else { 383 createdValidBuffer = false; 384 } 385 } catch (Exception e) { 386 createdValidBuffer = false; 387 } 388 389 if (createdValidBuffer == false) { 390 cm = ColorModel.getRGBdefault(); 391 raster = cm.createCompatibleWritableRaster(width, height); 392 iData = accessor.getDataInt(raster.getDataBuffer()); 393 bData = null; 394 forcedRGB = true; 395 } 396 } 397} 398