--- old/src/java.desktop/share/classes/sun/java2d/marlin/Curve.java 2017-08-28 16:48:24.941663252 +0200
+++ new/src/java.desktop/share/classes/sun/java2d/marlin/Curve.java 2017-08-28 16:48:24.797663254 +0200
@@ -56,12 +56,16 @@
float x3, float y3,
float x4, float y4)
{
- ax = 3.0f * (x2 - x3) + x4 - x1;
- ay = 3.0f * (y2 - y3) + y4 - y1;
- bx = 3.0f * (x1 - 2.0f * x2 + x3);
- by = 3.0f * (y1 - 2.0f * y2 + y3);
- cx = 3.0f * (x2 - x1);
- cy = 3.0f * (y2 - y1);
+ final float dx32 = 3.0f * (x3 - x2);
+ final float dy32 = 3.0f * (y3 - y2);
+ final float dx21 = 3.0f * (x2 - x1);
+ final float dy21 = 3.0f * (y2 - y1);
+ ax = (x4 - x1) - dx32;
+ ay = (y4 - y1) - dy32;
+ bx = (dx32 - dx21);
+ by = (dy32 - dy21);
+ cx = dx21;
+ cy = dy21;
dx = x1;
dy = y1;
dax = 3.0f * ax; day = 3.0f * ay;
@@ -72,11 +76,13 @@
float x2, float y2,
float x3, float y3)
{
+ final float dx21 = (x2 - x1);
+ final float dy21 = (y2 - y1);
ax = 0.0f; ay = 0.0f;
- bx = x1 - 2.0f * x2 + x3;
- by = y1 - 2.0f * y2 + y3;
- cx = 2.0f * (x2 - x1);
- cy = 2.0f * (y2 - y1);
+ bx = (x3 - x2) - dx21;
+ by = (y3 - y2) - dy21;
+ cx = 2.0f * dx21;
+ cy = 2.0f * dy21;
dx = x1;
dy = y1;
dax = 0.0f; day = 0.0f;
--- old/src/java.desktop/share/classes/sun/java2d/marlin/DCurve.java 2017-08-28 16:48:25.365663246 +0200
+++ new/src/java.desktop/share/classes/sun/java2d/marlin/DCurve.java 2017-08-28 16:48:25.205663248 +0200
@@ -56,12 +56,16 @@
double x3, double y3,
double x4, double y4)
{
- ax = 3.0d * (x2 - x3) + x4 - x1;
- ay = 3.0d * (y2 - y3) + y4 - y1;
- bx = 3.0d * (x1 - 2.0d * x2 + x3);
- by = 3.0d * (y1 - 2.0d * y2 + y3);
- cx = 3.0d * (x2 - x1);
- cy = 3.0d * (y2 - y1);
+ final double dx32 = 3.0d * (x3 - x2);
+ final double dy32 = 3.0d * (y3 - y2);
+ final double dx21 = 3.0d * (x2 - x1);
+ final double dy21 = 3.0d * (y2 - y1);
+ ax = (x4 - x1) - dx32;
+ ay = (y4 - y1) - dy32;
+ bx = (dx32 - dx21);
+ by = (dy32 - dy21);
+ cx = dx21;
+ cy = dy21;
dx = x1;
dy = y1;
dax = 3.0d * ax; day = 3.0d * ay;
@@ -72,11 +76,13 @@
double x2, double y2,
double x3, double y3)
{
+ final double dx21 = (x2 - x1);
+ final double dy21 = (y2 - y1);
ax = 0.0d; ay = 0.0d;
- bx = x1 - 2.0d * x2 + x3;
- by = y1 - 2.0d * y2 + y3;
- cx = 2.0d * (x2 - x1);
- cy = 2.0d * (y2 - y1);
+ bx = (x3 - x2) - dx21;
+ by = (y3 - y2) - dy21;
+ cx = 2.0d * dx21;
+ cy = 2.0d * dy21;
dx = x1;
dy = y1;
dax = 0.0d; day = 0.0d;
--- old/src/java.desktop/share/classes/sun/java2d/marlin/DDasher.java 2017-08-28 16:48:25.845663239 +0200
+++ new/src/java.desktop/share/classes/sun/java2d/marlin/DDasher.java 2017-08-28 16:48:25.701663241 +0200
@@ -137,7 +137,7 @@
dashOn = !dashOn;
}
}
- } else if (phase > 0) {
+ } else if (phase > 0.0d) {
if (cycles >= MAX_CYCLES) {
phase = 0.0d;
} else {
@@ -157,12 +157,13 @@
this.dash = dash;
this.dashLen = dashLen;
- this.startPhase = this.phase = phase;
+ this.phase = phase;
+ this.startPhase = phase;
this.startDashOn = dashOn;
this.startIdx = sidx;
this.starting = true;
- needsMoveTo = false;
- firstSegidx = 0;
+ this.needsMoveTo = false;
+ this.firstSegidx = 0;
this.recycleDashes = recycleDashes;
@@ -201,8 +202,8 @@
}
@Override
- public void moveTo(double x0, double y0) {
- if (firstSegidx > 0) {
+ public void moveTo(final double x0, final double y0) {
+ if (firstSegidx != 0) {
out.moveTo(sx, sy);
emitFirstSegments();
}
@@ -210,8 +211,10 @@
this.idx = startIdx;
this.dashOn = this.startDashOn;
this.phase = this.startPhase;
- this.sx = this.x0 = x0;
- this.sy = this.y0 = y0;
+ this.sx = x0;
+ this.sy = y0;
+ this.x0 = x0;
+ this.y0 = y0;
this.starting = true;
}
@@ -236,7 +239,7 @@
private void emitFirstSegments() {
final double[] fSegBuf = firstSegmentsBuffer;
- for (int i = 0; i < firstSegidx; ) {
+ for (int i = 0, len = firstSegidx; i < len; ) {
int type = (int)fSegBuf[i];
emitSeg(fSegBuf, i + 1, type);
i += (type - 1);
@@ -251,48 +254,59 @@
private int firstSegidx;
// precondition: pts must be in relative coordinates (relative to x0,y0)
- private void goTo(double[] pts, int off, final int type) {
- double x = pts[off + type - 4];
- double y = pts[off + type - 3];
- if (dashOn) {
+ private void goTo(final double[] pts, final int off, final int type,
+ final boolean on)
+ {
+ final int index = off + type;
+ final double x = pts[index - 4];
+ final double y = pts[index - 3];
+
+ if (on) {
if (starting) {
- int len = type - 1; // - 2 + 1
- int segIdx = firstSegidx;
- double[] buf = firstSegmentsBuffer;
- if (segIdx + len > buf.length) {
- if (DO_STATS) {
- rdrCtx.stats.stat_array_dasher_firstSegmentsBuffer
- .add(segIdx + len);
- }
- firstSegmentsBuffer = buf
- = firstSegmentsBuffer_ref.widenArray(buf, segIdx,
- segIdx + len);
- }
- buf[segIdx++] = type;
- len--;
- // small arraycopy (2, 4 or 6) but with offset:
- System.arraycopy(pts, off, buf, segIdx, len);
- segIdx += len;
- firstSegidx = segIdx;
+ goTo_starting(pts, off, type);
} else {
if (needsMoveTo) {
- out.moveTo(x0, y0);
needsMoveTo = false;
+ out.moveTo(x0, y0);
}
emitSeg(pts, off, type);
}
} else {
- starting = false;
+ if (starting) {
+ // low probability test (hotspot)
+ starting = false;
+ }
needsMoveTo = true;
}
this.x0 = x;
this.y0 = y;
}
+ private void goTo_starting(final double[] pts, final int off, final int type) {
+ int len = type - 1; // - 2 + 1
+ int segIdx = firstSegidx;
+ double[] buf = firstSegmentsBuffer;
+
+ if (segIdx + len > buf.length) {
+ if (DO_STATS) {
+ rdrCtx.stats.stat_array_dasher_firstSegmentsBuffer
+ .add(segIdx + len);
+ }
+ firstSegmentsBuffer = buf
+ = firstSegmentsBuffer_ref.widenArray(buf, segIdx,
+ segIdx + len);
+ }
+ buf[segIdx++] = type;
+ len--;
+ // small arraycopy (2, 4 or 6) but with offset:
+ System.arraycopy(pts, off, buf, segIdx, len);
+ firstSegidx = segIdx + len;
+ }
+
@Override
- public void lineTo(double x1, double y1) {
- double dx = x1 - x0;
- double dy = y1 - y0;
+ public void lineTo(final double x1, final double y1) {
+ final double dx = x1 - x0;
+ final double dy = y1 - y0;
double len = dx*dx + dy*dy;
if (len == 0.0d) {
@@ -307,48 +321,61 @@
final double[] _curCurvepts = curCurvepts;
final double[] _dash = dash;
+ final int _dashLen = this.dashLen;
+
+ int _idx = idx;
+ boolean _dashOn = dashOn;
+ double _phase = phase;
double leftInThisDashSegment;
- double dashdx, dashdy, p;
+ double d, dashdx, dashdy, p;
while (true) {
- leftInThisDashSegment = _dash[idx] - phase;
+ d = _dash[_idx];
+ leftInThisDashSegment = d - _phase;
if (len <= leftInThisDashSegment) {
_curCurvepts[0] = x1;
_curCurvepts[1] = y1;
- goTo(_curCurvepts, 0, 4);
+
+ goTo(_curCurvepts, 0, 4, _dashOn);
// Advance phase within current dash segment
- phase += len;
+ _phase += len;
+
// TODO: compare double values using epsilon:
if (len == leftInThisDashSegment) {
- phase = 0.0d;
- idx = (idx + 1) % dashLen;
- dashOn = !dashOn;
+ _phase = 0.0d;
+ _idx = (_idx + 1) % _dashLen;
+ _dashOn = !_dashOn;
}
+
+ // Save local state:
+ idx = _idx;
+ dashOn = _dashOn;
+ phase = _phase;
return;
}
- dashdx = _dash[idx] * cx;
- dashdy = _dash[idx] * cy;
+ dashdx = d * cx;
+ dashdy = d * cy;
- if (phase == 0.0d) {
+ if (_phase == 0.0d) {
_curCurvepts[0] = x0 + dashdx;
_curCurvepts[1] = y0 + dashdy;
} else {
- p = leftInThisDashSegment / _dash[idx];
+ p = leftInThisDashSegment / d;
_curCurvepts[0] = x0 + p * dashdx;
_curCurvepts[1] = y0 + p * dashdy;
}
- goTo(_curCurvepts, 0, 4);
+ goTo(_curCurvepts, 0, 4, _dashOn);
len -= leftInThisDashSegment;
// Advance to next dash segment
- idx = (idx + 1) % dashLen;
- dashOn = !dashOn;
- phase = 0.0d;
+ _idx = (_idx + 1) % _dashLen;
+ _dashOn = !_dashOn;
+ _phase = 0.0d;
}
}
@@ -361,39 +388,55 @@
if (pointCurve(curCurvepts, type)) {
return;
}
- li.initializeIterationOnCurve(curCurvepts, type);
+ final LengthIterator _li = li;
+ final double[] _curCurvepts = curCurvepts;
+ final double[] _dash = dash;
+ final int _dashLen = this.dashLen;
+
+ _li.initializeIterationOnCurve(_curCurvepts, type);
+
+ int _idx = idx;
+ boolean _dashOn = dashOn;
+ double _phase = phase;
// initially the current curve is at curCurvepts[0...type]
int curCurveoff = 0;
double lastSplitT = 0.0d;
double t;
- double leftInThisDashSegment = dash[idx] - phase;
+ double leftInThisDashSegment = _dash[_idx] - _phase;
- while ((t = li.next(leftInThisDashSegment)) < 1.0d) {
+ while ((t = _li.next(leftInThisDashSegment)) < 1.0d) {
if (t != 0.0d) {
DHelpers.subdivideAt((t - lastSplitT) / (1.0d - lastSplitT),
- curCurvepts, curCurveoff,
- curCurvepts, 0,
- curCurvepts, type, type);
+ _curCurvepts, curCurveoff,
+ _curCurvepts, 0,
+ _curCurvepts, type, type);
lastSplitT = t;
- goTo(curCurvepts, 2, type);
+ goTo(_curCurvepts, 2, type, _dashOn);
curCurveoff = type;
}
// Advance to next dash segment
- idx = (idx + 1) % dashLen;
- dashOn = !dashOn;
- phase = 0.0d;
- leftInThisDashSegment = dash[idx];
- }
- goTo(curCurvepts, curCurveoff+2, type);
- phase += li.lastSegLen();
- if (phase >= dash[idx]) {
- phase = 0.0d;
- idx = (idx + 1) % dashLen;
- dashOn = !dashOn;
- }
+ _idx = (_idx + 1) % _dashLen;
+ _dashOn = !_dashOn;
+ _phase = 0.0d;
+ leftInThisDashSegment = _dash[_idx];
+ }
+
+ goTo(_curCurvepts, curCurveoff + 2, type, _dashOn);
+
+ _phase += _li.lastSegLen();
+ if (_phase >= _dash[_idx]) {
+ _phase = 0.0d;
+ _idx = (_idx + 1) % _dashLen;
+ _dashOn = !_dashOn;
+ }
+ // Save local state:
+ idx = _idx;
+ dashOn = _dashOn;
+ phase = _phase;
+
// reset LengthIterator:
- li.reset();
+ _li.reset();
}
private static boolean pointCurve(double[] curve, int type) {
@@ -419,7 +462,7 @@
// tree; however, the trees we are interested in have the property that
// every non leaf node has exactly 2 children
static final class LengthIterator {
- private enum Side {LEFT, RIGHT};
+ private enum Side {LEFT, RIGHT}
// Holds the curves at various levels of the recursion. The root
// (i.e. the original curve) is at recCurveStack[0] (but then it
// gets subdivided, the left half is put at 1, so most of the time
@@ -669,11 +712,12 @@
// this is a bit of a hack. It returns -1 if we're not on a leaf, and
// the length of the leaf if we are on a leaf.
private double onLeaf() {
- double[] curve = recCurveStack[recLevel];
+ final double[] curve = recCurveStack[recLevel];
+ final int _curveType = curveType;
double polyLen = 0.0d;
double x0 = curve[0], y0 = curve[1];
- for (int i = 2; i < curveType; i += 2) {
+ for (int i = 2; i < _curveType; i += 2) {
final double x1 = curve[i], y1 = curve[i+1];
final double len = DHelpers.linelen(x0, y0, x1, y1);
polyLen += len;
@@ -683,8 +727,8 @@
}
final double lineLen = DHelpers.linelen(curve[0], curve[1],
- curve[curveType-2],
- curve[curveType-1]);
+ curve[_curveType-2],
+ curve[_curveType-1]);
if ((polyLen - lineLen) < ERR || recLevel == REC_LIMIT) {
return (polyLen + lineLen) / 2.0d;
}
@@ -693,9 +737,9 @@
}
@Override
- public void curveTo(double x1, double y1,
- double x2, double y2,
- double x3, double y3)
+ public void curveTo(final double x1, final double y1,
+ final double x2, final double y2,
+ final double x3, final double y3)
{
final double[] _curCurvepts = curCurvepts;
_curCurvepts[0] = x0; _curCurvepts[1] = y0;
@@ -706,7 +750,9 @@
}
@Override
- public void quadTo(double x1, double y1, double x2, double y2) {
+ public void quadTo(final double x1, final double y1,
+ final double x2, final double y2)
+ {
final double[] _curCurvepts = curCurvepts;
_curCurvepts[0] = x0; _curCurvepts[1] = y0;
_curCurvepts[2] = x1; _curCurvepts[3] = y1;
@@ -717,7 +763,7 @@
@Override
public void closePath() {
lineTo(sx, sy);
- if (firstSegidx > 0) {
+ if (firstSegidx != 0) {
if (!dashOn || needsMoveTo) {
out.moveTo(sx, sy);
}
@@ -728,7 +774,7 @@
@Override
public void pathDone() {
- if (firstSegidx > 0) {
+ if (firstSegidx != 0) {
out.moveTo(sx, sy);
emitFirstSegments();
}
--- old/src/java.desktop/share/classes/sun/java2d/marlin/DHelpers.java 2017-08-28 16:48:26.297663233 +0200
+++ new/src/java.desktop/share/classes/sun/java2d/marlin/DHelpers.java 2017-08-28 16:48:26.137663235 +0200
@@ -30,6 +30,10 @@
import static java.lang.Math.sqrt;
import static java.lang.Math.cbrt;
import static java.lang.Math.acos;
+import java.util.Arrays;
+import static sun.java2d.marlin.MarlinConst.INITIAL_EDGES_COUNT;
+import sun.java2d.marlin.stats.Histogram;
+import sun.java2d.marlin.stats.StatLong;
final class DHelpers implements MarlinConst {
@@ -171,15 +175,6 @@
return ret;
}
- static double polyLineLength(double[] poly, final int off, final int nCoords) {
- assert nCoords % 2 == 0 && poly.length >= off + nCoords : "";
- double acc = 0.0d;
- for (int i = off + 2; i < off + nCoords; i += 2) {
- acc += linelen(poly[i], poly[i+1], poly[i-2], poly[i-1]);
- }
- return acc;
- }
-
static double linelen(double x1, double y1, double x2, double y2) {
final double dx = x2 - x1;
final double dy = y2 - y1;
@@ -433,4 +428,283 @@
return;
}
}
+
+ // From sun.java2d.loops.GeneralRenderer:
+
+ static final int OUTCODE_TOP = 1;
+ static final int OUTCODE_BOTTOM = 2;
+ static final int OUTCODE_LEFT = 4;
+ static final int OUTCODE_RIGHT = 8;
+
+ static int outcode(final double x, final double y,
+ final double[] clipRect)
+ {
+ int code;
+ if (y < clipRect[0]) {
+ code = OUTCODE_TOP;
+ } else if (y >= clipRect[1]) {
+ code = OUTCODE_BOTTOM;
+ } else {
+ code = 0;
+ }
+ if (x < clipRect[2]) {
+ code |= OUTCODE_LEFT;
+ } else if (x >= clipRect[3]) {
+ code |= OUTCODE_RIGHT;
+ }
+ return code;
+ }
+
+ // a stack of polynomial curves where each curve shares endpoints with
+ // adjacent ones.
+ static final class PolyStack {
+ private static final byte TYPE_LINETO = (byte) 0;
+ private static final byte TYPE_QUADTO = (byte) 1;
+ private static final byte TYPE_CUBICTO = (byte) 2;
+
+ // curves capacity = edges count (8192) = edges x 2 (coords)
+ private static final int INITIAL_CURVES_COUNT = INITIAL_EDGES_COUNT << 1;
+
+ // types capacity = edges count (4096)
+ private static final int INITIAL_TYPES_COUNT = INITIAL_EDGES_COUNT;
+
+ double[] curves;
+ int end;
+ byte[] curveTypes;
+ int numCurves;
+
+ // curves ref (dirty)
+ final DoubleArrayCache.Reference curves_ref;
+ // curveTypes ref (dirty)
+ final ByteArrayCache.Reference curveTypes_ref;
+
+ // used marks (stats only)
+ int curveTypesUseMark;
+ int curvesUseMark;
+
+ private final StatLong stat_polystack_types;
+ private final StatLong stat_polystack_curves;
+ private final Histogram hist_polystack_curves;
+ private final StatLong stat_array_polystack_curves;
+ private final StatLong stat_array_polystack_curveTypes;
+
+ PolyStack(final DRendererContext rdrCtx) {
+ this(rdrCtx, null, null, null, null, null);
+ }
+
+ PolyStack(final DRendererContext rdrCtx,
+ final StatLong stat_polystack_types,
+ final StatLong stat_polystack_curves,
+ final Histogram hist_polystack_curves,
+ final StatLong stat_array_polystack_curves,
+ final StatLong stat_array_polystack_curveTypes)
+ {
+ curves_ref = rdrCtx.newDirtyDoubleArrayRef(INITIAL_CURVES_COUNT); // 32K
+ curves = curves_ref.initial;
+
+ curveTypes_ref = rdrCtx.newDirtyByteArrayRef(INITIAL_TYPES_COUNT); // 4K
+ curveTypes = curveTypes_ref.initial;
+ numCurves = 0;
+ end = 0;
+
+ if (DO_STATS) {
+ curveTypesUseMark = 0;
+ curvesUseMark = 0;
+ }
+ this.stat_polystack_types = stat_polystack_types;
+ this.stat_polystack_curves = stat_polystack_curves;
+ this.hist_polystack_curves = hist_polystack_curves;
+ this.stat_array_polystack_curves = stat_array_polystack_curves;
+ this.stat_array_polystack_curveTypes = stat_array_polystack_curveTypes;
+ }
+
+ /**
+ * Disposes this PolyStack:
+ * clean up before reusing this instance
+ */
+ void dispose() {
+ end = 0;
+ numCurves = 0;
+
+ if (DO_STATS) {
+ stat_polystack_types.add(curveTypesUseMark);
+ stat_polystack_curves.add(curvesUseMark);
+ hist_polystack_curves.add(curvesUseMark);
+
+ // reset marks
+ curveTypesUseMark = 0;
+ curvesUseMark = 0;
+ }
+
+ // Return arrays:
+ // curves and curveTypes are kept dirty
+ curves = curves_ref.putArray(curves);
+ curveTypes = curveTypes_ref.putArray(curveTypes);
+ }
+
+ private void ensureSpace(final int n) {
+ // use substraction to avoid integer overflow:
+ if (curves.length - end < n) {
+ if (DO_STATS) {
+ stat_array_polystack_curves.add(end + n);
+ }
+ curves = curves_ref.widenArray(curves, end, end + n);
+ }
+ if (curveTypes.length <= numCurves) {
+ if (DO_STATS) {
+ stat_array_polystack_curveTypes.add(numCurves + 1);
+ }
+ curveTypes = curveTypes_ref.widenArray(curveTypes,
+ numCurves,
+ numCurves + 1);
+ }
+ }
+
+ void pushCubic(double x0, double y0,
+ double x1, double y1,
+ double x2, double y2)
+ {
+ ensureSpace(6);
+ curveTypes[numCurves++] = TYPE_CUBICTO;
+ // we reverse the coordinate order to make popping easier
+ final double[] _curves = curves;
+ int e = end;
+ _curves[e++] = x2; _curves[e++] = y2;
+ _curves[e++] = x1; _curves[e++] = y1;
+ _curves[e++] = x0; _curves[e++] = y0;
+ end = e;
+ }
+
+ void pushQuad(double x0, double y0,
+ double x1, double y1)
+ {
+ ensureSpace(4);
+ curveTypes[numCurves++] = TYPE_QUADTO;
+ final double[] _curves = curves;
+ int e = end;
+ _curves[e++] = x1; _curves[e++] = y1;
+ _curves[e++] = x0; _curves[e++] = y0;
+ end = e;
+ }
+
+ void pushLine(double x, double y) {
+ ensureSpace(2);
+ curveTypes[numCurves++] = TYPE_LINETO;
+ curves[end++] = x; curves[end++] = y;
+ }
+
+ void pullAll(final DPathConsumer2D io) {
+ final int nc = numCurves;
+ if (nc == 0) {
+ return;
+ }
+ if (DO_STATS) {
+ // update used marks:
+ if (numCurves > curveTypesUseMark) {
+ curveTypesUseMark = numCurves;
+ }
+ if (end > curvesUseMark) {
+ curvesUseMark = end;
+ }
+ }
+ final byte[] _curveTypes = curveTypes;
+ final double[] _curves = curves;
+ int e = 0;
+
+ for (int i = 0; i < nc; i++) {
+ switch(_curveTypes[i]) {
+ case TYPE_LINETO:
+ io.lineTo(_curves[e], _curves[e+1]);
+ e += 2;
+ continue;
+ case TYPE_QUADTO:
+ io.quadTo(_curves[e+0], _curves[e+1],
+ _curves[e+2], _curves[e+3]);
+ e += 4;
+ continue;
+ case TYPE_CUBICTO:
+ io.curveTo(_curves[e+0], _curves[e+1],
+ _curves[e+2], _curves[e+3],
+ _curves[e+4], _curves[e+5]);
+ e += 6;
+ continue;
+ default:
+ }
+ }
+ numCurves = 0;
+ end = 0;
+ }
+
+ void popAll(final DPathConsumer2D io) {
+ int nc = numCurves;
+ if (nc == 0) {
+ return;
+ }
+ if (DO_STATS) {
+ // update used marks:
+ if (numCurves > curveTypesUseMark) {
+ curveTypesUseMark = numCurves;
+ }
+ if (end > curvesUseMark) {
+ curvesUseMark = end;
+ }
+ }
+ final byte[] _curveTypes = curveTypes;
+ final double[] _curves = curves;
+ int e = end;
+
+ while (nc != 0) {
+ switch(_curveTypes[--nc]) {
+ case TYPE_LINETO:
+ e -= 2;
+ io.lineTo(_curves[e], _curves[e+1]);
+ continue;
+ case TYPE_QUADTO:
+ e -= 4;
+ io.quadTo(_curves[e+0], _curves[e+1],
+ _curves[e+2], _curves[e+3]);
+ continue;
+ case TYPE_CUBICTO:
+ e -= 6;
+ io.curveTo(_curves[e+0], _curves[e+1],
+ _curves[e+2], _curves[e+3],
+ _curves[e+4], _curves[e+5]);
+ continue;
+ default:
+ }
+ }
+ numCurves = 0;
+ end = 0;
+ }
+
+ @Override
+ public String toString() {
+ String ret = "";
+ int nc = numCurves;
+ int last = end;
+ int len;
+ while (nc != 0) {
+ switch(curveTypes[--nc]) {
+ case TYPE_LINETO:
+ len = 2;
+ ret += "line: ";
+ break;
+ case TYPE_QUADTO:
+ len = 4;
+ ret += "quad: ";
+ break;
+ case TYPE_CUBICTO:
+ len = 6;
+ ret += "cubic: ";
+ break;
+ default:
+ len = 0;
+ }
+ last -= len;
+ ret += Arrays.toString(Arrays.copyOfRange(curves, last, last+len))
+ + "\n";
+ }
+ return ret;
+ }
+ }
}
--- old/src/java.desktop/share/classes/sun/java2d/marlin/DMarlinRenderingEngine.java 2017-08-28 16:48:26.669663228 +0200
+++ new/src/java.desktop/share/classes/sun/java2d/marlin/DMarlinRenderingEngine.java 2017-08-28 16:48:26.501663230 +0200
@@ -84,6 +84,9 @@
static final double UPPER_BND = Float.MAX_VALUE / 2.0d;
static final double LOWER_BND = -UPPER_BND;
+ static final boolean DO_CLIP = MarlinProperties.isDoClip();
+ static final boolean DO_TRACE = false;
+
/**
* Public constructor
*/
@@ -324,6 +327,7 @@
int dashLen = -1;
boolean recycleDashes = false;
+ double scale = 1.0d;
double[] dashesD = null;
// Ensure converting dashes to double precision:
@@ -364,7 +368,7 @@
// a*b == -c*d && a*a+c*c == b*b+d*d. In the actual check below, we
// leave a bit of room for error.
if (nearZero(a*b + c*d) && nearZero(a*a + c*c - (b*b + d*d))) {
- final double scale = Math.sqrt(a*a + c*c);
+ scale = Math.sqrt(a*a + c*c);
if (dashesD != null) {
for (int i = 0; i < dashLen; i++) {
@@ -399,23 +403,44 @@
at = null;
}
+ final DTransformingPathConsumer2D transformerPC2D = rdrCtx.transformerPC2D;
+
+ if (DO_TRACE) {
+ // trace Stroker:
+ pc2d = transformerPC2D.traceStroker(pc2d);
+ }
+
if (USE_SIMPLIFIER) {
// Use simplifier after stroker before Renderer
// to remove collinear segments (notably due to cap square)
pc2d = rdrCtx.simplifier.init(pc2d);
}
- final DTransformingPathConsumer2D transformerPC2D = rdrCtx.transformerPC2D;
+ // deltaTransformConsumer may adjust the clip rectangle:
pc2d = transformerPC2D.deltaTransformConsumer(pc2d, strokerat);
- pc2d = rdrCtx.stroker.init(pc2d, width, caps, join, miterlimit);
+ // stroker will adjust the clip rectangle (width / miter limit):
+ pc2d = rdrCtx.stroker.init(pc2d, width, caps, join, miterlimit, scale);
if (dashesD != null) {
pc2d = rdrCtx.dasher.init(pc2d, dashesD, dashLen, dashphase,
recycleDashes);
+ } else if (rdrCtx.doClip && (caps != Stroker.CAP_BUTT)) {
+ if (DO_TRACE) {
+ pc2d = transformerPC2D.traceClosedPathDetector(pc2d);
+ }
+
+ // If no dash and clip is enabled:
+ // detect closedPaths (polygons) for caps
+ pc2d = transformerPC2D.detectClosedPath(pc2d);
}
pc2d = transformerPC2D.inverseDeltaTransformConsumer(pc2d, strokerat);
+ if (DO_TRACE) {
+ // trace Input:
+ pc2d = transformerPC2D.traceInput(pc2d);
+ }
+
final PathIterator pi = norm.getNormalizingPathIterator(rdrCtx,
src.getPathIterator(at));
@@ -805,6 +830,18 @@
clip.getWidth(), clip.getHeight(),
PathIterator.WIND_NON_ZERO);
+ if (DO_CLIP) {
+ // Define the initial clip bounds:
+ final double[] clipRect = rdrCtx.clipRect;
+ clipRect[0] = clip.getLoY();
+ clipRect[1] = clip.getLoY() + clip.getHeight();
+ clipRect[2] = clip.getLoX();
+ clipRect[3] = clip.getLoX() + clip.getWidth();
+
+ // Enable clipping:
+ rdrCtx.doClip = true;
+ }
+
strokeTo(rdrCtx, s, _at, bs, thin, norm, true, r);
}
if (r.endRendering()) {
@@ -862,8 +899,8 @@
final DRendererContext rdrCtx = getRendererContext();
try {
r = rdrCtx.renderer.init(clip.getLoX(), clip.getLoY(),
- clip.getWidth(), clip.getHeight(),
- DRenderer.WIND_EVEN_ODD);
+ clip.getWidth(), clip.getHeight(),
+ DRenderer.WIND_EVEN_ODD);
r.moveTo( x, y);
r.lineTo( (x+dx1), (y+dy1));
@@ -1044,6 +1081,8 @@
// optimisation parameters
logInfo("sun.java2d.renderer.useSimplifier = "
+ MarlinConst.USE_SIMPLIFIER);
+ logInfo("sun.java2d.renderer.clip = "
+ + MarlinProperties.isDoClip());
// debugging parameters
logInfo("sun.java2d.renderer.doStats = "
--- old/src/java.desktop/share/classes/sun/java2d/marlin/DRenderer.java 2017-08-28 16:48:27.249663220 +0200
+++ new/src/java.desktop/share/classes/sun/java2d/marlin/DRenderer.java 2017-08-28 16:48:27.081663222 +0200
@@ -46,6 +46,9 @@
static final int SUBPIXEL_MASK_X = SUBPIXEL_POSITIONS_X - 1;
static final int SUBPIXEL_MASK_Y = SUBPIXEL_POSITIONS_Y - 1;
+ static final double RDR_OFFSET_X = 0.501d / SUBPIXEL_SCALE_X;
+ static final double RDR_OFFSET_Y = 0.501d / SUBPIXEL_SCALE_Y;
+
// number of subpixels corresponding to a tile line
private static final int SUBPIXEL_TILE
= TILE_H << SUBPIXEL_LG_POSITIONS_Y;
@@ -668,7 +671,7 @@
}
@Override
- public void moveTo(double pix_x0, double pix_y0) {
+ public void moveTo(final double pix_x0, final double pix_y0) {
closePath();
final double sx = tosubpixx(pix_x0);
final double sy = tosubpixy(pix_y0);
@@ -679,7 +682,7 @@
}
@Override
- public void lineTo(double pix_x1, double pix_y1) {
+ public void lineTo(final double pix_x1, final double pix_y1) {
final double x1 = tosubpixx(pix_x1);
final double y1 = tosubpixy(pix_y1);
addLine(x0, y0, x1, y1);
@@ -688,24 +691,26 @@
}
@Override
- public void curveTo(double x1, double y1,
- double x2, double y2,
- double x3, double y3)
+ public void curveTo(final double pix_x1, final double pix_y1,
+ final double pix_x2, final double pix_y2,
+ final double pix_x3, final double pix_y3)
{
- final double xe = tosubpixx(x3);
- final double ye = tosubpixy(y3);
- curve.set(x0, y0, tosubpixx(x1), tosubpixy(y1),
- tosubpixx(x2), tosubpixy(y2), xe, ye);
+ final double xe = tosubpixx(pix_x3);
+ final double ye = tosubpixy(pix_y3);
+ curve.set(x0, y0, tosubpixx(pix_x1), tosubpixy(pix_y1),
+ tosubpixx(pix_x2), tosubpixy(pix_y2), xe, ye);
curveBreakIntoLinesAndAdd(x0, y0, curve, xe, ye);
x0 = xe;
y0 = ye;
}
@Override
- public void quadTo(double x1, double y1, double x2, double y2) {
- final double xe = tosubpixx(x2);
- final double ye = tosubpixy(y2);
- curve.set(x0, y0, tosubpixx(x1), tosubpixy(y1), xe, ye);
+ public void quadTo(final double pix_x1, final double pix_y1,
+ final double pix_x2, final double pix_y2)
+ {
+ final double xe = tosubpixx(pix_x2);
+ final double ye = tosubpixy(pix_y2);
+ curve.set(x0, y0, tosubpixx(pix_x1), tosubpixy(pix_y1), xe, ye);
quadBreakIntoLinesAndAdd(x0, y0, curve, xe, ye);
x0 = xe;
y0 = ye;
@@ -713,9 +718,11 @@
@Override
public void closePath() {
- addLine(x0, y0, sx0, sy0);
- x0 = sx0;
- y0 = sy0;
+ if (x0 != sx0 || y0 != sy0) {
+ addLine(x0, y0, sx0, sy0);
+ x0 = sx0;
+ y0 = sy0;
+ }
}
@Override
--- old/src/java.desktop/share/classes/sun/java2d/marlin/DRendererContext.java 2017-08-28 16:48:27.705663214 +0200
+++ new/src/java.desktop/share/classes/sun/java2d/marlin/DRendererContext.java 2017-08-28 16:48:27.557663216 +0200
@@ -75,6 +75,12 @@
final MarlinCache cache;
// flag indicating the shape is stroked (1) or filled (0)
int stroking = 0;
+ // flag indicating to clip the shape
+ boolean doClip = false;
+ // flag indicating if the path is closed or not (in advance) to handle properly caps
+ boolean closedPath = false;
+ // clip rectangle (ymin, ymax, xmin, xmax):
+ final double[] clipRect = new double[4];
// Array caches:
/* clean int[] cache (zero-filled) = 5 refs */
@@ -119,7 +125,7 @@
nPQPathIterator = new NormalizingPathIterator.NearestPixelQuarter(double6);
// MarlinRenderingEngine.TransformingPathConsumer2D
- transformerPC2D = new DTransformingPathConsumer2D();
+ transformerPC2D = new DTransformingPathConsumer2D(this);
// Renderer:
cache = new MarlinCache(this);
@@ -141,7 +147,10 @@
}
stats.totalOffHeap = 0L;
}
- stroking = 0;
+ stroking = 0;
+ doClip = false;
+ closedPath = false;
+
// if context is maked as DIRTY:
if (dirty) {
// may happen if an exception if thrown in the pipeline processing:
--- old/src/java.desktop/share/classes/sun/java2d/marlin/DStroker.java 2017-08-28 16:48:28.105663208 +0200
+++ new/src/java.desktop/share/classes/sun/java2d/marlin/DStroker.java 2017-08-28 16:48:27.933663210 +0200
@@ -26,6 +26,7 @@
package sun.java2d.marlin;
import java.util.Arrays;
+import sun.java2d.marlin.DHelpers.PolyStack;
// TODO: some of the arithmetic here is too verbose and prone to hard to
// debug typos. We should consider making a small Point/Vector class that
@@ -71,7 +72,11 @@
// it to floating point, so that's why the divisions by 2^16 are there.
private static final double ROUND_JOIN_THRESHOLD = 1000.0d/65536.0d;
- private static final double C = 0.5522847498307933d;
+ // kappa = (4/3) * (SQRT(2) - 1)
+ private static final double C = (4.0d * (Math.sqrt(2.0d) - 1.0d) / 3.0d);
+
+ // SQRT(2)
+ private static final double SQRT_2 = Math.sqrt(2.0d);
private static final int MAX_N_CURVES = 11;
@@ -118,6 +123,20 @@
// dirty curve
final DCurve curve;
+ // Bounds of the drawing region, at pixel precision.
+ private double[] clipRect;
+
+ // the outcode of the current point
+ private int cOutCode = 0;
+
+ // the outcode of the starting point
+ private int sOutCode = 0;
+
+ // flag indicating if the path is opened (clipped)
+ private boolean opened = false;
+ // flag indicating if the starting point's cap is done
+ private boolean capStart = false;
+
/**
* Constructs a DStroker
.
* @param rdrCtx per-thread renderer context
@@ -125,7 +144,15 @@
DStroker(final DRendererContext rdrCtx) {
this.rdrCtx = rdrCtx;
- this.reverse = new PolyStack(rdrCtx);
+ this.reverse = (rdrCtx.stats != null) ?
+ new PolyStack(rdrCtx,
+ rdrCtx.stats.stat_str_polystack_types,
+ rdrCtx.stats.stat_str_polystack_curves,
+ rdrCtx.stats.hist_str_polystack_curves,
+ rdrCtx.stats.stat_array_str_polystack_curves,
+ rdrCtx.stats.stat_array_str_polystack_types)
+ : new PolyStack(rdrCtx);
+
this.curve = rdrCtx.curve;
}
@@ -141,13 +168,15 @@
* JOIN_MITER
, JOIN_ROUND
or
* JOIN_BEVEL
.
* @param miterLimit the desired miter limit
+ * @param scale scaling factor applied to clip boundaries
* @return this instance
*/
- DStroker init(DPathConsumer2D pc2d,
- double lineWidth,
- int capStyle,
- int joinStyle,
- double miterLimit)
+ DStroker init(final DPathConsumer2D pc2d,
+ final double lineWidth,
+ final int capStyle,
+ final int joinStyle,
+ final double miterLimit,
+ final double scale)
{
this.out = pc2d;
@@ -156,13 +185,41 @@
this.capStyle = capStyle;
this.joinStyle = joinStyle;
- double limit = miterLimit * lineWidth2;
+ final double limit = miterLimit * lineWidth2;
this.miterLimitSq = limit * limit;
this.prev = CLOSE;
rdrCtx.stroking = 1;
+ if (rdrCtx.doClip) {
+ // Adjust the clipping rectangle with the stroker margin (miter limit, width)
+ double rdrOffX = 0.0d, rdrOffY = 0.0d;
+ double margin = lineWidth2;
+
+ if (capStyle == CAP_SQUARE) {
+ margin *= SQRT_2;
+ }
+ if ((joinStyle == JOIN_MITER) && (margin < limit)) {
+ margin = limit;
+ }
+ if (scale != 1.0d) {
+ margin *= scale;
+ rdrOffX = scale * DRenderer.RDR_OFFSET_X;
+ rdrOffY = scale * DRenderer.RDR_OFFSET_Y;
+ }
+
+ // bounds as half-open intervals: minX <= x < maxX and minY <= y < maxY
+ // adjust clip rectangle (ymin, ymax, xmin, xmax):
+ final double[] _clipRect = rdrCtx.clipRect;
+ _clipRect[0] -= margin - rdrOffY;
+ _clipRect[1] += margin + rdrOffY;
+ _clipRect[2] -= margin - rdrOffX;
+ _clipRect[3] += margin + rdrOffX;
+ this.clipRect = _clipRect;
+ } else {
+ this.clipRect = null;
+ }
return this; // fluent API
}
@@ -173,6 +230,9 @@
void dispose() {
reverse.dispose();
+ opened = false;
+ capStart = false;
+
if (DO_CLEAN_DIRTY) {
// Force zero-fill dirty arrays:
Arrays.fill(offset0, 0.0d);
@@ -443,19 +503,62 @@
}
@Override
- public void moveTo(double x0, double y0) {
- if (prev == DRAWING_OP_TO) {
- finish();
+ public void moveTo(final double x0, final double y0) {
+ moveTo(x0, y0, cOutCode);
+ // update starting point:
+ this.sx0 = x0;
+ this.sy0 = y0;
+ this.sdx = 1.0d;
+ this.sdy = 0.0d;
+ this.opened = false;
+ this.capStart = false;
+
+ if (clipRect != null) {
+ final int outcode = DHelpers.outcode(x0, y0, clipRect);
+ this.cOutCode = outcode;
+ this.sOutCode = outcode;
+ }
+ }
+
+ private void moveTo(final double x0, final double y0,
+ final int outcode)
+ {
+ if (prev == MOVE_TO) {
+ this.cx0 = x0;
+ this.cy0 = y0;
+ } else {
+ if (prev == DRAWING_OP_TO) {
+ finish(outcode);
+ }
+ this.prev = MOVE_TO;
+ this.cx0 = x0;
+ this.cy0 = y0;
+ this.cdx = 1.0d;
+ this.cdy = 0.0d;
}
- this.sx0 = this.cx0 = x0;
- this.sy0 = this.cy0 = y0;
- this.cdx = this.sdx = 1.0d;
- this.cdy = this.sdy = 0.0d;
- this.prev = MOVE_TO;
}
@Override
- public void lineTo(double x1, double y1) {
+ public void lineTo(final double x1, final double y1) {
+ lineTo(x1, y1, false);
+ }
+
+ private void lineTo(final double x1, final double y1,
+ final boolean force)
+ {
+ final int outcode0 = this.cOutCode;
+ if (!force && clipRect != null) {
+ final int outcode1 = DHelpers.outcode(x1, y1, clipRect);
+ this.cOutCode = outcode1;
+
+ // basic rejection criteria
+ if ((outcode0 & outcode1) != 0) {
+ moveTo(x1, y1, outcode0);
+ opened = true;
+ return;
+ }
+ }
+
double dx = x1 - cx0;
double dy = y1 - cy0;
if (dx == 0.0d && dy == 0.0d) {
@@ -465,7 +568,7 @@
final double mx = offset0[0];
final double my = offset0[1];
- drawJoin(cdx, cdy, cx0, cy0, dx, dy, cmx, cmy, mx, my);
+ drawJoin(cdx, cdy, cx0, cy0, dx, dy, cmx, cmy, mx, my, outcode0);
emitLineTo(cx0 + mx, cy0 + my);
emitLineTo( x1 + mx, y1 + my);
@@ -473,43 +576,64 @@
emitLineToRev(cx0 - mx, cy0 - my);
emitLineToRev( x1 - mx, y1 - my);
- this.cmx = mx;
- this.cmy = my;
- this.cdx = dx;
- this.cdy = dy;
+ this.prev = DRAWING_OP_TO;
this.cx0 = x1;
this.cy0 = y1;
- this.prev = DRAWING_OP_TO;
+ this.cdx = dx;
+ this.cdy = dy;
+ this.cmx = mx;
+ this.cmy = my;
}
@Override
public void closePath() {
- if (prev != DRAWING_OP_TO) {
+ // distinguish empty path at all vs opened path ?
+ if (prev != DRAWING_OP_TO && !opened) {
if (prev == CLOSE) {
return;
}
emitMoveTo(cx0, cy0 - lineWidth2);
- this.cmx = this.smx = 0.0d;
- this.cmy = this.smy = -lineWidth2;
- this.cdx = this.sdx = 1.0d;
- this.cdy = this.sdy = 0.0d;
- finish();
+
+ this.sdx = 1.0d;
+ this.sdy = 0.0d;
+ this.cdx = 1.0d;
+ this.cdy = 0.0d;
+
+ this.smx = 0.0d;
+ this.smy = -lineWidth2;
+ this.cmx = 0.0d;
+ this.cmy = -lineWidth2;
+
+ finish(cOutCode);
return;
}
- if (cx0 != sx0 || cy0 != sy0) {
- lineTo(sx0, sy0);
- }
+ if (sOutCode == 0) {
+ if (cx0 != sx0 || cy0 != sy0) {
+ lineTo(sx0, sy0, true);
+ }
- drawJoin(cdx, cdy, cx0, cy0, sdx, sdy, cmx, cmy, smx, smy);
+ drawJoin(cdx, cdy, cx0, cy0, sdx, sdy, cmx, cmy, smx, smy, cOutCode);
- emitLineTo(sx0 + smx, sy0 + smy);
+ emitLineTo(sx0 + smx, sy0 + smy);
- emitMoveTo(sx0 - smx, sy0 - smy);
+ if (opened) {
+ emitLineTo(sx0 - smx, sy0 - smy);
+ } else {
+ emitMoveTo(sx0 - smx, sy0 - smy);
+ }
+ }
+ // Ignore caps like finish(false)
emitReverse();
this.prev = CLOSE;
- emitClose();
+
+ if (opened) {
+ // do not emit close
+ opened = false;
+ } else {
+ emitClose();
+ }
}
private void emitReverse() {
@@ -519,7 +643,7 @@
@Override
public void pathDone() {
if (prev == DRAWING_OP_TO) {
- finish();
+ finish(cOutCode);
}
out.pathDone();
@@ -532,23 +656,39 @@
dispose();
}
- private void finish() {
- if (capStyle == CAP_ROUND) {
- drawRoundCap(cx0, cy0, cmx, cmy);
- } else if (capStyle == CAP_SQUARE) {
- emitLineTo(cx0 - cmy + cmx, cy0 + cmx + cmy);
- emitLineTo(cx0 - cmy - cmx, cy0 + cmx - cmy);
- }
+ private void finish(final int outcode) {
+ // Problem: impossible to guess if the path will be closed in advance
+ // i.e. if caps must be drawn or not ?
+ // Solution: use the ClosedPathDetector before Stroker to determine
+ // if the path is a closed path or not
+ if (!rdrCtx.closedPath) {
+ if (outcode == 0) {
+ // current point = end's cap:
+ if (capStyle == CAP_ROUND) {
+ drawRoundCap(cx0, cy0, cmx, cmy);
+ } else if (capStyle == CAP_SQUARE) {
+ emitLineTo(cx0 - cmy + cmx, cy0 + cmx + cmy);
+ emitLineTo(cx0 - cmy - cmx, cy0 + cmx - cmy);
+ }
+ }
+ emitReverse();
- emitReverse();
+ if (!capStart) {
+ capStart = true;
- if (capStyle == CAP_ROUND) {
- drawRoundCap(sx0, sy0, -smx, -smy);
- } else if (capStyle == CAP_SQUARE) {
- emitLineTo(sx0 + smy - smx, sy0 - smx - smy);
- emitLineTo(sx0 + smy + smx, sy0 - smx + smy);
+ if (sOutCode == 0) {
+ // starting point = initial cap:
+ if (capStyle == CAP_ROUND) {
+ drawRoundCap(sx0, sy0, -smx, -smy);
+ } else if (capStyle == CAP_SQUARE) {
+ emitLineTo(sx0 + smy - smx, sy0 - smx - smy);
+ emitLineTo(sx0 + smy + smx, sy0 - smx + smy);
+ }
+ }
+ }
+ } else {
+ emitReverse();
}
-
emitClose();
}
@@ -620,23 +760,28 @@
double x0, double y0,
double dx, double dy,
double omx, double omy,
- double mx, double my)
+ double mx, double my,
+ final int outcode)
{
if (prev != DRAWING_OP_TO) {
emitMoveTo(x0 + mx, y0 + my);
- this.sdx = dx;
- this.sdy = dy;
- this.smx = mx;
- this.smy = my;
+ if (!opened) {
+ this.sdx = dx;
+ this.sdy = dy;
+ this.smx = mx;
+ this.smy = my;
+ }
} else {
- boolean cw = isCW(pdx, pdy, dx, dy);
- if (joinStyle == JOIN_MITER) {
- drawMiter(pdx, pdy, x0, y0, dx, dy, omx, omy, mx, my, cw);
- } else if (joinStyle == JOIN_ROUND) {
- drawRoundJoin(x0, y0,
- omx, omy,
- mx, my, cw,
- ROUND_JOIN_THRESHOLD);
+ final boolean cw = isCW(pdx, pdy, dx, dy);
+ if (outcode == 0) {
+ if (joinStyle == JOIN_MITER) {
+ drawMiter(pdx, pdy, x0, y0, dx, dy, omx, omy, mx, my, cw);
+ } else if (joinStyle == JOIN_ROUND) {
+ drawRoundJoin(x0, y0,
+ omx, omy,
+ mx, my, cw,
+ ROUND_JOIN_THRESHOLD);
+ }
}
emitLineTo(x0, y0, !cw);
}
@@ -941,10 +1086,29 @@
return ret;
}
- @Override public void curveTo(double x1, double y1,
- double x2, double y2,
- double x3, double y3)
- {
+ @Override
+ public void curveTo(final double x1, final double y1,
+ final double x2, final double y2,
+ final double x3, final double y3)
+ {
+ final int outcode0 = this.cOutCode;
+ if (clipRect != null) {
+ final int outcode3 = DHelpers.outcode(x3, y3, clipRect);
+ this.cOutCode = outcode3;
+
+ if (outcode3 != 0) {
+ final int outcode1 = DHelpers.outcode(x1, y1, clipRect);
+ final int outcode2 = DHelpers.outcode(x2, y2, clipRect);
+
+ // basic rejection criteria
+ if ((outcode0 & outcode1 & outcode2 & outcode3) != 0) {
+ moveTo(x3, y3, outcode0);
+ opened = true;
+ return;
+ }
+ }
+ }
+
final double[] mid = middle;
mid[0] = cx0; mid[1] = cy0;
@@ -953,7 +1117,7 @@
mid[6] = x3; mid[7] = y3;
// need these so we can update the state at the end of this method
- final double xf = mid[6], yf = mid[7];
+ final double xf = x3, yf = y3;
double dxs = mid[2] - mid[0];
double dys = mid[3] - mid[1];
double dxf = mid[6] - mid[4];
@@ -979,6 +1143,10 @@
}
if (dxs == 0.0d && dys == 0.0d) {
// this happens if the "curve" is just a point
+ // fix outcode0 for lineTo() call:
+ if (clipRect != null) {
+ this.cOutCode = outcode0;
+ }
lineTo(mid[0], mid[1]);
return;
}
@@ -997,7 +1165,7 @@
}
computeOffset(dxs, dys, lineWidth2, offset0);
- drawJoin(cdx, cdy, cx0, cy0, dxs, dys, cmx, cmy, offset0[0], offset0[1]);
+ drawJoin(cdx, cdy, cx0, cy0, dxs, dys, cmx, cmy, offset0[0], offset0[1], outcode0);
final int nSplits = findSubdivPoints(curve, mid, subdivTs, 8, lineWidth2);
@@ -1032,16 +1200,36 @@
emitLineToRev(r[kind - 2], r[kind - 1]);
}
- this.cmx = (l[kind - 2] - r[kind - 2]) / 2.0d;
- this.cmy = (l[kind - 1] - r[kind - 1]) / 2.0d;
- this.cdx = dxf;
- this.cdy = dyf;
+ this.prev = DRAWING_OP_TO;
this.cx0 = xf;
this.cy0 = yf;
- this.prev = DRAWING_OP_TO;
+ this.cdx = dxf;
+ this.cdy = dyf;
+ this.cmx = (l[kind - 2] - r[kind - 2]) / 2.0d;
+ this.cmy = (l[kind - 1] - r[kind - 1]) / 2.0d;
}
- @Override public void quadTo(double x1, double y1, double x2, double y2) {
+ @Override
+ public void quadTo(final double x1, final double y1,
+ final double x2, final double y2)
+ {
+ final int outcode0 = this.cOutCode;
+ if (clipRect != null) {
+ final int outcode2 = DHelpers.outcode(x2, y2, clipRect);
+ this.cOutCode = outcode2;
+
+ if (outcode2 != 0) {
+ final int outcode1 = DHelpers.outcode(x1, y1, clipRect);
+
+ // basic rejection criteria
+ if ((outcode0 & outcode1 & outcode2) != 0) {
+ moveTo(x2, y2, outcode0);
+ opened = true;
+ return;
+ }
+ }
+ }
+
final double[] mid = middle;
mid[0] = cx0; mid[1] = cy0;
@@ -1049,7 +1237,7 @@
mid[4] = x2; mid[5] = y2;
// need these so we can update the state at the end of this method
- final double xf = mid[4], yf = mid[5];
+ final double xf = x2, yf = y2;
double dxs = mid[2] - mid[0];
double dys = mid[3] - mid[1];
double dxf = mid[4] - mid[2];
@@ -1060,6 +1248,10 @@
}
if (dxs == 0.0d && dys == 0.0d) {
// this happens if the "curve" is just a point
+ // fix outcode0 for lineTo() call:
+ if (clipRect != null) {
+ this.cOutCode = outcode0;
+ }
lineTo(mid[0], mid[1]);
return;
}
@@ -1077,7 +1269,7 @@
}
computeOffset(dxs, dys, lineWidth2, offset0);
- drawJoin(cdx, cdy, cx0, cy0, dxs, dys, cmx, cmy, offset0[0], offset0[1]);
+ drawJoin(cdx, cdy, cx0, cy0, dxs, dys, cmx, cmy, offset0[0], offset0[1], outcode0);
int nSplits = findSubdivPoints(curve, mid, subdivTs, 6, lineWidth2);
@@ -1112,214 +1304,16 @@
emitLineToRev(r[kind - 2], r[kind - 1]);
}
- this.cmx = (l[kind - 2] - r[kind - 2]) / 2.0d;
- this.cmy = (l[kind - 1] - r[kind - 1]) / 2.0d;
- this.cdx = dxf;
- this.cdy = dyf;
+ this.prev = DRAWING_OP_TO;
this.cx0 = xf;
this.cy0 = yf;
- this.prev = DRAWING_OP_TO;
+ this.cdx = dxf;
+ this.cdy = dyf;
+ this.cmx = (l[kind - 2] - r[kind - 2]) / 2.0d;
+ this.cmy = (l[kind - 1] - r[kind - 1]) / 2.0d;
}
@Override public long getNativeConsumer() {
throw new InternalError("Stroker doesn't use a native consumer");
}
-
- // a stack of polynomial curves where each curve shares endpoints with
- // adjacent ones.
- static final class PolyStack {
- private static final byte TYPE_LINETO = (byte) 0;
- private static final byte TYPE_QUADTO = (byte) 1;
- private static final byte TYPE_CUBICTO = (byte) 2;
-
- // curves capacity = edges count (8192) = edges x 2 (coords)
- private static final int INITIAL_CURVES_COUNT = INITIAL_EDGES_COUNT << 1;
-
- // types capacity = edges count (4096)
- private static final int INITIAL_TYPES_COUNT = INITIAL_EDGES_COUNT;
-
- double[] curves;
- int end;
- byte[] curveTypes;
- int numCurves;
-
- // per-thread renderer context
- final DRendererContext rdrCtx;
-
- // curves ref (dirty)
- final DoubleArrayCache.Reference curves_ref;
- // curveTypes ref (dirty)
- final ByteArrayCache.Reference curveTypes_ref;
-
- // used marks (stats only)
- int curveTypesUseMark;
- int curvesUseMark;
-
- /**
- * Constructor
- * @param rdrCtx per-thread renderer context
- */
- PolyStack(final DRendererContext rdrCtx) {
- this.rdrCtx = rdrCtx;
-
- curves_ref = rdrCtx.newDirtyDoubleArrayRef(INITIAL_CURVES_COUNT); // 32K
- curves = curves_ref.initial;
-
- curveTypes_ref = rdrCtx.newDirtyByteArrayRef(INITIAL_TYPES_COUNT); // 4K
- curveTypes = curveTypes_ref.initial;
- numCurves = 0;
- end = 0;
-
- if (DO_STATS) {
- curveTypesUseMark = 0;
- curvesUseMark = 0;
- }
- }
-
- /**
- * Disposes this PolyStack:
- * clean up before reusing this instance
- */
- void dispose() {
- end = 0;
- numCurves = 0;
-
- if (DO_STATS) {
- rdrCtx.stats.stat_rdr_poly_stack_types.add(curveTypesUseMark);
- rdrCtx.stats.stat_rdr_poly_stack_curves.add(curvesUseMark);
- rdrCtx.stats.hist_rdr_poly_stack_curves.add(curvesUseMark);
-
- // reset marks
- curveTypesUseMark = 0;
- curvesUseMark = 0;
- }
-
- // Return arrays:
- // curves and curveTypes are kept dirty
- curves = curves_ref.putArray(curves);
- curveTypes = curveTypes_ref.putArray(curveTypes);
- }
-
- private void ensureSpace(final int n) {
- // use substraction to avoid integer overflow:
- if (curves.length - end < n) {
- if (DO_STATS) {
- rdrCtx.stats.stat_array_stroker_polystack_curves
- .add(end + n);
- }
- curves = curves_ref.widenArray(curves, end, end + n);
- }
- if (curveTypes.length <= numCurves) {
- if (DO_STATS) {
- rdrCtx.stats.stat_array_stroker_polystack_curveTypes
- .add(numCurves + 1);
- }
- curveTypes = curveTypes_ref.widenArray(curveTypes,
- numCurves,
- numCurves + 1);
- }
- }
-
- void pushCubic(double x0, double y0,
- double x1, double y1,
- double x2, double y2)
- {
- ensureSpace(6);
- curveTypes[numCurves++] = TYPE_CUBICTO;
- // we reverse the coordinate order to make popping easier
- final double[] _curves = curves;
- int e = end;
- _curves[e++] = x2; _curves[e++] = y2;
- _curves[e++] = x1; _curves[e++] = y1;
- _curves[e++] = x0; _curves[e++] = y0;
- end = e;
- }
-
- void pushQuad(double x0, double y0,
- double x1, double y1)
- {
- ensureSpace(4);
- curveTypes[numCurves++] = TYPE_QUADTO;
- final double[] _curves = curves;
- int e = end;
- _curves[e++] = x1; _curves[e++] = y1;
- _curves[e++] = x0; _curves[e++] = y0;
- end = e;
- }
-
- void pushLine(double x, double y) {
- ensureSpace(2);
- curveTypes[numCurves++] = TYPE_LINETO;
- curves[end++] = x; curves[end++] = y;
- }
-
- void popAll(DPathConsumer2D io) {
- if (DO_STATS) {
- // update used marks:
- if (numCurves > curveTypesUseMark) {
- curveTypesUseMark = numCurves;
- }
- if (end > curvesUseMark) {
- curvesUseMark = end;
- }
- }
- final byte[] _curveTypes = curveTypes;
- final double[] _curves = curves;
- int nc = numCurves;
- int e = end;
-
- while (nc != 0) {
- switch(_curveTypes[--nc]) {
- case TYPE_LINETO:
- e -= 2;
- io.lineTo(_curves[e], _curves[e+1]);
- continue;
- case TYPE_QUADTO:
- e -= 4;
- io.quadTo(_curves[e+0], _curves[e+1],
- _curves[e+2], _curves[e+3]);
- continue;
- case TYPE_CUBICTO:
- e -= 6;
- io.curveTo(_curves[e+0], _curves[e+1],
- _curves[e+2], _curves[e+3],
- _curves[e+4], _curves[e+5]);
- continue;
- default:
- }
- }
- numCurves = 0;
- end = 0;
- }
-
- @Override
- public String toString() {
- String ret = "";
- int nc = numCurves;
- int last = end;
- int len;
- while (nc != 0) {
- switch(curveTypes[--nc]) {
- case TYPE_LINETO:
- len = 2;
- ret += "line: ";
- break;
- case TYPE_QUADTO:
- len = 4;
- ret += "quad: ";
- break;
- case TYPE_CUBICTO:
- len = 6;
- ret += "cubic: ";
- break;
- default:
- len = 0;
- }
- last -= len;
- ret += Arrays.toString(Arrays.copyOfRange(curves, last, last+len))
- + "\n";
- }
- return ret;
- }
- }
}
--- old/src/java.desktop/share/classes/sun/java2d/marlin/DTransformingPathConsumer2D.java 2017-08-28 16:48:28.593663201 +0200
+++ new/src/java.desktop/share/classes/sun/java2d/marlin/DTransformingPathConsumer2D.java 2017-08-28 16:48:28.457663203 +0200
@@ -27,24 +27,58 @@
import java.awt.geom.AffineTransform;
import java.awt.geom.Path2D;
+import sun.java2d.marlin.DHelpers.PolyStack;
final class DTransformingPathConsumer2D {
- DTransformingPathConsumer2D() {
- // used by DRendererContext
- }
+ private final DRendererContext rdrCtx;
+
+ // recycled ClosedPathDetector instance from detectClosedPath()
+ private final ClosedPathDetector cpDetector;
// recycled DPathConsumer2D instance from wrapPath2d()
private final Path2DWrapper wp_Path2DWrapper = new Path2DWrapper();
+ // recycled DPathConsumer2D instances from deltaTransformConsumer()
+ private final DeltaScaleFilter dt_DeltaScaleFilter = new DeltaScaleFilter();
+ private final DeltaTransformFilter dt_DeltaTransformFilter = new DeltaTransformFilter();
+
+ // recycled DPathConsumer2D instances from inverseDeltaTransformConsumer()
+ private final DeltaScaleFilter iv_DeltaScaleFilter = new DeltaScaleFilter();
+ private final DeltaTransformFilter iv_DeltaTransformFilter = new DeltaTransformFilter();
+
+ // recycled PathTracer instances from tracer...() methods
+ private final PathTracer tracerInput = new PathTracer("[Input]");
+ private final PathTracer tracerCPDetector = new PathTracer("ClosedPathDetector");
+ private final PathTracer tracerStroker = new PathTracer("Stroker");
+
+ DTransformingPathConsumer2D(final DRendererContext rdrCtx) {
+ // used by RendererContext
+ this.rdrCtx = rdrCtx;
+ this.cpDetector = new ClosedPathDetector(rdrCtx);
+ }
+
DPathConsumer2D wrapPath2d(Path2D.Double p2d)
{
return wp_Path2DWrapper.init(p2d);
}
- // recycled DPathConsumer2D instances from deltaTransformConsumer()
- private final DeltaScaleFilter dt_DeltaScaleFilter = new DeltaScaleFilter();
- private final DeltaTransformFilter dt_DeltaTransformFilter = new DeltaTransformFilter();
+ DPathConsumer2D traceInput(DPathConsumer2D out) {
+ return tracerInput.init(out);
+ }
+
+ DPathConsumer2D traceClosedPathDetector(DPathConsumer2D out) {
+ return tracerCPDetector.init(out);
+ }
+
+ DPathConsumer2D traceStroker(DPathConsumer2D out) {
+ return tracerStroker.init(out);
+ }
+
+ DPathConsumer2D detectClosedPath(DPathConsumer2D out)
+ {
+ return cpDetector.init(out);
+ }
DPathConsumer2D deltaTransformConsumer(DPathConsumer2D out,
AffineTransform at)
@@ -61,16 +95,89 @@
if (mxx == 1.0d && myy == 1.0d) {
return out;
} else {
+ // Scale only
+ if (rdrCtx.doClip) {
+ // adjust clip rectangle (ymin, ymax, xmin, xmax):
+ adjustClipScale(rdrCtx.clipRect, mxx, myy);
+ }
return dt_DeltaScaleFilter.init(out, mxx, myy);
}
} else {
+ if (rdrCtx.doClip) {
+ // adjust clip rectangle (ymin, ymax, xmin, xmax):
+ adjustClipInverseDelta(rdrCtx.clipRect, mxx, mxy, myx, myy);
+ }
return dt_DeltaTransformFilter.init(out, mxx, mxy, myx, myy);
}
}
- // recycled DPathConsumer2D instances from inverseDeltaTransformConsumer()
- private final DeltaScaleFilter iv_DeltaScaleFilter = new DeltaScaleFilter();
- private final DeltaTransformFilter iv_DeltaTransformFilter = new DeltaTransformFilter();
+ private static void adjustClipOffset(final double[] clipRect) {
+ clipRect[0] += Renderer.RDR_OFFSET_Y;
+ clipRect[1] += Renderer.RDR_OFFSET_Y;
+ clipRect[2] += Renderer.RDR_OFFSET_X;
+ clipRect[3] += Renderer.RDR_OFFSET_X;
+ }
+
+ private static void adjustClipScale(final double[] clipRect,
+ final double mxx, final double myy)
+ {
+ adjustClipOffset(clipRect);
+
+ // Adjust the clipping rectangle (iv_DeltaScaleFilter):
+ clipRect[0] /= myy;
+ clipRect[1] /= myy;
+ clipRect[2] /= mxx;
+ clipRect[3] /= mxx;
+ }
+
+ private static void adjustClipInverseDelta(final double[] clipRect,
+ final double mxx, final double mxy,
+ final double myx, final double myy)
+ {
+ adjustClipOffset(clipRect);
+
+ // Adjust the clipping rectangle (iv_DeltaTransformFilter):
+ final double det = mxx * myy - mxy * myx;
+ final double imxx = myy / det;
+ final double imxy = -mxy / det;
+ final double imyx = -myx / det;
+ final double imyy = mxx / det;
+
+ double xmin, xmax, ymin, ymax;
+ double x, y;
+ // xmin, ymin:
+ x = clipRect[2] * imxx + clipRect[0] * imxy;
+ y = clipRect[2] * imyx + clipRect[0] * imyy;
+
+ xmin = xmax = x;
+ ymin = ymax = y;
+
+ // xmax, ymin:
+ x = clipRect[3] * imxx + clipRect[0] * imxy;
+ y = clipRect[3] * imyx + clipRect[0] * imyy;
+
+ if (x < xmin) { xmin = x; } else if (x > xmax) { xmax = x; }
+ if (y < ymin) { ymin = y; } else if (y > ymax) { ymax = y; }
+
+ // xmin, ymax:
+ x = clipRect[2] * imxx + clipRect[1] * imxy;
+ y = clipRect[2] * imyx + clipRect[1] * imyy;
+
+ if (x < xmin) { xmin = x; } else if (x > xmax) { xmax = x; }
+ if (y < ymin) { ymin = y; } else if (y > ymax) { ymax = y; }
+
+ // xmax, ymax:
+ x = clipRect[3] * imxx + clipRect[1] * imxy;
+ y = clipRect[3] * imyx + clipRect[1] * imyy;
+
+ if (x < xmin) { xmin = x; } else if (x > xmax) { xmax = x; }
+ if (y < ymin) { ymin = y; } else if (y > ymax) { ymax = y; }
+
+ clipRect[0] = ymin;
+ clipRect[1] = ymax;
+ clipRect[2] = xmin;
+ clipRect[3] = xmax;
+ }
DPathConsumer2D inverseDeltaTransformConsumer(DPathConsumer2D out,
AffineTransform at)
@@ -90,7 +197,7 @@
return iv_DeltaScaleFilter.init(out, 1.0d/mxx, 1.0d/myy);
}
} else {
- double det = mxx * myy - mxy * myx;
+ final double det = mxx * myy - mxy * myx;
return iv_DeltaTransformFilter.init(out,
myy / det,
-mxy / det,
@@ -99,7 +206,6 @@
}
}
-
static final class DeltaScaleFilter implements DPathConsumer2D {
private DPathConsumer2D out;
private double sx, sy;
@@ -270,6 +376,154 @@
}
@Override
+ public long getNativeConsumer() {
+ throw new InternalError("Not using a native peer");
+ }
+ }
+
+ static final class ClosedPathDetector implements DPathConsumer2D {
+
+ private final DRendererContext rdrCtx;
+ private final PolyStack stack;
+
+ private DPathConsumer2D out;
+
+ ClosedPathDetector(final DRendererContext rdrCtx) {
+ this.rdrCtx = rdrCtx;
+ this.stack = (rdrCtx.stats != null) ?
+ new PolyStack(rdrCtx,
+ rdrCtx.stats.stat_cpd_polystack_types,
+ rdrCtx.stats.stat_cpd_polystack_curves,
+ rdrCtx.stats.hist_cpd_polystack_curves,
+ rdrCtx.stats.stat_array_cpd_polystack_curves,
+ rdrCtx.stats.stat_array_cpd_polystack_types)
+ : new PolyStack(rdrCtx);
+ }
+
+ ClosedPathDetector init(DPathConsumer2D out) {
+ this.out = out;
+ return this; // fluent API
+ }
+
+ /**
+ * Disposes this instance:
+ * clean up before reusing this instance
+ */
+ void dispose() {
+ stack.dispose();
+ }
+
+ @Override
+ public void pathDone() {
+ // previous path is not closed:
+ finish(false);
+ out.pathDone();
+
+ // TODO: fix possible leak if exception happened
+ // Dispose this instance:
+ dispose();
+ }
+
+ @Override
+ public void closePath() {
+ // path is closed
+ finish(true);
+ out.closePath();
+ }
+
+ @Override
+ public void moveTo(double x0, double y0) {
+ // previous path is not closed:
+ finish(false);
+ out.moveTo(x0, y0);
+ }
+
+ private void finish(final boolean closed) {
+ rdrCtx.closedPath = closed;
+ stack.pullAll(out);
+ }
+
+ @Override
+ public void lineTo(double x1, double y1) {
+ stack.pushLine(x1, y1);
+ }
+
+ @Override
+ public void curveTo(double x3, double y3,
+ double x2, double y2,
+ double x1, double y1)
+ {
+ stack.pushCubic(x1, y1, x2, y2, x3, y3);
+ }
+
+ @Override
+ public void quadTo(double x2, double y2, double x1, double y1) {
+ stack.pushQuad(x1, y1, x2, y2);
+ }
+
+ @Override
+ public long getNativeConsumer() {
+ throw new InternalError("Not using a native peer");
+ }
+ }
+
+ static final class PathTracer implements DPathConsumer2D {
+ private final String prefix;
+ private DPathConsumer2D out;
+
+ PathTracer(String name) {
+ this.prefix = name + ": ";
+ }
+
+ PathTracer init(DPathConsumer2D out) {
+ this.out = out;
+ return this; // fluent API
+ }
+
+ @Override
+ public void moveTo(double x0, double y0) {
+ log("moveTo (" + x0 + ", " + y0 + ')');
+ out.moveTo(x0, y0);
+ }
+
+ @Override
+ public void lineTo(double x1, double y1) {
+ log("lineTo (" + x1 + ", " + y1 + ')');
+ out.lineTo(x1, y1);
+ }
+
+ @Override
+ public void curveTo(double x1, double y1,
+ double x2, double y2,
+ double x3, double y3)
+ {
+ log("curveTo P1(" + x1 + ", " + y1 + ") P2(" + x2 + ", " + y2 + ") P3(" + x3 + ", " + y3 + ')');
+ out.curveTo(x1, y1, x2, y2, x3, y3);
+ }
+
+ @Override
+ public void quadTo(double x1, double y1, double x2, double y2) {
+ log("quadTo P1(" + x1 + ", " + y1 + ") P2(" + x2 + ", " + y2 + ')');
+ out.quadTo(x1, y1, x2, y2);
+ }
+
+ @Override
+ public void closePath() {
+ log("closePath");
+ out.closePath();
+ }
+
+ @Override
+ public void pathDone() {
+ log("pathDone");
+ out.pathDone();
+ }
+
+ private void log(final String message) {
+ System.out.println(prefix + message);
+ }
+
+ @Override
public long getNativeConsumer() {
throw new InternalError("Not using a native peer");
}
--- old/src/java.desktop/share/classes/sun/java2d/marlin/Dasher.java 2017-08-28 16:48:29.093663194 +0200
+++ new/src/java.desktop/share/classes/sun/java2d/marlin/Dasher.java 2017-08-28 16:48:28.945663196 +0200
@@ -138,7 +138,7 @@
dashOn = !dashOn;
}
}
- } else if (phase > 0) {
+ } else if (phase > 0.0f) {
if (cycles >= MAX_CYCLES) {
phase = 0.0f;
} else {
@@ -158,12 +158,13 @@
this.dash = dash;
this.dashLen = dashLen;
- this.startPhase = this.phase = phase;
+ this.phase = phase;
+ this.startPhase = phase;
this.startDashOn = dashOn;
this.startIdx = sidx;
this.starting = true;
- needsMoveTo = false;
- firstSegidx = 0;
+ this.needsMoveTo = false;
+ this.firstSegidx = 0;
this.recycleDashes = recycleDashes;
@@ -202,8 +203,8 @@
}
@Override
- public void moveTo(float x0, float y0) {
- if (firstSegidx > 0) {
+ public void moveTo(final float x0, final float y0) {
+ if (firstSegidx != 0) {
out.moveTo(sx, sy);
emitFirstSegments();
}
@@ -211,8 +212,10 @@
this.idx = startIdx;
this.dashOn = this.startDashOn;
this.phase = this.startPhase;
- this.sx = this.x0 = x0;
- this.sy = this.y0 = y0;
+ this.sx = x0;
+ this.sy = y0;
+ this.x0 = x0;
+ this.y0 = y0;
this.starting = true;
}
@@ -237,7 +240,7 @@
private void emitFirstSegments() {
final float[] fSegBuf = firstSegmentsBuffer;
- for (int i = 0; i < firstSegidx; ) {
+ for (int i = 0, len = firstSegidx; i < len; ) {
int type = (int)fSegBuf[i];
emitSeg(fSegBuf, i + 1, type);
i += (type - 1);
@@ -252,48 +255,59 @@
private int firstSegidx;
// precondition: pts must be in relative coordinates (relative to x0,y0)
- private void goTo(float[] pts, int off, final int type) {
- float x = pts[off + type - 4];
- float y = pts[off + type - 3];
- if (dashOn) {
+ private void goTo(final float[] pts, final int off, final int type,
+ final boolean on)
+ {
+ final int index = off + type;
+ final float x = pts[index - 4];
+ final float y = pts[index - 3];
+
+ if (on) {
if (starting) {
- int len = type - 1; // - 2 + 1
- int segIdx = firstSegidx;
- float[] buf = firstSegmentsBuffer;
- if (segIdx + len > buf.length) {
- if (DO_STATS) {
- rdrCtx.stats.stat_array_dasher_firstSegmentsBuffer
- .add(segIdx + len);
- }
- firstSegmentsBuffer = buf
- = firstSegmentsBuffer_ref.widenArray(buf, segIdx,
- segIdx + len);
- }
- buf[segIdx++] = type;
- len--;
- // small arraycopy (2, 4 or 6) but with offset:
- System.arraycopy(pts, off, buf, segIdx, len);
- segIdx += len;
- firstSegidx = segIdx;
+ goTo_starting(pts, off, type);
} else {
if (needsMoveTo) {
- out.moveTo(x0, y0);
needsMoveTo = false;
+ out.moveTo(x0, y0);
}
emitSeg(pts, off, type);
}
} else {
- starting = false;
+ if (starting) {
+ // low probability test (hotspot)
+ starting = false;
+ }
needsMoveTo = true;
}
this.x0 = x;
this.y0 = y;
}
+ private void goTo_starting(final float[] pts, final int off, final int type) {
+ int len = type - 1; // - 2 + 1
+ int segIdx = firstSegidx;
+ float[] buf = firstSegmentsBuffer;
+
+ if (segIdx + len > buf.length) {
+ if (DO_STATS) {
+ rdrCtx.stats.stat_array_dasher_firstSegmentsBuffer
+ .add(segIdx + len);
+ }
+ firstSegmentsBuffer = buf
+ = firstSegmentsBuffer_ref.widenArray(buf, segIdx,
+ segIdx + len);
+ }
+ buf[segIdx++] = type;
+ len--;
+ // small arraycopy (2, 4 or 6) but with offset:
+ System.arraycopy(pts, off, buf, segIdx, len);
+ firstSegidx = segIdx + len;
+ }
+
@Override
- public void lineTo(float x1, float y1) {
- float dx = x1 - x0;
- float dy = y1 - y0;
+ public void lineTo(final float x1, final float y1) {
+ final float dx = x1 - x0;
+ final float dy = y1 - y0;
float len = dx*dx + dy*dy;
if (len == 0.0f) {
@@ -308,48 +322,61 @@
final float[] _curCurvepts = curCurvepts;
final float[] _dash = dash;
+ final int _dashLen = this.dashLen;
+
+ int _idx = idx;
+ boolean _dashOn = dashOn;
+ float _phase = phase;
float leftInThisDashSegment;
- float dashdx, dashdy, p;
+ float d, dashdx, dashdy, p;
while (true) {
- leftInThisDashSegment = _dash[idx] - phase;
+ d = _dash[_idx];
+ leftInThisDashSegment = d - _phase;
if (len <= leftInThisDashSegment) {
_curCurvepts[0] = x1;
_curCurvepts[1] = y1;
- goTo(_curCurvepts, 0, 4);
+
+ goTo(_curCurvepts, 0, 4, _dashOn);
// Advance phase within current dash segment
- phase += len;
+ _phase += len;
+
// TODO: compare float values using epsilon:
if (len == leftInThisDashSegment) {
- phase = 0.0f;
- idx = (idx + 1) % dashLen;
- dashOn = !dashOn;
+ _phase = 0.0f;
+ _idx = (_idx + 1) % _dashLen;
+ _dashOn = !_dashOn;
}
+
+ // Save local state:
+ idx = _idx;
+ dashOn = _dashOn;
+ phase = _phase;
return;
}
- dashdx = _dash[idx] * cx;
- dashdy = _dash[idx] * cy;
+ dashdx = d * cx;
+ dashdy = d * cy;
- if (phase == 0.0f) {
+ if (_phase == 0.0f) {
_curCurvepts[0] = x0 + dashdx;
_curCurvepts[1] = y0 + dashdy;
} else {
- p = leftInThisDashSegment / _dash[idx];
+ p = leftInThisDashSegment / d;
_curCurvepts[0] = x0 + p * dashdx;
_curCurvepts[1] = y0 + p * dashdy;
}
- goTo(_curCurvepts, 0, 4);
+ goTo(_curCurvepts, 0, 4, _dashOn);
len -= leftInThisDashSegment;
// Advance to next dash segment
- idx = (idx + 1) % dashLen;
- dashOn = !dashOn;
- phase = 0.0f;
+ _idx = (_idx + 1) % _dashLen;
+ _dashOn = !_dashOn;
+ _phase = 0.0f;
}
}
@@ -362,39 +389,55 @@
if (pointCurve(curCurvepts, type)) {
return;
}
- li.initializeIterationOnCurve(curCurvepts, type);
+ final LengthIterator _li = li;
+ final float[] _curCurvepts = curCurvepts;
+ final float[] _dash = dash;
+ final int _dashLen = this.dashLen;
+
+ _li.initializeIterationOnCurve(_curCurvepts, type);
+
+ int _idx = idx;
+ boolean _dashOn = dashOn;
+ float _phase = phase;
// initially the current curve is at curCurvepts[0...type]
int curCurveoff = 0;
float lastSplitT = 0.0f;
float t;
- float leftInThisDashSegment = dash[idx] - phase;
+ float leftInThisDashSegment = _dash[_idx] - _phase;
- while ((t = li.next(leftInThisDashSegment)) < 1.0f) {
+ while ((t = _li.next(leftInThisDashSegment)) < 1.0f) {
if (t != 0.0f) {
Helpers.subdivideAt((t - lastSplitT) / (1.0f - lastSplitT),
- curCurvepts, curCurveoff,
- curCurvepts, 0,
- curCurvepts, type, type);
+ _curCurvepts, curCurveoff,
+ _curCurvepts, 0,
+ _curCurvepts, type, type);
lastSplitT = t;
- goTo(curCurvepts, 2, type);
+ goTo(_curCurvepts, 2, type, _dashOn);
curCurveoff = type;
}
// Advance to next dash segment
- idx = (idx + 1) % dashLen;
- dashOn = !dashOn;
- phase = 0.0f;
- leftInThisDashSegment = dash[idx];
- }
- goTo(curCurvepts, curCurveoff+2, type);
- phase += li.lastSegLen();
- if (phase >= dash[idx]) {
- phase = 0.0f;
- idx = (idx + 1) % dashLen;
- dashOn = !dashOn;
- }
+ _idx = (_idx + 1) % _dashLen;
+ _dashOn = !_dashOn;
+ _phase = 0.0f;
+ leftInThisDashSegment = _dash[_idx];
+ }
+
+ goTo(_curCurvepts, curCurveoff + 2, type, _dashOn);
+
+ _phase += _li.lastSegLen();
+ if (_phase >= _dash[_idx]) {
+ _phase = 0.0f;
+ _idx = (_idx + 1) % _dashLen;
+ _dashOn = !_dashOn;
+ }
+ // Save local state:
+ idx = _idx;
+ dashOn = _dashOn;
+ phase = _phase;
+
// reset LengthIterator:
- li.reset();
+ _li.reset();
}
private static boolean pointCurve(float[] curve, int type) {
@@ -420,7 +463,7 @@
// tree; however, the trees we are interested in have the property that
// every non leaf node has exactly 2 children
static final class LengthIterator {
- private enum Side {LEFT, RIGHT};
+ private enum Side {LEFT, RIGHT}
// Holds the curves at various levels of the recursion. The root
// (i.e. the original curve) is at recCurveStack[0] (but then it
// gets subdivided, the left half is put at 1, so most of the time
@@ -670,11 +713,12 @@
// this is a bit of a hack. It returns -1 if we're not on a leaf, and
// the length of the leaf if we are on a leaf.
private float onLeaf() {
- float[] curve = recCurveStack[recLevel];
+ final float[] curve = recCurveStack[recLevel];
+ final int _curveType = curveType;
float polyLen = 0.0f;
float x0 = curve[0], y0 = curve[1];
- for (int i = 2; i < curveType; i += 2) {
+ for (int i = 2; i < _curveType; i += 2) {
final float x1 = curve[i], y1 = curve[i+1];
final float len = Helpers.linelen(x0, y0, x1, y1);
polyLen += len;
@@ -684,8 +728,8 @@
}
final float lineLen = Helpers.linelen(curve[0], curve[1],
- curve[curveType-2],
- curve[curveType-1]);
+ curve[_curveType-2],
+ curve[_curveType-1]);
if ((polyLen - lineLen) < ERR || recLevel == REC_LIMIT) {
return (polyLen + lineLen) / 2.0f;
}
@@ -694,9 +738,9 @@
}
@Override
- public void curveTo(float x1, float y1,
- float x2, float y2,
- float x3, float y3)
+ public void curveTo(final float x1, final float y1,
+ final float x2, final float y2,
+ final float x3, final float y3)
{
final float[] _curCurvepts = curCurvepts;
_curCurvepts[0] = x0; _curCurvepts[1] = y0;
@@ -707,7 +751,9 @@
}
@Override
- public void quadTo(float x1, float y1, float x2, float y2) {
+ public void quadTo(final float x1, final float y1,
+ final float x2, final float y2)
+ {
final float[] _curCurvepts = curCurvepts;
_curCurvepts[0] = x0; _curCurvepts[1] = y0;
_curCurvepts[2] = x1; _curCurvepts[3] = y1;
@@ -718,7 +764,7 @@
@Override
public void closePath() {
lineTo(sx, sy);
- if (firstSegidx > 0) {
+ if (firstSegidx != 0) {
if (!dashOn || needsMoveTo) {
out.moveTo(sx, sy);
}
@@ -729,7 +775,7 @@
@Override
public void pathDone() {
- if (firstSegidx > 0) {
+ if (firstSegidx != 0) {
out.moveTo(sx, sy);
emitFirstSegments();
}
--- old/src/java.desktop/share/classes/sun/java2d/marlin/Helpers.java 2017-08-28 16:48:29.541663188 +0200
+++ new/src/java.desktop/share/classes/sun/java2d/marlin/Helpers.java 2017-08-28 16:48:29.401663190 +0200
@@ -30,6 +30,11 @@
import static java.lang.Math.sqrt;
import static java.lang.Math.cbrt;
import static java.lang.Math.acos;
+import java.util.Arrays;
+import sun.awt.geom.PathConsumer2D;
+import static sun.java2d.marlin.MarlinConst.INITIAL_EDGES_COUNT;
+import sun.java2d.marlin.stats.Histogram;
+import sun.java2d.marlin.stats.StatLong;
final class Helpers implements MarlinConst {
@@ -176,15 +181,6 @@
return ret;
}
- static float polyLineLength(float[] poly, final int off, final int nCoords) {
- assert nCoords % 2 == 0 && poly.length >= off + nCoords : "";
- float acc = 0.0f;
- for (int i = off + 2; i < off + nCoords; i += 2) {
- acc += linelen(poly[i], poly[i+1], poly[i-2], poly[i-1]);
- }
- return acc;
- }
-
static float linelen(float x1, float y1, float x2, float y2) {
final float dx = x2 - x1;
final float dy = y2 - y1;
@@ -438,4 +434,283 @@
return;
}
}
+
+ // From sun.java2d.loops.GeneralRenderer:
+
+ static final int OUTCODE_TOP = 1;
+ static final int OUTCODE_BOTTOM = 2;
+ static final int OUTCODE_LEFT = 4;
+ static final int OUTCODE_RIGHT = 8;
+
+ static int outcode(final float x, final float y,
+ final float[] clipRect)
+ {
+ int code;
+ if (y < clipRect[0]) {
+ code = OUTCODE_TOP;
+ } else if (y >= clipRect[1]) {
+ code = OUTCODE_BOTTOM;
+ } else {
+ code = 0;
+ }
+ if (x < clipRect[2]) {
+ code |= OUTCODE_LEFT;
+ } else if (x >= clipRect[3]) {
+ code |= OUTCODE_RIGHT;
+ }
+ return code;
+ }
+
+ // a stack of polynomial curves where each curve shares endpoints with
+ // adjacent ones.
+ static final class PolyStack {
+ private static final byte TYPE_LINETO = (byte) 0;
+ private static final byte TYPE_QUADTO = (byte) 1;
+ private static final byte TYPE_CUBICTO = (byte) 2;
+
+ // curves capacity = edges count (8192) = edges x 2 (coords)
+ private static final int INITIAL_CURVES_COUNT = INITIAL_EDGES_COUNT << 1;
+
+ // types capacity = edges count (4096)
+ private static final int INITIAL_TYPES_COUNT = INITIAL_EDGES_COUNT;
+
+ float[] curves;
+ int end;
+ byte[] curveTypes;
+ int numCurves;
+
+ // curves ref (dirty)
+ final FloatArrayCache.Reference curves_ref;
+ // curveTypes ref (dirty)
+ final ByteArrayCache.Reference curveTypes_ref;
+
+ // used marks (stats only)
+ int curveTypesUseMark;
+ int curvesUseMark;
+
+ private final StatLong stat_polystack_types;
+ private final StatLong stat_polystack_curves;
+ private final Histogram hist_polystack_curves;
+ private final StatLong stat_array_polystack_curves;
+ private final StatLong stat_array_polystack_curveTypes;
+
+ PolyStack(final RendererContext rdrCtx) {
+ this(rdrCtx, null, null, null, null, null);
+ }
+
+ PolyStack(final RendererContext rdrCtx,
+ final StatLong stat_polystack_types,
+ final StatLong stat_polystack_curves,
+ final Histogram hist_polystack_curves,
+ final StatLong stat_array_polystack_curves,
+ final StatLong stat_array_polystack_curveTypes)
+ {
+ curves_ref = rdrCtx.newDirtyFloatArrayRef(INITIAL_CURVES_COUNT); // 32K
+ curves = curves_ref.initial;
+
+ curveTypes_ref = rdrCtx.newDirtyByteArrayRef(INITIAL_TYPES_COUNT); // 4K
+ curveTypes = curveTypes_ref.initial;
+ numCurves = 0;
+ end = 0;
+
+ if (DO_STATS) {
+ curveTypesUseMark = 0;
+ curvesUseMark = 0;
+ }
+ this.stat_polystack_types = stat_polystack_types;
+ this.stat_polystack_curves = stat_polystack_curves;
+ this.hist_polystack_curves = hist_polystack_curves;
+ this.stat_array_polystack_curves = stat_array_polystack_curves;
+ this.stat_array_polystack_curveTypes = stat_array_polystack_curveTypes;
+ }
+
+ /**
+ * Disposes this PolyStack:
+ * clean up before reusing this instance
+ */
+ void dispose() {
+ end = 0;
+ numCurves = 0;
+
+ if (DO_STATS) {
+ stat_polystack_types.add(curveTypesUseMark);
+ stat_polystack_curves.add(curvesUseMark);
+ hist_polystack_curves.add(curvesUseMark);
+
+ // reset marks
+ curveTypesUseMark = 0;
+ curvesUseMark = 0;
+ }
+
+ // Return arrays:
+ // curves and curveTypes are kept dirty
+ curves = curves_ref.putArray(curves);
+ curveTypes = curveTypes_ref.putArray(curveTypes);
+ }
+
+ private void ensureSpace(final int n) {
+ // use substraction to avoid integer overflow:
+ if (curves.length - end < n) {
+ if (DO_STATS) {
+ stat_array_polystack_curves.add(end + n);
+ }
+ curves = curves_ref.widenArray(curves, end, end + n);
+ }
+ if (curveTypes.length <= numCurves) {
+ if (DO_STATS) {
+ stat_array_polystack_curveTypes.add(numCurves + 1);
+ }
+ curveTypes = curveTypes_ref.widenArray(curveTypes,
+ numCurves,
+ numCurves + 1);
+ }
+ }
+
+ void pushCubic(float x0, float y0,
+ float x1, float y1,
+ float x2, float y2)
+ {
+ ensureSpace(6);
+ curveTypes[numCurves++] = TYPE_CUBICTO;
+ // we reverse the coordinate order to make popping easier
+ final float[] _curves = curves;
+ int e = end;
+ _curves[e++] = x2; _curves[e++] = y2;
+ _curves[e++] = x1; _curves[e++] = y1;
+ _curves[e++] = x0; _curves[e++] = y0;
+ end = e;
+ }
+
+ void pushQuad(float x0, float y0,
+ float x1, float y1)
+ {
+ ensureSpace(4);
+ curveTypes[numCurves++] = TYPE_QUADTO;
+ final float[] _curves = curves;
+ int e = end;
+ _curves[e++] = x1; _curves[e++] = y1;
+ _curves[e++] = x0; _curves[e++] = y0;
+ end = e;
+ }
+
+ void pushLine(float x, float y) {
+ ensureSpace(2);
+ curveTypes[numCurves++] = TYPE_LINETO;
+ curves[end++] = x; curves[end++] = y;
+ }
+
+ void pullAll(final PathConsumer2D io) {
+ final int nc = numCurves;
+ if (nc == 0) {
+ return;
+ }
+ if (DO_STATS) {
+ // update used marks:
+ if (numCurves > curveTypesUseMark) {
+ curveTypesUseMark = numCurves;
+ }
+ if (end > curvesUseMark) {
+ curvesUseMark = end;
+ }
+ }
+ final byte[] _curveTypes = curveTypes;
+ final float[] _curves = curves;
+ int e = 0;
+
+ for (int i = 0; i < nc; i++) {
+ switch(_curveTypes[i]) {
+ case TYPE_LINETO:
+ io.lineTo(_curves[e], _curves[e+1]);
+ e += 2;
+ continue;
+ case TYPE_QUADTO:
+ io.quadTo(_curves[e+0], _curves[e+1],
+ _curves[e+2], _curves[e+3]);
+ e += 4;
+ continue;
+ case TYPE_CUBICTO:
+ io.curveTo(_curves[e+0], _curves[e+1],
+ _curves[e+2], _curves[e+3],
+ _curves[e+4], _curves[e+5]);
+ e += 6;
+ continue;
+ default:
+ }
+ }
+ numCurves = 0;
+ end = 0;
+ }
+
+ void popAll(final PathConsumer2D io) {
+ int nc = numCurves;
+ if (nc == 0) {
+ return;
+ }
+ if (DO_STATS) {
+ // update used marks:
+ if (numCurves > curveTypesUseMark) {
+ curveTypesUseMark = numCurves;
+ }
+ if (end > curvesUseMark) {
+ curvesUseMark = end;
+ }
+ }
+ final byte[] _curveTypes = curveTypes;
+ final float[] _curves = curves;
+ int e = end;
+
+ while (nc != 0) {
+ switch(_curveTypes[--nc]) {
+ case TYPE_LINETO:
+ e -= 2;
+ io.lineTo(_curves[e], _curves[e+1]);
+ continue;
+ case TYPE_QUADTO:
+ e -= 4;
+ io.quadTo(_curves[e+0], _curves[e+1],
+ _curves[e+2], _curves[e+3]);
+ continue;
+ case TYPE_CUBICTO:
+ e -= 6;
+ io.curveTo(_curves[e+0], _curves[e+1],
+ _curves[e+2], _curves[e+3],
+ _curves[e+4], _curves[e+5]);
+ continue;
+ default:
+ }
+ }
+ numCurves = 0;
+ end = 0;
+ }
+
+ @Override
+ public String toString() {
+ String ret = "";
+ int nc = numCurves;
+ int last = end;
+ int len;
+ while (nc != 0) {
+ switch(curveTypes[--nc]) {
+ case TYPE_LINETO:
+ len = 2;
+ ret += "line: ";
+ break;
+ case TYPE_QUADTO:
+ len = 4;
+ ret += "quad: ";
+ break;
+ case TYPE_CUBICTO:
+ len = 6;
+ ret += "cubic: ";
+ break;
+ default:
+ len = 0;
+ }
+ last -= len;
+ ret += Arrays.toString(Arrays.copyOfRange(curves, last, last+len))
+ + "\n";
+ }
+ return ret;
+ }
+ }
}
--- old/src/java.desktop/share/classes/sun/java2d/marlin/MarlinProperties.java 2017-08-28 16:48:29.965663182 +0200
+++ new/src/java.desktop/share/classes/sun/java2d/marlin/MarlinProperties.java 2017-08-28 16:48:29.821663184 +0200
@@ -145,6 +145,10 @@
return getBoolean("sun.java2d.renderer.useSimplifier", "false");
}
+ public static boolean isDoClip() {
+ return getBoolean("sun.java2d.renderer.clip", "true");
+ }
+
// debugging parameters
public static boolean isDoStats() {
--- old/src/java.desktop/share/classes/sun/java2d/marlin/MarlinRenderingEngine.java 2017-08-28 16:48:30.473663175 +0200
+++ new/src/java.desktop/share/classes/sun/java2d/marlin/MarlinRenderingEngine.java 2017-08-28 16:48:30.337663177 +0200
@@ -85,6 +85,9 @@
static final float UPPER_BND = Float.MAX_VALUE / 2.0f;
static final float LOWER_BND = -UPPER_BND;
+ static final boolean DO_CLIP = MarlinProperties.isDoClip();
+ static final boolean DO_TRACE = false;
+
/**
* Public constructor
*/
@@ -324,6 +327,7 @@
int dashLen = -1;
boolean recycleDashes = false;
+ float scale = 1.0f;
if (at != null && !at.isIdentity()) {
final double a = at.getScaleX();
@@ -356,7 +360,7 @@
// a*b == -c*d && a*a+c*c == b*b+d*d. In the actual check below, we
// leave a bit of room for error.
if (nearZero(a*b + c*d) && nearZero(a*a + c*c - (b*b + d*d))) {
- final float scale = (float) Math.sqrt(a*a + c*c);
+ scale = (float) Math.sqrt(a*a + c*c);
if (dashes != null) {
recycleDashes = true;
@@ -394,16 +398,24 @@
at = null;
}
+ final TransformingPathConsumer2D transformerPC2D = rdrCtx.transformerPC2D;
+
+ if (DO_TRACE) {
+ // trace Stroker:
+ pc2d = transformerPC2D.traceStroker(pc2d);
+ }
+
if (USE_SIMPLIFIER) {
// Use simplifier after stroker before Renderer
// to remove collinear segments (notably due to cap square)
pc2d = rdrCtx.simplifier.init(pc2d);
}
- final TransformingPathConsumer2D transformerPC2D = rdrCtx.transformerPC2D;
+ // deltaTransformConsumer may adjust the clip rectangle:
pc2d = transformerPC2D.deltaTransformConsumer(pc2d, strokerat);
- pc2d = rdrCtx.stroker.init(pc2d, width, caps, join, miterlimit);
+ // stroker will adjust the clip rectangle (width / miter limit):
+ pc2d = rdrCtx.stroker.init(pc2d, width, caps, join, miterlimit, scale);
if (dashes != null) {
if (!recycleDashes) {
@@ -411,9 +423,22 @@
}
pc2d = rdrCtx.dasher.init(pc2d, dashes, dashLen, dashphase,
recycleDashes);
+ } else if (rdrCtx.doClip && (caps != Stroker.CAP_BUTT)) {
+ if (DO_TRACE) {
+ pc2d = transformerPC2D.traceClosedPathDetector(pc2d);
+ }
+
+ // If no dash and clip is enabled:
+ // detect closedPaths (polygons) for caps
+ pc2d = transformerPC2D.detectClosedPath(pc2d);
}
pc2d = transformerPC2D.inverseDeltaTransformConsumer(pc2d, strokerat);
+ if (DO_TRACE) {
+ // trace Input:
+ pc2d = transformerPC2D.traceInput(pc2d);
+ }
+
final PathIterator pi = norm.getNormalizingPathIterator(rdrCtx,
src.getPathIterator(at));
@@ -803,6 +828,18 @@
clip.getWidth(), clip.getHeight(),
PathIterator.WIND_NON_ZERO);
+ if (DO_CLIP) {
+ // Define the initial clip bounds:
+ final float[] clipRect = rdrCtx.clipRect;
+ clipRect[0] = clip.getLoY();
+ clipRect[1] = clip.getLoY() + clip.getHeight();
+ clipRect[2] = clip.getLoX();
+ clipRect[3] = clip.getLoX() + clip.getWidth();
+
+ // Enable clipping:
+ rdrCtx.doClip = true;
+ }
+
strokeTo(rdrCtx, s, _at, bs, thin, norm, true, r);
}
if (r.endRendering()) {
@@ -860,8 +897,8 @@
final RendererContext rdrCtx = getRendererContext();
try {
r = rdrCtx.renderer.init(clip.getLoX(), clip.getLoY(),
- clip.getWidth(), clip.getHeight(),
- Renderer.WIND_EVEN_ODD);
+ clip.getWidth(), clip.getHeight(),
+ Renderer.WIND_EVEN_ODD);
r.moveTo((float) x, (float) y);
r.lineTo((float) (x+dx1), (float) (y+dy1));
@@ -1045,6 +1082,8 @@
// optimisation parameters
logInfo("sun.java2d.renderer.useSimplifier = "
+ MarlinConst.USE_SIMPLIFIER);
+ logInfo("sun.java2d.renderer.clip = "
+ + MarlinProperties.isDoClip());
// debugging parameters
logInfo("sun.java2d.renderer.doStats = "
--- old/src/java.desktop/share/classes/sun/java2d/marlin/Renderer.java 2017-08-28 16:48:31.057663167 +0200
+++ new/src/java.desktop/share/classes/sun/java2d/marlin/Renderer.java 2017-08-28 16:48:30.841663170 +0200
@@ -47,6 +47,9 @@
static final int SUBPIXEL_MASK_X = SUBPIXEL_POSITIONS_X - 1;
static final int SUBPIXEL_MASK_Y = SUBPIXEL_POSITIONS_Y - 1;
+ static final float RDR_OFFSET_X = 0.501f / SUBPIXEL_SCALE_X;
+ static final float RDR_OFFSET_Y = 0.501f / SUBPIXEL_SCALE_Y;
+
// number of subpixels corresponding to a tile line
private static final int SUBPIXEL_TILE
= TILE_H << SUBPIXEL_LG_POSITIONS_Y;
@@ -672,7 +675,7 @@
}
@Override
- public void moveTo(float pix_x0, float pix_y0) {
+ public void moveTo(final float pix_x0, final float pix_y0) {
closePath();
final float sx = tosubpixx(pix_x0);
final float sy = tosubpixy(pix_y0);
@@ -683,7 +686,7 @@
}
@Override
- public void lineTo(float pix_x1, float pix_y1) {
+ public void lineTo(final float pix_x1, final float pix_y1) {
final float x1 = tosubpixx(pix_x1);
final float y1 = tosubpixy(pix_y1);
addLine(x0, y0, x1, y1);
@@ -692,24 +695,26 @@
}
@Override
- public void curveTo(float x1, float y1,
- float x2, float y2,
- float x3, float y3)
+ public void curveTo(final float pix_x1, final float pix_y1,
+ final float pix_x2, final float pix_y2,
+ final float pix_x3, final float pix_y3)
{
- final float xe = tosubpixx(x3);
- final float ye = tosubpixy(y3);
- curve.set(x0, y0, tosubpixx(x1), tosubpixy(y1),
- tosubpixx(x2), tosubpixy(y2), xe, ye);
+ final float xe = tosubpixx(pix_x3);
+ final float ye = tosubpixy(pix_y3);
+ curve.set(x0, y0, tosubpixx(pix_x1), tosubpixy(pix_y1),
+ tosubpixx(pix_x2), tosubpixy(pix_y2), xe, ye);
curveBreakIntoLinesAndAdd(x0, y0, curve, xe, ye);
x0 = xe;
y0 = ye;
}
@Override
- public void quadTo(float x1, float y1, float x2, float y2) {
- final float xe = tosubpixx(x2);
- final float ye = tosubpixy(y2);
- curve.set(x0, y0, tosubpixx(x1), tosubpixy(y1), xe, ye);
+ public void quadTo(final float pix_x1, final float pix_y1,
+ final float pix_x2, final float pix_y2)
+ {
+ final float xe = tosubpixx(pix_x2);
+ final float ye = tosubpixy(pix_y2);
+ curve.set(x0, y0, tosubpixx(pix_x1), tosubpixy(pix_y1), xe, ye);
quadBreakIntoLinesAndAdd(x0, y0, curve, xe, ye);
x0 = xe;
y0 = ye;
@@ -717,9 +722,11 @@
@Override
public void closePath() {
- addLine(x0, y0, sx0, sy0);
- x0 = sx0;
- y0 = sy0;
+ if (x0 != sx0 || y0 != sy0) {
+ addLine(x0, y0, sx0, sy0);
+ x0 = sx0;
+ y0 = sy0;
+ }
}
@Override
--- old/src/java.desktop/share/classes/sun/java2d/marlin/RendererContext.java 2017-08-28 16:48:31.629663159 +0200
+++ new/src/java.desktop/share/classes/sun/java2d/marlin/RendererContext.java 2017-08-28 16:48:31.465663162 +0200
@@ -25,6 +25,7 @@
package sun.java2d.marlin;
+import java.awt.Rectangle;
import java.awt.geom.Path2D;
import java.lang.ref.WeakReference;
import java.util.concurrent.atomic.AtomicInteger;
@@ -75,6 +76,12 @@
final MarlinCache cache;
// flag indicating the shape is stroked (1) or filled (0)
int stroking = 0;
+ // flag indicating to clip the shape
+ boolean doClip = false;
+ // flag indicating if the path is closed or not (in advance) to handle properly caps
+ boolean closedPath = false;
+ // clip rectangle (ymin, ymax, xmin, xmax):
+ final float[] clipRect = new float[4];
// Array caches:
/* clean int[] cache (zero-filled) = 5 refs */
@@ -116,7 +123,7 @@
nPQPathIterator = new NormalizingPathIterator.NearestPixelQuarter(float6);
// MarlinRenderingEngine.TransformingPathConsumer2D
- transformerPC2D = new TransformingPathConsumer2D();
+ transformerPC2D = new TransformingPathConsumer2D(this);
// Renderer:
cache = new MarlinCache(this);
@@ -138,7 +145,10 @@
}
stats.totalOffHeap = 0L;
}
- stroking = 0;
+ stroking = 0;
+ doClip = false;
+ closedPath = false;
+
// if context is maked as DIRTY:
if (dirty) {
// may happen if an exception if thrown in the pipeline processing:
--- old/src/java.desktop/share/classes/sun/java2d/marlin/RendererStats.java 2017-08-28 16:48:32.149663152 +0200
+++ new/src/java.desktop/share/classes/sun/java2d/marlin/RendererStats.java 2017-08-28 16:48:31.949663155 +0200
@@ -66,10 +66,6 @@
= new StatLong("cache.rowAAChunk");
final StatLong stat_cache_tiles
= new StatLong("cache.tiles");
- final StatLong stat_rdr_poly_stack_curves
- = new StatLong("renderer.poly.stack.curves");
- final StatLong stat_rdr_poly_stack_types
- = new StatLong("renderer.poly.stack.types");
final StatLong stat_rdr_addLine
= new StatLong("renderer.addLine");
final StatLong stat_rdr_addLine_skip
@@ -106,15 +102,19 @@
= new StatLong("renderer.crossings.bsearch");
final StatLong stat_rdr_crossings_msorts
= new StatLong("renderer.crossings.msorts");
+ final StatLong stat_str_polystack_curves
+ = new StatLong("stroker.polystack.curves");
+ final StatLong stat_str_polystack_types
+ = new StatLong("stroker.polystack.types");
+ final StatLong stat_cpd_polystack_curves
+ = new StatLong("closedPathDetector.polystack.curves");
+ final StatLong stat_cpd_polystack_types
+ = new StatLong("closedPathDetector.polystack.types");
// growable arrays
final StatLong stat_array_dasher_dasher
= new StatLong("array.dasher.dasher.d_float");
final StatLong stat_array_dasher_firstSegmentsBuffer
= new StatLong("array.dasher.firstSegmentsBuffer.d_float");
- final StatLong stat_array_stroker_polystack_curves
- = new StatLong("array.stroker.polystack.curves.d_float");
- final StatLong stat_array_stroker_polystack_curveTypes
- = new StatLong("array.stroker.polystack.curveTypes.d_byte");
final StatLong stat_array_marlincache_rowAAChunk
= new StatLong("array.marlincache.rowAAChunk.resize");
final StatLong stat_array_marlincache_touchedTile
@@ -133,11 +133,17 @@
= new StatLong("array.renderer.edgePtrs.int");
final StatLong stat_array_renderer_aux_edgePtrs
= new StatLong("array.renderer.aux_edgePtrs.int");
+ final StatLong stat_array_str_polystack_curves
+ = new StatLong("array.stroker.polystack.curves.d_float");
+ final StatLong stat_array_str_polystack_types
+ = new StatLong("array.stroker.polystack.curveTypes.d_byte");
+ final StatLong stat_array_cpd_polystack_curves
+ = new StatLong("array.closedPathDetector.polystack.curves.d_float");
+ final StatLong stat_array_cpd_polystack_types
+ = new StatLong("array.closedPathDetector.polystack.curveTypes.d_byte");
// histograms
final Histogram hist_rdr_edges_count
= new Histogram("renderer.edges.count");
- final Histogram hist_rdr_poly_stack_curves
- = new Histogram("renderer.polystack.curves");
final Histogram hist_rdr_crossings
= new Histogram("renderer.crossings");
final Histogram hist_rdr_crossings_ratio
@@ -148,6 +154,8 @@
= new Histogram("renderer.crossings.msorts");
final Histogram hist_rdr_crossings_msorts_adds
= new Histogram("renderer.crossings.msorts.adds");
+ final Histogram hist_str_polystack_curves
+ = new Histogram("stroker.polystack.curves");
final Histogram hist_tile_generator_alpha
= new Histogram("tile_generator.alpha");
final Histogram hist_tile_generator_encoding
@@ -158,13 +166,13 @@
= new Histogram("tile_generator.encoding.ratio");
final Histogram hist_tile_generator_encoding_runLen
= new Histogram("tile_generator.encoding.runLen");
+ final Histogram hist_cpd_polystack_curves
+ = new Histogram("closedPathDetector.polystack.curves");
// all stats
final StatLong[] statistics = new StatLong[]{
stat_cache_rowAA,
stat_cache_rowAAChunk,
stat_cache_tiles,
- stat_rdr_poly_stack_types,
- stat_rdr_poly_stack_curves,
stat_rdr_addLine,
stat_rdr_addLine_skip,
stat_rdr_curveBreak,
@@ -183,8 +191,11 @@
stat_rdr_crossings_sorts,
stat_rdr_crossings_bsearch,
stat_rdr_crossings_msorts,
+ stat_str_polystack_types,
+ stat_str_polystack_curves,
+ stat_cpd_polystack_curves,
+ stat_cpd_polystack_types,
hist_rdr_edges_count,
- hist_rdr_poly_stack_curves,
hist_rdr_crossings,
hist_rdr_crossings_ratio,
hist_rdr_crossings_adds,
@@ -195,10 +206,10 @@
hist_tile_generator_encoding_dist,
hist_tile_generator_encoding_ratio,
hist_tile_generator_encoding_runLen,
+ hist_str_polystack_curves,
+ hist_cpd_polystack_curves,
stat_array_dasher_dasher,
stat_array_dasher_firstSegmentsBuffer,
- stat_array_stroker_polystack_curves,
- stat_array_stroker_polystack_curveTypes,
stat_array_marlincache_rowAAChunk,
stat_array_marlincache_touchedTile,
stat_array_renderer_alphaline,
@@ -207,7 +218,11 @@
stat_array_renderer_edgeBuckets,
stat_array_renderer_edgeBucketCounts,
stat_array_renderer_edgePtrs,
- stat_array_renderer_aux_edgePtrs
+ stat_array_renderer_aux_edgePtrs,
+ stat_array_str_polystack_curves,
+ stat_array_str_polystack_types,
+ stat_array_cpd_polystack_curves,
+ stat_array_cpd_polystack_types
};
// monitors
final Monitor mon_pre_getAATileGenerator
--- old/src/java.desktop/share/classes/sun/java2d/marlin/Stroker.java 2017-08-28 16:48:32.685663145 +0200
+++ new/src/java.desktop/share/classes/sun/java2d/marlin/Stroker.java 2017-08-28 16:48:32.525663147 +0200
@@ -28,6 +28,7 @@
import java.util.Arrays;
import sun.awt.geom.PathConsumer2D;
+import sun.java2d.marlin.Helpers.PolyStack;
// TODO: some of the arithmetic here is too verbose and prone to hard to
// debug typos. We should consider making a small Point/Vector class that
@@ -73,7 +74,11 @@
// it to floating point, so that's why the divisions by 2^16 are there.
private static final float ROUND_JOIN_THRESHOLD = 1000.0f/65536.0f;
- private static final float C = 0.5522847498307933f;
+ // kappa = (4/3) * (SQRT(2) - 1)
+ private static final float C = (float)(4.0d * (Math.sqrt(2.0d) - 1.0d) / 3.0d);
+
+ // SQRT(2)
+ private static final float SQRT_2 = (float)Math.sqrt(2.0d);
private static final int MAX_N_CURVES = 11;
@@ -120,6 +125,20 @@
// dirty curve
final Curve curve;
+ // Bounds of the drawing region, at pixel precision.
+ private float[] clipRect;
+
+ // the outcode of the current point
+ private int cOutCode = 0;
+
+ // the outcode of the starting point
+ private int sOutCode = 0;
+
+ // flag indicating if the path is opened (clipped)
+ private boolean opened = false;
+ // flag indicating if the starting point's cap is done
+ private boolean capStart = false;
+
/**
* Constructs a Stroker
.
* @param rdrCtx per-thread renderer context
@@ -127,7 +146,15 @@
Stroker(final RendererContext rdrCtx) {
this.rdrCtx = rdrCtx;
- this.reverse = new PolyStack(rdrCtx);
+ this.reverse = (rdrCtx.stats != null) ?
+ new PolyStack(rdrCtx,
+ rdrCtx.stats.stat_str_polystack_types,
+ rdrCtx.stats.stat_str_polystack_curves,
+ rdrCtx.stats.hist_str_polystack_curves,
+ rdrCtx.stats.stat_array_str_polystack_curves,
+ rdrCtx.stats.stat_array_str_polystack_types)
+ : new PolyStack(rdrCtx);
+
this.curve = rdrCtx.curve;
}
@@ -143,13 +170,15 @@
* JOIN_MITER
, JOIN_ROUND
or
* JOIN_BEVEL
.
* @param miterLimit the desired miter limit
+ * @param scale scaling factor applied to clip boundaries
* @return this instance
*/
- Stroker init(PathConsumer2D pc2d,
- float lineWidth,
- int capStyle,
- int joinStyle,
- float miterLimit)
+ Stroker init(final PathConsumer2D pc2d,
+ final float lineWidth,
+ final int capStyle,
+ final int joinStyle,
+ final float miterLimit,
+ final float scale)
{
this.out = pc2d;
@@ -158,13 +187,41 @@
this.capStyle = capStyle;
this.joinStyle = joinStyle;
- float limit = miterLimit * lineWidth2;
+ final float limit = miterLimit * lineWidth2;
this.miterLimitSq = limit * limit;
this.prev = CLOSE;
rdrCtx.stroking = 1;
+ if (rdrCtx.doClip) {
+ // Adjust the clipping rectangle with the stroker margin (miter limit, width)
+ float rdrOffX = 0.0f, rdrOffY = 0.0f;
+ float margin = lineWidth2;
+
+ if (capStyle == CAP_SQUARE) {
+ margin *= SQRT_2;
+ }
+ if ((joinStyle == JOIN_MITER) && (margin < limit)) {
+ margin = limit;
+ }
+ if (scale != 1.0f) {
+ margin *= scale;
+ rdrOffX = scale * Renderer.RDR_OFFSET_X;
+ rdrOffY = scale * Renderer.RDR_OFFSET_Y;
+ }
+
+ // bounds as half-open intervals: minX <= x < maxX and minY <= y < maxY
+ // adjust clip rectangle (ymin, ymax, xmin, xmax):
+ final float[] _clipRect = rdrCtx.clipRect;
+ _clipRect[0] -= margin - rdrOffY;
+ _clipRect[1] += margin + rdrOffY;
+ _clipRect[2] -= margin - rdrOffX;
+ _clipRect[3] += margin + rdrOffX;
+ this.clipRect = _clipRect;
+ } else {
+ this.clipRect = null;
+ }
return this; // fluent API
}
@@ -175,6 +232,9 @@
void dispose() {
reverse.dispose();
+ opened = false;
+ capStart = false;
+
if (DO_CLEAN_DIRTY) {
// Force zero-fill dirty arrays:
Arrays.fill(offset0, 0.0f);
@@ -445,19 +505,62 @@
}
@Override
- public void moveTo(float x0, float y0) {
- if (prev == DRAWING_OP_TO) {
- finish();
+ public void moveTo(final float x0, final float y0) {
+ moveTo(x0, y0, cOutCode);
+ // update starting point:
+ this.sx0 = x0;
+ this.sy0 = y0;
+ this.sdx = 1.0f;
+ this.sdy = 0.0f;
+ this.opened = false;
+ this.capStart = false;
+
+ if (clipRect != null) {
+ final int outcode = Helpers.outcode(x0, y0, clipRect);
+ this.cOutCode = outcode;
+ this.sOutCode = outcode;
+ }
+ }
+
+ private void moveTo(final float x0, final float y0,
+ final int outcode)
+ {
+ if (prev == MOVE_TO) {
+ this.cx0 = x0;
+ this.cy0 = y0;
+ } else {
+ if (prev == DRAWING_OP_TO) {
+ finish(outcode);
+ }
+ this.prev = MOVE_TO;
+ this.cx0 = x0;
+ this.cy0 = y0;
+ this.cdx = 1.0f;
+ this.cdy = 0.0f;
}
- this.sx0 = this.cx0 = x0;
- this.sy0 = this.cy0 = y0;
- this.cdx = this.sdx = 1.0f;
- this.cdy = this.sdy = 0.0f;
- this.prev = MOVE_TO;
}
@Override
- public void lineTo(float x1, float y1) {
+ public void lineTo(final float x1, final float y1) {
+ lineTo(x1, y1, false);
+ }
+
+ private void lineTo(final float x1, final float y1,
+ final boolean force)
+ {
+ final int outcode0 = this.cOutCode;
+ if (!force && clipRect != null) {
+ final int outcode1 = Helpers.outcode(x1, y1, clipRect);
+ this.cOutCode = outcode1;
+
+ // basic rejection criteria
+ if ((outcode0 & outcode1) != 0) {
+ moveTo(x1, y1, outcode0);
+ opened = true;
+ return;
+ }
+ }
+
float dx = x1 - cx0;
float dy = y1 - cy0;
if (dx == 0.0f && dy == 0.0f) {
@@ -467,7 +570,7 @@
final float mx = offset0[0];
final float my = offset0[1];
- drawJoin(cdx, cdy, cx0, cy0, dx, dy, cmx, cmy, mx, my);
+ drawJoin(cdx, cdy, cx0, cy0, dx, dy, cmx, cmy, mx, my, outcode0);
emitLineTo(cx0 + mx, cy0 + my);
emitLineTo( x1 + mx, y1 + my);
@@ -475,43 +578,64 @@
emitLineToRev(cx0 - mx, cy0 - my);
emitLineToRev( x1 - mx, y1 - my);
- this.cmx = mx;
- this.cmy = my;
- this.cdx = dx;
- this.cdy = dy;
+ this.prev = DRAWING_OP_TO;
this.cx0 = x1;
this.cy0 = y1;
- this.prev = DRAWING_OP_TO;
+ this.cdx = dx;
+ this.cdy = dy;
+ this.cmx = mx;
+ this.cmy = my;
}
@Override
public void closePath() {
- if (prev != DRAWING_OP_TO) {
+ // distinguish empty path at all vs opened path ?
+ if (prev != DRAWING_OP_TO && !opened) {
if (prev == CLOSE) {
return;
}
emitMoveTo(cx0, cy0 - lineWidth2);
- this.cmx = this.smx = 0.0f;
- this.cmy = this.smy = -lineWidth2;
- this.cdx = this.sdx = 1.0f;
- this.cdy = this.sdy = 0.0f;
- finish();
+
+ this.sdx = 1.0f;
+ this.sdy = 0.0f;
+ this.cdx = 1.0f;
+ this.cdy = 0.0f;
+
+ this.smx = 0.0f;
+ this.smy = -lineWidth2;
+ this.cmx = 0.0f;
+ this.cmy = -lineWidth2;
+
+ finish(cOutCode);
return;
}
- if (cx0 != sx0 || cy0 != sy0) {
- lineTo(sx0, sy0);
- }
+ if (sOutCode == 0) {
+ if (cx0 != sx0 || cy0 != sy0) {
+ lineTo(sx0, sy0, true);
+ }
- drawJoin(cdx, cdy, cx0, cy0, sdx, sdy, cmx, cmy, smx, smy);
+ drawJoin(cdx, cdy, cx0, cy0, sdx, sdy, cmx, cmy, smx, smy, cOutCode);
- emitLineTo(sx0 + smx, sy0 + smy);
+ emitLineTo(sx0 + smx, sy0 + smy);
- emitMoveTo(sx0 - smx, sy0 - smy);
+ if (opened) {
+ emitLineTo(sx0 - smx, sy0 - smy);
+ } else {
+ emitMoveTo(sx0 - smx, sy0 - smy);
+ }
+ }
+ // Ignore caps like finish(false)
emitReverse();
this.prev = CLOSE;
- emitClose();
+
+ if (opened) {
+ // do not emit close
+ opened = false;
+ } else {
+ emitClose();
+ }
}
private void emitReverse() {
@@ -521,7 +645,7 @@
@Override
public void pathDone() {
if (prev == DRAWING_OP_TO) {
- finish();
+ finish(cOutCode);
}
out.pathDone();
@@ -534,23 +658,39 @@
dispose();
}
- private void finish() {
- if (capStyle == CAP_ROUND) {
- drawRoundCap(cx0, cy0, cmx, cmy);
- } else if (capStyle == CAP_SQUARE) {
- emitLineTo(cx0 - cmy + cmx, cy0 + cmx + cmy);
- emitLineTo(cx0 - cmy - cmx, cy0 + cmx - cmy);
- }
+ private void finish(final int outcode) {
+ // Problem: impossible to guess if the path will be closed in advance
+ // i.e. if caps must be drawn or not ?
+ // Solution: use the ClosedPathDetector before Stroker to determine
+ // if the path is a closed path or not
+ if (!rdrCtx.closedPath) {
+ if (outcode == 0) {
+ // current point = end's cap:
+ if (capStyle == CAP_ROUND) {
+ drawRoundCap(cx0, cy0, cmx, cmy);
+ } else if (capStyle == CAP_SQUARE) {
+ emitLineTo(cx0 - cmy + cmx, cy0 + cmx + cmy);
+ emitLineTo(cx0 - cmy - cmx, cy0 + cmx - cmy);
+ }
+ }
+ emitReverse();
- emitReverse();
+ if (!capStart) {
+ capStart = true;
- if (capStyle == CAP_ROUND) {
- drawRoundCap(sx0, sy0, -smx, -smy);
- } else if (capStyle == CAP_SQUARE) {
- emitLineTo(sx0 + smy - smx, sy0 - smx - smy);
- emitLineTo(sx0 + smy + smx, sy0 - smx + smy);
+ if (sOutCode == 0) {
+ // starting point = initial cap:
+ if (capStyle == CAP_ROUND) {
+ drawRoundCap(sx0, sy0, -smx, -smy);
+ } else if (capStyle == CAP_SQUARE) {
+ emitLineTo(sx0 + smy - smx, sy0 - smx - smy);
+ emitLineTo(sx0 + smy + smx, sy0 - smx + smy);
+ }
+ }
+ }
+ } else {
+ emitReverse();
}
-
emitClose();
}
@@ -622,23 +762,28 @@
float x0, float y0,
float dx, float dy,
float omx, float omy,
- float mx, float my)
+ float mx, float my,
+ final int outcode)
{
if (prev != DRAWING_OP_TO) {
emitMoveTo(x0 + mx, y0 + my);
- this.sdx = dx;
- this.sdy = dy;
- this.smx = mx;
- this.smy = my;
+ if (!opened) {
+ this.sdx = dx;
+ this.sdy = dy;
+ this.smx = mx;
+ this.smy = my;
+ }
} else {
- boolean cw = isCW(pdx, pdy, dx, dy);
- if (joinStyle == JOIN_MITER) {
- drawMiter(pdx, pdy, x0, y0, dx, dy, omx, omy, mx, my, cw);
- } else if (joinStyle == JOIN_ROUND) {
- drawRoundJoin(x0, y0,
- omx, omy,
- mx, my, cw,
- ROUND_JOIN_THRESHOLD);
+ final boolean cw = isCW(pdx, pdy, dx, dy);
+ if (outcode == 0) {
+ if (joinStyle == JOIN_MITER) {
+ drawMiter(pdx, pdy, x0, y0, dx, dy, omx, omy, mx, my, cw);
+ } else if (joinStyle == JOIN_ROUND) {
+ drawRoundJoin(x0, y0,
+ omx, omy,
+ mx, my, cw,
+ ROUND_JOIN_THRESHOLD);
+ }
}
emitLineTo(x0, y0, !cw);
}
@@ -943,10 +1088,29 @@
return ret;
}
- @Override public void curveTo(float x1, float y1,
- float x2, float y2,
- float x3, float y3)
- {
+ @Override
+ public void curveTo(final float x1, final float y1,
+ final float x2, final float y2,
+ final float x3, final float y3)
+ {
+ final int outcode0 = this.cOutCode;
+ if (clipRect != null) {
+ final int outcode3 = Helpers.outcode(x3, y3, clipRect);
+ this.cOutCode = outcode3;
+
+ if (outcode3 != 0) {
+ final int outcode1 = Helpers.outcode(x1, y1, clipRect);
+ final int outcode2 = Helpers.outcode(x2, y2, clipRect);
+
+ // basic rejection criteria
+ if ((outcode0 & outcode1 & outcode2 & outcode3) != 0) {
+ moveTo(x3, y3, outcode0);
+ opened = true;
+ return;
+ }
+ }
+ }
+
final float[] mid = middle;
mid[0] = cx0; mid[1] = cy0;
@@ -955,7 +1119,7 @@
mid[6] = x3; mid[7] = y3;
// need these so we can update the state at the end of this method
- final float xf = mid[6], yf = mid[7];
+ final float xf = x3, yf = y3;
float dxs = mid[2] - mid[0];
float dys = mid[3] - mid[1];
float dxf = mid[6] - mid[4];
@@ -981,6 +1145,10 @@
}
if (dxs == 0.0f && dys == 0.0f) {
// this happens if the "curve" is just a point
+ // fix outcode0 for lineTo() call:
+ if (clipRect != null) {
+ this.cOutCode = outcode0;
+ }
lineTo(mid[0], mid[1]);
return;
}
@@ -999,7 +1167,7 @@
}
computeOffset(dxs, dys, lineWidth2, offset0);
- drawJoin(cdx, cdy, cx0, cy0, dxs, dys, cmx, cmy, offset0[0], offset0[1]);
+ drawJoin(cdx, cdy, cx0, cy0, dxs, dys, cmx, cmy, offset0[0], offset0[1], outcode0);
final int nSplits = findSubdivPoints(curve, mid, subdivTs, 8, lineWidth2);
@@ -1034,16 +1202,36 @@
emitLineToRev(r[kind - 2], r[kind - 1]);
}
- this.cmx = (l[kind - 2] - r[kind - 2]) / 2.0f;
- this.cmy = (l[kind - 1] - r[kind - 1]) / 2.0f;
- this.cdx = dxf;
- this.cdy = dyf;
+ this.prev = DRAWING_OP_TO;
this.cx0 = xf;
this.cy0 = yf;
- this.prev = DRAWING_OP_TO;
+ this.cdx = dxf;
+ this.cdy = dyf;
+ this.cmx = (l[kind - 2] - r[kind - 2]) / 2.0f;
+ this.cmy = (l[kind - 1] - r[kind - 1]) / 2.0f;
}
- @Override public void quadTo(float x1, float y1, float x2, float y2) {
+ @Override
+ public void quadTo(final float x1, final float y1,
+ final float x2, final float y2)
+ {
+ final int outcode0 = this.cOutCode;
+ if (clipRect != null) {
+ final int outcode2 = Helpers.outcode(x2, y2, clipRect);
+ this.cOutCode = outcode2;
+
+ if (outcode2 != 0) {
+ final int outcode1 = Helpers.outcode(x1, y1, clipRect);
+
+ // basic rejection criteria
+ if ((outcode0 & outcode1 & outcode2) != 0) {
+ moveTo(x2, y2, outcode0);
+ opened = true;
+ return;
+ }
+ }
+ }
+
final float[] mid = middle;
mid[0] = cx0; mid[1] = cy0;
@@ -1051,7 +1239,7 @@
mid[4] = x2; mid[5] = y2;
// need these so we can update the state at the end of this method
- final float xf = mid[4], yf = mid[5];
+ final float xf = x2, yf = y2;
float dxs = mid[2] - mid[0];
float dys = mid[3] - mid[1];
float dxf = mid[4] - mid[2];
@@ -1062,6 +1250,10 @@
}
if (dxs == 0.0f && dys == 0.0f) {
// this happens if the "curve" is just a point
+ // fix outcode0 for lineTo() call:
+ if (clipRect != null) {
+ this.cOutCode = outcode0;
+ }
lineTo(mid[0], mid[1]);
return;
}
@@ -1079,7 +1271,7 @@
}
computeOffset(dxs, dys, lineWidth2, offset0);
- drawJoin(cdx, cdy, cx0, cy0, dxs, dys, cmx, cmy, offset0[0], offset0[1]);
+ drawJoin(cdx, cdy, cx0, cy0, dxs, dys, cmx, cmy, offset0[0], offset0[1], outcode0);
int nSplits = findSubdivPoints(curve, mid, subdivTs, 6, lineWidth2);
@@ -1114,214 +1306,16 @@
emitLineToRev(r[kind - 2], r[kind - 1]);
}
- this.cmx = (l[kind - 2] - r[kind - 2]) / 2.0f;
- this.cmy = (l[kind - 1] - r[kind - 1]) / 2.0f;
- this.cdx = dxf;
- this.cdy = dyf;
+ this.prev = DRAWING_OP_TO;
this.cx0 = xf;
this.cy0 = yf;
- this.prev = DRAWING_OP_TO;
+ this.cdx = dxf;
+ this.cdy = dyf;
+ this.cmx = (l[kind - 2] - r[kind - 2]) / 2.0f;
+ this.cmy = (l[kind - 1] - r[kind - 1]) / 2.0f;
}
@Override public long getNativeConsumer() {
throw new InternalError("Stroker doesn't use a native consumer");
}
-
- // a stack of polynomial curves where each curve shares endpoints with
- // adjacent ones.
- static final class PolyStack {
- private static final byte TYPE_LINETO = (byte) 0;
- private static final byte TYPE_QUADTO = (byte) 1;
- private static final byte TYPE_CUBICTO = (byte) 2;
-
- // curves capacity = edges count (8192) = edges x 2 (coords)
- private static final int INITIAL_CURVES_COUNT = INITIAL_EDGES_COUNT << 1;
-
- // types capacity = edges count (4096)
- private static final int INITIAL_TYPES_COUNT = INITIAL_EDGES_COUNT;
-
- float[] curves;
- int end;
- byte[] curveTypes;
- int numCurves;
-
- // per-thread renderer context
- final RendererContext rdrCtx;
-
- // curves ref (dirty)
- final FloatArrayCache.Reference curves_ref;
- // curveTypes ref (dirty)
- final ByteArrayCache.Reference curveTypes_ref;
-
- // used marks (stats only)
- int curveTypesUseMark;
- int curvesUseMark;
-
- /**
- * Constructor
- * @param rdrCtx per-thread renderer context
- */
- PolyStack(final RendererContext rdrCtx) {
- this.rdrCtx = rdrCtx;
-
- curves_ref = rdrCtx.newDirtyFloatArrayRef(INITIAL_CURVES_COUNT); // 32K
- curves = curves_ref.initial;
-
- curveTypes_ref = rdrCtx.newDirtyByteArrayRef(INITIAL_TYPES_COUNT); // 4K
- curveTypes = curveTypes_ref.initial;
- numCurves = 0;
- end = 0;
-
- if (DO_STATS) {
- curveTypesUseMark = 0;
- curvesUseMark = 0;
- }
- }
-
- /**
- * Disposes this PolyStack:
- * clean up before reusing this instance
- */
- void dispose() {
- end = 0;
- numCurves = 0;
-
- if (DO_STATS) {
- rdrCtx.stats.stat_rdr_poly_stack_types.add(curveTypesUseMark);
- rdrCtx.stats.stat_rdr_poly_stack_curves.add(curvesUseMark);
- rdrCtx.stats.hist_rdr_poly_stack_curves.add(curvesUseMark);
-
- // reset marks
- curveTypesUseMark = 0;
- curvesUseMark = 0;
- }
-
- // Return arrays:
- // curves and curveTypes are kept dirty
- curves = curves_ref.putArray(curves);
- curveTypes = curveTypes_ref.putArray(curveTypes);
- }
-
- private void ensureSpace(final int n) {
- // use substraction to avoid integer overflow:
- if (curves.length - end < n) {
- if (DO_STATS) {
- rdrCtx.stats.stat_array_stroker_polystack_curves
- .add(end + n);
- }
- curves = curves_ref.widenArray(curves, end, end + n);
- }
- if (curveTypes.length <= numCurves) {
- if (DO_STATS) {
- rdrCtx.stats.stat_array_stroker_polystack_curveTypes
- .add(numCurves + 1);
- }
- curveTypes = curveTypes_ref.widenArray(curveTypes,
- numCurves,
- numCurves + 1);
- }
- }
-
- void pushCubic(float x0, float y0,
- float x1, float y1,
- float x2, float y2)
- {
- ensureSpace(6);
- curveTypes[numCurves++] = TYPE_CUBICTO;
- // we reverse the coordinate order to make popping easier
- final float[] _curves = curves;
- int e = end;
- _curves[e++] = x2; _curves[e++] = y2;
- _curves[e++] = x1; _curves[e++] = y1;
- _curves[e++] = x0; _curves[e++] = y0;
- end = e;
- }
-
- void pushQuad(float x0, float y0,
- float x1, float y1)
- {
- ensureSpace(4);
- curveTypes[numCurves++] = TYPE_QUADTO;
- final float[] _curves = curves;
- int e = end;
- _curves[e++] = x1; _curves[e++] = y1;
- _curves[e++] = x0; _curves[e++] = y0;
- end = e;
- }
-
- void pushLine(float x, float y) {
- ensureSpace(2);
- curveTypes[numCurves++] = TYPE_LINETO;
- curves[end++] = x; curves[end++] = y;
- }
-
- void popAll(PathConsumer2D io) {
- if (DO_STATS) {
- // update used marks:
- if (numCurves > curveTypesUseMark) {
- curveTypesUseMark = numCurves;
- }
- if (end > curvesUseMark) {
- curvesUseMark = end;
- }
- }
- final byte[] _curveTypes = curveTypes;
- final float[] _curves = curves;
- int nc = numCurves;
- int e = end;
-
- while (nc != 0) {
- switch(_curveTypes[--nc]) {
- case TYPE_LINETO:
- e -= 2;
- io.lineTo(_curves[e], _curves[e+1]);
- continue;
- case TYPE_QUADTO:
- e -= 4;
- io.quadTo(_curves[e+0], _curves[e+1],
- _curves[e+2], _curves[e+3]);
- continue;
- case TYPE_CUBICTO:
- e -= 6;
- io.curveTo(_curves[e+0], _curves[e+1],
- _curves[e+2], _curves[e+3],
- _curves[e+4], _curves[e+5]);
- continue;
- default:
- }
- }
- numCurves = 0;
- end = 0;
- }
-
- @Override
- public String toString() {
- String ret = "";
- int nc = numCurves;
- int last = end;
- int len;
- while (nc != 0) {
- switch(curveTypes[--nc]) {
- case TYPE_LINETO:
- len = 2;
- ret += "line: ";
- break;
- case TYPE_QUADTO:
- len = 4;
- ret += "quad: ";
- break;
- case TYPE_CUBICTO:
- len = 6;
- ret += "cubic: ";
- break;
- default:
- len = 0;
- }
- last -= len;
- ret += Arrays.toString(Arrays.copyOfRange(curves, last, last+len))
- + "\n";
- }
- return ret;
- }
- }
}
--- old/src/java.desktop/share/classes/sun/java2d/marlin/TransformingPathConsumer2D.java 2017-08-28 16:48:33.181663138 +0200
+++ new/src/java.desktop/share/classes/sun/java2d/marlin/TransformingPathConsumer2D.java 2017-08-28 16:48:33.041663140 +0200
@@ -28,24 +28,58 @@
import sun.awt.geom.PathConsumer2D;
import java.awt.geom.AffineTransform;
import java.awt.geom.Path2D;
+import sun.java2d.marlin.Helpers.PolyStack;
final class TransformingPathConsumer2D {
- TransformingPathConsumer2D() {
- // used by RendererContext
- }
+ private final RendererContext rdrCtx;
+
+ // recycled ClosedPathDetector instance from detectClosedPath()
+ private final ClosedPathDetector cpDetector;
// recycled PathConsumer2D instance from wrapPath2d()
private final Path2DWrapper wp_Path2DWrapper = new Path2DWrapper();
+ // recycled PathConsumer2D instances from deltaTransformConsumer()
+ private final DeltaScaleFilter dt_DeltaScaleFilter = new DeltaScaleFilter();
+ private final DeltaTransformFilter dt_DeltaTransformFilter = new DeltaTransformFilter();
+
+ // recycled PathConsumer2D instances from inverseDeltaTransformConsumer()
+ private final DeltaScaleFilter iv_DeltaScaleFilter = new DeltaScaleFilter();
+ private final DeltaTransformFilter iv_DeltaTransformFilter = new DeltaTransformFilter();
+
+ // recycled PathTracer instances from tracer...() methods
+ private final PathTracer tracerInput = new PathTracer("[Input]");
+ private final PathTracer tracerCPDetector = new PathTracer("ClosedPathDetector");
+ private final PathTracer tracerStroker = new PathTracer("Stroker");
+
+ TransformingPathConsumer2D(final RendererContext rdrCtx) {
+ // used by RendererContext
+ this.rdrCtx = rdrCtx;
+ this.cpDetector = new ClosedPathDetector(rdrCtx);
+ }
+
PathConsumer2D wrapPath2d(Path2D.Float p2d)
{
return wp_Path2DWrapper.init(p2d);
}
- // recycled PathConsumer2D instances from deltaTransformConsumer()
- private final DeltaScaleFilter dt_DeltaScaleFilter = new DeltaScaleFilter();
- private final DeltaTransformFilter dt_DeltaTransformFilter = new DeltaTransformFilter();
+ PathConsumer2D traceInput(PathConsumer2D out) {
+ return tracerInput.init(out);
+ }
+
+ PathConsumer2D traceClosedPathDetector(PathConsumer2D out) {
+ return tracerCPDetector.init(out);
+ }
+
+ PathConsumer2D traceStroker(PathConsumer2D out) {
+ return tracerStroker.init(out);
+ }
+
+ PathConsumer2D detectClosedPath(PathConsumer2D out)
+ {
+ return cpDetector.init(out);
+ }
PathConsumer2D deltaTransformConsumer(PathConsumer2D out,
AffineTransform at)
@@ -53,25 +87,98 @@
if (at == null) {
return out;
}
- float mxx = (float) at.getScaleX();
- float mxy = (float) at.getShearX();
- float myx = (float) at.getShearY();
- float myy = (float) at.getScaleY();
+ final float mxx = (float) at.getScaleX();
+ final float mxy = (float) at.getShearX();
+ final float myx = (float) at.getShearY();
+ final float myy = (float) at.getScaleY();
if (mxy == 0.0f && myx == 0.0f) {
if (mxx == 1.0f && myy == 1.0f) {
return out;
} else {
+ // Scale only
+ if (rdrCtx.doClip) {
+ // adjust clip rectangle (ymin, ymax, xmin, xmax):
+ adjustClipScale(rdrCtx.clipRect, mxx, myy);
+ }
return dt_DeltaScaleFilter.init(out, mxx, myy);
}
} else {
+ if (rdrCtx.doClip) {
+ // adjust clip rectangle (ymin, ymax, xmin, xmax):
+ adjustClipInverseDelta(rdrCtx.clipRect, mxx, mxy, myx, myy);
+ }
return dt_DeltaTransformFilter.init(out, mxx, mxy, myx, myy);
}
}
- // recycled PathConsumer2D instances from inverseDeltaTransformConsumer()
- private final DeltaScaleFilter iv_DeltaScaleFilter = new DeltaScaleFilter();
- private final DeltaTransformFilter iv_DeltaTransformFilter = new DeltaTransformFilter();
+ private static void adjustClipOffset(final float[] clipRect) {
+ clipRect[0] += Renderer.RDR_OFFSET_Y;
+ clipRect[1] += Renderer.RDR_OFFSET_Y;
+ clipRect[2] += Renderer.RDR_OFFSET_X;
+ clipRect[3] += Renderer.RDR_OFFSET_X;
+ }
+
+ private static void adjustClipScale(final float[] clipRect,
+ final float mxx, final float myy)
+ {
+ adjustClipOffset(clipRect);
+
+ // Adjust the clipping rectangle (iv_DeltaScaleFilter):
+ clipRect[0] /= myy;
+ clipRect[1] /= myy;
+ clipRect[2] /= mxx;
+ clipRect[3] /= mxx;
+ }
+
+ private static void adjustClipInverseDelta(final float[] clipRect,
+ final float mxx, final float mxy,
+ final float myx, final float myy)
+ {
+ adjustClipOffset(clipRect);
+
+ // Adjust the clipping rectangle (iv_DeltaTransformFilter):
+ final float det = mxx * myy - mxy * myx;
+ final float imxx = myy / det;
+ final float imxy = -mxy / det;
+ final float imyx = -myx / det;
+ final float imyy = mxx / det;
+
+ float xmin, xmax, ymin, ymax;
+ float x, y;
+ // xmin, ymin:
+ x = clipRect[2] * imxx + clipRect[0] * imxy;
+ y = clipRect[2] * imyx + clipRect[0] * imyy;
+
+ xmin = xmax = x;
+ ymin = ymax = y;
+
+ // xmax, ymin:
+ x = clipRect[3] * imxx + clipRect[0] * imxy;
+ y = clipRect[3] * imyx + clipRect[0] * imyy;
+
+ if (x < xmin) { xmin = x; } else if (x > xmax) { xmax = x; }
+ if (y < ymin) { ymin = y; } else if (y > ymax) { ymax = y; }
+
+ // xmin, ymax:
+ x = clipRect[2] * imxx + clipRect[1] * imxy;
+ y = clipRect[2] * imyx + clipRect[1] * imyy;
+
+ if (x < xmin) { xmin = x; } else if (x > xmax) { xmax = x; }
+ if (y < ymin) { ymin = y; } else if (y > ymax) { ymax = y; }
+
+ // xmax, ymax:
+ x = clipRect[3] * imxx + clipRect[1] * imxy;
+ y = clipRect[3] * imyx + clipRect[1] * imyy;
+
+ if (x < xmin) { xmin = x; } else if (x > xmax) { xmax = x; }
+ if (y < ymin) { ymin = y; } else if (y > ymax) { ymax = y; }
+
+ clipRect[0] = ymin;
+ clipRect[1] = ymax;
+ clipRect[2] = xmin;
+ clipRect[3] = xmax;
+ }
PathConsumer2D inverseDeltaTransformConsumer(PathConsumer2D out,
AffineTransform at)
@@ -91,7 +198,7 @@
return iv_DeltaScaleFilter.init(out, 1.0f/mxx, 1.0f/myy);
}
} else {
- float det = mxx * myy - mxy * myx;
+ final float det = mxx * myy - mxy * myx;
return iv_DeltaTransformFilter.init(out,
myy / det,
-mxy / det,
@@ -100,7 +207,6 @@
}
}
-
static final class DeltaScaleFilter implements PathConsumer2D {
private PathConsumer2D out;
private float sx, sy;
@@ -271,6 +377,154 @@
}
@Override
+ public long getNativeConsumer() {
+ throw new InternalError("Not using a native peer");
+ }
+ }
+
+ static final class ClosedPathDetector implements PathConsumer2D {
+
+ private final RendererContext rdrCtx;
+ private final PolyStack stack;
+
+ private PathConsumer2D out;
+
+ ClosedPathDetector(final RendererContext rdrCtx) {
+ this.rdrCtx = rdrCtx;
+ this.stack = (rdrCtx.stats != null) ?
+ new PolyStack(rdrCtx,
+ rdrCtx.stats.stat_cpd_polystack_types,
+ rdrCtx.stats.stat_cpd_polystack_curves,
+ rdrCtx.stats.hist_cpd_polystack_curves,
+ rdrCtx.stats.stat_array_cpd_polystack_curves,
+ rdrCtx.stats.stat_array_cpd_polystack_types)
+ : new PolyStack(rdrCtx);
+ }
+
+ ClosedPathDetector init(PathConsumer2D out) {
+ this.out = out;
+ return this; // fluent API
+ }
+
+ /**
+ * Disposes this instance:
+ * clean up before reusing this instance
+ */
+ void dispose() {
+ stack.dispose();
+ }
+
+ @Override
+ public void pathDone() {
+ // previous path is not closed:
+ finish(false);
+ out.pathDone();
+
+ // TODO: fix possible leak if exception happened
+ // Dispose this instance:
+ dispose();
+ }
+
+ @Override
+ public void closePath() {
+ // path is closed
+ finish(true);
+ out.closePath();
+ }
+
+ @Override
+ public void moveTo(float x0, float y0) {
+ // previous path is not closed:
+ finish(false);
+ out.moveTo(x0, y0);
+ }
+
+ private void finish(final boolean closed) {
+ rdrCtx.closedPath = closed;
+ stack.pullAll(out);
+ }
+
+ @Override
+ public void lineTo(float x1, float y1) {
+ stack.pushLine(x1, y1);
+ }
+
+ @Override
+ public void curveTo(float x3, float y3,
+ float x2, float y2,
+ float x1, float y1)
+ {
+ stack.pushCubic(x1, y1, x2, y2, x3, y3);
+ }
+
+ @Override
+ public void quadTo(float x2, float y2, float x1, float y1) {
+ stack.pushQuad(x1, y1, x2, y2);
+ }
+
+ @Override
+ public long getNativeConsumer() {
+ throw new InternalError("Not using a native peer");
+ }
+ }
+
+ static final class PathTracer implements PathConsumer2D {
+ private final String prefix;
+ private PathConsumer2D out;
+
+ PathTracer(String name) {
+ this.prefix = name + ": ";
+ }
+
+ PathTracer init(PathConsumer2D out) {
+ this.out = out;
+ return this; // fluent API
+ }
+
+ @Override
+ public void moveTo(float x0, float y0) {
+ log("moveTo (" + x0 + ", " + y0 + ')');
+ out.moveTo(x0, y0);
+ }
+
+ @Override
+ public void lineTo(float x1, float y1) {
+ log("lineTo (" + x1 + ", " + y1 + ')');
+ out.lineTo(x1, y1);
+ }
+
+ @Override
+ public void curveTo(float x1, float y1,
+ float x2, float y2,
+ float x3, float y3)
+ {
+ log("curveTo P1(" + x1 + ", " + y1 + ") P2(" + x2 + ", " + y2 + ") P3(" + x3 + ", " + y3 + ')');
+ out.curveTo(x1, y1, x2, y2, x3, y3);
+ }
+
+ @Override
+ public void quadTo(float x1, float y1, float x2, float y2) {
+ log("quadTo P1(" + x1 + ", " + y1 + ") P2(" + x2 + ", " + y2 + ')');
+ out.quadTo(x1, y1, x2, y2);
+ }
+
+ @Override
+ public void closePath() {
+ log("closePath");
+ out.closePath();
+ }
+
+ @Override
+ public void pathDone() {
+ log("pathDone");
+ out.pathDone();
+ }
+
+ private void log(final String message) {
+ System.out.println(prefix + message);
+ }
+
+ @Override
public long getNativeConsumer() {
throw new InternalError("Not using a native peer");
}
--- old/src/java.desktop/share/classes/sun/java2d/marlin/Version.java 2017-08-28 16:48:33.685663131 +0200
+++ new/src/java.desktop/share/classes/sun/java2d/marlin/Version.java 2017-08-28 16:48:33.541663133 +0200
@@ -27,7 +27,7 @@
public final class Version {
- private static final String VERSION = "marlin-0.7.5-Unsafe-OpenJDK";
+ private static final String VERSION = "marlin-0.8.0-Unsafe-OpenJDK";
public static String getVersion() {
return VERSION;