/*
* Copyright (c) 1997, 2005, Oracle and/or its affiliates. All rights reserved.
* ORACLE PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*/
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;
}
}