| /* |
| * Licensed to the Apache Software Foundation (ASF) under one |
| * or more contributor license agreements. See the NOTICE file |
| * distributed with this work for additional information |
| * regarding copyright ownership. The ASF licenses this file |
| * to you under the Apache License, Version 2.0 (the |
| * "License"); you may not use this file except in compliance |
| * with the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, |
| * software distributed under the License is distributed on an |
| * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY |
| * KIND, either express or implied. See the License for the |
| * specific language governing permissions and limitations |
| * under the License. |
| */ |
| /** |
| * @author Oleg V. Khaschansky |
| * @version $Revision$ |
| */ |
| |
| package java.awt.image; |
| |
| import org.apache.harmony.awt.gl.AwtImageBackdoorAccessor; |
| import org.apache.harmony.awt.internal.nls.Messages; |
| |
| /** |
| * The BufferedImageFilter class provides filtering operations to the |
| * BufferedImage objects using operators which implement BufferedImageOp |
| * interface. |
| * |
| * @since Android 1.0 |
| */ |
| public class BufferedImageFilter extends ImageFilter implements Cloneable { |
| |
| /** |
| * The Constant accessor. |
| */ |
| private static final AwtImageBackdoorAccessor accessor = AwtImageBackdoorAccessor.getInstance(); |
| |
| /** |
| * The op. |
| */ |
| private BufferedImageOp op; |
| |
| /** |
| * The raster. |
| */ |
| private WritableRaster raster; |
| |
| /** |
| * The i data. |
| */ |
| private int iData[]; |
| |
| /** |
| * The b data. |
| */ |
| private byte bData[]; |
| |
| /** |
| * The width. |
| */ |
| private int width; |
| |
| /** |
| * The height. |
| */ |
| private int height; |
| |
| /** |
| * The cm. |
| */ |
| private ColorModel cm; |
| |
| /** |
| * The forced rgb. |
| */ |
| private boolean forcedRGB = false; |
| |
| /** |
| * The transfer type. |
| */ |
| private int transferType = DataBuffer.TYPE_UNDEFINED; |
| |
| /** |
| * Instantiates a new BufferedImageFilter with the specified BufferedImageOp |
| * operator. |
| * |
| * @param op |
| * the specified BufferedImageOp operator. |
| * @throws NullPointerException |
| * if BufferedImageOp is null. |
| */ |
| public BufferedImageFilter(BufferedImageOp op) { |
| if (op == null) { |
| throw new NullPointerException(Messages.getString("awt.05")); //$NON-NLS-1$ |
| } |
| this.op = op; |
| } |
| |
| /** |
| * Gets the BufferedImageOp operator associated with this |
| * BufferedImageFilter object. |
| * |
| * @return the BufferedImageOp associated with this BufferedImageFilter |
| * object. |
| */ |
| public BufferedImageOp getBufferedImageOp() { |
| return op; |
| } |
| |
| @Override |
| public void setDimensions(int width, int height) { |
| this.width = width; |
| this.height = height; |
| // Stop image consuming if no pixels expected. |
| if (width <= 0 || height <= 0) { |
| consumer.imageComplete(ImageConsumer.STATICIMAGEDONE); |
| reset(); |
| } |
| } |
| |
| @Override |
| public void setColorModel(ColorModel model) { |
| if (this.cm != null && this.cm != model && raster != null) { |
| forceRGB(); |
| } else { |
| this.cm = model; |
| } |
| } |
| |
| @Override |
| public void setPixels(int x, int y, int w, int h, ColorModel model, byte[] pixels, int off, |
| int scansize) { |
| setPixels(x, y, w, h, model, pixels, off, scansize, true); |
| } |
| |
| @Override |
| public void setPixels(int x, int y, int w, int h, ColorModel model, int[] pixels, int off, |
| int scansize) { |
| setPixels(x, y, w, h, model, pixels, off, scansize, false); |
| } |
| |
| @Override |
| public void imageComplete(int status) { |
| if (status == STATICIMAGEDONE || status == SINGLEFRAMEDONE) { |
| BufferedImage bim = new BufferedImage(cm, raster, cm.isAlphaPremultiplied, null); |
| bim = op.filter(bim, null); |
| DataBuffer dstDb = bim.getRaster().getDataBuffer(); |
| ColorModel dstCm = bim.getColorModel(); |
| int dstW = bim.getWidth(); |
| int dstH = bim.getHeight(); |
| |
| consumer.setDimensions(dstW, dstH); |
| |
| if (dstDb.getDataType() == DataBuffer.TYPE_INT) { |
| consumer.setColorModel(dstCm); |
| consumer.setPixels(0, 0, dstW, dstH, dstCm, accessor.getDataInt(dstDb), 0, dstW); |
| } else if (dstDb.getDataType() == DataBuffer.TYPE_BYTE) { |
| consumer.setColorModel(dstCm); |
| consumer.setPixels(0, 0, dstW, dstH, dstCm, accessor.getDataByte(dstDb), 0, dstW); |
| } else { |
| int dstData[] = bim.getRGB(0, 0, dstW, dstH, null, 0, dstW); |
| dstCm = ColorModel.getRGBdefault(); |
| consumer.setColorModel(dstCm); |
| consumer.setPixels(0, 0, dstW, dstH, dstCm, dstData, 0, dstW); |
| } |
| } else if (status == IMAGEERROR || status == IMAGEABORTED) { |
| reset(); |
| } |
| |
| consumer.imageComplete(status); |
| } |
| |
| /** |
| * Sets the pixels. |
| * |
| * @param x |
| * the x. |
| * @param y |
| * the y. |
| * @param w |
| * the w. |
| * @param h |
| * the h. |
| * @param model |
| * the model. |
| * @param pixels |
| * the pixels. |
| * @param off |
| * the off. |
| * @param scansize |
| * the scansize. |
| * @param isByteData |
| * the is byte data. |
| */ |
| private void setPixels(int x, int y, int w, int h, ColorModel model, Object pixels, int off, |
| int scansize, boolean isByteData) { |
| // Check bounds |
| // Need to copy only the pixels that will fit into the destination area |
| if (x < 0) { |
| w -= x; |
| off += x; |
| x = 0; |
| } |
| |
| if (y < 0) { |
| h -= y; |
| off += y * scansize; |
| y = 0; |
| } |
| |
| if (x + w > width) { |
| w = width - x; |
| } |
| |
| if (y + h > height) { |
| h = height - y; |
| } |
| |
| if (w <= 0 || h <= 0) { |
| return; |
| } |
| |
| // Check model |
| if (this.cm == null) { |
| setColorModel(model); |
| } else if (model == null) { |
| model = this.cm; |
| } else if (!model.equals(this.cm)) { |
| forceRGB(); |
| } |
| |
| boolean canArraycopy; |
| // Process pixels |
| switch (transferType) { |
| case DataBuffer.TYPE_UNDEFINED: { |
| if (isByteData) { |
| transferType = DataBuffer.TYPE_BYTE; |
| createRaster(transferType); |
| // bData = new byte[width*height]; |
| canArraycopy = !forcedRGB; |
| break; |
| } |
| transferType = DataBuffer.TYPE_INT; |
| createRaster(transferType); |
| // iData = new int[width*height]; |
| canArraycopy = !forcedRGB || model.equals(ColorModel.getRGBdefault()); |
| break; |
| } // And proceed to copy the pixels |
| case DataBuffer.TYPE_INT: { |
| if (isByteData) { // There are int data already but the new data |
| // are bytes |
| forceRGB(); |
| canArraycopy = false; |
| break; |
| } else if (!forcedRGB || model.equals(ColorModel.getRGBdefault())) { |
| canArraycopy = true; |
| break; |
| } // Else fallback to the RGB conversion |
| } |
| case DataBuffer.TYPE_BYTE: { |
| if (isByteData && !forcedRGB) { |
| canArraycopy = true; |
| break; |
| } |
| |
| // RGB conversion |
| canArraycopy = false; |
| break; |
| } |
| default: { |
| throw new IllegalStateException(Messages.getString("awt.06")); //$NON-NLS-1$ |
| } |
| } |
| |
| off += x; |
| int maxOffset = off + h * scansize; |
| int dstOffset = x + y * width; |
| |
| if (canArraycopy) { |
| Object dstArray = isByteData ? (Object)bData : (Object)iData; |
| for (; off < maxOffset; off += scansize, dstOffset += width) { |
| System.arraycopy(pixels, off, dstArray, dstOffset, w); |
| } |
| } else { |
| // RGB conversion |
| for (; off < maxOffset; off += scansize, dstOffset += width) { |
| int srcPos = off; |
| int dstPos = dstOffset; |
| int maxDstPos = dstOffset + w; |
| for (; dstPos < maxDstPos; dstPos++, srcPos++) { |
| iData[dstPos] = model.getRGB(isByteData ? ((byte[])pixels)[srcPos] |
| : ((int[])pixels)[srcPos]); |
| } |
| } |
| } |
| } |
| |
| /** |
| * Force rgb. |
| */ |
| private void forceRGB() { |
| if (!forcedRGB) { |
| forcedRGB = true; |
| int size = width * height; |
| int rgbData[] = new int[size]; |
| |
| if (bData != null) { |
| for (int i = 0; i < size; i++) { |
| rgbData[i] = cm.getRGB(bData[i]); |
| } |
| } else if (iData != null) { |
| for (int i = 0; i < size; i++) { |
| rgbData[i] = cm.getRGB(iData[i]); |
| } |
| } |
| |
| cm = ColorModel.getRGBdefault(); |
| DataBufferInt db = new DataBufferInt(rgbData, size); |
| int masks[] = new int[] { |
| 0x00ff0000, 0x0000ff00, 0x000000ff, 0xff000000 |
| }; |
| raster = Raster.createPackedRaster(db, width, height, width, masks, null); |
| iData = accessor.getDataInt(db); |
| bData = null; |
| transferType = DataBuffer.TYPE_INT; |
| } |
| } |
| |
| /** |
| * Reset. |
| */ |
| private void reset() { |
| width = 0; |
| height = 0; |
| forcedRGB = false; |
| cm = null; |
| iData = null; |
| bData = null; |
| transferType = DataBuffer.TYPE_UNDEFINED; |
| raster = null; |
| } |
| |
| /** |
| * Creates the raster. |
| * |
| * @param dataType |
| * the data type. |
| */ |
| private void createRaster(int dataType) { |
| boolean createdValidBuffer = false; |
| try { |
| raster = cm.createCompatibleWritableRaster(width, height); |
| int rasterType = raster.getDataBuffer().getDataType(); |
| if (rasterType == dataType) { |
| switch (rasterType) { |
| case DataBuffer.TYPE_INT: { |
| iData = accessor.getDataInt(raster.getDataBuffer()); |
| if (iData != null) { |
| createdValidBuffer = true; |
| } |
| break; |
| } |
| case DataBuffer.TYPE_BYTE: { |
| bData = accessor.getDataByte(raster.getDataBuffer()); |
| if (bData != null) { |
| createdValidBuffer = true; |
| } |
| break; |
| } |
| default: |
| createdValidBuffer = false; |
| } |
| |
| if (cm == ColorModel.getRGBdefault()) { |
| forcedRGB = true; |
| } |
| } else { |
| createdValidBuffer = false; |
| } |
| } catch (Exception e) { |
| createdValidBuffer = false; |
| } |
| |
| if (createdValidBuffer == false) { |
| cm = ColorModel.getRGBdefault(); |
| raster = cm.createCompatibleWritableRaster(width, height); |
| iData = accessor.getDataInt(raster.getDataBuffer()); |
| bData = null; |
| forcedRGB = true; |
| } |
| } |
| } |