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;
}
}
signature.asc
Description: Dies ist ein digital signierter Nachrichtenteil
