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