aboutsummaryrefslogtreecommitdiff
path: root/libjava/classpath/gnu/java/awt/java2d
diff options
context:
space:
mode:
authorMatthias Klose <doko@gcc.gnu.org>2007-06-03 23:18:43 +0000
committerMatthias Klose <doko@gcc.gnu.org>2007-06-03 23:18:43 +0000
commite1bea0c0687c5f4551b3a6058ec37ce3705fa6cc (patch)
treea9c9e7d91c484d53fe154f9285fc57325572ce50 /libjava/classpath/gnu/java/awt/java2d
parentaf333b9a7f9e1cc1029bec56d48f2de63acdf686 (diff)
downloadgcc-e1bea0c0687c5f4551b3a6058ec37ce3705fa6cc.zip
gcc-e1bea0c0687c5f4551b3a6058ec37ce3705fa6cc.tar.gz
gcc-e1bea0c0687c5f4551b3a6058ec37ce3705fa6cc.tar.bz2
libjava/classpath/ChangeLog.gcj:
2007-05-31 Matthias Klose <doko@ubuntu.com> * javax/management/NotificationBroadcasterSupport.java (getNotificationInfo): Add cast. * native/jni/qt-peer/Makefile.am (AM_CXXFLAGS): Add libstdc++ include directories. * native/jni/qt-peer/Makefile.in: Regenerate. libjava/ChangeLog: 2007-06-03 Matthias Klose <doko@ubuntu.com> * java/io/natFileWin32.cc (setFilePermissions): New (stub only). _access: Handle EXEC query, stub only. 2007-06-03 Matthias Klose <doko@ubuntu.com> Merged from classpath: * gnu/java/nio/SelectorProviderImpl.java: Whitespace merge. * java/lang/System.java(inheritedChannel): New. * java/lang/Character.java: Remove stray`;'. * java/net/MulticastSocket.java: Merged. * java/text/DateFormatSymbols.java(getInstance): New, comment updates. * java/text/Collator.java(getInstance): Merged. * java/util/Calendar.java: New attributes ALL_STYLES, SHORT, LONG. getDisplayName, getDisplayNames: New. * java/util/logging/Logger.java: Merged. * Regenerate .class and .h files. 2007-06-03 Matthias Klose <doko@ubuntu.com> * java/io/File.java: Merge with classpath-0.95, new method setFilePermissions, new attribute EXEC. * java/io/natFilePosix.cc (setFilePermissions): New. _access: Handle EXEC query. * classpath/lib/java/io/File.class, java/io/File.h: Regenerate. 2007-06-03 Matthias Klose <doko@ubuntu.com> Imported GNU Classpath 0.95. * classpath/Makefile.in, classpath/native/jni/midi-dssi/Makefile.in, classpath/native/jni/classpath/Makefile.in, classpath/native/jni/Makefile.in, classpath/native/jni/gconf-peer/Makefile.in, classpath/native/jni/java-io/Makefile.in, classpath/native/jni/native-lib/Makefile.in, classpath/native/jni/java-util/Makefile.in, classpath/native/jni/midi-alsa/Makefile.in, classpath/native/jni/java-lang/Makefile.in, classpath/native/jni/java-nio/Makefile.in, classpath/native/jni/java-net/Makefile.in, classpath/native/jni/xmlj/Makefile.in, classpath/native/jni/qt-peer/Makefile.in, classpath/native/jni/gtk-peer/Makefile.in, classpath/native/Makefile.in, classpath/native/jawt/Makefile.in, classpath/native/fdlibm/Makefile.in, classpath/native/plugin/Makefile.in, classpath/resource/Makefile.in, classpath/scripts/Makefile.in, classpath/tools/Makefile.in, classpath/doc/Makefile.in, classpath/doc/api/Makefile.in, classpath/lib/Makefile.in, classpath/external/Makefile.in, classpath/external/jsr166/Makefile.in, classpath/external/sax/Makefile.in, classpath/external/w3c_dom/Makefile.in, classpath/external/relaxngDatatype/Makefile.in, classpath/include/Makefile.in, classpath/examples/Makefile.in: Regenerate. * classpath/config.guess, classpath/config.sub, classpath/ltmain.sh : Update. * classpath/configure, classpath/depcomp, classpath/missing, classpath/aclocal.m4, classpath/install-sh: Regenerate. * gnu/classpath/Configuration.java (CLASSPATH_VERSION): Now 0.95. * sources.am: Regenerate. * Makefile.in: Regenerate. * Update the .class files and generated CNI header files, add new .class and generated CNI header files. * Remove generated files for removed java source files: classpath/gnu/java/net/BASE64.java, classpath/gnu/java/security/util/Base64.java, classpath/gnu/java/awt/peer/gtk/GThreadMutex.java, classpath/gnu/java/awt/peer/gtk/GThreadNativeMethodRunner.java, classpath/gnu/java/awt/font/autofit/Scaler.java, classpath/gnu/classpath/jdwp/util/Value.java, classpath/gnu/javax/net/ssl/Base64.java. * Remove empty directories. * Makefile.am(nat_source_files): Add natVMOperatingSystemMXBeanImpl.cc. * java/lang/Class.java(setAccessible): Merge from classpath. * java/util/Locale.java: Remove. * gnu/java/lang/management/VMOperatingSystemMXBeanImpl.java, gnu/java/lang/management/natVMOperatingSystemMXBeanImpl.cc: New. * gcj/javaprims.h: Update class declarations. * scripts/classes.pl: Update usage. * HACKING: Mention to build all peers. From-SVN: r125302
Diffstat (limited to 'libjava/classpath/gnu/java/awt/java2d')
-rw-r--r--libjava/classpath/gnu/java/awt/java2d/AbstractGraphics2D.java494
-rw-r--r--libjava/classpath/gnu/java/awt/java2d/ActiveEdges.java195
-rw-r--r--libjava/classpath/gnu/java/awt/java2d/PolyEdge.java71
-rw-r--r--libjava/classpath/gnu/java/awt/java2d/Scanline.java91
-rw-r--r--libjava/classpath/gnu/java/awt/java2d/ScanlineConverter.java404
5 files changed, 804 insertions, 451 deletions
diff --git a/libjava/classpath/gnu/java/awt/java2d/AbstractGraphics2D.java b/libjava/classpath/gnu/java/awt/java2d/AbstractGraphics2D.java
index da21253..f6c5ff0 100644
--- a/libjava/classpath/gnu/java/awt/java2d/AbstractGraphics2D.java
+++ b/libjava/classpath/gnu/java/awt/java2d/AbstractGraphics2D.java
@@ -67,8 +67,6 @@ import java.awt.geom.Ellipse2D;
import java.awt.geom.GeneralPath;
import java.awt.geom.Line2D;
import java.awt.geom.NoninvertibleTransformException;
-import java.awt.geom.PathIterator;
-import java.awt.geom.Rectangle2D;
import java.awt.geom.RoundRectangle2D;
import java.awt.image.BufferedImage;
import java.awt.image.BufferedImageOp;
@@ -82,7 +80,6 @@ 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;
/**
@@ -154,6 +151,11 @@ public abstract class AbstractGraphics2D
{
/**
+ * The default font to use on the graphics object.
+ */
+ private static final Font FONT = new Font("SansSerif", Font.PLAIN, 12);
+
+ /**
* Accuracy of the sampling in the anti-aliasing shape filler.
* Lower values give more speed, while higher values give more quality.
* It is advisable to choose powers of two.
@@ -164,7 +166,14 @@ public abstract class AbstractGraphics2D
* Caches certain shapes to avoid massive creation of such Shapes in
* the various draw* and fill* methods.
*/
- private static final ThreadLocal shapeCache = new ThreadLocal();
+ private static final ThreadLocal<ShapeCache> shapeCache =
+ new ThreadLocal<ShapeCache>();
+
+ /**
+ * The scanline converters by thread.
+ */
+ private static final ThreadLocal<ScanlineConverter> scanlineConverters =
+ new ThreadLocal<ScanlineConverter>();
/**
* The transformation for this Graphics2D instance
@@ -177,6 +186,11 @@ public abstract class AbstractGraphics2D
private Paint paint;
/**
+ * The paint context during rendering.
+ */
+ private PaintContext paintContext;
+
+ /**
* The background.
*/
private Color background;
@@ -239,6 +253,17 @@ public abstract class AbstractGraphics2D
*/
private boolean isOptimized = true;
+ private static final BasicStroke STANDARD_STROKE = new BasicStroke();
+
+ private static final HashMap STANDARD_HINTS;
+ static {
+ HashMap hints = new HashMap();
+ hints.put(RenderingHints.KEY_TEXT_ANTIALIASING,
+ RenderingHints.VALUE_TEXT_ANTIALIAS_DEFAULT);
+ hints.put(RenderingHints.KEY_ANTIALIASING,
+ RenderingHints.VALUE_ANTIALIAS_DEFAULT);
+ STANDARD_HINTS = hints;
+ }
/**
* Creates a new AbstractGraphics2D instance.
*/
@@ -247,13 +272,8 @@ public abstract class AbstractGraphics2D
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);
+ stroke = STANDARD_STROKE;
+ renderingHints = new RenderingHints(STANDARD_HINTS);
}
/**
@@ -958,15 +978,8 @@ public abstract class AbstractGraphics2D
*/
public void drawGlyphVector(GlyphVector gv, float x, float y)
{
- int numGlyphs = gv.getNumGlyphs();
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++)
- {
- Shape o = gv.getGlyphOutline(i);
- fillShape(o, true);
- }
+ fillShape(gv.getOutline(), true);
translate(-x, -y);
}
@@ -1557,21 +1570,14 @@ public abstract class AbstractGraphics2D
antialias = (v == RenderingHints.VALUE_ANTIALIAS_ON);
}
- Rectangle2D userBounds = s.getBounds2D();
- Rectangle2D deviceBounds = new Rectangle2D.Double();
- ArrayList segs = getSegments(s, transform, deviceBounds, false);
- Rectangle2D clipBounds = new Rectangle2D.Double();
- ArrayList clipSegs = getSegments(clip, transform, clipBounds, true);
- segs.addAll(clipSegs);
- Rectangle2D inclClipBounds = new Rectangle2D.Double();
- Rectangle2D.union(clipBounds, deviceBounds, inclClipBounds);
- if (segs.size() > 0)
+ ScanlineConverter sc = getScanlineConverter();
+ int resolution = 0;
+ if (antialias)
{
- if (antialias)
- fillShapeAntialias(segs, deviceBounds, userBounds, inclClipBounds);
- else
- fillShapeImpl(segs, deviceBounds, userBounds, inclClipBounds);
+ // Adjust resolution according to rendering hints.
+ resolution = 2;
}
+ sc.renderShape(this, s, clip, transform, resolution);
}
/**
@@ -1705,141 +1711,6 @@ public abstract class AbstractGraphics2D
}
/**
- * Fills the specified polygon without anti-aliasing.
- */
- 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();
- double maxX = deviceBounds2D.getMaxX();
- double maxY = deviceBounds2D.getMaxY();
- double icMinY = inclClipBounds.getMinY();
- double icMaxY = inclClipBounds.getMaxY();
- 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,
- userBounds, transform, renderingHints);
-
- ArrayList[] edgeTable = new ArrayList[(int) Math.ceil(icMaxY)
- - (int) Math.ceil(icMinY) + 1];
-
- for (Iterator i = segs.iterator(); i.hasNext();)
- {
- PolyEdge edge = (PolyEdge) i.next();
- int yindex = (int) Math.ceil(edge.y0) - (int) Math.ceil(icMinY);
- if (edgeTable[yindex] == null) // Create bucket when needed.
- edgeTable[yindex] = new ArrayList();
- edgeTable[yindex].add(edge); // Add edge to the bucket of its line.
- }
-
- // TODO: The following could be useful for a future optimization.
-// // Sort all the edges in the edge table within their buckets.
-// for (int y = 0; y < edgeTable.length; y++)
-// {
-// if (edgeTable[y] != null)
-// Collections.sort(edgeTable[y]);
-// }
-
- // 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 relevant lines.
- int minYInt = (int) Math.ceil(icMinY);
-
- Rectangle devClip = getDeviceBounds();
- int scanlineMax = (int) Math.min(maxY, devClip.getMaxY());
- for (int y = minYInt; y < scanlineMax; y++)
- {
- ArrayList bucket = edgeTable[y - minYInt];
- // 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();
- if (y > edge.y1)
- i.remove();
- else
- {
- edge.xIntersection += edge.slope;
- //edge.xIntersection = edge.x0 + edge.slope * (y - edge.y0);
- //System.err.println("edge.xIntersection: " + edge.xIntersection);
- }
- }
-
- if (bucket != null)
- activeEdges.addAll(bucket);
-
- // Sort current edges. We are using a bubble sort, because the order
- // of the intersections will not change in most situations. They
- // will only change, when edges intersect each other.
- int size = activeEdges.size();
- if (size > 1)
- {
- for (int i = 1; i < size; i++)
- {
- PolyEdge e1 = (PolyEdge) activeEdges.get(i - 1);
- PolyEdge e2 = (PolyEdge) activeEdges.get(i);
- if (comparator.compare(e1, e2) > 0)
- {
- // Swap e2 with its left neighbor until it 'fits'.
- int j = i;
- do
- {
- activeEdges.set(j, e1);
- activeEdges.set(j - 1, e2);
- j--;
- 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 insideShape = false;
- boolean insideClip = false;
- //System.err.println("scanline: " + y);
- for (Iterator i = activeEdges.iterator(); i.hasNext();)
- {
- PolyEdge edge = (PolyEdge) i.next();
- if (edge.y1 <= y)
- continue;
-
- // 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.
- previous = edge;
- if (edge.isClip)
- insideClip = ! insideClip;
- else
- insideShape = ! insideShape;
- }
- }
- pCtx.dispose();
- }
-
- /**
* Paints a scanline between x0 and x1. Override this when your backend
* can efficiently draw/fill horizontal lines.
*
@@ -1847,8 +1718,9 @@ public abstract class AbstractGraphics2D
* @param x1 the right offset
* @param y the scanline
*/
- protected void fillScanline(PaintContext pCtx, int x0, int x1, int y)
+ protected void fillScanline(int x0, int x1, int y)
{
+ PaintContext pCtx = paintContext;
Raster paintRaster = pCtx.getRaster(x0, y, x1 - x0, 1);
ColorModel paintColorModel = pCtx.getColorModel();
CompositeContext cCtx = composite.createContext(paintColorModel,
@@ -1860,198 +1732,6 @@ public abstract class AbstractGraphics2D
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 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(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(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) icMinY;
- //System.err.println("minY: " + minY);
- 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];
-
- //System.err.println("firstLineDouble: " + firstLineDouble);
-
- for (Iterator i = segs.iterator(); i.hasNext();)
- {
- PolyEdge edge = (PolyEdge) i.next();
- int yindex = (int) (Math.ceil((edge.y0 - firstLineDouble) * AA_SAMPLING));
- //System.err.println("yindex: " + yindex + " for y0: " + edge.y0);
- // Initialize edge's slope and initial xIntersection.
- edge.slope = ((edge.x1 - edge.x0) / (edge.y1 - edge.y0)) / AA_SAMPLING;
- if (edge.y0 == edge.y1) // Horizontal edge.
- edge.xIntersection = Math.min(edge.x0, edge.x1);
- else
- {
- double alignedFirst = Math.ceil(edge.y0 * AA_SAMPLING) / AA_SAMPLING;
- edge.xIntersection = edge.x0 + (edge.slope * AA_SAMPLING) * (alignedFirst - edge.y0);
- }
- //System.err.println(edge);
- // FIXME: Sanity check should not be needed when clipping works.
- if (yindex >= 0 && yindex < edgeTable.length)
- {
- if (edgeTable[yindex] == null) // Create bucket when needed.
- edgeTable[yindex] = new ArrayList();
- edgeTable[yindex].add(edge); // Add edge to the bucket of its line.
- }
- }
-
- // 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 <= 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.
- if ((y + ((double) subY / (double) AA_SAMPLING)) > edge.y1)
- i.remove();
- else
- {
- edge.xIntersection += edge.slope;
- //System.err.println("edge: " + edge);
- //edge.xIntersection = edge.x0 + edge.slope * (y - edge.y0);
- //System.err.println("edge.xIntersection: " + edge.xIntersection);
- }
- }
-
- if (bucket != null)
- {
- activeEdges.addAll(bucket);
- edgeTable[yindex].clear();
- }
-
- // Sort current edges. We are using a bubble sort, because the order
- // of the intersections will not change in most situations. They
- // will only change, when edges intersect each other.
- int size = activeEdges.size();
- if (size > 1)
- {
- for (int i = 1; i < size; i++)
- {
- PolyEdge e1 = (PolyEdge) activeEdges.get(i - 1);
- PolyEdge e2 = (PolyEdge) activeEdges.get(i);
- if (comparator.compare(e1, e2) > 0)
- {
- // Swap e2 with its left neighbor until it 'fits'.
- int j = i;
- do
- {
- activeEdges.set(j, e1);
- activeEdges.set(j - 1, e2);
- j--;
- 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 insideClip = false;
- boolean insideShape = false;
- //System.err.println("scanline: " + y + ", subscanline: " + subY);
- for (Iterator i = activeEdges.iterator(); i.hasNext();)
- {
- PolyEdge edge = (PolyEdge) i.next();
- 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.
- 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
- insideShape = ! insideShape;
- }
- yindex++;
- }
- firstSubline = 0;
- // Render full scanline.
- //System.err.println("scanline: " + y);
- if (! emptyScanline)
- fillScanlineAA(alpha, leftX, y, rightX - leftX, pCtx, (int) minX);
- }
-
- pCtx.dispose();
- }
/**
* Fills a horizontal line between x0 and x1 for anti aliased rendering.
@@ -2113,7 +1793,6 @@ public abstract class AbstractGraphics2D
cCtx.dispose();
}
-
/**
* Initializes this graphics object. This must be called by subclasses in
* order to correctly initialize the state of this object.
@@ -2121,13 +1800,8 @@ public abstract class AbstractGraphics2D
protected void init()
{
setPaint(Color.BLACK);
- setFont(new Font("SansSerif", Font.PLAIN, 12));
+ setFont(FONT);
isOptimized = true;
-
- // FIXME: Should not be necessary. A clip of null should mean
- // 'clip against device bounds.
- destinationRaster = getDestinationRaster();
- clip = getDeviceBounds();
}
/**
@@ -2267,91 +1941,33 @@ public abstract class AbstractGraphics2D
}
/**
- * Converts the specified shape into a list of segments.
- *
- * @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.
+ * Returns the ShapeCache for the calling thread.
*
- * @return a list of PolyEdge that form the shape in device space
+ * @return the ShapeCache for the calling thread
*/
- private ArrayList getSegments(Shape s, AffineTransform t,
- Rectangle2D deviceBounds, boolean isClip)
+ private ShapeCache getShapeCache()
{
- // 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];
-
- // TODO: Use ArrayList<PolyEdge> here when availble.
- ArrayList segs = new ArrayList();
- double segX = 0.; // The start point of the current edge.
- double segY = 0.;
- double polyX = 0.; // The start point of the current polygon.
- double polyY = 0.;
-
- double minX = Integer.MAX_VALUE;
- double maxX = Integer.MIN_VALUE;
- double minY = Integer.MAX_VALUE;
- double maxY = Integer.MIN_VALUE;
-
- //System.err.println("fill polygon");
- while (! path.isDone())
+ ShapeCache sc = shapeCache.get();
+ if (sc == null)
{
- int segType = path.currentSegment(seg);
- minX = Math.min(minX, seg[0]);
- maxX = Math.max(maxX, seg[0]);
- minY = Math.min(minY, seg[1]);
- maxY = Math.max(maxY, seg[1]);
-
- //System.err.println("segment: " + segType + ", " + seg[0] + ", " + seg[1]);
- 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);
- segs.add(edge);
- }
- else if (segType == PathIterator.SEG_LINETO)
- {
- PolyEdge edge = new PolyEdge(segX, segY,
- seg[0], seg[1], isClip);
- segs.add(edge);
- segX = seg[0];
- segY = seg[1];
- }
- path.next();
+ sc = new ShapeCache();
+ shapeCache.set(sc);
}
- deviceBounds.setRect(minX, minY, maxX - minX, maxY - minY);
- return segs;
+ return sc;
}
/**
- * Returns the ShapeCache for the calling thread.
+ * Returns the scanline converter for this thread.
*
- * @return the ShapeCache for the calling thread
+ * @return the scanline converter for this thread
*/
- private ShapeCache getShapeCache()
+ private ScanlineConverter getScanlineConverter()
{
- ShapeCache sc = (ShapeCache) shapeCache.get();
+ ScanlineConverter sc = scanlineConverters.get();
if (sc == null)
{
- sc = new ShapeCache();
- shapeCache.set(sc);
+ sc = new ScanlineConverter();
+ scanlineConverters.set(sc);
}
return sc;
}
diff --git a/libjava/classpath/gnu/java/awt/java2d/ActiveEdges.java b/libjava/classpath/gnu/java/awt/java2d/ActiveEdges.java
new file mode 100644
index 0000000..4d1e777
--- /dev/null
+++ b/libjava/classpath/gnu/java/awt/java2d/ActiveEdges.java
@@ -0,0 +1,195 @@
+/* ActiveEdges.java -- A collection of active edges for scanline conversion
+ Copyright (C) 2006 Free Software Foundation, Inc.
+
+This file is part of GNU Classpath.
+
+GNU Classpath is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+GNU Classpath is distributed in the hope that it will be useful, but
+WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU Classpath; see the file COPYING. If not, write to the
+Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 USA.
+
+Linking this library statically or dynamically with other modules is
+making a combined work based on this library. Thus, the terms and
+conditions of the GNU General Public License cover the whole
+combination.
+
+As a special exception, the copyright holders of this library give you
+permission to link this library with independent modules to produce an
+executable, regardless of the license terms of these independent
+modules, and to copy and distribute the resulting executable under
+terms of your choice, provided that you also meet, for each linked
+independent module, the terms and conditions of the license of that
+module. An independent module is a module which is not derived from
+or based on this library. If you modify this library, you may extend
+this exception to your version of the library, but you are not
+obligated to do so. If you do not wish to do so, delete this
+exception statement from your version. */
+
+
+package gnu.java.awt.java2d;
+
+/**
+ * A collection of active edges for scanline conversion.
+ */
+final class ActiveEdges
+{
+
+ /**
+ * The active edges. This can contain null values at arbirary locations.
+ * The method #sort() packs this together.
+ */
+ private PolyEdge[] activeEdges;
+
+ /**
+ * The actual number of active edges. The array can be bigger than this
+ * number.
+ */
+ private int numActiveEdges;
+
+ /**
+ * Creates a new ActiveEdges object.
+ */
+ ActiveEdges()
+ {
+ activeEdges = new PolyEdge[8];
+ numActiveEdges = 0;
+ }
+
+ /**
+ * Clears out all active edges. This is cheap as it simply resets the
+ * counter to 0. It does not release all references to PolyEdge instances.
+ */
+ void clear()
+ {
+ numActiveEdges = 0;
+ }
+
+ /**
+ * Adds the specified edge to the list of active edges. This does not yet
+ * sort the edges and therefore does destroy any order of the list.
+ *
+ * @param edge the edge to add
+ */
+ void add(PolyEdge edge)
+ {
+ // Grow array when necessary.
+ int oldSize = activeEdges.length;
+ if (numActiveEdges >= oldSize)
+ {
+ int newSize = oldSize + oldSize / 4 + 1;
+ PolyEdge[] newEdges = new PolyEdge[newSize];
+ System.arraycopy(activeEdges, 0, newEdges, 0, oldSize);
+ activeEdges = newEdges;
+ }
+ activeEdges[numActiveEdges] = edge;
+ numActiveEdges++;
+ }
+
+ /**
+ * Intersects all active edges, sorts them according to their intersection
+ * points and packs the array to remove unneeded edges. This does also
+ * remove any edges that do not intersect the scanline (i.e. they end above
+ * of the scanline).
+ *
+ * @param y the scanline height
+ */
+ void intersectSortAndPack(int n, int y)
+ {
+ // Intersect and pack in one go.
+ int last = 0;
+ PolyEdge tmp;
+ for (int i = 0; i < numActiveEdges; i++)
+ {
+ PolyEdge edge = activeEdges[i];
+ // Clear out edge that ends above the scanline.
+ if (edge != null && edge.y1 >= y)
+ {
+ assert edge.y1 >= y && edge.y0 <= y : "edge must cross scanline";
+ edge.intersect(n, y);
+ activeEdges[last] = edge;
+ last++;
+
+ // Bubble up the added edge.
+ for (int j = last - 1; j > 0; j--)
+ {
+ if (activeEdges[j].xIntersection
+ < activeEdges[j - 1].xIntersection)
+ {
+ tmp = activeEdges[j];
+ activeEdges[j] = activeEdges[j - 1];
+ activeEdges[j - 1] = tmp;
+ }
+ else
+ {
+ // The beginning of the list is already sorted.
+ break;
+ }
+ }
+ }
+ }
+ numActiveEdges = last;
+
+ }
+
+ /**
+ * Returns the number of active edges. This is only reliable after a
+ * call to {@link #intersectSortAndPack(int, int)}.
+ *
+ * @return the number of active edges
+ */
+ int getNumActiveEdges()
+ {
+ return numActiveEdges;
+ }
+
+ /**
+ * Returns the active edge at the position <code>i</code>.
+ *
+ * @param i the index
+ *
+ * @return the active edge at the specified index
+ */
+ PolyEdge getActiveEdge(int i)
+ {
+ return activeEdges[i];
+ }
+
+ /**
+ * Removes all edges that end above the specified height.
+ *
+ * @param y the cut-off height
+ */
+ void remove(int y)
+ {
+ for (int i = 0; i < numActiveEdges; i++)
+ {
+ PolyEdge edge = activeEdges[i];
+ if (edge != null && edge.y1 < y)
+ {
+ activeEdges[i] = null;
+ }
+ }
+ }
+
+ public String toString()
+ {
+ StringBuilder s = new StringBuilder();
+ s.append("[ActiveEdges] ");
+ for (int i = 0; i < numActiveEdges; i++)
+ {
+ s.append(activeEdges[i]);
+ s.append(',');
+ }
+ return s.toString();
+ }
+}
diff --git a/libjava/classpath/gnu/java/awt/java2d/PolyEdge.java b/libjava/classpath/gnu/java/awt/java2d/PolyEdge.java
index 6c3b546..eb0cc7f 100644
--- a/libjava/classpath/gnu/java/awt/java2d/PolyEdge.java
+++ b/libjava/classpath/gnu/java/awt/java2d/PolyEdge.java
@@ -38,31 +38,38 @@ exception statement from your version. */
package gnu.java.awt.java2d;
+import gnu.java.math.Fixed;
+
/**
- * An edge in a polygon. This is used by the scanline conversion algorithm
- * implemented in {@link AbstractGraphics2D#rawFillShape}.
+ * An edge in a polygon.
*
* @author Roman Kennke (kennke@aicas.com)
*/
-public class PolyEdge
+final class PolyEdge
implements Comparable
{
/**
* The start and end coordinates of the edge. y0 is always smaller or equal
* than y1.
+ *
+ * These values are stored as fixed-point decimals.
*/
- public double x0, y0, x1, y1;
+ public int x0, y0, x1, y1;
/**
* The slope of the edge. This is dx / dy.
+ *
+ * This is a fixed point decimal.
*/
- double slope;
+ private int slope;
/**
* The intersection of this edge with the current scanline.
+ *
+ * This is a fixed point decimal.
*/
- double xIntersection;
+ int xIntersection;
/**
* Indicates whether this edge is from the clip or from the target shape.
@@ -70,6 +77,24 @@ public class PolyEdge
boolean isClip;
/**
+ * Implements a linked list for the edge pool.
+ */
+ PolyEdge poolNext;
+
+ /**
+ * Implements a linked list for the scanline edge lists.
+ */
+ PolyEdge scanlineNext;
+
+ /**
+ * Create an uninitialized edge.
+ */
+ PolyEdge()
+ {
+ // Nothing to do here.
+ }
+
+ /**
* Creates a new PolyEdge with the specified coordinates.
*
* @param x0 the starting point, x coordinate
@@ -77,7 +102,20 @@ public class PolyEdge
* @param x1 the end point, x coordinate
* @param y1 the end point, y coordinate
*/
- PolyEdge(double x0, double y0, double x1, double y1, boolean clip)
+ PolyEdge(int n, int x0, int y0, int x1, int y1, boolean clip)
+ {
+ init(n, x0, y0, x1, y1, clip);
+ }
+
+ /**
+ * (Re-) Initializes this edge.
+ *
+ * @param x0
+ * @param y0
+ * @param x1
+ * @param y1
+ */
+ void init(int n, int x0, int y0, int x1, int y1, boolean clip)
{
isClip = clip;
if (y0 < y1)
@@ -94,11 +132,7 @@ public class PolyEdge
this.x1 = x0;
this.y1 = y0;
}
- slope = (this.x1 - this.x0) / (this.y1 - this.y0);
- if (this.y0 == this.y1) // Horizontal edge.
- xIntersection = Math.min(this.x0, this.x1);
- else
- xIntersection = this.x0 + slope * (Math.ceil(this.y0) - this.y0);
+ slope = Fixed.div(n, this.x1 - this.x0, this.y1 - this.y0);
}
/**
@@ -115,6 +149,19 @@ public class PolyEdge
return comp;
}
+ /**
+ * Intersects this edge with the scanline at height y. The result is
+ * stored in {@link #xIntersection}.
+ *
+ * @param y the scanline
+ */
+ void intersect(int n, int y)
+ {
+ int dy = y - y0;
+ int dx = Fixed.mul(n, slope, dy);
+ xIntersection = x0 + dx;
+ }
+
public String toString()
{
return "Edge: " + x0 + ", " + y0 + ", " + x1 + ", " + y1 + ", slope: "
diff --git a/libjava/classpath/gnu/java/awt/java2d/Scanline.java b/libjava/classpath/gnu/java/awt/java2d/Scanline.java
new file mode 100644
index 0000000..24c3d34
--- /dev/null
+++ b/libjava/classpath/gnu/java/awt/java2d/Scanline.java
@@ -0,0 +1,91 @@
+/* Scanline.java -- A scanline for the scanline converter
+ Copyright (C) 2006 Free Software Foundation, Inc.
+
+This file is part of GNU Classpath.
+
+GNU Classpath is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+GNU Classpath is distributed in the hope that it will be useful, but
+WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU Classpath; see the file COPYING. If not, write to the
+Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 USA.
+
+Linking this library statically or dynamically with other modules is
+making a combined work based on this library. Thus, the terms and
+conditions of the GNU General Public License cover the whole
+combination.
+
+As a special exception, the copyright holders of this library give you
+permission to link this library with independent modules to produce an
+executable, regardless of the license terms of these independent
+modules, and to copy and distribute the resulting executable under
+terms of your choice, provided that you also meet, for each linked
+independent module, the terms and conditions of the license of that
+module. An independent module is a module which is not derived from
+or based on this library. If you modify this library, you may extend
+this exception to your version of the library, but you are not
+obligated to do so. If you do not wish to do so, delete this
+exception statement from your version. */
+
+
+package gnu.java.awt.java2d;
+
+/**
+ * Represents a scanline in the {@link ScanlineConverter}. This is basically
+ * a sorted list of {@link PolyEdge}s that is made for maximum reuse.
+ */
+class Scanline
+{
+
+ /**
+ * The actual edges array. The fields can be null.
+ */
+ private PolyEdge edges;
+
+ /**
+ * Clears this scanline. This only resets the number of edges to 0. The
+ * actual PolyEdge objects are preserved for possible later reuse.
+ */
+ void clear()
+ {
+ edges = null;
+ }
+
+ /**
+ * Create a new Scanline.
+ */
+ Scanline()
+ {
+ // Nothing to do.
+ }
+
+ /**
+ * Inserts an edge into this scanline. This is performed in a sorted fashion,
+ * and so that it reuses as much existing resources as possible.
+ */
+ void addEdge(PolyEdge edge)
+ {
+
+ // Allocate PolyEdge when necessary or reuse an old one.
+ edge.scanlineNext = edges;
+ edges = edge;
+ }
+
+ /**
+ * Returns the edges queue.
+ *
+ * @return the edges queue
+ */
+ PolyEdge getEdges()
+ {
+ return edges;
+ }
+}
diff --git a/libjava/classpath/gnu/java/awt/java2d/ScanlineConverter.java b/libjava/classpath/gnu/java/awt/java2d/ScanlineConverter.java
new file mode 100644
index 0000000..9f9d892
--- /dev/null
+++ b/libjava/classpath/gnu/java/awt/java2d/ScanlineConverter.java
@@ -0,0 +1,404 @@
+/* ScanlineConverter.java -- Rasterizes Shapes
+ Copyright (C) 2006 Free Software Foundation, Inc.
+
+This file is part of GNU Classpath.
+
+GNU Classpath is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+GNU Classpath is distributed in the hope that it will be useful, but
+WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU Classpath; see the file COPYING. If not, write to the
+Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 USA.
+
+Linking this library statically or dynamically with other modules is
+making a combined work based on this library. Thus, the terms and
+conditions of the GNU General Public License cover the whole
+combination.
+
+As a special exception, the copyright holders of this library give you
+permission to link this library with independent modules to produce an
+executable, regardless of the license terms of these independent
+modules, and to copy and distribute the resulting executable under
+terms of your choice, provided that you also meet, for each linked
+independent module, the terms and conditions of the license of that
+module. An independent module is a module which is not derived from
+or based on this library. If you modify this library, you may extend
+this exception to your version of the library, but you are not
+obligated to do so. If you do not wish to do so, delete this
+exception statement from your version. */
+
+
+package gnu.java.awt.java2d;
+
+import gnu.java.math.Fixed;
+
+import java.awt.Shape;
+import java.awt.geom.AffineTransform;
+import java.awt.geom.PathIterator;
+
+/**
+ * Rasterizes {@link Shape} objects on an AbstractGraphics2D.
+ */
+final class ScanlineConverter
+{
+
+ /**
+ * The number of digits to use for fixed point arithmetics.
+ */
+ private static int FIXED_DIGITS = 6;
+
+ /**
+ * The fixed value for the number 1.
+ */
+ private static int ONE = Fixed.fixedValue(FIXED_DIGITS, 1);
+
+ /**
+ * The actual number of scanlines.
+ */
+ private int numScanlines;
+
+ /**
+ * The number of scanlines. This can contain more elements than we have
+ * scanlines. The real number of scanlines is stored in
+ * {@link #numScanlines}. This can also contain null values for empty
+ * scanlines.
+ */
+ private Scanline[] scanlines;
+
+ /**
+ * The upper bounds which correspond to the index 0 in the scanline array.
+ *
+ * This is a fixed point value.
+ */
+ private int upperBounds;
+
+ /**
+ * The resolution of the scanline converter.
+ *
+ * This is a fixed point value.
+ */
+ private int resolution;
+
+ /**
+ * One half step according to the resolution. This is stored to avoid
+ * unnecessary operations during rendering.
+ */
+ private int halfStep;
+
+ /**
+ * This is used in {@link #addShape(PathIterator, boolean)} to
+ * receive the coordinates of the path.
+ */
+ private float[] coords;
+
+ /**
+ * The active edges.
+ */
+ private ActiveEdges activeEdges;
+
+ private PolyEdge edgePool;
+ private PolyEdge edgePoolLast;
+
+ private int minY;
+ private int maxY;
+
+ /**
+ * Create a new ScanlineConverter.
+ */
+ ScanlineConverter()
+ {
+ scanlines = new Scanline[10];
+ coords = new float[6];
+ activeEdges = new ActiveEdges();
+ edgePool = new PolyEdge();
+ edgePoolLast = edgePool;
+ }
+
+ /**
+ * Renders the specified shape using the specified clip and transform.
+ *
+ * @param shape the shape to render
+ * @param clip the clip
+ * @param trans the transform
+ */
+ void renderShape(AbstractGraphics2D g, Shape shape, Shape clip,
+ AffineTransform trans, int res)
+ {
+ // Prepare resolution and upper bounds.
+ clear();
+ setResolution(res);
+
+ boolean haveClip = clip != null;
+
+ // Add shapes.
+ PathIterator path = shape.getPathIterator(trans, resolution);
+ addShape(path, false);
+ if (haveClip)
+ {
+ path= clip.getPathIterator(trans, resolution);
+ addShape(path, true);
+ }
+
+ setUpperBounds(minY);
+
+ PolyEdge edge = edgePool;
+ while (edge != edgePoolLast)
+ {
+ addEdge(edge);
+ edge = edge.poolNext;
+ }
+
+ int y = upperBounds;
+ int lastIndex = scanlineIndex(y - resolution);
+ int index;
+ activeEdges.clear();
+ // The render loop...
+ Scanline scanline = null;
+ while (y <= maxY)
+ {
+ // First we put together our list of active edges.
+ index = scanlineIndex(y);
+ // If we go outside the scanline array we still need to render the
+ // remaining edges until they end.
+ scanline = index < scanlines.length ? scanlines[index] : null;
+ if (scanline != null)
+ {
+ edge = scanline.getEdges();
+ while (edge != null)
+ {
+ activeEdges.add(edge);
+ edge = edge.scanlineNext;
+ }
+ }
+
+ // Then we intersect all active edges with the current scanline
+ // and sort them according to their intersection points.
+ activeEdges.intersectSortAndPack(FIXED_DIGITS, y + halfStep);
+
+ // Ok, now we can perform the actual scanlining.
+ boolean push = lastIndex != index;
+ doScanline(g, y, push, haveClip);
+
+ // Remove obsolete active edges.
+ //activeEdges.remove(y + halfStep);
+
+ // Go on with the next line...
+ y += resolution;
+ lastIndex = index;
+ }
+ }
+
+ /**
+ * Clears all scanlines.
+ */
+ private void clear()
+ {
+ // Reset edge pool.
+ edgePoolLast = edgePool;
+
+ // Reset scanlines.
+ for (int i = scanlines.length - 1; i >= 0 ; i--)
+ {
+ Scanline sl = scanlines[i];
+ if (sl != null)
+ sl.clear();
+ }
+
+ // Reset bounds.
+ minY = Integer.MAX_VALUE;
+ maxY = Integer.MIN_VALUE;
+ }
+
+ /**
+ * Performs the scanlining on the current set of active edges.
+ */
+ private void doScanline(AbstractGraphics2D g, int y, boolean push,
+ boolean haveClip)
+ {
+ // We begin outside the clip and outside the shape. We only draw when
+ // we are inside the clip AND inside the shape.
+ boolean inClip = ! haveClip;
+ boolean inShape = false;
+ PolyEdge lastEdge = null;
+ int numEdges = activeEdges.getNumActiveEdges();
+ for (int i = 0; i < numEdges; i++)
+ {
+ PolyEdge edge = activeEdges.getActiveEdge(i);
+ if (inClip && inShape)
+ {
+ assert lastEdge != null;
+ int x0 = lastEdge.xIntersection;
+ int x1 = edge.xIntersection;
+ assert x0 <= x1;
+ if (push)
+ {
+ if (resolution == ONE)
+ {
+ // Non-AA rendering.
+ g.fillScanline(Fixed.intValue(FIXED_DIGITS, x0),
+ Fixed.intValue(FIXED_DIGITS, x1 - resolution),
+ Fixed.intValue(FIXED_DIGITS, y));
+ }
+ else
+ {
+ // AA rendering.
+ // FIXME: Implement.
+ System.err.println("Implement AA rendering.");
+ }
+ }
+ }
+ if (edge.isClip)
+ inClip = ! inClip;
+ else
+ inShape = ! inShape;
+
+ lastEdge = edge;
+ }
+ }
+
+ /**
+ * Sets the resolution. A value of 0 rasterizes the shape normally without
+ * anti-aliasing. Greater values renders with a resolution of 2 ^ res.
+ *
+ * @param res the resolution
+ */
+ private void setResolution(int res)
+ {
+ int one = Fixed.fixedValue(FIXED_DIGITS, 1);
+ resolution = one / (1 << res);
+ halfStep = resolution / 2;
+ }
+
+ /**
+ * Sets the vertical bounds of that shape that is beeing rendered.
+ *
+ * @param y0 the upper bounds
+ */
+ private void setUpperBounds(int y0)
+ {
+ upperBounds = fit(y0);
+ }
+
+ /**
+ * Add a shape to the scanline converter.
+ *
+ * @param path
+ * @param clip
+ */
+ private void addShape(PathIterator path, boolean clip)
+ {
+ int startX = 0;
+ int startY = 0;
+ int lastX = 0;
+ int lastY = 0;
+ while (! path.isDone())
+ {
+ int type = path.currentSegment(coords);
+ switch (type)
+ {
+ case PathIterator.SEG_MOVETO:
+ startX = lastX = Fixed.fixedValue(FIXED_DIGITS, coords[0]);
+ startY = lastY = Fixed.fixedValue(FIXED_DIGITS, coords[1]);
+ minY = Math.min(startY, minY);
+ maxY = Math.max(startY, maxY);
+ break;
+ case PathIterator.SEG_LINETO:
+ int x = Fixed.fixedValue(FIXED_DIGITS, coords[0]);
+ int y = Fixed.fixedValue(FIXED_DIGITS, coords[1]);
+ edgePoolAdd(lastX, lastY, x, y, clip);
+ lastX = x;
+ lastY = y;
+ minY = Math.min(lastY, minY);
+ maxY = Math.max(lastY, maxY);
+ break;
+ case PathIterator.SEG_CLOSE:
+ edgePoolAdd(lastX, lastY, startX, startY, clip);
+ lastX = startX;
+ lastY = startY;
+ break;
+ case PathIterator.SEG_CUBICTO:
+ case PathIterator.SEG_QUADTO:
+ default:
+ assert false;
+ }
+ path.next();
+ }
+ }
+
+ /**
+ * Adds an edge into the scanline array.
+ */
+ private void addEdge(PolyEdge edge)
+ {
+ // Determine index.
+ int upper = Math.min(edge.y0, edge.y1);
+ // Fit to raster.
+ int index = scanlineIndex(upper);
+ // Grow array when necessary.
+ if (index >= scanlines.length)
+ {
+ int oldSize = scanlines.length;
+ int newSize = Math.max(oldSize + oldSize / 2 + 1, index + 10);
+ Scanline[] newScanlines = new Scanline[newSize];
+ System.arraycopy(scanlines, 0, newScanlines, 0, oldSize);
+ scanlines = newScanlines;
+ }
+
+ // Add edge.
+ if (scanlines[index] == null)
+ {
+ scanlines[index] = new Scanline();
+ }
+ scanlines[index].addEdge(edge);
+ }
+
+ /**
+ * Fits an Y coordinate to the grid.
+ *
+ * @param y the Y coordinate to fit
+ *
+ * @return the fitted Y coordinate
+ */
+ private int fit(int y)
+ {
+ int val1 = Fixed.div(FIXED_DIGITS, y, resolution);
+ int rounded = Fixed.round(FIXED_DIGITS, val1);
+ return Fixed.div(FIXED_DIGITS, rounded, resolution);
+ }
+
+ /**
+ * Calculates the scanline index for the specified y coordinate.
+ *
+ * @param y the y coordinate as fixed point value
+ *
+ * @return the scanline index
+ */
+ private int scanlineIndex(int y)
+ {
+ int fitted = fit(y);
+ // Cleverly skip the fixed point conversions here.
+ return (fitted - upperBounds)/ resolution;
+ }
+
+ private void edgePoolAdd(int x0, int y0, int x1, int y1, boolean clip)
+ {
+ // Don't need no horizontal edges.
+ if (y0 != y1)
+ {
+ edgePoolLast.init(FIXED_DIGITS, x0, y0, x1, y1, clip);
+ if (edgePoolLast.poolNext == null)
+ {
+ edgePoolLast.poolNext = new PolyEdge();
+ }
+ edgePoolLast = edgePoolLast.poolNext;
+ }
+ }
+}