I did some cleanup and added extensive API docs to the
AbstractGraphics2D work, so others can probably play a little with it,
while I'm in holiday. Have fun!

2006-05-19  Roman Kennke <[EMAIL PROTECTED]>

        * gnu/java/awt/java2d/AbstractGraphics2D.java
        Added class docs.
        (pixel): Removed obsolete field.
        (draw(Shape)): Removed commented out code.
        (drawImage): Formatting.
        (drawString): Added optimization hook.
        (setPaint): Removed rawSetForeground().
        (getFontRenderContext): Return context with correct transform.
        (drawGlyphVector): Draw complete outline in one go.
        (copyArea): Added optimization hook.
        (clearRect): Added optimization hook.
        (drawImage): Added optimization hook.
        (fillShape): (Temporarily) Set antialiasing off by default for
        font rendering. Adjust the shape by some bits to improve
rendering.
        Pass clip bounds to the render methods.
        (drawPixel): Removed.
        (rawSetPixel): Removed.
        (rawSetForeground): Removed.
        (rawDrawLine): Default impl calls standard pipeline.
        (rawDrawString): New method, calls standard pipeline for
rendering.
        (rawClearRect): New method, calls standard pipeline for
rendering.
        (rawFillRect): New method, calls standard pipeline for
rendering.
        (rawDrawImage): New method, calls standard pipeline for
rendering.
        (rawCopyArea): New method.
        (copyAreaImpl): New method.
        (rawFillShape): Renamed to fillShapeImpl(). Small optimization
        for rendering.
        (fillShapeAntialias): Fixed AA rendering.
        (fillScanlineAA): Fixed AA rendering.
        (getSegments): Take offset into account.

