This patch (committed) fixes a bug (ArrayIndexOutOfBoundsException) in the ConvolveOp class:

2006-07-20  David Gilbert  <[EMAIL PROTECTED]>

        PR Classpath/28422
        * java/awt/image/ConvolveOp.java
        (filter(Raster, WritableRaster)): Reimplemented,
        (fillEdge): New private method.

This class still needs more work to improve performance, and a lot more testing to make sure that the implementation is right.

Regards,

Dave
Index: java/awt/image/ConvolveOp.java
===================================================================
RCS file: /sources/classpath/classpath/java/awt/image/ConvolveOp.java,v
retrieving revision 1.8
diff -u -r1.8 ConvolveOp.java
--- java/awt/image/ConvolveOp.java      16 Mar 2006 15:42:49 -0000      1.8
+++ java/awt/image/ConvolveOp.java      20 Jul 2006 08:21:42 -0000
@@ -1,5 +1,5 @@
 /* ConvolveOp.java --
-   Copyright (C) 2004, 2005 Free Software Foundation -- ConvolveOp
+   Copyright (C) 2004, 2005, 2006, Free Software Foundation -- ConvolveOp
 
 This file is part of GNU Classpath.
 
@@ -42,7 +42,6 @@
 import java.awt.RenderingHints;
 import java.awt.geom.Point2D;
 import java.awt.geom.Rectangle2D;
-import java.util.Arrays;
 
 /**
  * Convolution filter.
@@ -190,112 +189,101 @@
    * @see java.awt.image.RasterOp#filter(java.awt.image.Raster,
    * java.awt.image.WritableRaster)
    */
