Source for java.awt.image.BandCombineOp

   1: /* BandCombineOp.java - perform a combination on the bands of a raster
   2:    Copyright (C) 2004, 2006  Free Software Foundation
   3: 
   4: This file is part of GNU Classpath.
   5: 
   6: GNU Classpath is free software; you can redistribute it and/or modify
   7: it under the terms of the GNU General Public License as published by
   8: the Free Software Foundation; either version 2, or (at your option)
   9: any later version.
  10: 
  11: GNU Classpath is distributed in the hope that it will be useful, but
  12: WITHOUT ANY WARRANTY; without even the implied warranty of
  13: MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  14: General Public License for more details.
  15: 
  16: You should have received a copy of the GNU General Public License
  17: along with GNU Classpath; see the file COPYING.  If not, write to the
  18: Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
  19: 02110-1301 USA.
  20: 
  21: Linking this library statically or dynamically with other modules is
  22: making a combined work based on this library.  Thus, the terms and
  23: conditions of the GNU General Public License cover the whole
  24: combination.
  25: 
  26: As a special exception, the copyright holders of this library give you
  27: permission to link this library with independent modules to produce an
  28: executable, regardless of the license terms of these independent
  29: modules, and to copy and distribute the resulting executable under
  30: terms of your choice, provided that you also meet, for each linked
  31: independent module, the terms and conditions of the license of that
  32: module.  An independent module is a module which is not derived from
  33: or based on this library.  If you modify this library, you may extend
  34: this exception to your version of the library, but you are not
  35: obligated to do so.  If you do not wish to do so, delete this
  36: exception statement from your version. */
  37: 
  38: package java.awt.image;
  39: 
  40: import java.awt.RenderingHints;
  41: import java.awt.geom.Point2D;
  42: import java.awt.geom.Rectangle2D;
  43: import java.util.Arrays;
  44: 
  45: /**
  46:  * Filter Raster pixels by applying a matrix.
  47:  * 
  48:  * BandCombineOp applies a matrix to each pixel to produce new pixel values.
  49:  * The width of the matrix must be the same or one more than the number of
  50:  * bands in the source Raster.  If one more, the pixels in the source are
  51:  * assumed to contain an implicit 1.0 at the end.
  52:  * 
  53:  * The rows of the matrix are multiplied by the pixel to produce the values
  54:  * for the destination.  Therefore the destination Raster must contain the
  55:  * same number of bands as the number of rows in the filter matrix.
  56:  * 
  57:  * This Op assumes that samples are integers; floating point sample types will
  58:  * be rounded to their nearest integer value during filtering.
  59:  * 
  60:  * @author Jerry Quinn (jlquinn@optonline.net)
  61:  */
  62: public class BandCombineOp implements RasterOp
  63: {
  64:   private RenderingHints hints;
  65:   private float[][] matrix;
  66:   
  67:   /**
  68:    * Construct a BandCombineOp.
  69:    * 
  70:    * @param matrix The matrix to filter pixels with.
  71:    * @param hints Rendering hints to apply.  Ignored.
  72:    * @throws ArrayIndexOutOfBoundsException if the matrix is invalid
  73:    */
  74:   public BandCombineOp(float[][] matrix, RenderingHints hints)
  75:   {
  76:     this.matrix = new float[matrix.length][];
  77:     int width = matrix[0].length;
  78:     for (int i = 0; i < matrix.length; i++)
  79:       {
  80:         this.matrix[i] = new float[width + 1];
  81:         for (int j = 0; j < width; j++)
  82:           this.matrix[i][j] = matrix[i][j];
  83: 
  84:         // The reference implementation pads the array with a trailing zero...
  85:         this.matrix[i][width] = 0;
  86:       }
  87: 
  88:     this.hints = hints;
  89:   }
  90: 
  91:   /**
  92:    * Filter Raster pixels through a matrix. Applies the Op matrix to source
  93:    * pixes to produce dest pixels. Each row of the matrix is multiplied by the
  94:    * src pixel components to produce the dest pixel. If matrix is one more than
  95:    * the number of bands in the src, the last element is implicitly multiplied
  96:    * by 1, i.e. added to the sum for that dest component. If dest is null, a
  97:    * suitable Raster is created. This implementation uses
  98:    * createCompatibleDestRaster.
  99:    * 
 100:    * @param src The source Raster.
 101:    * @param dest The destination Raster, or null.
 102:    * @throws IllegalArgumentException if the destination raster is incompatible
 103:    *           with the source raster.
 104:    * @return The filtered Raster.
 105:    * @see java.awt.image.RasterOp#filter(java.awt.image.Raster,
 106:    *      java.awt.image.WritableRaster)
 107:    */
 108:   public WritableRaster filter(Raster src, WritableRaster dest) {
 109:     if (dest == null)
 110:       dest = createCompatibleDestRaster(src);
 111:     else if (dest.getNumBands() != src.getNumBands()
 112:              || dest.getTransferType() != src.getTransferType())
 113:       throw new IllegalArgumentException("Destination raster is incompatible with source raster");
 114: 
 115:     // Filter the pixels
 116:     int[] spix = new int[matrix[0].length - 1];
 117:     int[] spix2 = new int[matrix[0].length - 1];
 118:     int[] dpix = new int[matrix.length];
 119:     for (int y = src.getMinY(); y < src.getHeight() + src.getMinY(); y++)
 120:       for (int x = src.getMinX(); x < src.getWidth() + src.getMinX(); x++)
 121:         {
 122:           // In case matrix rows have implicit translation
 123:           spix[spix.length - 1] = 1;
 124:           src.getPixel(x, y, spix);
 125:           
 126:           // Do not re-calculate if pixel is identical to the last one
 127:           // (ie, blocks of the same colour)
 128:           if (!Arrays.equals(spix, spix2))
 129:             {
 130:               System.arraycopy(spix, 0, spix2, 0, spix.length);
 131:               for (int i = 0; i < matrix.length; i++)
 132:                 {
 133:                   dpix[i] = 0;
 134:                   for (int j = 0; j < matrix[0].length - 1; j++)
 135:                     dpix[i] += spix[j] * (int)matrix[i][j];
 136:                 }
 137:             }
 138:           dest.setPixel(x, y, dpix);
 139:         }
 140: 
 141:     return dest;
 142:   }
 143: 
 144:   /* (non-Javadoc)
 145:    * @see java.awt.image.RasterOp#getBounds2D(java.awt.image.Raster)
 146:    */
 147:   public final Rectangle2D getBounds2D(Raster src)
 148:   {
 149:     return src.getBounds();
 150:   }
 151: 
 152:   /**
 153:    * Creates a new WritableRaster that can be used as the destination for this
 154:    * Op. The number of bands in the source raster must equal the number of rows
 155:    * in the op matrix, which must also be equal to either the number of columns
 156:    * or (columns - 1) in the matrix.
 157:    * 
 158:    * @param src The source raster.
 159:    * @return A compatible raster.
 160:    * @see java.awt.image.RasterOp#createCompatibleDestRaster(java.awt.image.Raster)
 161:    * @throws IllegalArgumentException if the raster is incompatible with the
 162:    *           matrix.
 163:    */
 164:   public WritableRaster createCompatibleDestRaster(Raster src)
 165:   {
 166:     // Destination raster must have same number of bands as source
 167:     if (src.getNumBands() != matrix.length)
 168:       throw new IllegalArgumentException("Number of rows in matrix specifies an "
 169:                                              + "incompatible number of bands");
 170: 
 171:     // We use -1 and -2 because we previously padded the rows with a trailing 0
 172:     if (src.getNumBands() != matrix[0].length - 1
 173:         && src.getNumBands() != matrix[0].length - 2)
 174:       throw new IllegalArgumentException("Incompatible number of bands: "
 175:                                              + "the number of bands in the raster must equal the number of "
 176:                                              + "columns in the matrix, optionally minus one");
 177: 
 178:     return src.createCompatibleWritableRaster();
 179:   }
 180: 
 181:   /**
 182:    * Return corresponding destination point for source point.  Because this is
 183:    * not a geometric operation, it simply returns a copy of the source.
 184:    * 
 185:    * @param src The source point.
 186:    * @param dst The destination point.
 187:    * @return dst The destination point.
 188:    * @see java.awt.image.RasterOp#getPoint2D(java.awt.geom.Point2D,
 189:    *java.awt.geom.Point2D) 
 190:    */
 191:   public final Point2D getPoint2D(Point2D src, Point2D dst)
 192:   {
 193:     if (dst == null)
 194:       return (Point2D)src.clone();
 195:     
 196:     dst.setLocation(src);
 197:     return dst;
 198:   }
 199: 
 200:   /* (non-Javadoc)
 201:    * @see java.awt.image.RasterOp#getRenderingHints()
 202:    */
 203:   public final RenderingHints getRenderingHints()
 204:   {
 205:     return hints;
 206:   }
 207:   
 208:   /**
 209:    * Return the matrix used in this operation.
 210:    *
 211:    * @return The matrix used in this operation.
 212:    */
 213:   public final float[][] getMatrix()
 214:   {
 215:     return matrix;
 216:   }
 217: 
 218: }