/Roman
-- 
“Improvement makes straight roads, but the crooked roads, without
Improvement, are roads of Genius.” - William Blake
Index: gnu/java/awt/java2d/AbstractGraphics2D.java
===================================================================
RCS file: /cvsroot/classpath/classpath/gnu/java/awt/java2d/AbstractGraphics2D.java,v
retrieving revision 1.7
diff -u -1 -0 -r1.7 AbstractGraphics2D.java
--- gnu/java/awt/java2d/AbstractGraphics2D.java	10 May 2006 09:13:20 -0000	1.7
+++ gnu/java/awt/java2d/AbstractGraphics2D.java	19 May 2006 22:22:06 -0000
@@ -77,21 +77,62 @@
 import java.awt.image.RenderedImage;
 import java.awt.image.WritableRaster;
 import java.awt.image.renderable.RenderableImage;
 import java.text.AttributedCharacterIterator;
 import java.util.ArrayList;
 import java.util.HashMap;
 import java.util.Iterator;
 import java.util.Map;
 
 /**
- * Implements general and shared behaviour for Graphics2D implementation.
+ * This is a 100% Java implementation of the Java2D rendering pipeline. It is
+ * meant as a base class for Graphics2D implementations.
+ *
+ * <h2>Backend interface</h2>
+ * <p>
+ * The backend must at the very least provide a Raster which the the rendering
+ * pipeline can paint into. This must be implemented in
+ * [EMAIL PROTECTED] #getDestinationRaster()}. For some backends that might be enough, like
+ * when the target surface can be directly access via the raster (like in
+ * BufferedImages). Other targets need some way to synchronize the raster with
+ * the surface, which can be achieved by implementing the
+ * [EMAIL PROTECTED] #updateRaster(Raster, int, int, int, int)} method, which always gets
+ * called after a chunk of data got painted into the raster.
+ * </p>
+ * <p>The backend is free to provide implementations for the various raw*
+ * methods for optimized AWT 1.1 style painting of some primitives. This should
+ * accelerate painting of Swing greatly. When doing so, the backend must also
+ * keep track of the clip and translation, probably by overriding
+ * some clip and translate methods. Don't forget to message super in such a
+ * case.</p>
+ *
+ * <h2>Acceleration options</h2>
+ * <p>
+ * The fact that it is
+ * pure Java makes it a little slow. However, there are several ways of
+ * accelerating the rendering pipeline:
+ * <ol>
+ * <li><em>Optimization hooks for AWT 1.1 - like graphics operations.</em>
+ *   The most important methods from the [EMAIL PROTECTED] java.awt.Graphics} class
+ *   have a corresponding <code>raw*</code> method, which get called when
+ *   several optimization conditions are fullfilled. These conditions are
+ *   described below. Subclasses can override these methods and delegate
+ *   it directly to a native backend.</li>
+ * <li><em>Native PaintContexts and CompositeContext.</em> The implementations
+ *   for the 3 PaintContexts and AlphaCompositeContext can be accelerated
+ *   using native code. These have proved to two of the most performance
+ *   critical points in the rendering pipeline and cannot really be done quickly
+ *   in plain Java because they involve lots of shuffling around with large
+ *   arrays. In fact, you really would want to let the graphics card to the
+ *   work, they are made for this.</li>
+ * </ol>
+ * </p>
  *
  * @author Roman Kennke ([EMAIL PROTECTED])
  */
 public abstract class AbstractGraphics2D
   extends Graphics2D
   implements Cloneable
 {
 
   /**
    * Accuracy of the sampling in the anti-aliasing shape filler.
@@ -139,43 +180,38 @@
    * The rendering hints.
    */
   private RenderingHints renderingHints;
 
   /**
    * The paint raster.
    */
   private Raster paintRaster;
 
   /**
-   * A cached pixel array.
-   */
-  private int[] pixel;
-
-  /**
    * The raster of the destination surface. This is where the painting is
    * performed.
    */
   private WritableRaster destinationRaster;
 
   /**
    * Stores the alpha values for a scanline in the anti-aliasing shape
    * renderer.
    */
   private transient int[] alpha;
 
   /**
    * The edge table for the scanline conversion algorithms.
    */
   private transient ArrayList[] edgeTable;
 
   /**
-   * Indicates if cerain graphics primitives can be rendered in an optimized
+   * Indicates if certain graphics primitives can be rendered in an optimized
    * fashion. This will be the case if the following conditions are met:
    * - The transform may only be a translation, no rotation, shearing or
    *   scaling.
    * - The paint must be a solid color.
    * - The composite must be an AlphaComposite.SrcOver.
    * - The clip must be a Rectangle.
    * - The stroke must be a plain BasicStroke().
    *
    * These conditions represent the standard settings of a new
    * AbstractGraphics2D object and will be the most commonly used setting
@@ -191,62 +227,53 @@
     transform = new AffineTransform();
     background = Color.WHITE;
     composite = AlphaComposite.SrcOver;
     stroke = new BasicStroke();
     HashMap hints = new HashMap();
     hints.put(RenderingHints.KEY_TEXT_ANTIALIASING,
               RenderingHints.VALUE_TEXT_ANTIALIAS_DEFAULT);
     hints.put(RenderingHints.KEY_ANTIALIASING,
               RenderingHints.VALUE_ANTIALIAS_DEFAULT);
     renderingHints = new RenderingHints(hints);
-
-    pixel = new int[4];
   }
 
   /**
    * Draws the specified shape. The shape is passed through the current stroke
    * and is then forwarded to [EMAIL PROTECTED] #fillShape}.
    *
    * @param shape the shape to draw
    */
   public void draw(Shape shape)
   {
     // Stroke the shape.
     Shape strokedShape = stroke.createStrokedShape(shape);
-
-    // Clip the stroked shape.
-//    Shape clipped = clipShape(strokedShape);
-//    if (clipped != null)
-//      {
-//        // Fill the shape.
-//        fillShape(clipped, false);
-//      }
-    // FIXME: Clipping doesn't seem to work.
+    // Fill the stroked shape.
     fillShape(strokedShape, false);
   }
 
 
   /**
    * Draws the specified image and apply the transform for image space ->
    * user space conversion.
    *
    * This method is implemented to special case RenderableImages and
    * RenderedImages and delegate to
    * [EMAIL PROTECTED] #drawRenderableImage(RenderableImage, AffineTransform)} and
    * [EMAIL PROTECTED] #drawRenderedImage(RenderedImage, AffineTransform)} accordingly.
    * Other image types are not yet handled.
    *
    * @param image the image to be rendered
    * @param xform the transform from image space to user space
    * @param obs the image observer to be notified
    */
-  public boolean drawImage(Image image, AffineTransform xform, ImageObserver obs)
+  public boolean drawImage(Image image, AffineTransform xform,
+                           ImageObserver obs)
   {
     boolean ret = false;
     Rectangle areaOfInterest = new Rectangle(0, 0, image.getWidth(obs),
                                              image.getHeight(obs));
     return drawImageImpl(image, xform, obs, areaOfInterest);
   }
 
   /**
    * Draws the specified image and apply the transform for image space ->
    * user space conversion. This method only draw the part of the image
@@ -428,23 +455,28 @@
 
   /**
    * Draws the specified string at the specified location.
    *
    * @param text the string to draw
    * @param x the x location, relative to the bounding rectangle of the text
    * @param y the y location, relative to the bounding rectangle of the text
    */
   public void drawString(String text, int x, int y)
   {
-    FontRenderContext ctx = getFontRenderContext();
-    GlyphVector gv = font.createGlyphVector(ctx, text.toCharArray());
-    drawGlyphVector(gv, x, y);
+    if (isOptimized)
+      rawDrawString(text, x, y);
+    else
+      {
+        FontRenderContext ctx = getFontRenderContext();
+        GlyphVector gv = font.createGlyphVector(ctx, text.toCharArray());
+        drawGlyphVector(gv, x, y);
+      }
   }
 
   /**
    * Draws the specified string at the specified location.
    *
    * @param text the string to draw
    * @param x the x location, relative to the bounding rectangle of the text
    * @param y the y location, relative to the bounding rectangle of the text
    */
   public void drawString(String text, float x, float y)
@@ -523,21 +555,20 @@
   {
     if (p != null)
       {
         paint = p;
 
         if (! (paint instanceof Color))
           isOptimized = false;
         else
           {
             updateOptimization();
-            rawSetForeground((Color) paint);
           }
       }
   }
 
   /**
    * Sets the stroke for this graphics object.
    *
    * @param s the stroke to set
    */
   public void setStroke(Stroke s)
@@ -880,50 +911,42 @@
        current.intersect(intersect);
        clip = current;
        isOptimized = false;
        // Call setClip so that subclasses get notified.
        setClip(clip);
      }
   }
 
   public FontRenderContext getFontRenderContext()
   {
-    //return new FontRenderContext(transform, false, false);
-    return new FontRenderContext(new AffineTransform(), false, false);
+    return new FontRenderContext(transform, false, true);
   }
 
   /**
    * Draws the specified glyph vector at the specified location.
    *
    * @param gv the glyph vector to draw
    * @param x the location, x coordinate
    * @param y the location, y coordinate
    */
   public void drawGlyphVector(GlyphVector gv, float x, float y)
   {
     int numGlyphs = gv.getNumGlyphs();
-    AffineTransform t = new AffineTransform();
-    t.translate(x, y);
-
-//    // TODO: We could use fill(gv.getOutline()), but that seems to be
-      // slightly more inefficient. 
+    translate(x, y);
+    // TODO: We could use fill(gv.getOutline()), but that seems to be
+    // slightly more inefficient.
     for (int i = 0; i < numGlyphs; i++)
     {
-      //fill(gv.getGlyphVisualBounds(i));
-      GeneralPath p = new GeneralPath(gv.getGlyphOutline(i));
-      p.transform(t);
-      //Shape clipped = clipShape(p);
-      //if (clipped != null)
-      //  fillShape(clipped, true);
-      // FIXME: Clipping doesn't seem to work correctly.
-      fillShape(p, true);
+      Shape o = gv.getGlyphOutline(i);
+      fillShape(o, true);
     }
+    translate(-x, -y);
   }
 
   /**
    * Creates a copy of this graphics object.
    *
    * @return a copy of this graphics object
    */
   public Graphics create()
   {
     AbstractGraphics2D copy = (AbstractGraphics2D) clone();
@@ -1086,22 +1109,24 @@
   {
     clip = c;
     if (! (clip instanceof Rectangle))
       isOptimized = false;
     else
       updateOptimization();
   }
 
   public void copyArea(int x, int y, int width, int height, int dx, int dy)
   {
-    // FIXME: Implement this.
-    throw new UnsupportedOperationException("Not yet implemented");
+    if (isOptimized)
+      rawCopyArea(x, y, width, height, dx, dy);
+    else
+      copyAreaImpl(x, y, width, height, dx, dy);
   }
 
   /**
    * Draws a line from (x1, y1) to (x2, y2).
    *
    * This implementation transforms the coordinates and forwards the call to
    * [EMAIL PROTECTED] #rawDrawLine}.
    */
   public void drawLine(int x1, int y1, int x2, int y2)
   {
@@ -1146,25 +1171,29 @@
    * This implementation temporarily sets the foreground color to the 
    * background and forwards the call to [EMAIL PROTECTED] #fillRect(int, int, int, int)}.
    *
    * @param x the upper left corner, X coordinate
    * @param y the upper left corner, Y coordinate
    * @param width the width of the rectangle
    * @param height the height of the rectangle
    */
   public void clearRect(int x, int y, int width, int height)
   {
-    Paint savedForeground = getPaint();
-    setPaint(getBackground());
-    //System.err.println("clearRect transform type: " + transform.getType());
-    fillRect(x, y, width, height);
-    setPaint(savedForeground);
+    if (isOptimized)
+      rawClearRect(x, y, width, height);
+    else
+      {
+        Paint savedForeground = getPaint();
+        setPaint(getBackground());
+        fillRect(x, y, width, height);
+        setPaint(savedForeground);
+      }
   }
 
   /**
    * Draws a rounded rectangle.
    *
    * @param x the x coordinate of the rectangle
    * @param y the y coordinate of the rectangle
    * @param width the width of the rectangle
    * @param height the height of the rectangle
    * @param arcWidth the width of the arcs
@@ -1266,23 +1295,30 @@
    * Draws the specified image at the specified location. This forwards
    * to [EMAIL PROTECTED] #drawImage(Image, AffineTransform, ImageObserver)}.
    *
    * @param image the image to render
    * @param x the x location to render to
    * @param y the y location to render to
    * @param observer the image observer to receive notification
    */
   public boolean drawImage(Image image, int x, int y, ImageObserver observer)
   {
-    AffineTransform t = new AffineTransform();
-    t.translate(x, y);
-    return drawImage(image, t, observer);
+    boolean ret;
+    if (isOptimized)
+      ret = rawDrawImage(image, x, y, observer);
+    else
+      {
+        AffineTransform t = new AffineTransform();
+        t.translate(x, y);
+        ret = drawImage(image, t, observer);
+      }
+    return ret;
   }
 
   /**
    * Draws the specified image at the specified location. The image
    * is scaled to the specified width and height. This forwards
    * to [EMAIL PROTECTED] #drawImage(Image, AffineTransform, ImageObserver)}.
    *
    * @param image the image to render
    * @param x the x location to render to
    * @param y the y location to render to
@@ -1421,91 +1457,48 @@
    */
   protected void fillShape(Shape s, boolean isFont)
   {
     // Determine if we need to antialias stuff.
     boolean antialias = false;
     if (isFont)
       {
         Object v = renderingHints.get(RenderingHints.KEY_TEXT_ANTIALIASING);
         // We default to antialiasing on for text as long as we have no
         // good hinting implemented.
-        antialias = (v == RenderingHints.VALUE_TEXT_ANTIALIAS_ON
-                     || v == RenderingHints.VALUE_TEXT_ANTIALIAS_DEFAULT);
+        antialias = (v == RenderingHints.VALUE_TEXT_ANTIALIAS_ON);
+                     //|| v == RenderingHints.VALUE_TEXT_ANTIALIAS_DEFAULT);
       }
     else
       {
         Object v = renderingHints.get(RenderingHints.KEY_ANTIALIASING);
         antialias = (v == RenderingHints.VALUE_ANTIALIAS_ON);
       }
 
+    double offs = 0.5;
+    if (antialias)
+      offs = offs / AA_SAMPLING;
+
     Rectangle2D userBounds = s.getBounds2D();
     Rectangle2D deviceBounds = new Rectangle2D.Double();
-    ArrayList segs = getSegments(s, transform, deviceBounds, false);
+    ArrayList segs = getSegments(s, transform, deviceBounds, false, offs);
     Rectangle2D clipBounds = new Rectangle2D.Double();
-    ArrayList clipSegs = getSegments(clip, transform, clipBounds, true);
+    ArrayList clipSegs = getSegments(clip, transform, clipBounds, true, offs);
     segs.addAll(clipSegs);
     Rectangle2D inclClipBounds = new Rectangle2D.Double();
     Rectangle2D.union(clipBounds, deviceBounds, inclClipBounds);
     if (segs.size() > 0)
       {
         if (antialias)
-          fillShapeAntialias(segs, deviceBounds, userBounds);
+          fillShapeAntialias(segs, deviceBounds, userBounds, inclClipBounds);
         else
-          rawFillShape(segs, deviceBounds, userBounds, inclClipBounds);
-      }
-  }
-
-  /**
-   * Draws one pixel in the target coordinate space. This method draws the
-   * specified pixel by getting the painting pixel for that coordinate
-   * from the paintContext and compositing the pixel with the compositeContext.
-   * The resulting pixel is then set by calling [EMAIL PROTECTED] #rawSetPixel}.
-   *
-   * @param x the x coordinate
-   * @param y the y coordinate
-   */
-  protected void drawPixel(int x, int y)
-  {
-    // FIXME: Implement efficient compositing.
-    if (! (paint instanceof Color))
-      {
-        int[] paintPixel = paintRaster.getPixel(x, y, pixel);
-        Color c = new Color(paintPixel[0], paintPixel[1], paintPixel[2]);
-        rawSetForeground(c);
+          fillShapeImpl(segs, deviceBounds, userBounds, inclClipBounds);
       }
-    rawSetPixel(x, y);
-  }
-
-  /**
-   * Draws a pixel in the target coordinate space using the specified color.
-   * 
-   * @param x the x coordinate
-   * @param y the y coordinate
-   */
-  protected void rawSetPixel(int x, int y)
-  {
-    // FIXME: Provide default implementation or remove method.
-  }
-
-  /**
-   * Sets the foreground color for drawing.
-   *
-   * @param c the color to set
-   */
-  protected void rawSetForeground(Color c)
-  {
-    // Probably remove method.
-  }
-
-  protected void rawSetForeground(int r, int g, int b)
-  {
-    rawSetForeground(new Color(r, g, b));
   }
 
   /**
    * Returns the color model of this Graphics object.
    *
    * @return the color model of this Graphics object
    */
   protected abstract ColorModel getColorModel();
 
   /**
@@ -1513,120 +1506,136 @@
    *
    * @return the bounds of the target
    */
   protected Rectangle getDeviceBounds()
   {
     return destinationRaster.getBounds();
   }
 
   /**
    * Draws a line in optimization mode. The implementation should respect the
-   * clip but can assume that it is a rectangle.
+   * clip and translation. It can assume that the clip is a rectangle and that
+   * the transform is only a translating transform.
    *
    * @param x0 the starting point, X coordinate
    * @param y0 the starting point, Y coordinate
    * @param x1 the end point, X coordinate 
    * @param y1 the end point, Y coordinate
    */
   protected void rawDrawLine(int x0, int y0, int x1, int y1)
   {
-    // This is an implementation of Bresenham's line drawing algorithm.
-    int dy = y1 - y0;
-    int dx = x1 - x0;
-    int stepx, stepy;
+    draw(new Line2D.Float(x0, y0, x1, y1));
+  }
 
-    if (dy < 0)
-      {
-        dy = -dy;
-        stepy = -1;
-      }
-    else
-      {
-        stepy = 1;
-      }
-    if (dx < 0)
-      {
-        dx = -dx;
-        stepx = -1;
-        }
-    else
-      {
-        stepx = 1;
-      }
-    dy <<= 1;
-    dx <<= 1;
+  /**
+   * Draws a string in optimization mode. The implementation should respect the
+   * clip and translation. It can assume that the clip is a rectangle and that
+   * the transform is only a translating transform.
+   *
+   * @param text the string to be drawn
+   * @param x the start of the baseline, X coordinate
+   * @param y the start of the baseline, Y coordinate
+   */
+  protected void rawDrawString(String text, int x, int y)
+  {
+    FontRenderContext ctx = getFontRenderContext();
+    GlyphVector gv = font.createGlyphVector(ctx, text.toCharArray());
+    drawGlyphVector(gv, x, y);
+  }
 
-    drawPixel(x0, y0);
-    if (dx > dy)
-      {
-        int fraction = dy - (dx >> 1); // same as 2*dy - dx
-        while (x0 != x1)
-          {
-            if (fraction >= 0)
-              {
-                y0 += stepy;
-                fraction -= dx;
-              }
-            x0 += stepx;
-            fraction += dy;
-            drawPixel(x0, y0);
-          }
-      }
-    else
-      {
-        int fraction = dx - (dy >> 1);
-        while (y0 != y1)
-          {
-            if (fraction >= 0)
-              {
-                x0 += stepx;
-                fraction -= dy;
-              }
-            y0 += stepy;
-            fraction += dx;
-            drawPixel(x0, y0);
-          }
-      }
+  /**
+   * Clears a rectangle in optimization mode. The implementation should respect the
+   * clip and translation. It can assume that the clip is a rectangle and that
+   * the transform is only a translating transform.
+   *
+   * @param x the upper left corner, X coordinate
+   * @param y the upper left corner, Y coordinate
+   * @param w the width
+   * @param h the height
+   */
+  protected void rawClearRect(int x, int y, int w, int h)
+  {
+    Paint savedForeground = getPaint();
+    setPaint(getBackground());
+    rawFillRect(x, y, w, h);
+    setPaint(savedForeground);
   }
 
   /**
    * Fills a rectangle in optimization mode. The implementation should respect
    * the clip but can assume that it is a rectangle.
    *
    * @param x the upper left corner, X coordinate
    * @param y the upper left corner, Y coordinate
    * @param w the width
    * @param h the height
    */
   protected void rawFillRect(int x, int y, int w, int h)
   {
-    int x2 = x + w;
-    int y2 = y + h;
-    for (int xc = x; xc < x2; xc++)
-      {
-        for (int yc = y; yc < y2; yc++)
-          {
-            drawPixel(xc, yc);
-          }
-      }
+    fill(new Rectangle(x, y, w, h));
+  }
+
+  /**
+   * Draws an image in optimization mode. The implementation should respect
+   * the clip but can assume that it is a rectangle.
+   *
+   * @param image the image to be painted
+   * @param x the location, X coordinate
+   * @param y the location, Y coordinate
+   * @param obs the image observer to be notified
+   *
+   * @return <code>true</code> when the image is painted completely,
+   *         <code>false</code> if it is still rendered
+   */
+  protected boolean rawDrawImage(Image image, int x, int y, ImageObserver obs)
+  {
+    AffineTransform t = new AffineTransform();
+    t.translate(x, y);
+    return drawImage(image, t, obs);
+  }
+
+  /**
+   * Copies a rectangular region to another location.
+   *
+   * @param x the upper left corner, X coordinate
+   * @param y the upper left corner, Y coordinate
+   * @param w the width
+   * @param h the height
+   * @param dx
+   * @param dy
+   */
+  protected void rawCopyArea(int x, int y, int w, int h, int dx, int dy)
+  {
+    copyAreaImpl(x, y, w, h, dx, dy);
+  }
+
+  // Private implementation methods.
+
+  /**
+   * Copies a rectangular area of the target raster to a different location.
+   */
+  private void copyAreaImpl(int x, int y, int w, int h, int dx, int dy)
+  {
+    // FIXME: Implement this properly.
+    throw new UnsupportedOperationException("Not implemented yet.");
   }
 
   /**
    * Fills the specified polygon. This should be overridden by backends
    * that support accelerated (native) polygon filling, which is the
    * case for most toolkit window and offscreen image implementations.
    *
    * The polygon is already clipped when this method is called.
    */
-  protected void rawFillShape(ArrayList segs, Rectangle2D deviceBounds2D,
-                              Rectangle2D userBounds,
-                              Rectangle2D inclClipBounds)
+  private void fillShapeImpl(ArrayList segs, Rectangle2D deviceBounds2D,
+                             Rectangle2D userBounds,
+                             Rectangle2D inclClipBounds)
   {
     // This is an implementation of a polygon scanline conversion algorithm
     // described here:
     // http://www.cs.berkeley.edu/~ug/slide/pipeline/assignments/scan/
 
     // Create table of all edges.
     // The edge buckets, sorted and indexed by their Y values.
 
     double minX = deviceBounds2D.getMinX();
     double minY = deviceBounds2D.getMinY();
@@ -1729,28 +1738,25 @@
             // Draw scanline when we are inside the shape AND inside the
             // clip.
             if (insideClip && insideShape)
               {
                 int x0 = (int) previous.xIntersection;
                 int x1 = (int) edge.xIntersection;
                 if (x0 < x1)
                   fillScanline(pCtx, x0, x1, y);
               }
             // Update state.
-            if (edge.y1 > y)
-              {
-                previous = edge;
-                if (edge.isClip)
-                  insideClip = ! insideClip;
-                else
-                  insideShape = ! insideShape;
-              }
+            previous = edge;
+            if (edge.isClip)
+              insideClip = ! insideClip;
+            else
+              insideShape = ! insideShape;
           }
       }
     pCtx.dispose();
   }
 
   /**
    * Paints a scanline between x0 and x1.
    *
    * @param x0 the left offset
    * @param x1 the right offset
@@ -1768,50 +1774,55 @@
     updateRaster(destinationRaster, x0, y, x1 - x0, 1);
     cCtx.dispose();
   }
 
   /**
    * Fills arbitrary shapes in an anti-aliased fashion.
    *
    * @param segs the line segments which define the shape which is to be filled
    */
   private void fillShapeAntialias(ArrayList segs, Rectangle2D deviceBounds2D,
-                                  Rectangle2D userBounds)
+                                  Rectangle2D userBounds,
+                                  Rectangle2D inclClipBounds)
   {
     // This is an implementation of a polygon scanline conversion algorithm
     // described here:
     // http://www.cs.berkeley.edu/~ug/slide/pipeline/assignments/scan/
     // The antialiasing is implemented using a sampling technique, we do
     // not scan whole lines but fractions of the line.
 
     double minX = deviceBounds2D.getMinX();
     double minY = deviceBounds2D.getMinY();
     double maxX = deviceBounds2D.getMaxX();
     double maxY = deviceBounds2D.getMaxY();
-
+    double icMinY = inclClipBounds.getMinY();
+    double icMaxY = inclClipBounds.getMaxY();
+    double icMinX = inclClipBounds.getMinX();
+    double icMaxX = inclClipBounds.getMaxX();
     Rectangle deviceBounds = new Rectangle((int) minX, (int) minY,
                                            (int) Math.ceil(maxX) - (int) minX,
                                            (int) Math.ceil(maxY) - (int) minY);
-    PaintContext pCtx = paint.createContext(getColorModel(), deviceBounds,
+    PaintContext pCtx = paint.createContext(ColorModel.getRGBdefault(),
+                                            deviceBounds,
                                             userBounds, transform,
                                             renderingHints);
 
     // This array will contain the oversampled transparency values for
     // each pixel in the scanline.
-    int numScanlines = (int) Math.ceil(maxY) - (int) minY;
-    int numScanlinePixels = (int) Math.ceil(maxX) - (int) minX + 1;
+    int numScanlines = (int) Math.ceil(icMaxY) - (int) icMinY;
+    int numScanlinePixels = (int) Math.ceil(icMaxX) - (int) icMinX + 1;
     if (alpha == null || alpha.length < (numScanlinePixels + 1))
       alpha = new int[numScanlinePixels + 1];
     
-    int firstLine = (int) minY;
+    int firstLine = (int) icMinY;
     //System.err.println("minY: " + minY);
-    int firstSubline = (int) (Math.ceil((minY - Math.floor(minY)) * AA_SAMPLING));
+    int firstSubline = (int) (Math.ceil((icMinY - Math.floor(icMinY)) * AA_SAMPLING));
     double firstLineDouble = firstLine + firstSubline / (double) AA_SAMPLING;
     //System.err.println("firstSubline: " + firstSubline);
 
     // Create table of all edges.
     // The edge buckets, sorted and indexed by their Y values.
     //System.err.println("numScanlines: " + numScanlines);
     if (edgeTable == null
         || edgeTable.length < numScanlines * AA_SAMPLING + AA_SAMPLING)
       edgeTable = new ArrayList[numScanlines * AA_SAMPLING + AA_SAMPLING];
 
@@ -1842,22 +1853,25 @@
       }
     
     // The activeEdges list contains all the edges of the current scanline
     // ordered by their intersection points with this scanline.
     ArrayList activeEdges = new ArrayList();
     PolyEdgeComparator comparator = new PolyEdgeComparator();
     
     // Scan all lines.
     int yindex = 0;
     //System.err.println("firstLine: " + firstLine + ", maxY: " + maxY + ", firstSubline: " + firstSubline);
-    for (int y = firstLine; y <= maxY; y++)
+    for (int y = firstLine; y <= icMaxY; y++)
       {
+        int leftX = (int) icMaxX;
+        int rightX = (int) icMinX;
+        boolean emptyScanline = true;
         for (int subY = firstSubline; subY < AA_SAMPLING; subY++)
           {
             //System.err.println("scanline: " + y + ", subScanline: " + subY);
             ArrayList bucket = edgeTable[yindex];
             // Update all the x intersections in the current activeEdges table
             // and remove entries that are no longer in the scanline.
             for (Iterator i = activeEdges.iterator(); i.hasNext();)
               {
                 PolyEdge edge = (PolyEdge) i.next();
                 // TODO: Do the following using integer arithmetics.
@@ -1900,114 +1914,125 @@
                             if (j >= 1)
                               e1 = (PolyEdge) activeEdges.get(j - 1);
                           } while (j >= 1 && comparator.compare(e1, e2) > 0);
                       }
                   }
               }
         
             // Now draw all pixels inside the polygon.
             // This is the last edge that intersected the scanline.
             PolyEdge previous = null; // Gets initialized below.
-            boolean active = false;
+            boolean insideClip = false;
+            boolean insideShape = false;
             //System.err.println("scanline: " + y + ", subscanline: " + subY);
             for (Iterator i = activeEdges.iterator(); i.hasNext();)
               {
                 PolyEdge edge = (PolyEdge) i.next();
-                // Only fill scanline, if the current edge actually intersects
-                // the scanline. There may be edges that lie completely
-                // within the current scanline.
-                //System.err.println("previous: " + previous);
-                //System.err.println("edge: " + edge);
-                if (active)
+                if (edge.y1 <= (y + (subY / (double) AA_SAMPLING)))
+                  continue;
+
+                if (insideClip && insideShape)
                   {
                     // TODO: Use integer arithmetics here.
                     if (edge.y1 > (y + (subY / (double) AA_SAMPLING)))
                       {
                         //System.err.println(edge);
                         // TODO: Eliminate the aligments.
                         int x0 = (int) Math.min(Math.max(previous.xIntersection, minX), maxX);
                         int x1 = (int) Math.min(Math.max(edge.xIntersection, minX), maxX);
                         //System.err.println("minX: " + minX + ", x0: " + x0 + ", x1: " + x1 + ", maxX: " + maxX);
                         // TODO: Pull out cast.
-                        alpha[x0 - (int) minX]++;
-                        alpha[x1 - (int) minX + 1]--;
-                        previous = edge;
-                        active = false;
+                        int left = x0 - (int) minX;
+                        int right = x1 - (int) minX + 1; 
+                        alpha[left]++;
+                        alpha[right]--;
+                        leftX = Math.min(x0, leftX);
+                        rightX = Math.max(x1+2, rightX);
+                        emptyScanline = false;
                       }
                   }
+                previous = edge;
+                if (edge.isClip)
+                  insideClip = ! insideClip;
                 else
-                  {
-                    // TODO: Use integer arithmetics here.
-                    if (edge.y1 > (y + (subY / (double) AA_SAMPLING)))
-                      {
-                        //System.err.println(edge);
-                        previous = edge;
-                        active = true;
-                      }
-                  }
+                  insideShape = ! insideShape;
               }
             yindex++;
           }
         firstSubline = 0;
         // Render full scanline.
         //System.err.println("scanline: " + y);
-        fillScanlineAA(alpha, (int) minX, (int) y, numScanlinePixels, pCtx);
+        if (! emptyScanline)
+          fillScanlineAA(alpha, leftX, (int) y, rightX - leftX, pCtx,
+                         (int) minX);
       }
-    if (paint instanceof Color && composite == AlphaComposite.SrcOver)
-      rawSetForeground((Color) paint);
 
     pCtx.dispose();
   }
 
   /**
    * Fills a horizontal line between x0 and x1 for anti aliased rendering.
    * the alpha array contains the deltas of the alpha values from one pixel
    * to the next.
    *
    * @param alpha the alpha values in the scanline
    * @param x0 the beginning of the scanline
    * @param y the y coordinate of the line
    */
-  private void fillScanlineAA(int[] alpha, int x0, int y, int numScanlinePixels,
-                              PaintContext pCtx)
+  private void fillScanlineAA(int[] alpha, int x0, int yy, int numPixels,
+                              PaintContext pCtx, int offs)
   {
-    // FIXME: This doesn't work. Fixit.
     CompositeContext cCtx = composite.createContext(pCtx.getColorModel(),
                                                     getColorModel(),
                                                     renderingHints);
-    Raster paintRaster = pCtx.getRaster(x0, y, numScanlinePixels, 1);
-    System.err.println("paintColorModel: " + pCtx.getColorModel());
+    Raster paintRaster = pCtx.getRaster(x0, yy, numPixels, 1);
+    //System.err.println("paintColorModel: " + pCtx.getColorModel());
     WritableRaster aaRaster = paintRaster.createCompatibleWritableRaster();
     int numBands = paintRaster.getNumBands();
-    int[] pixels = new int[numScanlinePixels + paintRaster.getNumBands()];
-    pixels = paintRaster.getPixels(x0, y, numScanlinePixels, 1, pixels);
     ColorModel cm = pCtx.getColorModel();
-    
     double lastAlpha = 0.;
     int lastAlphaInt = 0;
-    int[] components = new int[4];
-    
-    for (int i = 0; i < pixels.length; i++)
+
+    Object pixel = null;
+    int[] comps = null;
+    int x1 = x0 + numPixels;
+    for (int x = x0; x < x1; x++)
       {
+        int i = x - offs;
         if (alpha[i] != 0)
           {
             lastAlphaInt += alpha[i];
-            lastAlpha = lastAlphaInt / AA_SAMPLING;
+            lastAlpha = (double) lastAlphaInt / (double) AA_SAMPLING;
+            alpha[i] = 0;
+          }
+        pixel = paintRaster.getDataElements(x - x0, 0, pixel);
+        comps = cm.getComponents(pixel, comps, 0);
+        if (cm.hasAlpha() && ! cm.isAlphaPremultiplied())
+          comps[comps.length - 1] *= lastAlpha;
+        else
+          {
+            int max;
+            if (cm.hasAlpha())
+              max = comps.length - 2;
+            else
+              max = comps.length - 1;
+            for (int j = 0; j < max; j++) 
+              comps[j] *= lastAlpha;
           }
-        components = cm.getComponents(pixel[i], components, 0);
-        components[0] = (int) (components[0] * lastAlpha);
-        pixel[i] = cm.getDataElement(components, 0);
+        pixel = cm.getDataElements(comps, 0, pixel);
+        aaRaster.setDataElements(x - x0, 0, pixel);
       }
 
-    aaRaster.setPixels(0, 0, numScanlinePixels, 1, pixels);
-    cCtx.compose(aaRaster, destinationRaster, destinationRaster);
-    updateRaster(destinationRaster, x0, y, numScanlinePixels, 1);
+    WritableRaster targetChild =
+      destinationRaster.createWritableTranslatedChild(-x0, -yy);
+    cCtx.compose(aaRaster, targetChild, targetChild);
+    updateRaster(destinationRaster, x0, yy, numPixels, 1);
 
     cCtx.dispose();
   }
 
 
   /**
    * Initializes this graphics object. This must be called by subclasses in
    * order to correctly initialize the state of this object.
    */
   protected void init()
