FileDocCategorySizeDatePackage
AffineTransformOp.javaAPI DocAndroid 1.5 API22623Wed May 06 22:41:54 BST 2009java.awt.image

AffineTransformOp

public class AffineTransformOp extends Object implements BufferedImageOp, RasterOp
The AffineTransform class translates coordinates from 2D coordinates in the source image or Raster to 2D coordinates in the destination image or Raster using affine transformation. The number of bands in the source Raster should equal to the number of bands in the destination Raster.
since
Android 1.0

Fields Summary
public static final int
TYPE_NEAREST_NEIGHBOR
The Constant TYPE_NEAREST_NEIGHBOR indicates nearest-neighbor interpolation type.
public static final int
TYPE_BILINEAR
The Constant TYPE_BILINEAR indicates bilinear interpolation type.
public static final int
TYPE_BICUBIC
The Constant TYPE_BICUBIC indicates bi-cubic interpolation type.
private int
iType
The i type.
private AffineTransform
at
The at.
private RenderingHints
hints
The hints.
Constructors Summary
public AffineTransformOp(AffineTransform xform, RenderingHints hints)
Instantiates a new AffineTransformOp with the specified AffineTransform and RenderingHints object which defines the interpolation type.

param
xform the AffineTransform.
param
hints the RenderingHints object which defines the interpolation type.


     
        // TODO - uncomment
        // System.loadLibrary("imageops");
    
        this(xform, TYPE_NEAREST_NEIGHBOR);
        this.hints = hints;

        if (hints != null) {
            Object hint = hints.get(RenderingHints.KEY_INTERPOLATION);
            if (hint != null) {
                // Nearest neighbor is default
                if (hint == RenderingHints.VALUE_INTERPOLATION_BILINEAR) {
                    this.iType = TYPE_BILINEAR;
                } else if (hint == RenderingHints.VALUE_INTERPOLATION_BICUBIC) {
                    this.iType = TYPE_BICUBIC;
                }
            } else {
                hint = hints.get(RenderingHints.KEY_RENDERING);
                // Determine from rendering quality
                if (hint == RenderingHints.VALUE_RENDER_QUALITY) {
                    this.iType = TYPE_BILINEAR;
                    // For speed use nearest neighbor
                }
            }
        }
    
public AffineTransformOp(AffineTransform xform, int interp)
Instantiates a new AffineTransformOp with the specified AffineTransform and a specified interpolation type from the list of predefined interpolation types.

param
xform the AffineTransform.
param
interp the one of predefined interpolation types: TYPE_NEAREST_NEIGHBOR, TYPE_BILINEAR, or TYPE_BICUBIC.

        if (Math.abs(xform.getDeterminant()) <= Double.MIN_VALUE) {
            // awt.24F=Unable to invert transform {0}
            throw new ImagingOpException(Messages.getString("awt.24F", xform)); //$NON-NLS-1$
        }

        this.at = (AffineTransform)xform.clone();

        if (interp != TYPE_NEAREST_NEIGHBOR && interp != TYPE_BILINEAR && interp != TYPE_BICUBIC) {
            // awt.250=Unknown interpolation type: {0}
            throw new IllegalArgumentException(Messages.getString("awt.250", interp)); //$NON-NLS-1$
        }

        this.iType = interp;
    
Methods Summary
public java.awt.image.BufferedImagecreateCompatibleDestImage(java.awt.image.BufferedImage src, java.awt.image.ColorModel destCM)

        Rectangle2D newBounds = getBounds2D(src);

        // Destination image should include (0,0) + positive part
        // of the area bounded by newBounds (in source coordinate system).
        double dstWidth = newBounds.getX() + newBounds.getWidth();
        double dstHeight = newBounds.getY() + newBounds.getHeight();

        if (dstWidth <= 0 || dstHeight <= 0) {
            // awt.251=Transformed width ({0}) and height ({1}) should be
            // greater than 0
            throw new RasterFormatException(Messages.getString("awt.251", dstWidth, dstHeight)); //$NON-NLS-1$
        }

        if (destCM != null) {
            return new BufferedImage(destCM, destCM.createCompatibleWritableRaster((int)dstWidth,
                    (int)dstHeight), destCM.isAlphaPremultiplied(), null);
        }

        ColorModel cm = src.getColorModel();

        // Interpolation other than NN doesn't make any sense for index color
        if (iType != TYPE_NEAREST_NEIGHBOR && cm instanceof IndexColorModel) {
            return new BufferedImage((int)dstWidth, (int)dstHeight, BufferedImage.TYPE_INT_ARGB);
        }

        // OK, we can get source color model
        return new BufferedImage(cm, src.getRaster().createCompatibleWritableRaster((int)dstWidth,
                (int)dstHeight), cm.isAlphaPremultiplied(), null);
    
