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
This commit is contained in:
parent
af333b9a7f
commit
e1bea0c068
2951 changed files with 80982 additions and 68583 deletions
|
@ -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;
|
||||
|
||||
/**
|
||||
|
@ -153,6 +150,11 @@ public abstract class AbstractGraphics2D
|
|||
implements Cloneable
|
||||
{
|
||||
|
||||
/**
|
||||
* 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.
|
||||
|
@ -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
|
||||
|
@ -176,6 +185,11 @@ public abstract class AbstractGraphics2D
|
|||
*/
|
||||
private Paint paint;
|
||||
|
||||
/**
|
||||
* The paint context during rendering.
|
||||
*/
|
||||
private PaintContext paintContext;
|
||||
|
||||
/**
|
||||
* The 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);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -1704,141 +1710,6 @@ public abstract class AbstractGraphics2D
|
|||
throw new UnsupportedOperationException("Not implemented yet.");
|
||||
}
|
||||
|
||||
/**
|
||||
* 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();
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -2266,80 +1940,6 @@ public abstract class AbstractGraphics2D
|
|||
p.transform(t);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*
|
||||
* @return a list of PolyEdge that form the shape in device space
|
||||
*/
|
||||
private ArrayList getSegments(Shape s, AffineTransform t,
|
||||
Rectangle2D deviceBounds, boolean isClip)
|
||||
{
|
||||
// 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())
|
||||
{
|
||||
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();
|
||||
}
|
||||
deviceBounds.setRect(minX, minY, maxX - minX, maxY - minY);
|
||||
return segs;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the ShapeCache for the calling thread.
|
||||
*
|
||||
|
@ -2347,7 +1947,7 @@ public abstract class AbstractGraphics2D
|
|||
*/
|
||||
private ShapeCache getShapeCache()
|
||||
{
|
||||
ShapeCache sc = (ShapeCache) shapeCache.get();
|
||||
ShapeCache sc = shapeCache.get();
|
||||
if (sc == null)
|
||||
{
|
||||
sc = new ShapeCache();
|
||||
|
@ -2355,4 +1955,20 @@ public abstract class AbstractGraphics2D
|
|||
}
|
||||
return sc;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the scanline converter for this thread.
|
||||
*
|
||||
* @return the scanline converter for this thread
|
||||
*/
|
||||
private ScanlineConverter getScanlineConverter()
|
||||
{
|
||||
ScanlineConverter sc = scanlineConverters.get();
|
||||
if (sc == null)
|
||||
{
|
||||
sc = new ScanlineConverter();
|
||||
scanlineConverters.set(sc);
|
||||
}
|
||||
return sc;
|
||||
}
|
||||
}
|
||||
|
|
195
libjava/classpath/gnu/java/awt/java2d/ActiveEdges.java
Normal file
195
libjava/classpath/gnu/java/awt/java2d/ActiveEdges.java
Normal file
|
@ -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();
|
||||
}
|
||||
}
|
|
@ -38,37 +38,62 @@ 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.
|
||||
*/
|
||||
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.
|
||||
*
|
||||
|
@ -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: "
|
||||
|
|
91
libjava/classpath/gnu/java/awt/java2d/Scanline.java
Normal file
91
libjava/classpath/gnu/java/awt/java2d/Scanline.java
Normal file
|
@ -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;
|
||||
}
|
||||
}
|
404
libjava/classpath/gnu/java/awt/java2d/ScanlineConverter.java
Normal file
404
libjava/classpath/gnu/java/awt/java2d/ScanlineConverter.java
Normal file
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue