Package com.lightcrafts.media.jai.mlib

Source Code of com.lightcrafts.media.jai.mlib.MlibAffineOpImage

/*
* $RCSfile: MlibAffineOpImage.java,v $
*
* Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved.
*
* Use is subject to license terms.
*
* $Revision: 1.1 $
* $Date: 2005/02/11 04:55:49 $
* $State: Exp $
*/
package com.lightcrafts.media.jai.mlib;
import java.awt.Point;
import java.awt.Rectangle;
import java.awt.image.Raster;
import java.awt.image.RenderedImage;
import java.awt.image.WritableRaster;
import java.awt.geom.AffineTransform;
import java.awt.geom.Point2D;
import com.lightcrafts.mediax.jai.BorderExtender;
import com.lightcrafts.mediax.jai.GeometricOpImage;
import com.lightcrafts.mediax.jai.ImageLayout;
import com.lightcrafts.mediax.jai.Interpolation;
import com.lightcrafts.mediax.jai.InterpolationNearest;
import com.lightcrafts.mediax.jai.InterpolationBilinear;
import com.lightcrafts.mediax.jai.InterpolationBicubic;
import com.lightcrafts.mediax.jai.InterpolationBicubic2;
import com.lightcrafts.mediax.jai.util.ImagingException;
import com.lightcrafts.mediax.jai.util.ImagingListener;
import java.util.Map;

import com.lightcrafts.media.jai.util.ImageUtil;
// import com.lightcrafts.media.jai.test.OpImageTester;

/**
* An OpImage class to perform AffineTransform on a source image.
*
*/
class MlibAffineOpImage extends GeometricOpImage {

    /**
     * The transformation in matrix form (medialib expects this form)
     */
    protected double f_transform[];
    protected double m_transform[];
    protected double medialib_tr[];
    protected AffineTransform transform;
    protected AffineTransform i_transform;

    /** The Interpolation object. */
    protected Interpolation interp;

    /** Store source & padded rectangle info */
    private Rectangle srcimg, padimg;

    /** The BorderExtender */
    protected BorderExtender extender;

    /** The true writable area */
    private Rectangle theDest;

    /** Cache the ImagingListener */
    private ImagingListener listener;

    /**
     * Padding values for interpolation
     */
    public int lpad, rpad, tpad, bpad;

    private static ImageLayout layoutHelper(ImageLayout layout,
                                            RenderedImage source,
                                            AffineTransform forward_tr) {
        ImageLayout newLayout;
        if (layout != null) {
            newLayout = (ImageLayout)layout.clone();
        } else {
            newLayout = new ImageLayout();
        }

        //
        // Get sx0,sy0 coordinates & width & height of the source.
        //
        float sx0 = (float)source.getMinX();
        float sy0 = (float)source.getMinY();
        float sw = (float)source.getWidth();
        float sh = (float)source.getHeight();

        //
        // The 4 points (clockwise order) are
        //      (sx0, sy0),    (sx0+sw, sy0)
        //      (sx0, sy0+sh), (sx0+sw, sy0+sh)
        //
        Point2D[] pts = new Point2D[4];
        pts[0] = new Point2D.Float(sx0, sy0);
        pts[1] = new Point2D.Float((sx0+sw), sy0);
        pts[2] = new Point2D.Float((sx0+sw), (sy0+sh));
        pts[3] = new Point2D.Float(sx0, (sy0+sh));

        // Forward map
        forward_tr.transform(pts, 0, pts, 0, 4);

        float dx0 = Float.MAX_VALUE;
        float dy0 = Float.MAX_VALUE;
        float dx1 = -Float.MAX_VALUE;
        float dy1 = -Float.MAX_VALUE;
        for (int i = 0; i < 4; i++) {
            float px = (float)pts[i].getX();
            float py = (float)pts[i].getY();

            dx0 = Math.min(dx0, px);
            dy0 = Math.min(dy0, py);
            dx1 = Math.max(dx1, px);
            dy1 = Math.max(dy1, py);
        }

        //
        // Get the width & height of the resulting bounding box.
        // This is set on the layout
        //
        int lw = (int)(dx1 - dx0);
        int lh = (int)(dy1 - dy0);

        //
        // Set the starting integral coordinate
        // with the following criterion.
        // If it's greater than 0.5, set it to the next integral value (ceil)
        // else set it to the integral value (floor).
        //
        int lx0, ly0;

        int i_dx0 = (int)Math.floor(dx0);
        if (Math.abs(dx0 - i_dx0) <= 0.5) {
            lx0 = i_dx0;
        } else {
            lx0 = (int) Math.ceil(dx0);
        }

        int i_dy0 = (int)Math.floor(dy0);
        if (Math.abs(dy0 - i_dy0) <= 0.5) {
            ly0 = i_dy0;
        } else {
            ly0 = (int) Math.ceil(dy0);
        }

        //
        // Create the layout
        //
        newLayout.setMinX(lx0);
        newLayout.setMinY(ly0);
        newLayout.setWidth(lw);
        newLayout.setHeight(lh);

        return newLayout;
    }