public java.awt.image.WritableRastercreateCompatibleDestRaster(java.awt.image.Raster src)

        // Here approach is other then in createCompatibleDestImage -
        // destination should include only
        // transformed image, but not (0,0) in source coordinate system

        Rectangle2D newBounds = getBounds2D(src);
        return src.createCompatibleWritableRaster((int)newBounds.getX(), (int)newBounds.getY(),
                (int)newBounds.getWidth(), (int)newBounds.getHeight());
    
public final java.awt.image.BufferedImagefilter(java.awt.image.BufferedImage src, java.awt.image.BufferedImage dst)

        if (src == dst) {
            // awt.252=Source can't be same as the destination
            throw new IllegalArgumentException(Messages.getString("awt.252")); //$NON-NLS-1$
        }

        ColorModel srcCM = src.getColorModel();
        BufferedImage finalDst = null;

        if (srcCM instanceof IndexColorModel
                && (iType != TYPE_NEAREST_NEIGHBOR || srcCM.getPixelSize() % 8 != 0)) {
            src = ((IndexColorModel)srcCM).convertToIntDiscrete(src.getRaster(), true);
            srcCM = src.getColorModel();
        }

        if (dst == null) {
            dst = createCompatibleDestImage(src, srcCM);
        } else {
            if (!srcCM.equals(dst.getColorModel())) {
                // Treat BufferedImage.TYPE_INT_RGB and
                // BufferedImage.TYPE_INT_ARGB as same
                if (!((src.getType() == BufferedImage.TYPE_INT_RGB || src.getType() == BufferedImage.TYPE_INT_ARGB) && (dst
                        .getType() == BufferedImage.TYPE_INT_RGB || dst.getType() == BufferedImage.TYPE_INT_ARGB))) {
                    finalDst = dst;
                    dst = createCompatibleDestImage(src, srcCM);
                }
            }
        }

        // Skip alpha channel for TYPE_INT_RGB images
        if (slowFilter(src.getRaster(), dst.getRaster()) != 0) {
            // awt.21F=Unable to transform source
            throw new ImagingOpException(Messages.getString("awt.21F")); //$NON-NLS-1$
            // TODO - uncomment
            // if (ippFilter(src.getRaster(), dst.getRaster(), src.getType()) !=
            // 0)
            // throw new ImagingOpException ("Unable to transform source");
        }

        if (finalDst != null) {
            Graphics2D g = finalDst.createGraphics();
            g.setComposite(AlphaComposite.Src);
            g.drawImage(dst, 0, 0, null);
        } else {
            finalDst = dst;
        }

        return finalDst;
    
public final java.awt.image.WritableRasterfilter(java.awt.image.Raster src, java.awt.image.WritableRaster dst)

        if (src == dst) {
            // awt.252=Source can't be same as the destination
            throw new IllegalArgumentException(Messages.getString("awt.252")); //$NON-NLS-1$
        }

        if (dst == null) {
            dst = createCompatibleDestRaster(src);
        } else if (src.getNumBands() != dst.getNumBands()) {
            // awt.253=Different number of bands in source and destination
            throw new IllegalArgumentException(Messages.getString("awt.253")); //$NON-NLS-1$
        }

        if (slowFilter(src, dst) != 0) {
            // awt.21F=Unable to transform source
            throw new ImagingOpException(Messages.getString("awt.21F")); //$NON-NLS-1$
            // TODO - uncomment
            // if (ippFilter(src, dst, BufferedImage.TYPE_CUSTOM) != 0)
            // throw new ImagingOpException("Unable to transform source");
        }

        return dst;
    
public final java.awt.geom.Rectangle2DgetBounds2D(java.awt.image.BufferedImage src)

        return getBounds2D(src.getRaster());
    
public final java.awt.geom.Rectangle2DgetBounds2D(java.awt.image.Raster src)

        // We position source raster to (0,0) even if it is translated child
        // raster.
        // This means that we need only width and height of the src
        int width = src.getWidth();
        int height = src.getHeight();

        float[] corners = {
                0, 0, width, 0, width, height, 0, height
        };

        at.transform(corners, 0, corners, 0, 4);

        Rectangle2D.Float bounds = new Rectangle2D.Float(corners[0], corners[1], 0, 0);
        bounds.add(corners[2], corners[3]);
        bounds.add(corners[4], corners[5]);
        bounds.add(corners[6], corners[7]);

        return bounds;
    
public final intgetInterpolationType()
Gets the interpolation type.

return
the interpolation type.

        return iType;
    
public final java.awt.geom.Point2DgetPoint2D(java.awt.geom.Point2D srcPt, java.awt.geom.Point2D dstPt)

        return at.transform(srcPt, dstPt);
    
