| /* |
| * 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; |
| } |
| } |