diff options
Diffstat (limited to 'awt/java/awt/image/BandCombineOp.java')
-rw-r--r-- | awt/java/awt/image/BandCombineOp.java | 610 |
1 files changed, 610 insertions, 0 deletions
diff --git a/awt/java/awt/image/BandCombineOp.java b/awt/java/awt/image/BandCombineOp.java new file mode 100644 index 0000000..cd77a21 --- /dev/null +++ b/awt/java/awt/image/BandCombineOp.java @@ -0,0 +1,610 @@ +/* + * 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$ + * + * @date: Sep 20, 2005 + */ + +package java.awt.image; + +import java.awt.*; +import java.awt.geom.Point2D; +import java.awt.geom.Rectangle2D; +import java.util.Arrays; + +import org.apache.harmony.awt.gl.AwtImageBackdoorAccessor; +import org.apache.harmony.awt.internal.nls.Messages; + +/** + * The BandCombineOp class translates coordinates from + * coordinates in the source Raster to coordinates in + * the destination Raster by an arbitrary linear combination + * of the bands in a source Raster, using a specified matrix. + * The number of bands in the matrix should equal to + * the number of bands in the source Raster plus 1. + */ +public class BandCombineOp implements RasterOp { + + /** The Constant offsets3c. */ + static final int offsets3c[] = {16, 8, 0}; + + /** The Constant offsets4ac. */ + static final int offsets4ac[] = {16, 8, 0, 24}; + + /** The Constant masks3c. */ + static final int masks3c[] = {0xFF0000, 0xFF00, 0xFF}; + + /** The Constant masks4ac. */ + static final int masks4ac[] = {0xFF0000, 0xFF00, 0xFF, 0xFF000000}; + + /** The Constant piOffsets. */ + private static final int piOffsets[] = {0, 1, 2}; + + /** The Constant piInvOffsets. */ + private static final int piInvOffsets[] = {2, 1, 0}; + + /** The Constant TYPE_BYTE3C. */ + private static final int TYPE_BYTE3C = 0; + + /** The Constant TYPE_BYTE4AC. */ + private static final int TYPE_BYTE4AC = 1; + + /** The Constant TYPE_USHORT3C. */ + private static final int TYPE_USHORT3C = 2; + + /** The Constant TYPE_SHORT3C. */ + private static final int TYPE_SHORT3C = 3; + + /** The mx width. */ + private int mxWidth; + + /** The mx height. */ + private int mxHeight; + + /** The matrix. */ + private float matrix[][]; + + /** The r hints. */ + private RenderingHints rHints; + + static { + // XXX - todo + //System.loadLibrary("imageops"); + } + + /** + * Instantiates a new BandCombineOp object with the specified + * matrix. + * + * @param matrix the specified matrix for band combining. + * @param hints the RenderingHints. + */ + public BandCombineOp(float matrix[][], RenderingHints hints) { + this.mxHeight = matrix.length; + this.mxWidth = matrix[0].length; + this.matrix = new float[mxHeight][mxWidth]; + + for (int i=0; i<mxHeight; i++){ + System.arraycopy(matrix[i], 0, this.matrix[i], 0, mxWidth); + } + + this.rHints = hints; + } + + public final RenderingHints getRenderingHints(){ + return this.rHints; + } + + /** + * Gets the matrix associated with this BandCombineOp object. + * + * @return the matrix associated with this BandCombineOp object. + */ + public final float[][] getMatrix() { + float res[][] = new float[mxHeight][mxWidth]; + + for (int i=0; i<mxHeight; i++) { + System.arraycopy(matrix[i], 0, res[i], 0, mxWidth); + } + + return res; + } + + public final Point2D getPoint2D (Point2D srcPoint, Point2D dstPoint) { + if (dstPoint == null) { + dstPoint = new Point2D.Float(); + } + + dstPoint.setLocation(srcPoint); + return dstPoint; + } + + public final Rectangle2D getBounds2D(Raster src){ + return src.getBounds(); + } + + public WritableRaster createCompatibleDestRaster (Raster src) { + int numBands = src.getNumBands(); + if (mxWidth != numBands && mxWidth != (numBands+1) || numBands != mxHeight) { + // awt.254=Number of bands in the source raster ({0}) is + // incompatible with the matrix [{1}x{2}] + throw new IllegalArgumentException(Messages.getString("awt.254", //$NON-NLS-1$ + new Object[]{numBands, mxWidth, mxHeight})); + } + + return src.createCompatibleWritableRaster(src.getWidth(), src.getHeight()); + } + + public WritableRaster filter(Raster src, WritableRaster dst) { + int numBands = src.getNumBands(); + + if (mxWidth != numBands && mxWidth != (numBands+1)) { + // awt.254=Number of bands in the source raster ({0}) is + // incompatible with the matrix [{1}x{2}] + throw new IllegalArgumentException( + Messages.getString("awt.254", //$NON-NLS-1$ + new Object[]{numBands, mxWidth, mxHeight})); + } + + if (dst == null) { + dst = createCompatibleDestRaster(src); + } else if (dst.getNumBands() != mxHeight) { + // awt.255=Number of bands in the destination raster ({0}) is incompatible with the matrix [{1}x{2}] + throw new IllegalArgumentException(Messages.getString("awt.255", //$NON-NLS-1$ + new Object[]{dst.getNumBands(), mxWidth, mxHeight})); + } + + // XXX - todo + //if (ippFilter(src, dst) != 0) + if (verySlowFilter(src, dst) != 0) { + // awt.21F=Unable to transform source + throw new ImagingOpException (Messages.getString("awt.21F")); //$NON-NLS-1$ + } + + return dst; + } + + /** + * The Class SampleModelInfo. + */ + private static final class SampleModelInfo { + + /** The channels. */ + int channels; + + /** The channels order. */ + int channelsOrder[]; + + /** The stride. */ + int stride; + } + + /** + * Check sample model. + * + * @param sm the sm + * + * @return the sample model info + */ + private final SampleModelInfo checkSampleModel(SampleModel sm) { + SampleModelInfo ret = new SampleModelInfo(); + + if (sm instanceof PixelInterleavedSampleModel) { + // Check PixelInterleavedSampleModel + if (sm.getDataType() != DataBuffer.TYPE_BYTE) { + return null; + } + + ret.channels = sm.getNumBands(); + ret.stride = ((ComponentSampleModel) sm).getScanlineStride(); + ret.channelsOrder = ((ComponentSampleModel) sm).getBandOffsets(); + + } else if (sm instanceof SinglePixelPackedSampleModel) { + // Check SinglePixelPackedSampleModel + SinglePixelPackedSampleModel sppsm1 = (SinglePixelPackedSampleModel) sm; + + ret.channels = sppsm1.getNumBands(); + if (sppsm1.getDataType() != DataBuffer.TYPE_INT) { + return null; + } + + // Check sample models + for (int i=0; i<ret.channels; i++) { + if (sppsm1.getSampleSize(i) != 8) { + return null; + } + } + + ret.channelsOrder = new int[ret.channels]; + int bitOffsets[] = sppsm1.getBitOffsets(); + for (int i=0; i<ret.channels; i++) { + if (bitOffsets[i] % 8 != 0) { + return null; + } + + ret.channelsOrder[i] = bitOffsets[i] / 8; + } + + ret.channels = 4; + ret.stride = sppsm1.getScanlineStride() * 4; + } else { + return null; + } + + return ret; + } + + /** + * Slow filter. + * + * @param src the src + * @param dst the dst + * + * @return the int + */ + private final int slowFilter(Raster src, WritableRaster dst) { + int res = 0; + + SampleModelInfo srcInfo, dstInfo; + int offsets[] = null; + + srcInfo = checkSampleModel(src.getSampleModel()); + dstInfo = checkSampleModel(dst.getSampleModel()); + if (srcInfo == null || dstInfo == null) { + return verySlowFilter(src, dst); + } + + // Fill offsets if there's a child raster + if (src.getParent() != null || dst.getParent() != null) { + if (src.getSampleModelTranslateX() != 0 || src.getSampleModelTranslateY() != 0 || + dst.getSampleModelTranslateX() != 0 || dst.getSampleModelTranslateY() != 0) { + offsets = new int[4]; + offsets[0] = -src.getSampleModelTranslateX() + src.getMinX(); + offsets[1] = -src.getSampleModelTranslateY() + src.getMinY(); + offsets[2] = -dst.getSampleModelTranslateX() + dst.getMinX(); + offsets[3] = -dst.getSampleModelTranslateY() + dst.getMinY(); + } + } + + int rmxWidth = (srcInfo.channels+1); // width of the reordered matrix + float reorderedMatrix[] = new float[rmxWidth*dstInfo.channels]; + for (int j=0; j<dstInfo.channels; j++) { + if (j >= dstInfo.channelsOrder.length) { + continue; + } + + for (int i=0; i<srcInfo.channels; i++) { + if (i >= srcInfo.channelsOrder.length) { + break; + } + + reorderedMatrix[dstInfo.channelsOrder[j]*rmxWidth + srcInfo.channelsOrder[i]] = + matrix[j][i]; + } + if (mxWidth == rmxWidth) { + reorderedMatrix[(dstInfo.channelsOrder[j]+1)*rmxWidth - 1] = matrix[j][mxWidth-1]; + } + } + + Object srcData, dstData; + AwtImageBackdoorAccessor dbAccess = AwtImageBackdoorAccessor.getInstance(); + try { + srcData = dbAccess.getData(src.getDataBuffer()); + dstData = dbAccess.getData(dst.getDataBuffer()); + } catch (IllegalArgumentException e) { + return -1; // Unknown data buffer type + } + + simpleCombineBands( + srcData, src.getWidth(), src.getHeight(), srcInfo.stride, srcInfo.channels, + dstData, dstInfo.stride, dstInfo.channels, + reorderedMatrix, offsets + ); + + return res; + } + + /** + * Very slow filter. + * + * @param src the src + * @param dst the dst + * + * @return the int + */ + private int verySlowFilter(Raster src, WritableRaster dst) { + int numBands = src.getNumBands(); + + int srcMinX = src.getMinX(); + int srcY = src.getMinY(); + + int dstMinX = dst.getMinX(); + int dstY = dst.getMinY(); + + int dX = src.getWidth();//< dst.getWidth() ? src.getWidth() : dst.getWidth(); + int dY = src.getHeight();//< dst.getHeight() ? src.getHeight() : dst.getHeight(); + + float sample; + int srcPixels[] = new int[numBands*dX*dY]; + int dstPixels[] = new int[mxHeight*dX*dY]; + + srcPixels = src.getPixels(srcMinX, srcY, dX, dY, srcPixels); + + if (numBands == mxWidth) { + for (int i=0, j=0; i<srcPixels.length; i+=numBands) { + for (int dstB = 0; dstB < mxHeight; dstB++) { + sample = 0f; + for (int srcB = 0; srcB < numBands; srcB++) { + sample += matrix[dstB][srcB] * srcPixels[i+srcB]; + } + dstPixels[j++] = (int) sample; + } + } + } else { + for (int i=0, j=0; i<srcPixels.length; i+=numBands) { + for (int dstB = 0; dstB < mxHeight; dstB++) { + sample = 0f; + for (int srcB = 0; srcB < numBands; srcB++) { + sample += matrix[dstB][srcB] * srcPixels[i+srcB]; + } + dstPixels[j++] = (int) (sample + matrix[dstB][numBands]); + } + } + } + + dst.setPixels(dstMinX, dstY, dX, dY, dstPixels); + + return 0; + } + + //TODO remove when method is used + /** + * Ipp filter. + * + * @param src the src + * @param dst the dst + * + * @return the int + */ + @SuppressWarnings("unused") + private int ippFilter(Raster src, WritableRaster dst) { + boolean invertChannels; + boolean inPlace = (src == dst); + int type; + int srcStride, dstStride; + int offsets[] = null; + + int srcBands = src.getNumBands(); + int dstBands = dst.getNumBands(); + + if ( + dstBands != 3 || + (srcBands != 3 && + !(srcBands == 4 && + matrix[0][3] == 0 && + matrix[1][3] == 0 && + matrix[2][3] == 0) + ) + ) { + return slowFilter(src, dst); + } + + SampleModel srcSM = src.getSampleModel(); + SampleModel dstSM = dst.getSampleModel(); + + if ( + srcSM instanceof SinglePixelPackedSampleModel && + dstSM instanceof SinglePixelPackedSampleModel + ) { + // Check SinglePixelPackedSampleModel + SinglePixelPackedSampleModel sppsm1 = (SinglePixelPackedSampleModel) srcSM; + SinglePixelPackedSampleModel sppsm2 = (SinglePixelPackedSampleModel) dstSM; + + if ( + sppsm1.getDataType() != DataBuffer.TYPE_INT || + sppsm2.getDataType() != DataBuffer.TYPE_INT + ) { + return slowFilter(src, dst); + } + + // Check sample models + if ( + !Arrays.equals(sppsm2.getBitOffsets(), offsets3c) || + !Arrays.equals(sppsm2.getBitMasks(), masks3c) + ) { + return slowFilter(src, dst); + } + + if (srcBands == 3) { + if ( + !Arrays.equals(sppsm1.getBitOffsets(), offsets3c) || + !Arrays.equals(sppsm1.getBitMasks(), masks3c) + ) { + return slowFilter(src, dst); + } + } else if (srcBands == 4) { + if ( + !Arrays.equals(sppsm1.getBitOffsets(), offsets4ac) || + !Arrays.equals(sppsm1.getBitMasks(), masks4ac) + ) { + return slowFilter(src, dst); + } + } + + type = TYPE_BYTE4AC; + invertChannels = true; + + srcStride = sppsm1.getScanlineStride() * 4; + dstStride = sppsm2.getScanlineStride() * 4; + } else if ( + srcSM instanceof PixelInterleavedSampleModel && + dstSM instanceof PixelInterleavedSampleModel + ) { + if (srcBands != 3) { + return slowFilter(src, dst); + } + + int srcDataType = srcSM.getDataType(); + + switch (srcDataType) { + case DataBuffer.TYPE_BYTE: + type = TYPE_BYTE3C; + break; + case DataBuffer.TYPE_USHORT: + type = TYPE_USHORT3C; + break; + case DataBuffer.TYPE_SHORT: + type = TYPE_SHORT3C; + break; + default: + return slowFilter(src, dst); + } + + // Check PixelInterleavedSampleModel + PixelInterleavedSampleModel pism1 = (PixelInterleavedSampleModel) srcSM; + PixelInterleavedSampleModel pism2 = (PixelInterleavedSampleModel) dstSM; + + if ( + srcDataType != pism2.getDataType() || + pism1.getPixelStride() != 3 || + pism2.getPixelStride() != 3 || + !Arrays.equals(pism1.getBandOffsets(), pism2.getBandOffsets()) + ) { + return slowFilter(src, dst); + } + + if (Arrays.equals(pism1.getBandOffsets(), piInvOffsets)) { + invertChannels = true; + } else if (Arrays.equals(pism1.getBandOffsets(), piOffsets)) { + invertChannels = false; + } else { + return slowFilter(src, dst); + } + + int dataTypeSize = DataBuffer.getDataTypeSize(srcDataType) / 8; + + srcStride = pism1.getScanlineStride() * dataTypeSize; + dstStride = pism2.getScanlineStride() * dataTypeSize; + } else { // XXX - todo - IPP allows support for planar data also + return slowFilter(src, dst); + } + + // Fill offsets if there's a child raster + if (src.getParent() != null || dst.getParent() != null) { + if (src.getSampleModelTranslateX() != 0 || src.getSampleModelTranslateY() != 0 || + dst.getSampleModelTranslateX() != 0 || dst.getSampleModelTranslateY() != 0) { + offsets = new int[4]; + offsets[0] = -src.getSampleModelTranslateX() + src.getMinX(); + offsets[1] = -src.getSampleModelTranslateY() + src.getMinY(); + offsets[2] = -dst.getSampleModelTranslateX() + dst.getMinX(); + offsets[3] = -dst.getSampleModelTranslateY() + dst.getMinY(); + } + } + + Object srcData, dstData; + AwtImageBackdoorAccessor dbAccess = AwtImageBackdoorAccessor.getInstance(); + try { + srcData = dbAccess.getData(src.getDataBuffer()); + dstData = dbAccess.getData(dst.getDataBuffer()); + } catch (IllegalArgumentException e) { + return -1; // Unknown data buffer type + } + + float ippMatrix[] = new float[12]; + + if (invertChannels) { + // IPP treats big endian integers like BGR, so we have to + // swap columns 1 and 3 and rows 1 and 3 + for (int i = 0; i < mxHeight; i++) { + ippMatrix[i*4] = matrix[2-i][2]; + ippMatrix[i*4+1] = matrix[2-i][1]; + ippMatrix[i*4+2] = matrix[2-i][0]; + + if (mxWidth == 4) { + ippMatrix[i*4+3] = matrix[2-i][3]; + } else if (mxWidth == 5) { + ippMatrix[i*4+3] = matrix[2-i][4]; + } + } + } else { + for (int i = 0; i < mxHeight; i++) { + ippMatrix[i*4] = matrix[i][0]; + ippMatrix[i*4+1] = matrix[i][1]; + ippMatrix[i*4+2] = matrix[i][2]; + + if (mxWidth == 4) { + ippMatrix[i*4+3] = matrix[i][3]; + } else if (mxWidth == 5) { + ippMatrix[i*4+3] = matrix[i][4]; + } + } + } + + return ippColorTwist( + srcData, src.getWidth(), src.getHeight(), srcStride, + dstData, dst.getWidth(), dst.getHeight(), dstStride, + ippMatrix, type, offsets, inPlace); + } + + /** + * Ipp color twist. + * + * @param srcData the src data + * @param srcWidth the src width + * @param srcHeight the src height + * @param srcStride the src stride + * @param dstData the dst data + * @param dstWidth the dst width + * @param dstHeight the dst height + * @param dstStride the dst stride + * @param ippMatrix the ipp matrix + * @param type the type + * @param offsets the offsets + * @param inPlace the in place + * + * @return the int + */ + private final native int ippColorTwist( + Object srcData, int srcWidth, int srcHeight, int srcStride, + Object dstData, int dstWidth, int dstHeight, int dstStride, + float ippMatrix[], int type, int offsets[], boolean inPlace + ); + + /** + * Simple combine bands. + * + * @param srcData the src data + * @param srcWidth the src width + * @param srcHeight the src height + * @param srcStride the src stride + * @param srcChannels the src channels + * @param dstData the dst data + * @param dstStride the dst stride + * @param dstChannels the dst channels + * @param m the m + * @param offsets the offsets + * + * @return the int + */ + private final native int simpleCombineBands( + Object srcData, int srcWidth, int srcHeight, int srcStride, int srcChannels, + Object dstData, int dstStride, int dstChannels, + float m[], int offsets[] + ); +} |