public final java.awt.RenderingHintsgetRenderingHints()

        if (hints == null) {
            Object value = null;

            switch (iType) {
                case TYPE_NEAREST_NEIGHBOR:
                    value = RenderingHints.VALUE_INTERPOLATION_NEAREST_NEIGHBOR;
                    break;
                case TYPE_BILINEAR:
                    value = RenderingHints.VALUE_INTERPOLATION_BILINEAR;
                    break;
                case TYPE_BICUBIC:
                    value = RenderingHints.VALUE_INTERPOLATION_BICUBIC;
                    break;
                default:
                    value = RenderingHints.VALUE_INTERPOLATION_NEAREST_NEIGHBOR;
            }

            hints = new RenderingHints(RenderingHints.KEY_INTERPOLATION, value);
        }

        return hints;
    
public final java.awt.geom.AffineTransformgetTransform()
Gets the affine transform associated with this AffineTransformOp.

return
the AffineTransform.

        return (AffineTransform)at.clone();
    
private native intippAffineTransform(double m00, double m01, double m02, double m10, double m11, double m12, java.lang.Object src, int srcWidth, int srcHeight, int srcStride, java.lang.Object dst, int dstWidth, int dstHeight, int dstStride, int iType, int channels, boolean skipChannel, int[] offsets)
Ipp affine transform.

param
m00 the m00.
param
m01 the m01.
param
m02 the m02.
param
m10 the m10.
param
m11 the m11.
param
m12 the m12.
param
src the src.
param
srcWidth the src width.
param
srcHeight the src height.
param
srcStride the src stride.
param
dst the dst.
param
dstWidth the dst width.
param
dstHeight the dst height.
param
dstStride the dst stride.
param
iType the i type.
param
channels the channels.
param
skipChannel the skip channel.
param
offsets the offsets.
return
the int.

private intippFilter(java.awt.image.Raster src, java.awt.image.WritableRaster dst, int imageType)
Ipp filter.

param
src the src.
param
dst the dst.
param
imageType the image type.
return
the int.

        int srcStride, dstStride;
        boolean skipChannel = false;
        int channels;
        int offsets[] = null;

        switch (imageType) {
            case BufferedImage.TYPE_INT_RGB:
            case BufferedImage.TYPE_INT_BGR: {
                channels = 4;
                srcStride = src.getWidth() * 4;
                dstStride = dst.getWidth() * 4;
                skipChannel = true;
                break;
            }

            case BufferedImage.TYPE_INT_ARGB:
            case BufferedImage.TYPE_INT_ARGB_PRE:
            case BufferedImage.TYPE_4BYTE_ABGR:
            case BufferedImage.TYPE_4BYTE_ABGR_PRE: {
                channels = 4;
                srcStride = src.getWidth() * 4;
                dstStride = dst.getWidth() * 4;
                break;
            }

            case BufferedImage.TYPE_BYTE_GRAY:
            case BufferedImage.TYPE_BYTE_INDEXED: {
                channels = 1;
                srcStride = src.getWidth();
                dstStride = dst.getWidth();
                break;
            }

            case BufferedImage.TYPE_3BYTE_BGR: {
                channels = 3;
                srcStride = src.getWidth() * 3;
                dstStride = dst.getWidth() * 3;
                break;
            }

            case BufferedImage.TYPE_USHORT_GRAY: // TODO - could be done in
                // native code?
            case BufferedImage.TYPE_USHORT_565_RGB:
            case BufferedImage.TYPE_USHORT_555_RGB:
            case BufferedImage.TYPE_BYTE_BINARY: {
                return slowFilter(src, dst);
            }

            default: {
                SampleModel srcSM = src.getSampleModel();
                SampleModel dstSM = dst.getSampleModel();

                if (srcSM instanceof PixelInterleavedSampleModel
                        && dstSM instanceof PixelInterleavedSampleModel) {
                    // Check PixelInterleavedSampleModel
                    if (srcSM.getDataType() != DataBuffer.TYPE_BYTE
                            || dstSM.getDataType() != DataBuffer.TYPE_BYTE) {
                        return slowFilter(src, dst);
                    }

                    channels = srcSM.getNumBands(); // Have IPP functions for 1,
                    // 3 and 4 channels
                    if (channels != 1 && channels != 3 && channels != 4) {
                        return slowFilter(src, dst);
                    }

                    int dataTypeSize = DataBuffer.getDataTypeSize(srcSM.getDataType()) / 8;

                    srcStride = ((ComponentSampleModel)srcSM).getScanlineStride() * dataTypeSize;
                    dstStride = ((ComponentSampleModel)dstSM).getScanlineStride() * dataTypeSize;
                } else if (srcSM instanceof SinglePixelPackedSampleModel
                        && dstSM instanceof SinglePixelPackedSampleModel) {
                    // Check SinglePixelPackedSampleModel
                    SinglePixelPackedSampleModel sppsm1 = (SinglePixelPackedSampleModel)srcSM;
                    SinglePixelPackedSampleModel sppsm2 = (SinglePixelPackedSampleModel)dstSM;

                    // No IPP function for this type
                    if (sppsm1.getDataType() == DataBuffer.TYPE_USHORT) {
                        return slowFilter(src, dst);
                    }

                    channels = sppsm1.getNumBands();
                    // Have IPP functions for 1, 3 and 4 channels
                    if (channels != 1 && channels != 3 && channels != 4) {
                        return slowFilter(src, dst);
                    }

                    // Check compatibility of sample models
                    if (sppsm1.getDataType() != sppsm2.getDataType()
                            || !Arrays.equals(sppsm1.getBitOffsets(), sppsm2.getBitOffsets())
                            || !Arrays.equals(sppsm1.getBitMasks(), sppsm2.getBitMasks())) {
                        return slowFilter(src, dst);
                    }

                    for (int i = 0; i < channels; i++) {
                        if (sppsm1.getSampleSize(i) != 8) {
                            return slowFilter(src, dst);
                        }
                    }

                    if (channels == 3) {
                        channels = 4;
                    }

                    int dataTypeSize = DataBuffer.getDataTypeSize(sppsm1.getDataType()) / 8;

                    srcStride = sppsm1.getScanlineStride() * dataTypeSize;
                    dstStride = sppsm2.getScanlineStride() * dataTypeSize;
                } else {
                    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();
                    }
                }
            }
        }

        double m00 = at.getScaleX();
        double m01 = at.getShearX();
        double m02 = at.getTranslateX();
        double m10 = at.getShearY();
        double m11 = at.getScaleY();
        double m12 = at.getTranslateY();

        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
        }

        return ippAffineTransform(m00, m01, m02, m10, m11, m12, srcData, src.getWidth(), src
                .getHeight(), srcStride, dstData, dst.getWidth(), dst.getHeight(), dstStride,
                iType, channels, skipChannel, offsets);
    
