blob: 440b2d66e00ae12696d3c5773676e64b00c045ac [file] [log] [blame]
J. Duke319a3b92007-12-01 00:00:00 +00001/*
2 * Copyright 1997-2005 Sun Microsystems, Inc. All Rights Reserved.
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * This code is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 only, as
7 * published by the Free Software Foundation. Sun designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Sun in the LICENSE file that accompanied this code.
10 *
11 * This code is distributed in the hope that it will be useful, but WITHOUT
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14 * version 2 for more details (a copy is included in the LICENSE file that
15 * accompanied this code).
16 *
17 * You should have received a copy of the GNU General Public License version
18 * 2 along with this work; if not, write to the Free Software Foundation,
19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20 *
21 * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
22 * CA 95054 USA or visit www.sun.com if you need additional information or
23 * have any questions.
24 */
25
26package java.awt.image;
27
28import java.awt.GraphicsEnvironment;
29import java.awt.color.ICC_Profile;
30import java.awt.geom.Rectangle2D;
31import java.awt.Rectangle;
32import java.awt.geom.Point2D;
33import java.awt.RenderingHints;
34import sun.awt.image.ImagingLib;
35import java.util.Arrays;
36
37/**
38 * This class performs an arbitrary linear combination of the bands
39 * in a <CODE>Raster</CODE>, using a specified matrix.
40 * <p>
41 * The width of the matrix must be equal to the number of bands in the
42 * source <CODE>Raster</CODE>, optionally plus one. If there is one more
43 * column in the matrix than the number of bands, there is an implied 1 at the
44 * end of the vector of band samples representing a pixel. The height
45 * of the matrix must be equal to the number of bands in the destination.
46 * <p>
47 * For example, a 3-banded <CODE>Raster</CODE> might have the following
48 * transformation applied to each pixel in order to invert the second band of
49 * the <CODE>Raster</CODE>.
50 * <pre>
51 * [ 1.0 0.0 0.0 0.0 ] [ b1 ]
52 * [ 0.0 -1.0 0.0 255.0 ] x [ b2 ]
53 * [ 0.0 0.0 1.0 0.0 ] [ b3 ]
54 * [ 1 ]
55 * </pre>
56 *
57 * <p>
58 * Note that the source and destination can be the same object.
59 */
60public class BandCombineOp implements RasterOp {
61 float[][] matrix;
62 int nrows = 0;
63 int ncols = 0;
64 RenderingHints hints;
65
66 /**
67 * Constructs a <CODE>BandCombineOp</CODE> with the specified matrix.
68 * The width of the matrix must be equal to the number of bands in
69 * the source <CODE>Raster</CODE>, optionally plus one. If there is one
70 * more column in the matrix than the number of bands, there is an implied
71 * 1 at the end of the vector of band samples representing a pixel. The
72 * height of the matrix must be equal to the number of bands in the
73 * destination.
74 * <p>
75 * The first subscript is the row index and the second
76 * is the column index. This operation uses none of the currently
77 * defined rendering hints; the <CODE>RenderingHints</CODE> argument can be
78 * null.
79 *
80 * @param matrix The matrix to use for the band combine operation.
81 * @param hints The <CODE>RenderingHints</CODE> object for this operation.
82 * Not currently used so it can be null.
83 */
84 public BandCombineOp (float[][] matrix, RenderingHints hints) {
85 nrows = matrix.length;
86 ncols = matrix[0].length;
87 this.matrix = new float[nrows][];
88 for (int i=0; i < nrows; i++) {
89 /* Arrays.copyOf is forgiving of the source array being
90 * too short, but it is also faster than other cloning
91 * methods, so we provide our own protection for short
92 * matrix rows.
93 */
94 if (ncols > matrix[i].length) {
95 throw new IndexOutOfBoundsException("row "+i+" too short");
96 }
97 this.matrix[i] = Arrays.copyOf(matrix[i], ncols);
98 }
99 this.hints = hints;
100 }
101
102 /**
103 * Returns a copy of the linear combination matrix.
104 *
105 * @return The matrix associated with this band combine operation.
106 */
107 public final float[][] getMatrix() {
108 float[][] ret = new float[nrows][];
109 for (int i = 0; i < nrows; i++) {
110 ret[i] = Arrays.copyOf(matrix[i], ncols);
111 }
112 return ret;
113 }
114
115 /**
116 * Transforms the <CODE>Raster</CODE> using the matrix specified in the
117 * constructor. An <CODE>IllegalArgumentException</CODE> may be thrown if
118 * the number of bands in the source or destination is incompatible with
119 * the matrix. See the class comments for more details.
120 * <p>
121 * If the destination is null, it will be created with a number of bands
122 * equalling the number of rows in the matrix. No exception is thrown
123 * if the operation causes a data overflow.
124 *
125 * @param src The <CODE>Raster</CODE> to be filtered.
126 * @param dst The <CODE>Raster</CODE> in which to store the results
127 * of the filter operation.
128 *
129 * @return The filtered <CODE>Raster</CODE>.
130 *
131 * @throws IllegalArgumentException If the number of bands in the
132 * source or destination is incompatible with the matrix.
133 */
134 public WritableRaster filter(Raster src, WritableRaster dst) {
135 int nBands = src.getNumBands();
136 if (ncols != nBands && ncols != (nBands+1)) {
137 throw new IllegalArgumentException("Number of columns in the "+
138 "matrix ("+ncols+
139 ") must be equal to the number"+
140 " of bands ([+1]) in src ("+
141 nBands+").");
142 }
143 if (dst == null) {
144 dst = createCompatibleDestRaster(src);
145 }
146 else if (nrows != dst.getNumBands()) {
147 throw new IllegalArgumentException("Number of rows in the "+
148 "matrix ("+nrows+
149 ") must be equal to the number"+
150 " of bands ([+1]) in dst ("+
151 nBands+").");
152 }
153
154 if (ImagingLib.filter(this, src, dst) != null) {
155 return dst;
156 }
157
158 int[] pixel = null;
159 int[] dstPixel = new int[dst.getNumBands()];
160 float accum;
161 int sminX = src.getMinX();
162 int sY = src.getMinY();
163 int dminX = dst.getMinX();
164 int dY = dst.getMinY();
165 int sX;
166 int dX;
167 if (ncols == nBands) {
168 for (int y=0; y < src.getHeight(); y++, sY++, dY++) {
169 dX = dminX;
170 sX = sminX;
171 for (int x=0; x < src.getWidth(); x++, sX++, dX++) {
172 pixel = src.getPixel(sX, sY, pixel);
173 for (int r=0; r < nrows; r++) {
174 accum = 0.f;
175 for (int c=0; c < ncols; c++) {
176 accum += matrix[r][c]*pixel[c];
177 }
178 dstPixel[r] = (int) accum;
179 }
180 dst.setPixel(dX, dY, dstPixel);
181 }
182 }
183 }
184 else {
185 // Need to add constant
186 for (int y=0; y < src.getHeight(); y++, sY++, dY++) {
187 dX = dminX;
188 sX = sminX;
189 for (int x=0; x < src.getWidth(); x++, sX++, dX++) {
190 pixel = src.getPixel(sX, sY, pixel);
191 for (int r=0; r < nrows; r++) {
192 accum = 0.f;
193 for (int c=0; c < nBands; c++) {
194 accum += matrix[r][c]*pixel[c];
195 }
196 dstPixel[r] = (int) (accum+matrix[r][nBands]);
197 }
198 dst.setPixel(dX, dY, dstPixel);
199 }
200 }
201 }
202
203 return dst;
204 }
205
206 /**
207 * Returns the bounding box of the transformed destination. Since
208 * this is not a geometric operation, the bounding box is the same for
209 * the source and destination.
210 * An <CODE>IllegalArgumentException</CODE> may be thrown if the number of
211 * bands in the source is incompatible with the matrix. See
212 * the class comments for more details.
213 *
214 * @param src The <CODE>Raster</CODE> to be filtered.
215 *
216 * @return The <CODE>Rectangle2D</CODE> representing the destination
217 * image's bounding box.
218 *
219 * @throws IllegalArgumentException If the number of bands in the source
220 * is incompatible with the matrix.
221 */
222 public final Rectangle2D getBounds2D (Raster src) {
223 return src.getBounds();
224 }
225
226
227 /**
228 * Creates a zeroed destination <CODE>Raster</CODE> with the correct size
229 * and number of bands.
230 * An <CODE>IllegalArgumentException</CODE> may be thrown if the number of
231 * bands in the source is incompatible with the matrix. See
232 * the class comments for more details.
233 *
234 * @param src The <CODE>Raster</CODE> to be filtered.
235 *
236 * @return The zeroed destination <CODE>Raster</CODE>.
237 */
238 public WritableRaster createCompatibleDestRaster (Raster src) {
239 int nBands = src.getNumBands();
240 if ((ncols != nBands) && (ncols != (nBands+1))) {
241 throw new IllegalArgumentException("Number of columns in the "+
242 "matrix ("+ncols+
243 ") must be equal to the number"+
244 " of bands ([+1]) in src ("+
245 nBands+").");
246 }
247 if (src.getNumBands() == nrows) {
248 return src.createCompatibleWritableRaster();
249 }
250 else {
251 throw new IllegalArgumentException("Don't know how to create a "+
252 " compatible Raster with "+
253 nrows+" bands.");
254 }
255 }
256
257 /**
258 * Returns the location of the corresponding destination point given a
259 * point in the source <CODE>Raster</CODE>. If <CODE>dstPt</CODE> is
260 * specified, it is used to hold the return value.
261 * Since this is not a geometric operation, the point returned
262 * is the same as the specified <CODE>srcPt</CODE>.
263 *
264 * @param srcPt The <code>Point2D</code> that represents the point in
265 * the source <code>Raster</code>
266 * @param dstPt The <CODE>Point2D</CODE> in which to store the result.
267 *
268 * @return The <CODE>Point2D</CODE> in the destination image that
269 * corresponds to the specified point in the source image.
270 */
271 public final Point2D getPoint2D (Point2D srcPt, Point2D dstPt) {
272 if (dstPt == null) {
273 dstPt = new Point2D.Float();
274 }
275 dstPt.setLocation(srcPt.getX(), srcPt.getY());
276
277 return dstPt;
278 }
279
280 /**
281 * Returns the rendering hints for this operation.
282 *
283 * @return The <CODE>RenderingHints</CODE> object associated with this
284 * operation. Returns null if no hints have been set.
285 */
286 public final RenderingHints getRenderingHints() {
287 return hints;
288 }
289}