    /**
     * Creates a MlibAffineOpImage given a ParameterBlock containing the
     * image source and the AffineTransform and the interpolation.
     * The image dimensions are derived from the source image.  The tile
     * grid layout, SampleModel, and ColorModel may optionally be specified
     * by an ImageLayout object.
     *
     * @param source a RenderedImage.
     * @param extender a BorderExtender, or null.

     *        or null.  If null, a default cache will be used.
     * @param layout an ImageLayout optionally containing the tile grid layout,
     *        SampleModel, and ColorModel, or null.
     * @param kernel the convolution KernelJAI.
     */
    public MlibAffineOpImage(RenderedImage source,
                             ImageLayout layout,
                             Map config,
                             BorderExtender extender,
                             AffineTransform transform,
                             Interpolation interp,
                             double[] backgroundValues) {
        super(vectorize(source),
              layoutHelper(layout,
                           source,
                           transform),
              config,
              true,
              extender,
              interp,
              backgroundValues);

        // store the interp and extender objects
        this.interp = interp;

        // the extender
        this.extender = extender;

        // cache the listener
        listener = ImageUtil.getImagingListener((java.awt.RenderingHints)config);

        // Store the padding values
        lpad = interp.getLeftPadding();
        rpad = interp.getRightPadding();
        tpad = interp.getTopPadding();
        bpad = interp.getBottomPadding();

        //
        // Store source bounds rectangle
        // and the padded rectangle (for extension cases)
        //
        srcimg = new Rectangle(getSourceImage(0).getMinX(),
                               getSourceImage(0).getMinY(),
                               getSourceImage(0).getWidth(),
                               getSourceImage(0).getHeight());
        padimg = new Rectangle(srcimg.x - lpad,
                               srcimg.y - tpad,
                               srcimg.width + lpad + rpad,
                               srcimg.height + tpad + bpad);

        if (extender == null) {
            //
            // Source has to be shrunk as per interpolation
            // as a result the destination produced could
            // be different from the layout
            //

            //
            // Get sx0,sy0 coordinates and width & height of the source
            //
            float sx0 = (float) srcimg.x;
            float sy0 = (float) srcimg.y;
            float sw = (float) srcimg.width;
            float sh = (float) srcimg.height;

            //
            // get padding amounts as per interpolation
            //
            float f_lpad = (float)lpad;
            float f_rpad = (float)rpad;
            float f_tpad = (float)tpad;
            float f_bpad = (float)bpad;

            //
            // As per pixel defined to be at (0.5, 0.5)
            //
            if ((interp instanceof InterpolationBilinear) ||
                (interp instanceof InterpolationBicubic) ||
                (interp instanceof InterpolationBicubic2)) {
                f_lpad += 0.5;
                f_tpad += 0.5;
                f_rpad += 0.5;
                f_bpad += 0.5;
            }

            //
            // Shrink the source by padding amount prior to forward map
            // This is the maxmimum available source than can be mapped
            //
            sx0 += f_lpad;
            sy0 += f_tpad;
            sw -= (f_lpad + f_rpad);
            sh -= (f_tpad + f_bpad);

            //
            // The 4 points are (x0, y0),     (x0+w, y0)
            //                  (x0+w, y0+h), (x0, y0+h)
            //
            Point2D[] pts = new Point2D[4];
            pts[0] = new Point2D.Float(sx0, sy0);
            pts[1] = new Point2D.Float((sx0 + sw), sy0);
            pts[2] = new Point2D.Float((sx0 + sw), (sy0 + sh));
            pts[3] = new Point2D.Float(sx0, (sy0 + sh));

            // Forward map
            transform.transform(pts, 0, pts, 0, 4);

            float dx0 = Float.MAX_VALUE;
            float dy0 = Float.MAX_VALUE;
            float dx1 = -Float.MAX_VALUE;
            float dy1 = -Float.MAX_VALUE;
            for (int i = 0; i < 4; i++) {
                float px = (float)pts[i].getX();
                float py = (float)pts[i].getY();

                dx0 = Math.min(dx0, px);
                dy0 = Math.min(dy0, py);
                dx1 = Math.max(dx1, px);
                dy1 = Math.max(dy1, py);
            }

            //
            // The layout is the wholly contained integer area of the
            // corresponding floating point bounding box.
            // We cannot round the corners of the floating rect because it
            // would increase the size of the rect, so we need to ceil the
            // upper corner and floor the lower corner.
            //
            int lx0 = (int)Math.ceil(dx0);
            int ly0 = (int)Math.ceil(dy0);
            int lx1 = (int)Math.floor(dx1);
            int ly1 = (int)Math.floor(dy1);

            theDest = new Rectangle(lx0,
                                    ly0,
                                    lx1 - lx0,
                                    ly1 - ly0);
        } else {
            theDest = getBounds();
        }

        // Store the inverse and forward transform
        try {
            this.i_transform = transform.createInverse();
        } catch (java.awt.geom.NoninvertibleTransformException e) {
            String message = JaiI18N.getString("MlibAffineOpImage0");
            listener.errorOccurred(message,
                                   new ImagingException(message, e),
                                   this,
                                   false);
//            throw new RuntimeException(JaiI18N.getString("MlibAffineOpImage0"));
        }
        this.transform = (AffineTransform)transform.clone();

        //
        // Get the forward transform into an array
        // Java returns the values into the array as
        // {m00 m10 m01 m11 m02 m12}
        //
        this.f_transform = new double[6];
        transform.getMatrix(this.f_transform);

        //
        // Rearrange the transform to medialib specifications
        // J2D's transform is [m00 m01 m02] [m10 m11 m12]
        // Medialib's transform is [a b tx] [c d ty]
        //
        this.medialib_tr = new double[6];
        medialib_tr[0] = f_transform[0]; // a  <---> m00
        medialib_tr[1] = f_transform[2]; // b  <---> m01
        medialib_tr[2] = f_transform[4]; // tx <---> m02
        medialib_tr[3] = f_transform[1]; // c  <---> m10
        medialib_tr[4] = f_transform[3]; // d  <---> m11
        medialib_tr[5] = f_transform[5]; // ty <---> m12

        //
        // Make a copy for our internal use
        //
        this.m_transform = new double[6];
        m_transform[0] = f_transform[0]; // a  <---> m00
        m_transform[1] = f_transform[2]; // b  <---> m01
        m_transform[2] = f_transform[4]; // tx <---> m02
        m_transform[3] = f_transform[1]; // c  <---> m10
        m_transform[4] = f_transform[3]; // d  <---> m11
        m_transform[5] = f_transform[5]; // ty <---> m12
    }


