1. /*
  2. * @(#)BandCombineOp.java 1.39 03/12/19
  3. *
  4. * Copyright 2004 Sun Microsystems, Inc. All rights reserved.
  5. * SUN PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
  6. */
  7. package java.awt.image;
  8. import java.awt.GraphicsEnvironment;
  9. import java.awt.color.ICC_Profile;
  10. import java.awt.geom.Rectangle2D;
  11. import java.awt.Rectangle;
  12. import java.awt.geom.Point2D;
  13. import java.awt.RenderingHints;
  14. import sun.awt.image.ImagingLib;
  15. /**
  16. * This class performs an arbitrary linear combination of the bands
  17. * in a <CODE>Raster</CODE>, using a specified matrix.
  18. * <p>
  19. * The width of the matrix must be equal to the number of bands in the
  20. * source <CODE>Raster</CODE>, optionally plus one. If there is one more
  21. * column in the matrix than the number of bands, there is an implied 1 at the
  22. * end of the vector of band samples representing a pixel. The height
  23. * of the matrix must be equal to the number of bands in the destination.
  24. * <p>
  25. * For example, a 3-banded <CODE>Raster</CODE> might have the following
  26. * transformation applied to each pixel in order to invert the second band of
  27. * the <CODE>Raster</CODE>.
  28. * <pre>
  29. * [ 1.0 0.0 0.0 0.0 ] [ b1 ]
  30. * [ 0.0 -1.0 0.0 255.0 ] x [ b2 ]
  31. * [ 0.0 0.0 1.0 0.0 ] [ b3 ]
  32. * [ 1 ]
  33. * </pre>
  34. *
  35. * <p>
  36. * Note that the source and destination can be the same object.
  37. * @version 10 Feb 1997
  38. */
  39. public class BandCombineOp implements RasterOp {
  40. float[][] matrix;
  41. int nrows = 0;
  42. int ncols = 0;
  43. RenderingHints hints;
  44. /**
  45. * Constructs a <CODE>BandCombineOp</CODE> with the specified matrix.
  46. * The width of the matrix must be equal to the number of bands in
  47. * the source <CODE>Raster</CODE>, optionally plus one. If there is one
  48. * more column in the matrix than the number of bands, there is an implied
  49. * 1 at the end of the vector of band samples representing a pixel. The
  50. * height of the matrix must be equal to the number of bands in the
  51. * destination.
  52. * <p>
  53. * The first subscript is the row index and the second
  54. * is the column index. This operation uses none of the currently
  55. * defined rendering hints; the <CODE>RenderingHints</CODE> argument can be
  56. * null.
  57. *
  58. * @param matrix The matrix to use for the band combine operation.
  59. * @param hints The <CODE>RenderingHints</CODE> object for this operation.
  60. * Not currently used so it can be null.
  61. */
  62. public BandCombineOp (float[][] matrix, RenderingHints hints) {
  63. nrows = matrix.length;
  64. ncols = matrix[0].length;
  65. this.matrix = new float[nrows][ncols+1];
  66. for (int i=0; i < nrows; i++) {
  67. System.arraycopy(matrix[i], 0, this.matrix[i], 0, ncols);
  68. }
  69. this.hints = hints;
  70. }
  71. /**
  72. * Returns the matrix.
  73. *
  74. * @return The matrix associated with this band combine operation.
  75. */
  76. public final float[][] getMatrix() {
  77. return (float[][]) matrix.clone();
  78. }
  79. /**
  80. * Transforms the <CODE>Raster</CODE> using the matrix specified in the
  81. * constructor. An <CODE>IllegalArgumentException</CODE> may be thrown if
  82. * the number of bands in the source or destination is incompatible with
  83. * the matrix. See the class comments for more details.
  84. * <p>
  85. * If the destination is null, it will be created with a number of bands
  86. * equalling the number of rows in the matrix. No exception is thrown
  87. * if the operation causes a data overflow.
  88. *
  89. * @param src The <CODE>Raster</CODE> to be filtered.
  90. * @param dst The <CODE>Raster</CODE> in which to store the results
  91. * of the filter operation.
  92. *
  93. * @return The filtered <CODE>Raster</CODE>.
  94. *
  95. * @throws IllegalArgumentException If the number of bands in the
  96. * source or destination is incompatible with the matrix.
  97. */
  98. public WritableRaster filter(Raster src, WritableRaster dst) {
  99. int nBands = src.getNumBands();
  100. if (ncols != nBands && ncols != (nBands+1)) {
  101. throw new IllegalArgumentException("Number of columns in the "+
  102. "matrix ("+ncols+
  103. ") must be equal to the number"+
  104. " of bands ([+1]) in src ("+
  105. nBands+").");
  106. }
  107. if (dst == null) {
  108. dst = createCompatibleDestRaster(src);
  109. }
  110. else if (nrows != dst.getNumBands()) {
  111. throw new IllegalArgumentException("Number of rows in the "+
  112. "matrix ("+nrows+
  113. ") must be equal to the number"+
  114. " of bands ([+1]) in dst ("+
  115. nBands+").");
  116. }
  117. if (ImagingLib.filter(this, src, dst) != null) {
  118. return dst;
  119. }
  120. int[] pixel = null;
  121. int[] dstPixel = new int[dst.getNumBands()];
  122. float accum;
  123. int sminX = src.getMinX();
  124. int sY = src.getMinY();
  125. int dminX = dst.getMinX();
  126. int dY = dst.getMinY();
  127. int sX;
  128. int dX;
  129. if (ncols == nBands) {
  130. for (int y=0; y < src.getHeight(); y++, sY++, dY++) {
  131. dX = dminX;
  132. sX = sminX;
  133. for (int x=0; x < src.getWidth(); x++, sX++, dX++) {
  134. pixel = src.getPixel(sX, sY, pixel);
  135. for (int r=0; r < nrows; r++) {
  136. accum = 0.f;
  137. for (int c=0; c < ncols; c++) {
  138. accum += matrix[r][c]*pixel[c];
  139. }
  140. dstPixel[r] = (int) accum;
  141. }
  142. dst.setPixel(dX, dY, dstPixel);
  143. }
  144. }
  145. }
  146. else {
  147. // Need to add constant
  148. for (int y=0; y < src.getHeight(); y++, sY++, dY++) {
  149. dX = dminX;
  150. sX = sminX;
  151. for (int x=0; x < src.getWidth(); x++, sX++, dX++) {
  152. pixel = src.getPixel(sX, sY, pixel);
  153. for (int r=0; r < nrows; r++) {
  154. accum = 0.f;
  155. for (int c=0; c < nBands; c++) {
  156. accum += matrix[r][c]*pixel[c];
  157. }
  158. dstPixel[r] = (int) (accum+matrix[r][nBands]);
  159. }
  160. dst.setPixel(dX, dY, dstPixel);
  161. }
  162. }
  163. }
  164. return dst;
  165. }
  166. /**
  167. * Returns the bounding box of the transformed destination. Since
  168. * this is not a geometric operation, the bounding box is the same for
  169. * the source and destination.
  170. * An <CODE>IllegalArgumentException</CODE> may be thrown if the number of
  171. * bands in the source is incompatible with the matrix. See
  172. * the class comments for more details.
  173. *
  174. * @param src The <CODE>Raster</CODE> to be filtered.
  175. *
  176. * @return The <CODE>Rectangle2D</CODE> representing the destination
  177. * image's bounding box.
  178. *
  179. * @throws IllegalArgumentException If the number of bands in the source
  180. * is incompatible with the matrix.
  181. */
  182. public final Rectangle2D getBounds2D (Raster src) {
  183. return src.getBounds();
  184. }
  185. /**
  186. * Creates a zeroed destination <CODE>Raster</CODE> with the correct size
  187. * and number of bands.
  188. * An <CODE>IllegalArgumentException</CODE> may be thrown if the number of
  189. * bands in the source is incompatible with the matrix. See
  190. * the class comments for more details.
  191. *
  192. * @param src The <CODE>Raster</CODE> to be filtered.
  193. *
  194. * @return The zeroed destination <CODE>Raster</CODE>.
  195. */
  196. public WritableRaster createCompatibleDestRaster (Raster src) {
  197. int nBands = src.getNumBands();
  198. if ((ncols != nBands) && (ncols != (nBands+1))) {
  199. throw new IllegalArgumentException("Number of columns in the "+
  200. "matrix ("+ncols+
  201. ") must be equal to the number"+
  202. " of bands ([+1]) in src ("+
  203. nBands+").");
  204. }
  205. if (src.getNumBands() == nrows) {
  206. return src.createCompatibleWritableRaster();
  207. }
  208. else {
  209. throw new IllegalArgumentException("Don't know how to create a "+
  210. " compatible Raster with "+
  211. nrows+" bands.");
  212. }
  213. }
  214. /**
  215. * Returns the location of the corresponding destination point given a
  216. * point in the source <CODE>Raster</CODE>. If <CODE>dstPt</CODE> is
  217. * specified, it is used to hold the return value.
  218. * Since this is not a geometric operation, the point returned
  219. * is the same as the specified <CODE>srcPt</CODE>.
  220. *
  221. * @param srcPt The <code>Point2D</code> that represents the point in
  222. * the source <code>Raster</code>
  223. * @param dstPt The <CODE>Point2D</CODE> in which to store the result.
  224. *
  225. * @return The <CODE>Point2D</CODE> in the destination image that
  226. * corresponds to the specified point in the source image.
  227. */
  228. public final Point2D getPoint2D (Point2D srcPt, Point2D dstPt) {
  229. if (dstPt == null) {
  230. dstPt = new Point2D.Float();
  231. }
  232. dstPt.setLocation(srcPt.getX(), srcPt.getY());
  233. return dstPt;
  234. }
  235. /**
  236. * Returns the rendering hints for this operation.
  237. *
  238. * @return The <CODE>RenderingHints</CODE> object associated with this
  239. * operation. Returns null if no hints have been set.
  240. */
  241. public final RenderingHints getRenderingHints() {
  242. return hints;
  243. }
  244. }