com.day.image.DistortOp Maven / Gradle / Ivy
Show all versions of aem-sdk-api Show documentation
/*************************************************************************
*
* ADOBE CONFIDENTIAL
* __________________
*
* Copyright 2012 Adobe Systems Incorporated
* All Rights Reserved.
*
* NOTICE: All information contained herein is, and remains
* the property of Adobe Systems Incorporated and its suppliers,
* if any. The intellectual and technical concepts contained
* herein are proprietary to Adobe Systems Incorporated and its
* suppliers and are protected by trade secret or copyright law.
* Dissemination of this information or reproduction of this material
* is strictly forbidden unless prior written permission is obtained
* from Adobe Systems Incorporated.
**************************************************************************/
package com.day.image;
import java.awt.Color;
import java.awt.Rectangle;
import java.awt.geom.Point2D;
import java.awt.geom.Rectangle2D;
import java.awt.image.BufferedImage;
/**
* The DistortOp
class implements the distortion operation on a
* BufferedImage
. The rectangular image is distorted such that the
* orignal corners of the image are at four new coordinate locations. This
* distortion is generally not linear and requires quite some calculation.
*
*
* NOTE: This operation works uses copies of the full image to caclulate the
* new image. This takes an amount of memory relative to the size of the image.
* To calculate the needed space let srcWidth and srcHeight be the width and
* height, resp., of the source and dstWidth and dstHeight be the width and
* height, resp., of the destination image. Then the the source image needs :
* srcWidth * srcHeight * 4 channels * 4 bytes per sample bytes and the
* destination image needs dstWidth * dstHeight * 4 channels * 4 bytes.
*
*
* @version $Revision$
* @author fmeschbe
* @since coati
* @audience wad
*/
public class DistortOp extends AbstractBufferedImageOp {
/** The coordinates */
private final double x00;
private final double x01;
private final double x10;
private final double x11;
private final double y00;
private final double y01;
private final double y10;
private final double y11;
/** Whether to crop the result to the current size */
private final boolean crop;
/** the background color for unlit pixels */
private final Color bgColor;
/**
* Creates the DistortOp
object by supplying the coordinates
* of the new corners of the image, where the result is not cropped and
* pixels not filled with any part of the distorted image is replaced with
* black.
*
* @param coords 4x2 matrix describing an array of 4 scale factors by which
* to transform each corner of an image distorted image in order
* top/left, top/right, bottom/left, bottom/right.
*
* @throws IllegalArgumentException if not enough - namely four - coordinate
* pairs are supplied to the constructor.
* @throws NullPointerException if the coords parameter is null
* or if any of the coordinates is null
.
*/
public DistortOp(float[][] coords) {
this(coords, false, Color.black);
}
/**
* Creates the DistortOp
object by supplying the coordinates
* of the new corners of the image.
*
* @param coords 4x2 matrix describing an array of 4 scale factors by which
* to transform each corner of an image distorted image in order
* top/left, top/right, bottom/left, bottom/right.
* @param crop true
if the result should be cropped to the
* size of the source image.
* @param bgColor The color to use for pixels not covered by the distorted
* part of the image.
*
* @throws IllegalArgumentException if not enough - namely four - coordinate
* pairs are supplied to the constructor.
* @throws NullPointerException if the coords parameter is null
* or if any of the coordinates is null
.
*/
public DistortOp(float[][] coords, boolean crop, Color bgColor) {
super(null);
// check arguments
if (coords == null) {
throw new NullPointerException("coords");
}
if (coords.length < 3) {
throw new IllegalArgumentException("need 4 coordinate pairs");
}
for (int i=0; i < 4; i++) {
if (coords[i] == null) {
throw new NullPointerException("coords[" + i + "]");
}
if (coords[i].length < 2) {
throw new IllegalArgumentException("missing value on coords["
+ i + "]");
}
}
// assign values
this.x00 = coords[0][0];
this.y00 = coords[0][1];
this.x10 = coords[1][0];
this.y10 = coords[1][1];
this.x01 = coords[2][0];
this.y01 = coords[2][1];
this.x11 = coords[3][0];
this.y11 = coords[3][1];
this.crop = crop;
this.bgColor = bgColor;
}
//----------- BufferedImageOp interface ------------------------------------
/**
* Returns the bounding box of the filtered destination image.
* The IllegalArgumentException may be thrown if the source
* image is incompatible with the types of images allowed
* by the class implementing this filter.
*/
public Rectangle2D getBounds2D(BufferedImage src) {
if (crop) {
double x0 = x00 < x01 ? x00 : x01;
double y0 = y00 < y10 ? y00 : y10;
double x1 = x10 > x11 ? x10 : x11;
double y1 = y01 > y11 ? y01 : y11;
return new Rectangle((int) Math.ceil((x1-x0) * src.getWidth()),
(int) Math.ceil((y1-y0) * src.getHeight()));
} else {
return super.getBounds2D(src);
}
}
/**
* Returns the location of the destination point given a
* point in the source image. If dstPt is non-null, it
* will be used to hold the return value.
*/
public Point2D getPoint2D(Point2D srcPt, Point2D dstPt) {
return super.getPoint2D(srcPt, dstPt);
}
//---------- protected -----------------------------------------------------
protected void doFilter(BufferedImage src, BufferedImage dst) {
// get dimensions
int sw = src.getWidth();
int sh = src.getHeight();
int dw = dst.getWidth();
int dh = dst.getHeight();
// get local copies of the field values
double x00 = this.x00;
double y00 = this.y00;
double x01 = this.x01;
double y01 = this.y01;
double x10 = this.x10;
double y10 = this.y10;
double x11 = this.x11;
double y11 = this.y11;
// calculate crop
if (crop) {
double x0 = x00 < x01 ? x00 : x01;
double y0 = y00 < y10 ? y00 : y10;
x00 -= x0;
x01 -= x0;
x10 -= x0;
x11 -= x0;
y00 -= y0;
y01 -= y0;
y10 -= y0;
y11 -= y0;
}
// coefficients
double b00, b01, b02, b10, b11, b12, b20, b21, b22;
b00 = ((-y00 + y01)*(x10*y00 - x11*y00 - x00*y10 + x11*y10 + x00*y11 - x10*y11)*(x10*y01 - x11*y01 - x01*y10 + x11*y10 + x01*y11 - x10*y11));
b10 = ((y00 - y10)*(x01*y00 - x11*y00 - x00*y01 + x11*y01 + x00*y11 - x01*y11)*(-(x10*y01) + x11*y01 + x01*y10 - x11*y10 - x01*y11 + x10*y11));
b20 = (-(x10*x10*y00*y00*y01) + 2*x10*x11*y00*y00*y01 - x11*x11*y00*y00*y01 + x10*x10*y00*y01*y01 - 2*x10*x11*y00*y01*y01 + x11*x11*y00*y01*y01 +
x01*x01*y00*y00*y10 - 2*x01*x11*y00*y00*y10 + x11*x11*y00*y00*y10 - 2*x00*x01*y00*y01*y10 + 2*x00*x10*y00*y01*y10 + 2*x01*x11*y00*y01*y10 -
2*x10*x11*y00*y01*y10 + x00*x00*y01*y01*y10 - 2*x00*x10*y01*y01*y10 + 2*x10*x11*y01*y01*y10 - x11*x11*y01*y01*y10 - x01*x01*y00*y10*y10 +
2*x01*x11*y00*y10*y10 - x11*x11*y00*y10*y10 - x00*x00*y01*y10*y10 + 2*x00*x01*y01*y10*y10 - 2*x01*x11*y01*y10*y10 + x11*x11*y01*y10*y10 -
x01*x01*y00*y00*y11 + x10*x10*y00*y00*y11 + 2*x01*x11*y00*y00*y11 - 2*x10*x11*y00*y00*y11 + 2*x00*x01*y00*y01*y11 - 2*x00*x10*y00*y01*y11 -
2*x01*x11*y00*y01*y11 + 2*x10*x11*y00*y01*y11 - x00*x00*y01*y01*y11 + 2*x00*x10*y01*y01*y11 - x10*x10*y01*y01*y11 + 2*x00*x01*y00*y10*y11 -
2*x00*x10*y00*y10*y11 - 2*x01*x11*y00*y10*y11 + 2*x10*x11*y00*y10*y11 - 2*x00*x01*y01*y10*y11 + 2*x00*x10*y01*y10*y11 + 2*x01*x11*y01*y10*y11 -
2*x10*x11*y01*y10*y11 + x00*x00*y10*y10*y11 - 2*x00*x01*y10*y10*y11 + x01*x01*y10*y10*y11 - 2*x00*x01*y00*y11*y11 + x01*x01*y00*y11*y11 +
2*x00*x10*y00*y11*y11 - x10*x10*y00*y11*y11 + x00*x00*y01*y11*y11 - 2*x00*x10*y01*y11*y11 + x10*x10*y01*y11*y11 - x00*x00*y10*y11*y11 +
2*x00*x01*y10*y11*y11 - x01*x01*y10*y11*y11);
b01 = ((-x00 + x01)*(x10*y01 - x11*y01 - x01*y10 + x11*y10 + x01*y11 - x10*y11)*(-(x10*y00) + x11*y00 + x00*y10 - x11*y10 - x00*y11 + x10*y11));
b11 = ((-x00 + x10)*(-(x01*y00) + x11*y00 + x00*y01 - x11*y01 - x00*y11 + x01*y11)*(x10*y01 - x11*y01 - x01*y10 + x11*y10 + x01*y11 - x10*y11));
b21 = (-(x01*x01*x10*y00*y00) + x01*x10*x10*y00*y00 + x01*x01*x11*y00*y00 - x10*x10*x11*y00*y00 - x01*x11*x11*y00*y00 + x10*x11*x11*y00*y00 +
2*x00*x01*x10*y00*y01 - 2*x01*x10*x10*y00*y01 - 2*x00*x01*x11*y00*y01 - 2*x00*x10*x11*y00*y01 + 2*x01*x10*x11*y00*y01 + 2*x10*x10*x11*y00*y01 + 2*x00*x11*x11*y00*y01 -
2*x10*x11*x11*y00*y01 - x00*x00*x10*y01*y01 + x00*x10*x10*y01*y01 + x00*x00*x11*y01*y01 - x10*x10*x11*y01*y01 - x00*x11*x11*y01*y01 +
x10*x11*x11*y01*y01 - 2*x00*x01*x10*y00*y10 + 2*x01*x01*x10*y00*y10 + 2*x00*x01*x11*y00*y10 - 2*x01*x01*x11*y00*y10 + 2*x00*x10*x11*y00*y10 - 2*x01*x10*x11*y00*y10 -
2*x00*x11*x11*y00*y10 + 2*x01*x11*x11*y00*y10 + x00*x00*x01*y10*y10 - x00*x01*x01*y10*y10 - x00*x00*x11*y10*y10 + x01*x01*x11*y10*y10 +
x00*x11*x11*y10*y10 - x01*x11*x11*y10*y10 + 2*x00*x00*x10*y01*y11 - 2*x00*x01*x10*y01*y11 - 2*x00*x10*x10*y01*y11 + 2*x01*x10*x10*y01*y11 -
2*x00*x00*x11*y01*y11 + 2*x00*x01*x11*y01*y11 + 2*x00*x10*x11*y01*y11 - 2*x01*x10*x11*y01*y11 - 2*x00*x00*x01*y10*y11 + 2*x00*x01*x01*y10*y11 + 2*x00*x01*x10*y10*y11 -
2*x01*x01*x10*y10*y11 + 2*x00*x00*x11*y10*y11 - 2*x00*x01*x11*y10*y11 - 2*x00*x10*x11*y10*y11 + 2*x01*x10*x11*y10*y11 + x00*x00*x01*y11*y11 - x00*x01*x01*y11*y11 -
x00*x00*x10*y11*y11 + x01*x01*x10*y11*y11 + x00*x10*x10*y11*y11 - x01*x10*x10*y11*y11);
b02 = ((-(x01*y00) + x00*y01)*(x10*y01 - x11*y01 - x01*y10 + x11*y10 + x01*y11 - x10*y11)*(-(x10*y00) + x11*y00 + x00*y10 - x11*y10 - x00*y11 + x10*y11));
b12 = ((-(x10*y00) + x00*y10)*(x01*y00 - x11*y00 - x00*y01 + x11*y01 + x00*y11 - x01*y11)*(-(x10*y01) + x11*y01 + x01*y10 - x11*y10 - x01*y11 + x10*y11));
b22 = (x01*x10*x10*y00*y00*y01 - 2*x01*x10*x11*y00*y00*y01 + x01*x11*x11*y00*y00*y01 - x00*x10*x10*y00*y01*y01 + 2*x00*x10*x11*y00*y01*y01 - x00*x11*x11*y00*y01*y01 -
x01*x01*x10*y00*y00*y10 + 2*x01*x10*x11*y00*y00*y10 - x10*x11*x11*y00*y00*y10 + 2*x00*x01*x11*y00*y01*y10 - 2*x00*x10*x11*y00*y01*y10 - 2*x01*x11*x11*y00*y01*y10 +
2*x10*x11*x11*y00*y01*y10 + x00*x00*x10*y01*y01*y10 - 2*x00*x00*x11*y01*y01*y10 + 2*x00*x11*x11*y01*y01*y10 -
x10*x11*x11*y01*y01*y10 + x00*x01*x01*y00*y10*y10 - 2*x00*x01*x11*y00*y10*y10 + x00*x11*x11*y00*y10*y10 - x00*x00*x01*y01*y10*y10 +
2*x00*x00*x11*y01*y10*y10 - 2*x00*x11*x11*y01*y10*y10 + x01*x11*x11*y01*y10*y10 + 2*x01*x01*x10*y00*y00*y11 -
2*x01*x10*x10*y00*y00*y11 - x01*x01*x11*y00*y00*y11 + x10*x10*x11*y00*y00*y11 - 2*x00*x01*x10*y00*y01*y11 + 2*x00*x10*x10*y00*y01*y11 +
2*x01*x10*x11*y00*y01*y11 - 2*x10*x10*x11*y00*y01*y11 + x00*x00*x11*y01*y01*y11 - 2*x00*x10*x11*y01*y01*y11 + x10*x10*x11*y01*y01*y11 - 2*x00*x01*x01*y00*y10*y11 +
2*x00*x01*x10*y00*y10*y11 + 2*x01*x01*x11*y00*y10*y11 - 2*x01*x10*x11*y00*y10*y11 + 2*x00*x00*x01*y01*y10*y11 - 2*x00*x00*x10*y01*y10*y11 - 2*x00*x01*x11*y01*y10*y11 +
2*x00*x10*x11*y01*y10*y11 - x00*x00*x11*y10*y10*y11 + 2*x00*x01*x11*y10*y10*y11 - x01*x01*x11*y10*y10*y11 + x00*x01*x01*y00*y11*y11 -
2*x01*x01*x10*y00*y11*y11 - x00*x10*x10*y00*y11*y11 + 2*x01*x10*x10*y00*y11*y11 - x00*x00*x01*y01*y11*y11 +
2*x00*x01*x10*y01*y11*y11 - x01*x10*x10*y01*y11*y11 + x00*x00*x10*y10*y11*y11 - 2*x00*x01*x10*y10*y11*y11 + x01*x01*x10*y10*y11*y11);
// adjust coeefs
// b00/=(double) sw;
b01 *= (double) sw / (double) sh;
b02 *= sw;
b10 *= (double) sh / (double) sw;
//b11/=(double) sh;
b12 *= sh;
b20 /= sw;
b21 /= sh;
int[] s0 = src.getRaster().getSamples(0, 0, sw, sh, 0, (int[]) null);
int[] s1 = src.getRaster().getSamples(0, 0, sw, sh, 1, (int[]) null);
int[] s2 = src.getRaster().getSamples(0, 0, sw, sh, 2, (int[]) null);
int[] s3 = src.getRaster().getSamples(0, 0, sw, sh, 3, (int[]) null);
int doff = 0;
int[] d0 = new int[s0.length];
int[] d1 = new int[s1.length];
int[] d2 = new int[s2.length];
int[] d3 = new int[s3.length];
for (int dy=0; dy < dh; dy++) {
for (int dx=0; dx < dw; dx++) {
double t0 = 0.0;
double t1 = 0.0;
double t2 = 0.0;
double t3 = 0.0;
double vv = dy;
for (int sy2=4; sy2 > 0; sy2--) {
double uu = dx;
for (int sx2=4; sx2 > 0; sx2--) {
double v0 = b00 * uu + b01 * vv + b02;
double v1 = b10 * uu + b11 * vv + b12;
double v2 = b20 * uu + b21 * vv + b22;
int sx1 = (int) Math.floor(v0 / v2);
int sy1 = (int) Math.floor(v1 / v2);
if ((sx1 >= 0) && (sx1 < sw) && (sy1 >= 0)
&& (sy1 < sh)) {
int soff = sx1 + sy1 * sw;
t0 += s0[soff];
t1 += s1[soff];
t2 += s2[soff];
t3 += s3[soff];
} else {
t0 += bgColor.getRed();
t1 += bgColor.getGreen();
t2 += bgColor.getBlue();
t3 += bgColor.getAlpha();
}
uu += 0.25;
}
vv += 0.25;
}
d0[doff] = (int) (t0 / 16.0);
d1[doff] = (int) (t1 / 16.0);
d2[doff] = (int) (t2 / 16.0);
d3[doff] = (int) (t3 / 16.0);
doff++;
}
}
dst.getRaster().setSamples(0, 0, dw, dh, 0, d0);
dst.getRaster().setSamples(0, 0, dw, dh, 1, d1);
dst.getRaster().setSamples(0, 0, dw, dh, 2, d2);
dst.getRaster().setSamples(0, 0, dw, dh, 3, d3);
}
}