    /**
     * Computes the source point corresponding to the supplied point.
     *
     * @param destPt the position in destination image coordinates
     * to map to source image coordinates.
     *
     * @return a <code>Point2D</code> of the same class as
     * <code>destPt</code>.
     *
     * @throws IllegalArgumentException if <code>destPt</code> is
     * <code>null</code>.
     *
     * @since JAI 1.1.2
     */
    public Point2D mapDestPoint(Point2D destPt) {
        if (destPt == null) {
            throw new IllegalArgumentException(JaiI18N.getString("Generic0"));
        }

        Point2D dpt = (Point2D)destPt.clone();
        dpt.setLocation(dpt.getX() + 0.5, dpt.getY() + 0.5);

        Point2D spt = i_transform.transform(dpt, null);
        spt.setLocation(spt.getX() - 0.5, spt.getY() - 0.5);

        return spt;
    }

    /**
     * Computes the destination point corresponding to the supplied point.
     *
     * @param sourcePt the position in source image coordinates
     * to map to destination image coordinates.
     *
     * @return a <code>Point2D</code> of the same class as
     * <code>sourcePt</code>.
     *
     * @throws IllegalArgumentException if <code>destPt</code> is
     * <code>null</code>.
     *
     * @since JAI 1.1.2
     */
    public Point2D mapSourcePoint(Point2D sourcePt) {
        if (sourcePt == null) {
            throw new IllegalArgumentException(JaiI18N.getString("Generic0"));
        }

        Point2D spt = (Point2D)sourcePt.clone();
        spt.setLocation(spt.getX() + 0.5, spt.getY() + 0.5);

        Point2D dpt = transform.transform(spt, null);
        dpt.setLocation(dpt.getX() - 0.5, dpt.getY() - 0.5);

        return dpt;
    }

    /**
     * Forward map the source Rectangle.
     */
    protected Rectangle forwardMapRect(Rectangle sourceRect,
                                       int sourceIndex) {
        return transform.createTransformedShape(sourceRect).getBounds();
    }

