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