OSDN Git Service

Imported GNU Classpath 0.90
[pf3gnuchains/gcc-fork.git] / libjava / classpath / java / awt / image / ConvolveOp.java
1 /* ConvolveOp.java --
2    Copyright (C) 2004, 2005 Free Software Foundation -- ConvolveOp
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
39 package java.awt.image;
40
41 import java.awt.Graphics2D;
42 import java.awt.RenderingHints;
43 import java.awt.geom.Point2D;
44 import java.awt.geom.Rectangle2D;
45 import java.util.Arrays;
46
47 /**
48  * Convolution filter.
49  * 
50  * ConvolveOp convolves the source image with a Kernel to generate a
51  * destination image.  This involves multiplying each pixel and its neighbors
52  * with elements in the kernel to compute a new pixel.
53  * 
54  * Each band in a Raster is convolved and copied to the destination Raster.
55  * 
56  * For BufferedImages, convolution is applied to all components.  If the
57  * source is not premultiplied, the data will be premultiplied before
58  * convolving.  Premultiplication will be undone if the destination is not
59  * premultiplied.  Color conversion will be applied if needed.
60  * 
61  * @author jlquinn@optonline.net
62  */
63 public class ConvolveOp implements BufferedImageOp, RasterOp
64 {
65   /** Edge pixels are set to 0. */
66   public static final int EDGE_ZERO_FILL = 0;
67   
68   /** Edge pixels are copied from the source. */
69   public static final int EDGE_NO_OP = 1;
70   
71   private Kernel kernel;
72   private int edge;
73   private RenderingHints hints;
74
75   /**
76    * Construct a ConvolveOp.
77    * 
78    * The edge condition specifies that pixels outside the area that can be
79    * filtered are either set to 0 or copied from the source image.
80    * 
81    * @param kernel The kernel to convolve with.
82    * @param edgeCondition Either EDGE_ZERO_FILL or EDGE_NO_OP.
83    * @param hints Rendering hints for color conversion, or null.
84    */
85   public ConvolveOp(Kernel kernel,
86                                 int edgeCondition,
87                                 RenderingHints hints)
88   {
89     this.kernel = kernel;
90     edge = edgeCondition;
91     this.hints = hints;
92   }
93   
94   /**
95    * Construct a ConvolveOp.
96    * 
97    * The edge condition defaults to EDGE_ZERO_FILL.
98    * 
99    * @param kernel The kernel to convolve with.
100    */
101   public ConvolveOp(Kernel kernel)
102   {
103     this.kernel = kernel;
104     edge = EDGE_ZERO_FILL;
105     hints = null;
106   }
107
108   
109   /* (non-Javadoc)
110    * @see java.awt.image.BufferedImageOp#filter(java.awt.image.BufferedImage,
111    * java.awt.image.BufferedImage)
112    */
113   public final BufferedImage filter(BufferedImage src, BufferedImage dst)
114   {
115     if (src == dst)
116       throw new IllegalArgumentException();
117     
118     if (dst == null)
119       dst = createCompatibleDestImage(src, src.getColorModel());
120     
121     // Make sure source image is premultiplied
122     BufferedImage src1 = src;
123     if (!src.isPremultiplied)
124     {
125       src1 = createCompatibleDestImage(src, src.getColorModel());
126       src.copyData(src1.getRaster());
127       src1.coerceData(true);
128     }
129
130     BufferedImage dst1 = dst;
131     if (!src.getColorModel().equals(dst.getColorModel()))
132       dst1 = createCompatibleDestImage(src, src.getColorModel());
133
134     filter(src1.getRaster(), dst1.getRaster());
135     
136     if (dst1 != dst)
137     {
138       // Convert between color models.
139       // TODO Check that premultiplied alpha is handled correctly here.
140       Graphics2D gg = dst.createGraphics();
141       gg.setRenderingHints(hints);
142       gg.drawImage(dst1, 0, 0, null);
143       gg.dispose();
144     }
145     
146     return dst;
147   }
148
149   /* (non-Javadoc)
150    * @see
151    * java.awt.image.BufferedImageOp#createCompatibleDestImage(java.awt.image.BufferedImage,
152    * java.awt.image.ColorModel)
153    */
154   public BufferedImage createCompatibleDestImage(BufferedImage src,
155                                                  ColorModel dstCM)
156   {
157     // FIXME: set properties to those in src
158     return new BufferedImage(dstCM,
159                              src.getRaster().createCompatibleWritableRaster(),
160                              src.isPremultiplied, null);
161   }
162
163   /* (non-Javadoc)
164    * @see java.awt.image.RasterOp#getRenderingHints()
165    */
166   public final RenderingHints getRenderingHints()
167   {
168     return hints;
169   }
170   
171   /**
172    * @return The edge condition.
173    */
174   public int getEdgeCondition()
175   {
176     return edge;
177   }
178   
179   /**
180    * Returns (a clone of) the convolution kernel.
181    *
182    * @return The convolution kernel.
183    */
184   public final Kernel getKernel()
185   {
186     return (Kernel) kernel.clone();
187   }
188
189   /* (non-Javadoc)
190    * @see java.awt.image.RasterOp#filter(java.awt.image.Raster,
191    * java.awt.image.WritableRaster)
192    */
193   public final WritableRaster filter(Raster src, WritableRaster dest) {
194     if (src == dest)
195       throw new IllegalArgumentException();
196     if (src.getWidth() < kernel.getWidth() ||
197         src.getHeight() < kernel.getHeight())
198       throw new ImagingOpException(null);
199     
200     if (dest == null)
201       dest = createCompatibleDestRaster(src);
202     else if (src.numBands != dest.numBands)
203       throw new ImagingOpException(null);
204
205     // Deal with bottom edge
206     if (edge == EDGE_ZERO_FILL)
207     {
208       float[] zeros = new float[src.getNumBands() * src.getWidth()
209                                 * (kernel.getYOrigin() - 1)];
210       Arrays.fill(zeros, 0);
211       dest.setPixels(src.getMinX(), src.getMinY(), src.getWidth(),
212                      kernel.getYOrigin() - 1, zeros);
213     }
214     else
215     {
216       float[] vals = new float[src.getNumBands() * src.getWidth()
217                                * (kernel.getYOrigin() - 1)];
218       src.getPixels(src.getMinX(), src.getMinY(), src.getWidth(),
219                     kernel.getYOrigin() - 1, vals);
220       dest.setPixels(src.getMinX(), src.getMinY(), src.getWidth(),
221                      kernel.getYOrigin() - 1, vals);
222     }
223     
224     // Handle main section
225     float[] kvals = kernel.getKernelData(null);
226
227     float[] tmp = new float[kernel.getWidth() * kernel.getHeight()];
228     for (int y = src.getMinY() + kernel.getYOrigin();
229          y < src.getMinY() + src.getHeight() - kernel.getYOrigin() / 2; y++)
230     {
231       // Handle unfiltered edge pixels at start of line
232       float[] t1 = new float[(kernel.getXOrigin() - 1) * src.getNumBands()];
233       if (edge == EDGE_ZERO_FILL)
234         Arrays.fill(t1, 0);
235       else
236         src.getPixels(src.getMinX(), y, kernel.getXOrigin() - 1, 1, t1);
237       dest.setPixels(src.getMinX(), y, kernel.getXOrigin() - 1, 1, t1);
238       
239       for (int x = src.getMinX(); x < src.getWidth() + src.getMinX(); x++)
240       {
241         // FIXME: This needs a much more efficient implementation
242         for (int b = 0; b < src.getNumBands(); b++)
243         {
244           float v = 0;
245           src.getSamples(x, y, kernel.getWidth(), kernel.getHeight(), b, tmp);
246           for (int i=0; i < tmp.length; i++)
247             v += tmp[i] * kvals[i];
248           dest.setSample(x, y, b, v);
249         }
250       }
251
252       // Handle unfiltered edge pixels at end of line
253       float[] t2 = new float[(kernel.getWidth() / 2) * src.getNumBands()];
254       if (edge == EDGE_ZERO_FILL)
255         Arrays.fill(t2, 0);
256       else
257         src.getPixels(src.getMinX() + src.getWidth()
258                       - (kernel.getWidth() / 2),
259                       y, kernel.getWidth() / 2, 1, t2);
260       dest.setPixels(src.getMinX() + src.getWidth() - (kernel.getWidth() / 2),
261                      y, kernel.getWidth() / 2, 1, t2);
262     }
263     for (int y = src.getMinY(); y < src.getHeight() + src.getMinY(); y++)
264       for (int x = src.getMinX(); x< src.getWidth() + src.getMinX(); x++)
265       {
266         
267       }
268     for (int y = src.getMinY(); y < src.getHeight() + src.getMinY(); y++)
269       for (int x = src.getMinX(); x< src.getWidth() + src.getMinX(); x++)
270       {
271         
272       }
273       
274     // Handle top edge
275     if (edge == EDGE_ZERO_FILL)
276     {
277       float[] zeros = new float[src.getNumBands() * src.getWidth() *
278                                 (kernel.getHeight() / 2)];
279       Arrays.fill(zeros, 0);
280       dest.setPixels(src.getMinX(),
281           src.getHeight() + src.getMinY() - (kernel.getHeight() / 2),
282           src.getWidth(), kernel.getHeight() / 2, zeros);
283     }
284     else
285     {
286       float[] vals = new float[src.getNumBands() * src.getWidth() *
287                                (kernel.getHeight() / 2)];
288       src.getPixels(src.getMinX(),
289                     src.getHeight() + src.getMinY()
290                     - (kernel.getHeight() / 2),
291                     src.getWidth(), kernel.getHeight() / 2, vals);
292       dest.setPixels(src.getMinX(),
293                      src.getHeight() + src.getMinY()
294                      - (kernel.getHeight() / 2),
295                      src.getWidth(), kernel.getHeight() / 2, vals);
296     }
297     
298     return dest;
299   }
300
301   /* (non-Javadoc)
302    * @see java.awt.image.RasterOp#createCompatibleDestRaster(java.awt.image.Raster)
303    */
304   public WritableRaster createCompatibleDestRaster(Raster src)
305   {
306     return src.createCompatibleWritableRaster();
307   }
308
309   /* (non-Javadoc)
310    * @see java.awt.image.BufferedImageOp#getBounds2D(java.awt.image.BufferedImage)
311    */
312   public final Rectangle2D getBounds2D(BufferedImage src)
313   {
314     return src.getRaster().getBounds();
315   }
316
317   /* (non-Javadoc)
318    * @see java.awt.image.RasterOp#getBounds2D(java.awt.image.Raster)
319    */
320   public final Rectangle2D getBounds2D(Raster src)
321   {
322     return src.getBounds();
323   }
324
325   /** Return corresponding destination point for source point.
326    * 
327    * ConvolveOp will return the value of src unchanged.
328    * @param src The source point.
329    * @param dst The destination point.
330    * @see java.awt.image.RasterOp#getPoint2D(java.awt.geom.Point2D,
331    * java.awt.geom.Point2D)
332    */
333   public final Point2D getPoint2D(Point2D src, Point2D dst)
334   {
335     if (dst == null) return (Point2D)src.clone();
336     dst.setLocation(src);
337     return dst;
338   }
339 }