    /**
     * Backward map the destination Rectangle.
     */
    protected Rectangle backwardMapRect(Rectangle destRect,
                                        int sourceIndex) {
        //
        // Backward map the destination rectangle to get the
        // corresponding source rectangle
        //
        float dx0 = (float) destRect.x;
        float dy0 = (float) destRect.y;
        float dw = (float) (destRect.width);
        float dh = (float) (destRect.height);

        Point2D[] pts = new Point2D[4];
        pts[0] = new Point2D.Float(dx0, dy0);
        pts[1] = new Point2D.Float((dx0 + dw), dy0);
        pts[2] = new Point2D.Float((dx0 + dw), (dy0 + dh));
        pts[3] = new Point2D.Float(dx0, (dy0 + dh));

        i_transform.transform(pts, 0, pts, 0, 4);

        float f_sx0 = Float.MAX_VALUE;
        float f_sy0 = Float.MAX_VALUE;
        float f_sx1 = -Float.MAX_VALUE;
        float f_sy1 = -Float.MAX_VALUE;
        for (int i = 0; i < 4; i++) {
            float px = (float)pts[i].getX();
            float py = (float)pts[i].getY();

            f_sx0 = Math.min(f_sx0, px);
            f_sy0 = Math.min(f_sy0, py);
            f_sx1 = Math.max(f_sx1, px);
            f_sy1 = Math.max(f_sy1, py);
        }

        int s_x0 = 0, s_y0 = 0, s_x1 = 0, s_y1 = 0;

        // Find the bounding box of the source rectangle
        if (interp instanceof InterpolationNearest) {
            s_x0 = (int) Math.floor(f_sx0);
            s_y0 = (int) Math.floor(f_sy0);
            s_x1 = (int) Math.ceil(f_sx1);
            s_y1 = (int) Math.ceil(f_sy1);
        } else {
            s_x0 = (int) Math.floor(f_sx0 - 0.5);
            s_y0 = (int) Math.floor(f_sy0 - 0.5);
            s_x1 = (int) Math.ceil(f_sx1);
            s_y1 = (int) Math.ceil(f_sy1);
        }

        //
        // Return the new rectangle
        //
        return new Rectangle(s_x0,
                             s_y0,
                             s_x1 - s_x0,
                             s_y1 - s_y0);
    }

    /*
     * Compute a given tile
     */
    public Raster computeTile(int tileX, int tileY) {
        //
        // Create a new WritableRaster to represent this tile.
        //
        Point org = new Point(tileXToX(tileX), tileYToY(tileY));
        WritableRaster dest = createWritableRaster(sampleModel, org);

        //
        // Clip output rectangle to image bounds.
        //
        Rectangle rect = new Rectangle(org.x,
                                       org.y,
                                       tileWidth,
                                       tileHeight);

        //
        // Clip destination tile against the writable destination
        // area. This is either the layout or a smaller area if
        // no extension is specified.
        //
        Rectangle destRect = rect.intersection(theDest);
        Rectangle destRect1 = rect.intersection(getBounds());
        if ((destRect.width <= 0) || (destRect.height <= 0)) {
      if (setBackground) {
    ImageUtil.fillBackground(dest, destRect1, backgroundValues);
      }
            // No area to write
            return dest;
        }

        //
        // determine the source rectangle needed to compute the destRect
        //
        Rectangle srcRect = mapDestRect(destRect, 0);
        if (extender == null) {
            srcRect = srcRect.intersection(srcimg);
        } else {
            srcRect = srcRect.intersection(padimg);
        }

        if (srcRect.width <= 0 || srcRect.height <= 0) {
            // destRect backward mapped outside the source
      if (setBackground) {
    ImageUtil.fillBackground(dest, destRect1, backgroundValues);
      }
            return dest;
        }

        if (!destRect1.equals(destRect)) {
            // beware that destRect1 contains destRect
            ImageUtil.fillBordersWithBackgroundValues(destRect1, destRect, dest, backgroundValues);
        }

        Raster[] sources = new Raster[1];

        // Get the source data
        if (extender == null) {
            sources[0] = getSourceImage(0).getData(srcRect);
        } else {
            sources[0] = getSourceImage(0).getExtendedData(srcRect, extender);
        }

        computeRect(sources, dest, destRect);

        // Recycle the source tile
        if(getSourceImage(0).overlapsMultipleTiles(srcRect)) {
            recycleTile(sources[0]);
        }

        return dest;
    }
}
TOP

Related Classes of com.lightcrafts.media.jai.mlib.MlibAffineOpImage

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.