-  public final WritableRaster filter(Raster src, WritableRaster dest) {
+  public final WritableRaster filter(Raster src, WritableRaster dest)
+  {
     if (src == dest)
-      throw new IllegalArgumentException();
-    if (src.getWidth() < kernel.getWidth() ||
-        src.getHeight() < kernel.getHeight())
-      throw new ImagingOpException(null);
-    
+      throw new IllegalArgumentException("src == dest is not allowed.");
+    if (kernel.getWidth() > src.getWidth() 
+        || kernel.getHeight() > src.getHeight())
+      throw new ImagingOpException("The kernel is too large.");
     if (dest == null)
       dest = createCompatibleDestRaster(src);
-    else if (src.numBands != dest.numBands)
-      throw new ImagingOpException(null);
+    else if (src.getNumBands() != dest.getNumBands())
+      throw new ImagingOpException("src and dest have different band counts.");
 
-    // Deal with bottom edge
-    if (edge == EDGE_ZERO_FILL)
-    {
-      float[] zeros = new float[src.getNumBands() * src.getWidth()
-                               * (kernel.getYOrigin() - 1)];
-      Arrays.fill(zeros, 0);
-      dest.setPixels(src.getMinX(), src.getMinY(), src.getWidth(),
-                    kernel.getYOrigin() - 1, zeros);
-    }
-    else
-    {
-      float[] vals = new float[src.getNumBands() * src.getWidth()
-                              * (kernel.getYOrigin() - 1)];
-      src.getPixels(src.getMinX(), src.getMinY(), src.getWidth(),
-                   kernel.getYOrigin() - 1, vals);
-      dest.setPixels(src.getMinX(), src.getMinY(), src.getWidth(),
-                    kernel.getYOrigin() - 1, vals);
-    }
+    // calculate the borders that the op can't reach...
+    int kWidth = kernel.getWidth();
+    int kHeight = kernel.getHeight();
+    int left = kernel.getXOrigin();
+    int right = Math.max(kWidth - left - 1, 0);
+    int top = kernel.getYOrigin();
+    int bottom = Math.max(kHeight - top - 1, 0);
     
-    // Handle main section
+    // process the region that is reachable...
+    int regionW = src.width - left - right;
+    int regionH = src.height - top - bottom;
     float[] kvals = kernel.getKernelData(null);
+    float[] tmp = new float[kWidth * kHeight];
 
-    float[] tmp = new float[kernel.getWidth() * kernel.getHeight()];
-    for (int y = src.getMinY() + kernel.getYOrigin();
-        y < src.getMinY() + src.getHeight() - kernel.getYOrigin() / 2; y++)
-    {
-      // Handle unfiltered edge pixels at start of line
-      float[] t1 = new float[(kernel.getXOrigin() - 1) * src.getNumBands()];
-      if (edge == EDGE_ZERO_FILL)
-        Arrays.fill(t1, 0);
-      else
-        src.getPixels(src.getMinX(), y, kernel.getXOrigin() - 1, 1, t1);
-      dest.setPixels(src.getMinX(), y, kernel.getXOrigin() - 1, 1, t1);
-      
-      for (int x = src.getMinX(); x < src.getWidth() + src.getMinX(); x++)
+    for (int x = 0; x < regionW; x++)
       {
-        // FIXME: This needs a much more efficient implementation
-        for (int b = 0; b < src.getNumBands(); b++)
-        {
-          float v = 0;
-          src.getSamples(x, y, kernel.getWidth(), kernel.getHeight(), b, tmp);
-          for (int i=0; i < tmp.length; i++)
-            v += tmp[i] * kvals[i];
-          dest.setSample(x, y, b, v);
-        }
+        for (int y = 0; y < regionH; y++)
+          {
+            // FIXME: This needs a much more efficient implementation
+            for (int b = 0; b < src.getNumBands(); b++)
+            {
+              float v = 0;
+              src.getSamples(x, y, kWidth, kHeight, b, tmp);
+              for (int i = 0; i < tmp.length; i++)
+                v += tmp[tmp.length - i - 1] * kvals[i];
+                // FIXME: in the above line, I've had to reverse the order of 
+                // the samples array to make the tests pass.  I haven't worked 
+                // out why this is necessary. 
+              dest.setSample(x + kernel.getXOrigin(), y + kernel.getYOrigin(), 
+                             b, v);
+            }
+          }
       }
-
-      // Handle unfiltered edge pixels at end of line
-      float[] t2 = new float[(kernel.getWidth() / 2) * src.getNumBands()];
-      if (edge == EDGE_ZERO_FILL)
-        Arrays.fill(t2, 0);
-      else
-        src.getPixels(src.getMinX() + src.getWidth()
-                     - (kernel.getWidth() / 2),
-                     y, kernel.getWidth() / 2, 1, t2);
-      dest.setPixels(src.getMinX() + src.getWidth() - (kernel.getWidth() / 2),
-                    y, kernel.getWidth() / 2, 1, t2);
-    }
-    for (int y = src.getMinY(); y < src.getHeight() + src.getMinY(); y++)
-      for (int x = src.getMinX(); x< src.getWidth() + src.getMinX(); x++)
+    
+    // fill in the top border
+    fillEdge(src, dest, 0, 0, src.width, top, edge);
+    
+    // fill in the bottom border
+    fillEdge(src, dest, 0, src.height - bottom, src.width, bottom, edge);
+    
+    // fill in the left border
+    fillEdge(src, dest, 0, top, left, regionH, edge);
+    
+    // fill in the right border
+    fillEdge(src, dest, src.width - right, top, right, regionH, edge);
+    
+    return dest;  
+  }
+  
+  /**
+   * Fills a range of pixels (typically at the edge of a raster) with either
+   * zero values (if <code>edgeOp</code> is <code>EDGE_ZERO_FILL</code>) or 
the 
+   * corresponding pixel values from the source raster (if <code>edgeOp</code>
+   * is <code>EDGE_NO_OP</code>).  This utility method is called by the 
+   * [EMAIL PROTECTED] #fillEdge(Raster, WritableRaster, int, int, int, int, 
int)} method.
+   * 
+   * @param src  the source raster.
+   * @param dest  the destination raster.
+   * @param x  the x-coordinate of the top left pixel in the range.
+   * @param y  the y-coordinate of the top left pixel in the range.
+   * @param w  the width of the pixel range.
+   * @param h  the height of the pixel range.
+   * @param edgeOp  indicates how to determine the values for the range
+   *     (either [EMAIL PROTECTED] #EDGE_ZERO_FILL} or [EMAIL PROTECTED] 
#EDGE_NO_OP}).
+   */
+  private void fillEdge(Raster src, WritableRaster dest, int x, int y, int w, 
+                        int h, int edgeOp) 
+  {
+    if (w <= 0)
+      return;
+    if (h <= 0)
+      return;
+    if (edgeOp == EDGE_ZERO_FILL)  // fill region with zeroes
       {
-        
+        float[] zeros = new float[src.getNumBands() * w * h];
+        dest.setPixels(x, y, w, h, zeros); 
       }
-    for (int y = src.getMinY(); y < src.getHeight() + src.getMinY(); y++)
-      for (int x = src.getMinX(); x< src.getWidth() + src.getMinX(); x++)
+    else  // copy pixels from source
       {
-        
+        float[] pixels = new float[src.getNumBands() * w * h];
+        src.getPixels(x, y, w, h, pixels);
+        dest.setPixels(x, y, w, h, pixels);
       }
-      
-    // Handle top edge
-    if (edge == EDGE_ZERO_FILL)
-    {
-      float[] zeros = new float[src.getNumBands() * src.getWidth() *
-                                (kernel.getHeight() / 2)];
-      Arrays.fill(zeros, 0);
-      dest.setPixels(src.getMinX(),
-          src.getHeight() + src.getMinY() - (kernel.getHeight() / 2),
-          src.getWidth(), kernel.getHeight() / 2, zeros);
-    }
-    else
-    {
-      float[] vals = new float[src.getNumBands() * src.getWidth() *
-                               (kernel.getHeight() / 2)];
-      src.getPixels(src.getMinX(),
-                   src.getHeight() + src.getMinY()
-                   - (kernel.getHeight() / 2),
-                   src.getWidth(), kernel.getHeight() / 2, vals);
-      dest.setPixels(src.getMinX(),
-                    src.getHeight() + src.getMinY()
-                    - (kernel.getHeight() / 2),
-                    src.getWidth(), kernel.getHeight() / 2, vals);
-    }
-    
-    return dest;
   }
 
   /* (non-Javadoc)

Reply via email to