@@ -2137,21 +2162,22 @@
    * @param s the shape to convert
    * @param t the transformation to apply before converting
    * @param deviceBounds an output parameter; holds the bounding rectangle of
    *        s in device space after return
    * @param isClip true when the shape is a clip, false for normal shapes;
    *        this influences the settings in the created PolyEdge instances.
    *
    * @return a list of PolyEdge that form the shape in device space
    */
   private ArrayList getSegments(Shape s, AffineTransform t,
-                                Rectangle2D deviceBounds, boolean isClip)
+                                Rectangle2D deviceBounds, boolean isClip,
+                                double offs)
   {
     // Flatten the path. TODO: Determine the best flattening factor
     // wrt to speed and quality.
     PathIterator path = s.getPathIterator(getTransform(), 1.0);
 
     // Build up polygons and let the native backend render this using
     // rawFillShape() which would provide a default implementation for
     // drawPixel using a PolyScan algorithm.
     double[] seg = new double[6];
 
@@ -2180,26 +2206,28 @@
         if (segType == PathIterator.SEG_MOVETO)
           {
             segX = seg[0];
             segY = seg[1];
             polyX = seg[0];
             polyY = seg[1];
           }
         else if (segType == PathIterator.SEG_CLOSE)
           {
             // Close the polyline.
-            PolyEdge edge = new PolyEdge(segX, segY, polyX, polyY, isClip);
+            PolyEdge edge = new PolyEdge(segX, segY - offs,
+                                         polyX, polyY - offs, isClip);
             segs.add(edge);
           }
         else if (segType == PathIterator.SEG_LINETO)
           {
-            PolyEdge edge = new PolyEdge(segX, segY, seg[0], seg[1], isClip);
+            PolyEdge edge = new PolyEdge(segX, segY - offs,
+                                         seg[0], seg[1] - offs, isClip);
             segs.add(edge);
             segX = seg[0];
             segY = seg[1];
           }
         path.next();
       }
     deviceBounds.setRect(minX, minY, maxX - minX, maxY - minY);
     return segs;
   }
 }

Attachment: signature.asc
Description: Dies ist ein digital signierter Nachrichtenteil

Reply via email to