/* | 
|
 * Copyright (c) 1997, 2005, Oracle and/or its affiliates. All rights reserved. | 
|
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. | 
|
 * | 
|
 * This code is free software; you can redistribute it and/or modify it | 
|
 * under the terms of the GNU General Public License version 2 only, as | 
|
 * published by the Free Software Foundation.  Oracle designates this | 
|
 * particular file as subject to the "Classpath" exception as provided | 
|
 * by Oracle in the LICENSE file that accompanied this code. | 
|
 * | 
|
 * This code is distributed in the hope that it will be useful, but WITHOUT | 
|
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | 
|
 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License | 
|
 * version 2 for more details (a copy is included in the LICENSE file that | 
|
 * accompanied this code). | 
|
 * | 
|
 * You should have received a copy of the GNU General Public License version | 
|
 * 2 along with this work; if not, write to the Free Software Foundation, | 
|
 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. | 
|
 * | 
|
 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA | 
|
 * or visit www.oracle.com if you need additional information or have any | 
|
 * questions. | 
|
*/  | 
|
package java.awt.image;  | 
|
import java.awt.GraphicsEnvironment;  | 
|
import java.awt.color.ICC_Profile;  | 
|
import java.awt.geom.Rectangle2D;  | 
|
import java.awt.Rectangle;  | 
|
import java.awt.geom.Point2D;  | 
|
import java.awt.RenderingHints;  | 
|
import sun.awt.image.ImagingLib;  | 
|
import java.util.Arrays;  | 
|
/** | 
|
 * This class performs an arbitrary linear combination of the bands | 
|
 * in a <CODE>Raster</CODE>, using a specified matrix. | 
|
 * <p> | 
|
 * The width of the matrix must be equal to the number of bands in the | 
|
 * source <CODE>Raster</CODE>, optionally plus one.  If there is one more | 
|
 * column in the matrix than the number of bands, there is an implied 1 at the | 
|
 * end of the vector of band samples representing a pixel.  The height | 
|
 * of the matrix must be equal to the number of bands in the destination. | 
|
 * <p> | 
|
 * For example, a 3-banded <CODE>Raster</CODE> might have the following | 
|
 * transformation applied to each pixel in order to invert the second band of | 
|
 * the <CODE>Raster</CODE>. | 
|
 * <pre> | 
|
 *   [ 1.0   0.0   0.0    0.0  ]     [ b1 ] | 
|
 *   [ 0.0  -1.0   0.0  255.0  ]  x  [ b2 ] | 
|
 *   [ 0.0   0.0   1.0    0.0  ]     [ b3 ] | 
|
 *                                   [ 1 ] | 
|
 * </pre> | 
|
 * | 
|
 * <p> | 
|
 * Note that the source and destination can be the same object. | 
|
*/  | 
|
public class BandCombineOp implements RasterOp {  | 
|
float[][] matrix;  | 
|
int nrows = 0;  | 
|
int ncols = 0;  | 
|
RenderingHints hints;  | 
|
    /** | 
|
     * Constructs a <CODE>BandCombineOp</CODE> with the specified matrix. | 
|
     * The width of the matrix must be equal to the number of bands in | 
|
     * the source <CODE>Raster</CODE>, optionally plus one.  If there is one | 
|
     * more column in the matrix than the number of bands, there is an implied | 
|
     * 1 at the end of the vector of band samples representing a pixel.  The | 
|
     * height of the matrix must be equal to the number of bands in the | 
|
     * destination. | 
|
     * <p> | 
|
     * The first subscript is the row index and the second | 
|
     * is the column index.  This operation uses none of the currently | 
|
     * defined rendering hints; the <CODE>RenderingHints</CODE> argument can be | 
|
     * null. | 
|
     * | 
|
     * @param matrix The matrix to use for the band combine operation. | 
|
     * @param hints The <CODE>RenderingHints</CODE> object for this operation. | 
|
     * Not currently used so it can be null. | 
|
*/  | 
|
public BandCombineOp (float[][] matrix, RenderingHints hints) {  | 
|
nrows = matrix.length;  | 
|
ncols = matrix[0].length;  | 
|
this.matrix = new float[nrows][];  | 
|
for (int i=0; i < nrows; i++) {  | 
|
            /* Arrays.copyOf is forgiving of the source array being | 
|
             * too short, but it is also faster than other cloning | 
|
             * methods, so we provide our own protection for short | 
|
             * matrix rows. | 
|
*/  | 
|
if (ncols > matrix[i].length) {  | 
|
throw new IndexOutOfBoundsException("row "+i+" too short");  | 
|
}  | 
|
this.matrix[i] = Arrays.copyOf(matrix[i], ncols);  | 
|
}  | 
|
this.hints = hints;  | 
|
}  | 
|
    /** | 
|
     * Returns a copy of the linear combination matrix. | 
|
     * | 
|
     * @return The matrix associated with this band combine operation. | 
|
*/  | 
|
    public final float[][] getMatrix() { | 
|
float[][] ret = new float[nrows][];  | 
|
for (int i = 0; i < nrows; i++) {  | 
|
ret[i] = Arrays.copyOf(matrix[i], ncols);  | 
|
}  | 
|
return ret;  | 
|
}  | 
|
    /** | 
|
     * Transforms the <CODE>Raster</CODE> using the matrix specified in the | 
|
     * constructor. An <CODE>IllegalArgumentException</CODE> may be thrown if | 
|
     * the number of bands in the source or destination is incompatible with | 
|
     * the matrix.  See the class comments for more details. | 
|
     * <p> | 
|
     * If the destination is null, it will be created with a number of bands | 
|
     * equalling the number of rows in the matrix. No exception is thrown | 
|
     * if the operation causes a data overflow. | 
|
     * | 
|
     * @param src The <CODE>Raster</CODE> to be filtered. | 
|
     * @param dst The <CODE>Raster</CODE> in which to store the results | 
|
     * of the filter operation. | 
|
     * | 
|
     * @return The filtered <CODE>Raster</CODE>. | 
|
     * | 
|
     * @throws IllegalArgumentException If the number of bands in the | 
|
     * source or destination is incompatible with the matrix. | 
|
*/  | 
|
public WritableRaster filter(Raster src, WritableRaster dst) {  | 
|
int nBands = src.getNumBands();  | 
|
if (ncols != nBands && ncols != (nBands+1)) {  | 
|
throw new IllegalArgumentException("Number of columns in the "+  | 
|
"matrix ("+ncols+  | 
|
                                               ") must be equal to the number"+ | 
|
                                               " of bands ([+1]) in src ("+ | 
|
nBands+").");  | 
|
}  | 
|
if (dst == null) {  | 
|
dst = createCompatibleDestRaster(src);  | 
|
}  | 
|
else if (nrows != dst.getNumBands()) {  | 
|
throw new IllegalArgumentException("Number of rows in the "+  | 
|
"matrix ("+nrows+  | 
|
                                               ") must be equal to the number"+ | 
|
                                               " of bands ([+1]) in dst ("+ | 
|
nBands+").");  | 
|
}  | 
|
if (ImagingLib.filter(this, src, dst) != null) {  | 
|
return dst;  | 
|
}  | 
|
int[] pixel = null;  | 
|
int[] dstPixel = new int[dst.getNumBands()];  | 
|
float accum;  | 
|
int sminX = src.getMinX();  | 
|
int sY = src.getMinY();  | 
|
int dminX = dst.getMinX();  | 
|
int dY = dst.getMinY();  | 
|
int sX;  | 
|
int dX;  | 
|
if (ncols == nBands) {  | 
|
for (int y=0; y < src.getHeight(); y++, sY++, dY++) {  | 
|
dX = dminX;  | 
|
sX = sminX;  | 
|
for (int x=0; x < src.getWidth(); x++, sX++, dX++) {  | 
|
pixel = src.getPixel(sX, sY, pixel);  | 
|
for (int r=0; r < nrows; r++) {  | 
|
accum = 0.f;  | 
|
for (int c=0; c < ncols; c++) {  | 
|
accum += matrix[r][c]*pixel[c];  | 
|
}  | 
|
dstPixel[r] = (int) accum;  | 
|
}  | 
|
dst.setPixel(dX, dY, dstPixel);  | 
|
}  | 
|
}  | 
|
}  | 
|
        else { | 
|
            // Need to add constant | 
|
for (int y=0; y < src.getHeight(); y++, sY++, dY++) {  | 
|
dX = dminX;  | 
|
sX = sminX;  | 
|
for (int x=0; x < src.getWidth(); x++, sX++, dX++) {  | 
|
pixel = src.getPixel(sX, sY, pixel);  | 
|
for (int r=0; r < nrows; r++) {  | 
|
accum = 0.f;  | 
|
for (int c=0; c < nBands; c++) {  | 
|
accum += matrix[r][c]*pixel[c];  | 
|
}  | 
|
dstPixel[r] = (int) (accum+matrix[r][nBands]);  | 
|
}  | 
|
dst.setPixel(dX, dY, dstPixel);  | 
|
}  | 
|
}  | 
|
}  | 
|
return dst;  | 
|
}  | 
|
    /** | 
|
     * Returns the bounding box of the transformed destination.  Since | 
|
     * this is not a geometric operation, the bounding box is the same for | 
|
     * the source and destination. | 
|
     * An <CODE>IllegalArgumentException</CODE> may be thrown if the number of | 
|
     * bands in the source is incompatible with the matrix.  See | 
|
     * the class comments for more details. | 
|
     * | 
|
     * @param src The <CODE>Raster</CODE> to be filtered. | 
|
     * | 
|
     * @return The <CODE>Rectangle2D</CODE> representing the destination | 
|
     * image's bounding box. | 
|
     * | 
|
     * @throws IllegalArgumentException If the number of bands in the source | 
|
     * is incompatible with the matrix. | 
|
*/  | 
|
public final Rectangle2D getBounds2D (Raster src) {  | 
|
return src.getBounds();  | 
|
}  | 
|
    /** | 
|
     * Creates a zeroed destination <CODE>Raster</CODE> with the correct size | 
|
     * and number of bands. | 
|
     * An <CODE>IllegalArgumentException</CODE> may be thrown if the number of | 
|
     * bands in the source is incompatible with the matrix.  See | 
|
     * the class comments for more details. | 
|
     * | 
|
     * @param src The <CODE>Raster</CODE> to be filtered. | 
|
     * | 
|
     * @return The zeroed destination <CODE>Raster</CODE>. | 
|
*/  | 
|
public WritableRaster createCompatibleDestRaster (Raster src) {  | 
|
int nBands = src.getNumBands();  | 
|
if ((ncols != nBands) && (ncols != (nBands+1))) {  | 
|
throw new IllegalArgumentException("Number of columns in the "+  | 
|
"matrix ("+ncols+  | 
|
                                               ") must be equal to the number"+ | 
|
                                               " of bands ([+1]) in src ("+ | 
|
nBands+").");  | 
|
}  | 
|
if (src.getNumBands() == nrows) {  | 
|
return src.createCompatibleWritableRaster();  | 
|
}  | 
|
        else { | 
|
throw new IllegalArgumentException("Don't know how to create a "+  | 
|
                                               " compatible Raster with "+ | 
|
nrows+" bands.");  | 
|
}  | 
|
}  | 
|
    /** | 
|
     * Returns the location of the corresponding destination point given a | 
|
     * point in the source <CODE>Raster</CODE>.  If <CODE>dstPt</CODE> is | 
|
     * specified, it is used to hold the return value. | 
|
     * Since this is not a geometric operation, the point returned | 
|
     * is the same as the specified <CODE>srcPt</CODE>. | 
|
     * | 
|
     * @param srcPt The <code>Point2D</code> that represents the point in | 
|
     *              the source <code>Raster</code> | 
|
     * @param dstPt The <CODE>Point2D</CODE> in which to store the result. | 
|
     * | 
|
     * @return The <CODE>Point2D</CODE> in the destination image that | 
|
     * corresponds to the specified point in the source image. | 
|
*/  | 
|
public final Point2D getPoint2D (Point2D srcPt, Point2D dstPt) {  | 
|
if (dstPt == null) {  | 
|
dstPt = new Point2D.Float();  | 
|
}  | 
|
dstPt.setLocation(srcPt.getX(), srcPt.getY());  | 
|
return dstPt;  | 
|
}  | 
|
    /** | 
|
     * Returns the rendering hints for this operation. | 
|
     * | 
|
     * @return The <CODE>RenderingHints</CODE> object associated with this | 
|
     * operation.  Returns null if no hints have been set. | 
|
*/  | 
|
public final RenderingHints getRenderingHints() {  | 
|
return hints;  | 
|
}  | 
|
}  |