private intslowFilter(java.awt.image.Raster src, java.awt.image.WritableRaster dst)
Slow filter.

param
src the src.
param
dst the dst.
return
the int.

        // TODO: make correct interpolation
        // TODO: what if there are different data types?

        Rectangle srcBounds = src.getBounds();
        Rectangle dstBounds = dst.getBounds();
        Rectangle normDstBounds = new Rectangle(0, 0, dstBounds.width, dstBounds.height);
        Rectangle bounds = getBounds2D(src).getBounds().intersection(normDstBounds);

        AffineTransform inv = null;
        try {
            inv = at.createInverse();
        } catch (NoninvertibleTransformException e) {
            return -1;
        }

        double[] m = new double[6];
        inv.getMatrix(m);

        int minSrcX = srcBounds.x;
        int minSrcY = srcBounds.y;
        int maxSrcX = srcBounds.x + srcBounds.width;
        int maxSrcY = srcBounds.y + srcBounds.height;

        int minX = bounds.x + dstBounds.x;
        int minY = bounds.y + dstBounds.y;
        int maxX = minX + bounds.width;
        int maxY = minY + bounds.height;

        int hx = (int)(m[0] * 256);
        int hy = (int)(m[1] * 256);
        int vx = (int)(m[2] * 256);
        int vy = (int)(m[3] * 256);
        int sx = (int)(m[4] * 256) + hx * bounds.x + vx * bounds.y + (srcBounds.x) * 256;
        int sy = (int)(m[5] * 256) + hy * bounds.x + vy * bounds.y + (srcBounds.y) * 256;

        vx -= hx * bounds.width;
        vy -= hy * bounds.width;

        if (src.getTransferType() == dst.getTransferType()) {
            for (int y = minY; y < maxY; y++) {
                for (int x = minX; x < maxX; x++) {
                    int px = sx >> 8;
                    int py = sy >> 8;
                    if (px >= minSrcX && py >= minSrcY && px < maxSrcX && py < maxSrcY) {
                        Object val = src.getDataElements(px, py, null);
                        dst.setDataElements(x, y, val);
                    }
                    sx += hx;
                    sy += hy;
                }
                sx += vx;
                sy += vy;
            }
        } else {
            float pixel[] = null;
            for (int y = minY; y < maxY; y++) {
                for (int x = minX; x < maxX; x++) {
                    int px = sx >> 8;
                    int py = sy >> 8;
                    if (px >= minSrcX && py >= minSrcY && px < maxSrcX && py < maxSrcY) {
                        pixel = src.getPixel(px, py, pixel);
                        dst.setPixel(x, y, pixel);
                    }
                    sx += hx;
                    sy += hy;
                }
                sx += vx;
                sy += vy;
            }
        }

        return 0;