8198885: upgrade Marlin (java2d) to 0.9.1
Summary: clipping implemented in Dasher (curve subdivision at clip edges) + higher quality(curve, subpixels) + new path simplifier
Reviewed-by: prr, serb
--- a/src/java.desktop/share/classes/sun/java2d/marlin/ByteArrayCache.java Fri Mar 23 13:43:39 2018 -0700
+++ b/src/java.desktop/share/classes/sun/java2d/marlin/ByteArrayCache.java Tue Mar 27 22:09:43 2018 +0200
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2015, 2017, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2015, 2018, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -99,7 +99,7 @@
Reference(final ByteArrayCache cache, final int initialSize) {
this.cache = cache;
this.clean = cache.clean;
- this.initial = createArray(initialSize, clean);
+ this.initial = createArray(initialSize);
if (DO_STATS) {
cache.stats.totalInitial += initialSize;
}
@@ -116,7 +116,7 @@
logInfo(getLogPrefix(clean) + "ByteArrayCache: "
+ "getArray[oversize]: length=\t" + length);
}
- return createArray(length, clean);
+ return createArray(length);
}
byte[] widenArray(final byte[] array, final int usedSize,
@@ -202,7 +202,7 @@
if (DO_STATS) {
stats.createOp++;
}
- return createArray(arraySize, clean);
+ return createArray(arraySize);
}
void putArray(final byte[] array)
@@ -229,12 +229,8 @@
}
}
- static byte[] createArray(final int length, final boolean clean) {
- if (clean) {
- return new byte[length];
- }
- // use JDK9 Unsafe.allocateUninitializedArray(class, length):
- return (byte[]) OffHeapArray.UNSAFE.allocateUninitializedArray(byte.class, length);
+ static byte[] createArray(final int length) {
+ return new byte[length];
}
static void fill(final byte[] array, final int fromIndex,
--- a/src/java.desktop/share/classes/sun/java2d/marlin/Curve.java Fri Mar 23 13:43:39 2018 -0700
+++ b/src/java.desktop/share/classes/sun/java2d/marlin/Curve.java Tue Mar 27 22:09:43 2018 +0200
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2007, 2017, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2007, 2018, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -33,86 +33,94 @@
Curve() {
}
- void set(float[] points, int type) {
- switch(type) {
- case 8:
+ void set(final float[] points, final int type) {
+ // if instead of switch (perf + most probable cases first)
+ if (type == 8) {
set(points[0], points[1],
points[2], points[3],
points[4], points[5],
points[6], points[7]);
- return;
- case 6:
+ } else if (type == 4) {
+ set(points[0], points[1],
+ points[2], points[3]);
+ } else {
set(points[0], points[1],
points[2], points[3],
points[4], points[5]);
- return;
- default:
- throw new InternalError("Curves can only be cubic or quadratic");
}
}
- void set(float x1, float y1,
- float x2, float y2,
- float x3, float y3,
- float x4, float y4)
+ void set(final float x1, final float y1,
+ final float x2, final float y2,
+ final float x3, final float y3,
+ final float x4, final float y4)
{
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;
+ ax = (x4 - x1) - dx32; // A = P3 - P0 - 3 (P2 - P1) = (P3 - P0) + 3 (P1 - P2)
ay = (y4 - y1) - dy32;
- bx = (dx32 - dx21);
+ bx = (dx32 - dx21); // B = 3 (P2 - P1) - 3(P1 - P0) = 3 (P2 + P0) - 6 P1
by = (dy32 - dy21);
- cx = dx21;
+ cx = dx21; // C = 3 (P1 - P0)
cy = dy21;
- dx = x1;
+ dx = x1; // D = P0
dy = y1;
- dax = 3.0f * ax; day = 3.0f * ay;
- dbx = 2.0f * bx; dby = 2.0f * by;
+ dax = 3.0f * ax;
+ day = 3.0f * ay;
+ dbx = 2.0f * bx;
+ dby = 2.0f * by;
}
- void set(float x1, float y1,
- float x2, float y2,
- float x3, float y3)
+ void set(final float x1, final float y1,
+ final float x2, final float y2,
+ final float x3, final float y3)
{
final float dx21 = (x2 - x1);
final float dy21 = (y2 - y1);
- ax = 0.0f; ay = 0.0f;
- bx = (x3 - x2) - dx21;
+ ax = 0.0f; // A = 0
+ ay = 0.0f;
+ bx = (x3 - x2) - dx21; // B = P3 - P0 - 2 P2
by = (y3 - y2) - dy21;
- cx = 2.0f * dx21;
+ cx = 2.0f * dx21; // C = 2 (P2 - P1)
cy = 2.0f * dy21;
- dx = x1;
+ dx = x1; // D = P1
dy = y1;
- dax = 0.0f; day = 0.0f;
- dbx = 2.0f * bx; dby = 2.0f * by;
+ dax = 0.0f;
+ day = 0.0f;
+ dbx = 2.0f * bx;
+ dby = 2.0f * by;
}
- float xat(float t) {
- return t * (t * (t * ax + bx) + cx) + dx;
- }
- float yat(float t) {
- return t * (t * (t * ay + by) + cy) + dy;
+ void set(final float x1, final float y1,
+ final float x2, final float y2)
+ {
+ final float dx21 = (x2 - x1);
+ final float dy21 = (y2 - y1);
+ ax = 0.0f; // A = 0
+ ay = 0.0f;
+ bx = 0.0f; // B = 0
+ by = 0.0f;
+ cx = dx21; // C = (P2 - P1)
+ cy = dy21;
+ dx = x1; // D = P1
+ dy = y1;
+ dax = 0.0f;
+ day = 0.0f;
+ dbx = 0.0f;
+ dby = 0.0f;
}
- float dxat(float t) {
- return t * (t * dax + dbx) + cx;
- }
-
- float dyat(float t) {
- return t * (t * day + dby) + cy;
- }
-
- int dxRoots(float[] roots, int off) {
+ int dxRoots(final float[] roots, final int off) {
return Helpers.quadraticRoots(dax, dbx, cx, roots, off);
}
- int dyRoots(float[] roots, int off) {
+ int dyRoots(final float[] roots, final int off) {
return Helpers.quadraticRoots(day, dby, cy, roots, off);
}
- int infPoints(float[] pts, int off) {
+ int infPoints(final float[] pts, final int off) {
// inflection point at t if -f'(t)x*f''(t)y + f'(t)y*f''(t)x == 0
// Fortunately, this turns out to be quadratic, so there are at
// most 2 inflection points.
@@ -123,19 +131,30 @@
return Helpers.quadraticRoots(a, b, c, pts, off);
}
+ int xPoints(final float[] ts, final int off, final float x)
+ {
+ return Helpers.cubicRootsInAB(ax, bx, cx, dx - x, ts, off, 0.0f, 1.0f);
+ }
+
+ int yPoints(final float[] ts, final int off, final float y)
+ {
+ return Helpers.cubicRootsInAB(ay, by, cy, dy - y, ts, off, 0.0f, 1.0f);
+ }
+
// finds points where the first and second derivative are
// perpendicular. This happens when g(t) = f'(t)*f''(t) == 0 (where
// * is a dot product). Unfortunately, we have to solve a cubic.
- private int perpendiculardfddf(float[] pts, int off) {
+ private int perpendiculardfddf(final float[] pts, final int off) {
assert pts.length >= off + 4;
// these are the coefficients of some multiple of g(t) (not g(t),
// because the roots of a polynomial are not changed after multiplication
// by a constant, and this way we save a few multiplications).
- final float a = 2.0f * (dax*dax + day*day);
- final float b = 3.0f * (dax*dbx + day*dby);
- final float c = 2.0f * (dax*cx + day*cy) + dbx*dbx + dby*dby;
- final float d = dbx*cx + dby*cy;
+ final float a = 2.0f * (dax * dax + day * day);
+ final float b = 3.0f * (dax * dbx + day * dby);
+ final float c = 2.0f * (dax * cx + day * cy) + dbx * dbx + dby * dby;
+ final float d = dbx * cx + dby * cy;
+
return Helpers.cubicRootsInAB(a, b, c, d, pts, off, 0.0f, 1.0f);
}
@@ -152,22 +171,24 @@
// at most 4 sub-intervals of (0,1). ROC has asymptotes at inflection
// points, so roc-w can have at least 6 roots. This shouldn't be a
// problem for what we're trying to do (draw a nice looking curve).
- int rootsOfROCMinusW(float[] roots, int off, final float w, final float err) {
+ int rootsOfROCMinusW(final float[] roots, final int off, final float w2, final float err) {
// no OOB exception, because by now off<=6, and roots.length >= 10
assert off <= 6 && roots.length >= 10;
+
int ret = off;
- int numPerpdfddf = perpendiculardfddf(roots, off);
- float t0 = 0.0f, ft0 = ROCsq(t0) - w*w;
- roots[off + numPerpdfddf] = 1.0f; // always check interval end points
- numPerpdfddf++;
- for (int i = off; i < off + numPerpdfddf; i++) {
- float t1 = roots[i], ft1 = ROCsq(t1) - w*w;
+ final int end = off + perpendiculardfddf(roots, off);
+ roots[end] = 1.0f; // always check interval end points
+
+ float t0 = 0.0f, ft0 = ROCsq(t0) - w2;
+
+ for (int i = off; i <= end; i++) {
+ float t1 = roots[i], ft1 = ROCsq(t1) - w2;
if (ft0 == 0.0f) {
roots[ret++] = t0;
} else if (ft1 * ft0 < 0.0f) { // have opposite signs
// (ROC(t)^2 == w^2) == (ROC(t) == w) is true because
// ROC(t) >= 0 for all t.
- roots[ret++] = falsePositionROCsqMinusX(t0, t1, w*w, err);
+ roots[ret++] = falsePositionROCsqMinusX(t0, t1, w2, err);
}
t0 = t1;
ft0 = ft1;
@@ -176,9 +197,9 @@
return ret - off;
}
- private static float eliminateInf(float x) {
+ private static float eliminateInf(final float x) {
return (x == Float.POSITIVE_INFINITY ? Float.MAX_VALUE :
- (x == Float.NEGATIVE_INFINITY ? Float.MIN_VALUE : x));
+ (x == Float.NEGATIVE_INFINITY ? Float.MIN_VALUE : x));
}
// A slight modification of the false position algorithm on wikipedia.
@@ -188,17 +209,18 @@
// expressions make it into the language), depending on how closures
// and turn out. Same goes for the newton's method
// algorithm in Helpers.java
- private float falsePositionROCsqMinusX(float x0, float x1,
- final float x, final float err)
+ private float falsePositionROCsqMinusX(final float t0, final float t1,
+ final float w2, final float err)
{
final int iterLimit = 100;
int side = 0;
- float t = x1, ft = eliminateInf(ROCsq(t) - x);
- float s = x0, fs = eliminateInf(ROCsq(s) - x);
+ float t = t1, ft = eliminateInf(ROCsq(t) - w2);
+ float s = t0, fs = eliminateInf(ROCsq(s) - w2);
float r = s, fr;
+
for (int i = 0; i < iterLimit && Math.abs(t - s) > err * Math.abs(t + s); i++) {
r = (fs * t - ft * s) / (fs - ft);
- fr = ROCsq(r) - x;
+ fr = ROCsq(r) - w2;
if (sameSign(fr, ft)) {
ft = fr; t = r;
if (side < 0) {
@@ -207,7 +229,7 @@
} else {
side = -1;
}
- } else if (fr * fs > 0) {
+ } else if (fr * fs > 0.0f) {
fs = fr; s = r;
if (side > 0) {
ft /= (1 << side);
@@ -222,7 +244,7 @@
return r;
}
- private static boolean sameSign(float x, float y) {
+ private static boolean sameSign(final float x, final float y) {
// another way is to test if x*y > 0. This is bad for small x, y.
return (x < 0.0f && y < 0.0f) || (x > 0.0f && y > 0.0f);
}
@@ -230,14 +252,13 @@
// returns the radius of curvature squared at t of this curve
// see http://en.wikipedia.org/wiki/Radius_of_curvature_(applications)
private float ROCsq(final float t) {
- // dx=xat(t) and dy=yat(t). These calls have been inlined for efficiency
final float dx = t * (t * dax + dbx) + cx;
final float dy = t * (t * day + dby) + cy;
final float ddx = 2.0f * dax * t + dbx;
final float ddy = 2.0f * day * t + dby;
- final float dx2dy2 = dx*dx + dy*dy;
- final float ddx2ddy2 = ddx*ddx + ddy*ddy;
- final float ddxdxddydy = ddx*dx + ddy*dy;
- return dx2dy2*((dx2dy2*dx2dy2) / (dx2dy2 * ddx2ddy2 - ddxdxddydy*ddxdxddydy));
+ final float dx2dy2 = dx * dx + dy * dy;
+ final float ddx2ddy2 = ddx * ddx + ddy * ddy;
+ final float ddxdxddydy = ddx * dx + ddy * dy;
+ return dx2dy2 * ((dx2dy2 * dx2dy2) / (dx2dy2 * ddx2ddy2 - ddxdxddydy * ddxdxddydy));
}
}
--- a/src/java.desktop/share/classes/sun/java2d/marlin/DCurve.java Fri Mar 23 13:43:39 2018 -0700
+++ b/src/java.desktop/share/classes/sun/java2d/marlin/DCurve.java Tue Mar 27 22:09:43 2018 +0200
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2007, 2017, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2007, 2018, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -33,86 +33,94 @@
DCurve() {
}
- void set(double[] points, int type) {
- switch(type) {
- case 8:
+ void set(final double[] points, final int type) {
+ // if instead of switch (perf + most probable cases first)
+ if (type == 8) {
set(points[0], points[1],
points[2], points[3],
points[4], points[5],
points[6], points[7]);
- return;
- case 6:
+ } else if (type == 4) {
+ set(points[0], points[1],
+ points[2], points[3]);
+ } else {
set(points[0], points[1],
points[2], points[3],
points[4], points[5]);
- return;
- default:
- throw new InternalError("Curves can only be cubic or quadratic");
}
}
- void set(double x1, double y1,
- double x2, double y2,
- double x3, double y3,
- double x4, double y4)
+ void set(final double x1, final double y1,
+ final double x2, final double y2,
+ final double x3, final double y3,
+ final double x4, final double y4)
{
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;
+ ax = (x4 - x1) - dx32; // A = P3 - P0 - 3 (P2 - P1) = (P3 - P0) + 3 (P1 - P2)
ay = (y4 - y1) - dy32;
- bx = (dx32 - dx21);
+ bx = (dx32 - dx21); // B = 3 (P2 - P1) - 3(P1 - P0) = 3 (P2 + P0) - 6 P1
by = (dy32 - dy21);
- cx = dx21;
+ cx = dx21; // C = 3 (P1 - P0)
cy = dy21;
- dx = x1;
+ dx = x1; // D = P0
dy = y1;
- dax = 3.0d * ax; day = 3.0d * ay;
- dbx = 2.0d * bx; dby = 2.0d * by;
+ dax = 3.0d * ax;
+ day = 3.0d * ay;
+ dbx = 2.0d * bx;
+ dby = 2.0d * by;
}
- void set(double x1, double y1,
- double x2, double y2,
- double x3, double y3)
+ void set(final double x1, final double y1,
+ final double x2, final double y2,
+ final double x3, final double y3)
{
final double dx21 = (x2 - x1);
final double dy21 = (y2 - y1);
- ax = 0.0d; ay = 0.0d;
- bx = (x3 - x2) - dx21;
+ ax = 0.0d; // A = 0
+ ay = 0.0d;
+ bx = (x3 - x2) - dx21; // B = P3 - P0 - 2 P2
by = (y3 - y2) - dy21;
- cx = 2.0d * dx21;
+ cx = 2.0d * dx21; // C = 2 (P2 - P1)
cy = 2.0d * dy21;
- dx = x1;
+ dx = x1; // D = P1
dy = y1;
- dax = 0.0d; day = 0.0d;
- dbx = 2.0d * bx; dby = 2.0d * by;
+ dax = 0.0d;
+ day = 0.0d;
+ dbx = 2.0d * bx;
+ dby = 2.0d * by;
}
- double xat(double t) {
- return t * (t * (t * ax + bx) + cx) + dx;
- }
- double yat(double t) {
- return t * (t * (t * ay + by) + cy) + dy;
+ void set(final double x1, final double y1,
+ final double x2, final double y2)
+ {
+ final double dx21 = (x2 - x1);
+ final double dy21 = (y2 - y1);
+ ax = 0.0d; // A = 0
+ ay = 0.0d;
+ bx = 0.0d; // B = 0
+ by = 0.0d;
+ cx = dx21; // C = (P2 - P1)
+ cy = dy21;
+ dx = x1; // D = P1
+ dy = y1;
+ dax = 0.0d;
+ day = 0.0d;
+ dbx = 0.0d;
+ dby = 0.0d;
}
- double dxat(double t) {
- return t * (t * dax + dbx) + cx;
- }
-
- double dyat(double t) {
- return t * (t * day + dby) + cy;
- }
-
- int dxRoots(double[] roots, int off) {
+ int dxRoots(final double[] roots, final int off) {
return DHelpers.quadraticRoots(dax, dbx, cx, roots, off);
}
- int dyRoots(double[] roots, int off) {
+ int dyRoots(final double[] roots, final int off) {
return DHelpers.quadraticRoots(day, dby, cy, roots, off);
}
- int infPoints(double[] pts, int off) {
+ int infPoints(final double[] pts, final int off) {
// inflection point at t if -f'(t)x*f''(t)y + f'(t)y*f''(t)x == 0
// Fortunately, this turns out to be quadratic, so there are at
// most 2 inflection points.
@@ -123,19 +131,30 @@
return DHelpers.quadraticRoots(a, b, c, pts, off);
}
+ int xPoints(final double[] ts, final int off, final double x)
+ {
+ return DHelpers.cubicRootsInAB(ax, bx, cx, dx - x, ts, off, 0.0d, 1.0d);
+ }
+
+ int yPoints(final double[] ts, final int off, final double y)
+ {
+ return DHelpers.cubicRootsInAB(ay, by, cy, dy - y, ts, off, 0.0d, 1.0d);
+ }
+
// finds points where the first and second derivative are
// perpendicular. This happens when g(t) = f'(t)*f''(t) == 0 (where
// * is a dot product). Unfortunately, we have to solve a cubic.
- private int perpendiculardfddf(double[] pts, int off) {
+ private int perpendiculardfddf(final double[] pts, final int off) {
assert pts.length >= off + 4;
// these are the coefficients of some multiple of g(t) (not g(t),
// because the roots of a polynomial are not changed after multiplication
// by a constant, and this way we save a few multiplications).
- final double a = 2.0d * (dax*dax + day*day);
- final double b = 3.0d * (dax*dbx + day*dby);
- final double c = 2.0d * (dax*cx + day*cy) + dbx*dbx + dby*dby;
- final double d = dbx*cx + dby*cy;
+ final double a = 2.0d * (dax * dax + day * day);
+ final double b = 3.0d * (dax * dbx + day * dby);
+ final double c = 2.0d * (dax * cx + day * cy) + dbx * dbx + dby * dby;
+ final double d = dbx * cx + dby * cy;
+
return DHelpers.cubicRootsInAB(a, b, c, d, pts, off, 0.0d, 1.0d);
}
@@ -152,22 +171,24 @@
// at most 4 sub-intervals of (0,1). ROC has asymptotes at inflection
// points, so roc-w can have at least 6 roots. This shouldn't be a
// problem for what we're trying to do (draw a nice looking curve).
- int rootsOfROCMinusW(double[] roots, int off, final double w, final double err) {
+ int rootsOfROCMinusW(final double[] roots, final int off, final double w2, final double err) {
// no OOB exception, because by now off<=6, and roots.length >= 10
assert off <= 6 && roots.length >= 10;
+
int ret = off;
- int numPerpdfddf = perpendiculardfddf(roots, off);
- double t0 = 0.0d, ft0 = ROCsq(t0) - w*w;
- roots[off + numPerpdfddf] = 1.0d; // always check interval end points
- numPerpdfddf++;
- for (int i = off; i < off + numPerpdfddf; i++) {
- double t1 = roots[i], ft1 = ROCsq(t1) - w*w;
+ final int end = off + perpendiculardfddf(roots, off);
+ roots[end] = 1.0d; // always check interval end points
+
+ double t0 = 0.0d, ft0 = ROCsq(t0) - w2;
+
+ for (int i = off; i <= end; i++) {
+ double t1 = roots[i], ft1 = ROCsq(t1) - w2;
if (ft0 == 0.0d) {
roots[ret++] = t0;
} else if (ft1 * ft0 < 0.0d) { // have opposite signs
// (ROC(t)^2 == w^2) == (ROC(t) == w) is true because
// ROC(t) >= 0 for all t.
- roots[ret++] = falsePositionROCsqMinusX(t0, t1, w*w, err);
+ roots[ret++] = falsePositionROCsqMinusX(t0, t1, w2, err);
}
t0 = t1;
ft0 = ft1;
@@ -176,9 +197,9 @@
return ret - off;
}
- private static double eliminateInf(double x) {
+ private static double eliminateInf(final double x) {
return (x == Double.POSITIVE_INFINITY ? Double.MAX_VALUE :
- (x == Double.NEGATIVE_INFINITY ? Double.MIN_VALUE : x));
+ (x == Double.NEGATIVE_INFINITY ? Double.MIN_VALUE : x));
}
// A slight modification of the false position algorithm on wikipedia.
@@ -188,17 +209,18 @@
// expressions make it into the language), depending on how closures
// and turn out. Same goes for the newton's method
// algorithm in DHelpers.java
- private double falsePositionROCsqMinusX(double x0, double x1,
- final double x, final double err)
+ private double falsePositionROCsqMinusX(final double t0, final double t1,
+ final double w2, final double err)
{
final int iterLimit = 100;
int side = 0;
- double t = x1, ft = eliminateInf(ROCsq(t) - x);
- double s = x0, fs = eliminateInf(ROCsq(s) - x);
+ double t = t1, ft = eliminateInf(ROCsq(t) - w2);
+ double s = t0, fs = eliminateInf(ROCsq(s) - w2);
double r = s, fr;
+
for (int i = 0; i < iterLimit && Math.abs(t - s) > err * Math.abs(t + s); i++) {
r = (fs * t - ft * s) / (fs - ft);
- fr = ROCsq(r) - x;
+ fr = ROCsq(r) - w2;
if (sameSign(fr, ft)) {
ft = fr; t = r;
if (side < 0) {
@@ -207,7 +229,7 @@
} else {
side = -1;
}
- } else if (fr * fs > 0) {
+ } else if (fr * fs > 0.0d) {
fs = fr; s = r;
if (side > 0) {
ft /= (1 << side);
@@ -222,7 +244,7 @@
return r;
}
- private static boolean sameSign(double x, double y) {
+ private static boolean sameSign(final double x, final double y) {
// another way is to test if x*y > 0. This is bad for small x, y.
return (x < 0.0d && y < 0.0d) || (x > 0.0d && y > 0.0d);
}
@@ -230,14 +252,13 @@
// returns the radius of curvature squared at t of this curve
// see http://en.wikipedia.org/wiki/Radius_of_curvature_(applications)
private double ROCsq(final double t) {
- // dx=xat(t) and dy=yat(t). These calls have been inlined for efficiency
final double dx = t * (t * dax + dbx) + cx;
final double dy = t * (t * day + dby) + cy;
final double ddx = 2.0d * dax * t + dbx;
final double ddy = 2.0d * day * t + dby;
- final double dx2dy2 = dx*dx + dy*dy;
- final double ddx2ddy2 = ddx*ddx + ddy*ddy;
- final double ddxdxddydy = ddx*dx + ddy*dy;
- return dx2dy2*((dx2dy2*dx2dy2) / (dx2dy2 * ddx2ddy2 - ddxdxddydy*ddxdxddydy));
+ final double dx2dy2 = dx * dx + dy * dy;
+ final double ddx2ddy2 = ddx * ddx + ddy * ddy;
+ final double ddxdxddydy = ddx * dx + ddy * dy;
+ return dx2dy2 * ((dx2dy2 * dx2dy2) / (dx2dy2 * ddx2ddy2 - ddxdxddydy * ddxdxddydy));
}
}
--- a/src/java.desktop/share/classes/sun/java2d/marlin/DDasher.java Fri Mar 23 13:43:39 2018 -0700
+++ b/src/java.desktop/share/classes/sun/java2d/marlin/DDasher.java Tue Mar 27 22:09:43 2018 +0200
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2007, 2017, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2007, 2018, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -26,6 +26,8 @@
package sun.java2d.marlin;
import java.util.Arrays;
+import sun.java2d.marlin.DTransformingPathConsumer2D.CurveBasicMonotonizer;
+import sun.java2d.marlin.DTransformingPathConsumer2D.CurveClipSplitter;
/**
* The <code>DDasher</code> class takes a series of linear commands
@@ -40,8 +42,9 @@
*/
final class DDasher implements DPathConsumer2D, MarlinConst {
- static final int REC_LIMIT = 4;
- static final double ERR = 0.01d;
+ /* huge circle with radius ~ 2E9 only needs 12 subdivision levels */
+ static final int REC_LIMIT = 16;
+ static final double CURVE_LEN_ERR = MarlinProperties.getCurveLengthError(); // 0.01 initial
static final double MIN_T_INC = 1.0d / (1 << REC_LIMIT);
// More than 24 bits of mantissa means we can no longer accurately
@@ -63,8 +66,10 @@
private boolean dashOn;
private double phase;
- private double sx, sy;
- private double x0, y0;
+ // The starting point of the path
+ private double sx0, sy0;
+ // the current point
+ private double cx0, cy0;
// temporary storage for the current curve
private final double[] curCurvepts;
@@ -75,11 +80,34 @@
// flag to recycle dash array copy
boolean recycleDashes;
+ // We don't emit the first dash right away. If we did, caps would be
+ // drawn on it, but we need joins to be drawn if there's a closePath()
+ // So, we store the path elements that make up the first dash in the
+ // buffer below.
+ private double[] firstSegmentsBuffer; // dynamic array
+ private int firstSegidx;
+
// dashes ref (dirty)
final DoubleArrayCache.Reference dashes_ref;
// firstSegmentsBuffer ref (dirty)
final DoubleArrayCache.Reference firstSegmentsBuffer_ref;
+ // Bounds of the drawing region, at pixel precision.
+ private double[] clipRect;
+
+ // the outcode of the current point
+ private int cOutCode = 0;
+
+ private boolean subdivide = DO_CLIP_SUBDIVIDER;
+
+ private final LengthIterator li = new LengthIterator();
+
+ private final CurveClipSplitter curveSplitter;
+
+ private double cycleLen;
+ private boolean outside;
+ private double totalSkipLen;
+
/**
* Constructs a <code>DDasher</code>.
* @param rdrCtx per-thread renderer context
@@ -95,6 +123,8 @@
// we need curCurvepts to be able to contain 2 curves because when
// dashing curves, we need to subdivide it
curCurvepts = new double[8 * 2];
+
+ this.curveSplitter = rdrCtx.curveClipSplitter;
}
/**
@@ -115,10 +145,13 @@
// Normalize so 0 <= phase < dash[0]
int sidx = 0;
dashOn = true;
+
double sum = 0.0d;
for (double d : dash) {
sum += d;
}
+ this.cycleLen = sum;
+
double cycles = phase / sum;
if (phase < 0.0d) {
if (-cycles >= MAX_CYCLES) {
@@ -167,6 +200,12 @@
this.recycleDashes = recycleDashes;
+ if (rdrCtx.doClip) {
+ this.clipRect = rdrCtx.clipRect;
+ } else {
+ this.clipRect = null;
+ this.cOutCode = 0;
+ }
return this; // fluent API
}
@@ -204,33 +243,42 @@
@Override
public void moveTo(final double x0, final double y0) {
if (firstSegidx != 0) {
- out.moveTo(sx, sy);
+ out.moveTo(sx0, sy0);
emitFirstSegments();
}
- needsMoveTo = true;
+ this.needsMoveTo = true;
this.idx = startIdx;
this.dashOn = this.startDashOn;
this.phase = this.startPhase;
- this.sx = x0;
- this.sy = y0;
- this.x0 = x0;
- this.y0 = y0;
+ this.cx0 = x0;
+ this.cy0 = y0;
+
+ // update starting point:
+ this.sx0 = x0;
+ this.sy0 = y0;
this.starting = true;
+
+ if (clipRect != null) {
+ final int outcode = DHelpers.outcode(x0, y0, clipRect);
+ this.cOutCode = outcode;
+ this.outside = false;
+ this.totalSkipLen = 0.0d;
+ }
}
private void emitSeg(double[] buf, int off, int type) {
switch (type) {
case 8:
- out.curveTo(buf[off+0], buf[off+1],
- buf[off+2], buf[off+3],
- buf[off+4], buf[off+5]);
+ out.curveTo(buf[off ], buf[off + 1],
+ buf[off + 2], buf[off + 3],
+ buf[off + 4], buf[off + 5]);
return;
case 6:
- out.quadTo(buf[off+0], buf[off+1],
- buf[off+2], buf[off+3]);
+ out.quadTo(buf[off ], buf[off + 1],
+ buf[off + 2], buf[off + 3]);
return;
case 4:
- out.lineTo(buf[off], buf[off+1]);
+ out.lineTo(buf[off], buf[off + 1]);
return;
default:
}
@@ -246,12 +294,6 @@
}
firstSegidx = 0;
}
- // We don't emit the first dash right away. If we did, caps would be
- // drawn on it, but we need joins to be drawn if there's a closePath()
- // So, we store the path elements that make up the first dash in the
- // buffer below.
- private double[] firstSegmentsBuffer; // dynamic array
- private int firstSegidx;
// precondition: pts must be in relative coordinates (relative to x0,y0)
private void goTo(final double[] pts, final int off, final int type,
@@ -267,7 +309,7 @@
} else {
if (needsMoveTo) {
needsMoveTo = false;
- out.moveTo(x0, y0);
+ out.moveTo(cx0, cy0);
}
emitSeg(pts, off, type);
}
@@ -278,8 +320,8 @@
}
needsMoveTo = true;
}
- this.x0 = x;
- this.y0 = y;
+ this.cx0 = x;
+ this.cy0 = y;
}
private void goTo_starting(final double[] pts, final int off, final int type) {
@@ -305,10 +347,56 @@
@Override
public void lineTo(final double x1, final double y1) {
- final double dx = x1 - x0;
- final double dy = y1 - y0;
+ final int outcode0 = this.cOutCode;
+
+ if (clipRect != null) {
+ final int outcode1 = DHelpers.outcode(x1, y1, clipRect);
+
+ // Should clip
+ final int orCode = (outcode0 | outcode1);
+
+ if (orCode != 0) {
+ final int sideCode = outcode0 & outcode1;
- double len = dx*dx + dy*dy;
+ // basic rejection criteria:
+ if (sideCode == 0) {
+ // ovelap clip:
+ if (subdivide) {
+ // avoid reentrance
+ subdivide = false;
+ // subdivide curve => callback with subdivided parts:
+ boolean ret = curveSplitter.splitLine(cx0, cy0, x1, y1,
+ orCode, this);
+ // reentrance is done:
+ subdivide = true;
+ if (ret) {
+ return;
+ }
+ }
+ // already subdivided so render it
+ } else {
+ this.cOutCode = outcode1;
+ skipLineTo(x1, y1);
+ return;
+ }
+ }
+
+ this.cOutCode = outcode1;
+
+ if (this.outside) {
+ this.outside = false;
+ // Adjust current index, phase & dash:
+ skipLen();
+ }
+ }
+ _lineTo(x1, y1);
+ }
+
+ private void _lineTo(final double x1, final double y1) {
+ final double dx = x1 - cx0;
+ final double dy = y1 - cy0;
+
+ double len = dx * dx + dy * dy;
if (len == 0.0d) {
return;
}
@@ -327,8 +415,7 @@
boolean _dashOn = dashOn;
double _phase = phase;
- double leftInThisDashSegment;
- double d, dashdx, dashdy, p;
+ double leftInThisDashSegment, d;
while (true) {
d = _dash[_idx];
@@ -349,24 +436,15 @@
_idx = (_idx + 1) % _dashLen;
_dashOn = !_dashOn;
}
-
- // Save local state:
- idx = _idx;
- dashOn = _dashOn;
- phase = _phase;
- return;
+ break;
}
- dashdx = d * cx;
- dashdy = d * cy;
-
if (_phase == 0.0d) {
- _curCurvepts[0] = x0 + dashdx;
- _curCurvepts[1] = y0 + dashdy;
+ _curCurvepts[0] = cx0 + d * cx;
+ _curCurvepts[1] = cy0 + d * cy;
} else {
- p = leftInThisDashSegment / d;
- _curCurvepts[0] = x0 + p * dashdx;
- _curCurvepts[1] = y0 + p * dashdy;
+ _curCurvepts[0] = cx0 + leftInThisDashSegment * cx;
+ _curCurvepts[1] = cy0 + leftInThisDashSegment * cy;
}
goTo(_curCurvepts, 0, 4, _dashOn);
@@ -377,19 +455,95 @@
_dashOn = !_dashOn;
_phase = 0.0d;
}
+ // Save local state:
+ idx = _idx;
+ dashOn = _dashOn;
+ phase = _phase;
+ }
+
+ private void skipLineTo(final double x1, final double y1) {
+ final double dx = x1 - cx0;
+ final double dy = y1 - cy0;
+
+ double len = dx * dx + dy * dy;
+ if (len != 0.0d) {
+ len = Math.sqrt(len);
+ }
+
+ // Accumulate skipped length:
+ this.outside = true;
+ this.totalSkipLen += len;
+
+ // Fix initial move:
+ this.needsMoveTo = true;
+ this.starting = false;
+
+ this.cx0 = x1;
+ this.cy0 = y1;
}
- // shared instance in DDasher
- private final LengthIterator li = new LengthIterator();
+ public void skipLen() {
+ double len = this.totalSkipLen;
+ this.totalSkipLen = 0.0d;
+
+ final double[] _dash = dash;
+ final int _dashLen = this.dashLen;
+
+ int _idx = idx;
+ boolean _dashOn = dashOn;
+ double _phase = phase;
+
+ // -2 to ensure having 2 iterations of the post-loop
+ // to compensate the remaining phase
+ final long fullcycles = (long)Math.floor(len / cycleLen) - 2L;
+
+ if (fullcycles > 0L) {
+ len -= cycleLen * fullcycles;
+
+ final long iterations = fullcycles * _dashLen;
+ _idx = (int) (iterations + _idx) % _dashLen;
+ _dashOn = (iterations + (_dashOn ? 1L : 0L) & 1L) == 1L;
+ }
+
+ double leftInThisDashSegment, d;
+
+ while (true) {
+ d = _dash[_idx];
+ leftInThisDashSegment = d - _phase;
+
+ if (len <= leftInThisDashSegment) {
+ // Advance phase within current dash segment
+ _phase += len;
+
+ // TODO: compare double values using epsilon:
+ if (len == leftInThisDashSegment) {
+ _phase = 0.0d;
+ _idx = (_idx + 1) % _dashLen;
+ _dashOn = !_dashOn;
+ }
+ break;
+ }
+
+ len -= leftInThisDashSegment;
+ // Advance to next dash segment
+ _idx = (_idx + 1) % _dashLen;
+ _dashOn = !_dashOn;
+ _phase = 0.0d;
+ }
+ // Save local state:
+ idx = _idx;
+ dashOn = _dashOn;
+ phase = _phase;
+ }
// preconditions: curCurvepts must be an array of length at least 2 * type,
// that contains the curve we want to dash in the first type elements
private void somethingTo(final int type) {
- if (pointCurve(curCurvepts, type)) {
+ final double[] _curCurvepts = curCurvepts;
+ if (pointCurve(_curCurvepts, type)) {
return;
}
final LengthIterator _li = li;
- final double[] _curCurvepts = curCurvepts;
final double[] _dash = dash;
final int _dashLen = this.dashLen;
@@ -401,17 +555,16 @@
// initially the current curve is at curCurvepts[0...type]
int curCurveoff = 0;
- double lastSplitT = 0.0d;
+ double prevT = 0.0d;
double t;
double leftInThisDashSegment = _dash[_idx] - _phase;
while ((t = _li.next(leftInThisDashSegment)) < 1.0d) {
if (t != 0.0d) {
- DHelpers.subdivideAt((t - lastSplitT) / (1.0d - lastSplitT),
+ DHelpers.subdivideAt((t - prevT) / (1.0d - prevT),
_curCurvepts, curCurveoff,
- _curCurvepts, 0,
- _curCurvepts, type, type);
- lastSplitT = t;
+ _curCurvepts, 0, type);
+ prevT = t;
goTo(_curCurvepts, 2, type, _dashOn);
curCurveoff = type;
}
@@ -439,7 +592,29 @@
_li.reset();
}
- private static boolean pointCurve(double[] curve, int type) {
+ private void skipSomethingTo(final int type) {
+ final double[] _curCurvepts = curCurvepts;
+ if (pointCurve(_curCurvepts, type)) {
+ return;
+ }
+ final LengthIterator _li = li;
+
+ _li.initializeIterationOnCurve(_curCurvepts, type);
+
+ // In contrary to somethingTo(),
+ // just estimate properly the curve length:
+ final double len = _li.totalLength();
+
+ // Accumulate skipped length:
+ this.outside = true;
+ this.totalSkipLen += len;
+
+ // Fix initial move:
+ this.needsMoveTo = true;
+ this.starting = false;
+ }
+
+ private static boolean pointCurve(final double[] curve, final int type) {
for (int i = 2; i < type; i++) {
if (curve[i] != curve[i-2]) {
return false;
@@ -462,15 +637,14 @@
// 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}
// 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
// only the right half of the original curve is at 0)
private final double[][] recCurveStack; // dirty
- // sides[i] indicates whether the node at level i+1 in the path from
+ // sidesRight[i] indicates whether the node at level i+1 in the path from
// the root to the current leaf is a left or right child of its parent.
- private final Side[] sides; // dirty
+ private final boolean[] sidesRight; // dirty
private int curveType;
// lastT and nextT delimit the current leaf.
private double nextT;
@@ -491,7 +665,7 @@
LengthIterator() {
this.recCurveStack = new double[REC_LIMIT + 1][8];
- this.sides = new Side[REC_LIMIT];
+ this.sidesRight = new boolean[REC_LIMIT];
// if any methods are called without first initializing this object
// on a curve, we want it to fail ASAP.
this.nextT = Double.MAX_VALUE;
@@ -513,7 +687,7 @@
for (int i = recLimit; i >= 0; i--) {
Arrays.fill(recCurveStack[i], 0.0d);
}
- Arrays.fill(sides, Side.LEFT);
+ Arrays.fill(sidesRight, false);
Arrays.fill(curLeafCtrlPolyLengths, 0.0d);
Arrays.fill(nextRoots, 0.0d);
Arrays.fill(flatLeafCoefCache, 0.0d);
@@ -521,7 +695,7 @@
}
}
- void initializeIterationOnCurve(double[] pts, int type) {
+ void initializeIterationOnCurve(final double[] pts, final int type) {
// optimize arraycopy (8 values faster than 6 = type):
System.arraycopy(pts, 0, recCurveStack[0], 0, 8);
this.curveType = type;
@@ -533,11 +707,11 @@
goLeft(); // initializes nextT and lenAtNextT properly
this.lenAtLastSplit = 0.0d;
if (recLevel > 0) {
- this.sides[0] = Side.LEFT;
+ this.sidesRight[0] = false;
this.done = false;
} else {
// the root of the tree is a leaf so we're done.
- this.sides[0] = Side.RIGHT;
+ this.sidesRight[0] = true;
this.done = true;
}
this.lastSegLen = 0.0d;
@@ -546,7 +720,7 @@
// 0 == false, 1 == true, -1 == invalid cached value.
private int cachedHaveLowAcceleration = -1;
- private boolean haveLowAcceleration(double err) {
+ private boolean haveLowAcceleration(final double err) {
if (cachedHaveLowAcceleration == -1) {
final double len1 = curLeafCtrlPolyLengths[0];
final double len2 = curLeafCtrlPolyLengths[1];
@@ -613,7 +787,7 @@
if (_flatLeafCoefCache[2] < 0.0d) {
double x = curLeafCtrlPolyLengths[0],
- y = x + curLeafCtrlPolyLengths[1];
+ y = x + curLeafCtrlPolyLengths[1];
if (curveType == 8) {
double z = y + curLeafCtrlPolyLengths[2];
_flatLeafCoefCache[0] = 3.0d * (x - y) + z;
@@ -635,7 +809,7 @@
// we use cubicRootsInAB here, because we want only roots in 0, 1,
// and our quadratic root finder doesn't filter, so it's just a
// matter of convenience.
- int n = DHelpers.cubicRootsInAB(a, b, c, d, nextRoots, 0, 0.0d, 1.0d);
+ final int n = DHelpers.cubicRootsInAB(a, b, c, d, nextRoots, 0, 0.0d, 1.0d);
if (n == 1 && !Double.isNaN(nextRoots[0])) {
t = nextRoots[0];
}
@@ -656,6 +830,16 @@
return t;
}
+ double totalLength() {
+ while (!done) {
+ goToNextLeaf();
+ }
+ // reset LengthIterator:
+ reset();
+
+ return lenAtNextT;
+ }
+
double lastSegLen() {
return lastSegLen;
}
@@ -665,11 +849,11 @@
private void goToNextLeaf() {
// We must go to the first ancestor node that has an unvisited
// right child.
+ final boolean[] _sides = sidesRight;
int _recLevel = recLevel;
- final Side[] _sides = sides;
+ _recLevel--;
- _recLevel--;
- while(_sides[_recLevel] == Side.RIGHT) {
+ while(_sides[_recLevel]) {
if (_recLevel == 0) {
recLevel = 0;
done = true;
@@ -678,19 +862,17 @@
_recLevel--;
}
- _sides[_recLevel] = Side.RIGHT;
+ _sides[_recLevel] = true;
// optimize arraycopy (8 values faster than 6 = type):
- System.arraycopy(recCurveStack[_recLevel], 0,
- recCurveStack[_recLevel+1], 0, 8);
- _recLevel++;
-
+ System.arraycopy(recCurveStack[_recLevel++], 0,
+ recCurveStack[_recLevel], 0, 8);
recLevel = _recLevel;
goLeft();
}
// go to the leftmost node from the current node. Return its length.
private void goLeft() {
- double len = onLeaf();
+ final double len = onLeaf();
if (len >= 0.0d) {
lastT = nextT;
lenAtLastT = lenAtNextT;
@@ -700,10 +882,11 @@
flatLeafCoefCache[2] = -1.0d;
cachedHaveLowAcceleration = -1;
} else {
- DHelpers.subdivide(recCurveStack[recLevel], 0,
- recCurveStack[recLevel+1], 0,
- recCurveStack[recLevel], 0, curveType);
- sides[recLevel] = Side.LEFT;
+ DHelpers.subdivide(recCurveStack[recLevel],
+ recCurveStack[recLevel + 1],
+ recCurveStack[recLevel], curveType);
+
+ sidesRight[recLevel] = false;
recLevel++;
goLeft();
}
@@ -718,7 +901,7 @@
double x0 = curve[0], y0 = curve[1];
for (int i = 2; i < _curveType; i += 2) {
- final double x1 = curve[i], y1 = curve[i+1];
+ final double x1 = curve[i], y1 = curve[i + 1];
final double len = DHelpers.linelen(x0, y0, x1, y1);
polyLen += len;
curLeafCtrlPolyLengths[(i >> 1) - 1] = len;
@@ -726,10 +909,9 @@
y0 = y1;
}
- final double lineLen = DHelpers.linelen(curve[0], curve[1],
- curve[_curveType-2],
- curve[_curveType-1]);
- if ((polyLen - lineLen) < ERR || recLevel == REC_LIMIT) {
+ final double lineLen = DHelpers.linelen(curve[0], curve[1], x0, y0);
+
+ if ((polyLen - lineLen) < CURVE_LEN_ERR || recLevel == REC_LIMIT) {
return (polyLen + lineLen) / 2.0d;
}
return -1.0d;
@@ -741,41 +923,190 @@
final double x2, final double y2,
final double x3, final double y3)
{
+ final int outcode0 = this.cOutCode;
+
+ if (clipRect != null) {
+ final int outcode1 = DHelpers.outcode(x1, y1, clipRect);
+ final int outcode2 = DHelpers.outcode(x2, y2, clipRect);
+ final int outcode3 = DHelpers.outcode(x3, y3, clipRect);
+
+ // Should clip
+ final int orCode = (outcode0 | outcode1 | outcode2 | outcode3);
+ if (orCode != 0) {
+ final int sideCode = outcode0 & outcode1 & outcode2 & outcode3;
+
+ // basic rejection criteria:
+ if (sideCode == 0) {
+ // ovelap clip:
+ if (subdivide) {
+ // avoid reentrance
+ subdivide = false;
+ // subdivide curve => callback with subdivided parts:
+ boolean ret = curveSplitter.splitCurve(cx0, cy0, x1, y1, x2, y2, x3, y3,
+ orCode, this);
+ // reentrance is done:
+ subdivide = true;
+ if (ret) {
+ return;
+ }
+ }
+ // already subdivided so render it
+ } else {
+ this.cOutCode = outcode3;
+ skipCurveTo(x1, y1, x2, y2, x3, y3);
+ return;
+ }
+ }
+
+ this.cOutCode = outcode3;
+
+ if (this.outside) {
+ this.outside = false;
+ // Adjust current index, phase & dash:
+ skipLen();
+ }
+ }
+ _curveTo(x1, y1, x2, y2, x3, y3);
+ }
+
+ private 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;
- _curCurvepts[2] = x1; _curCurvepts[3] = y1;
- _curCurvepts[4] = x2; _curCurvepts[5] = y2;
- _curCurvepts[6] = x3; _curCurvepts[7] = y3;
- somethingTo(8);
+
+ // monotonize curve:
+ final CurveBasicMonotonizer monotonizer
+ = rdrCtx.monotonizer.curve(cx0, cy0, x1, y1, x2, y2, x3, y3);
+
+ final int nSplits = monotonizer.nbSplits;
+ final double[] mid = monotonizer.middle;
+
+ for (int i = 0, off = 0; i <= nSplits; i++, off += 6) {
+ // optimize arraycopy (8 values faster than 6 = type):
+ System.arraycopy(mid, off, _curCurvepts, 0, 8);
+
+ somethingTo(8);
+ }
+ }
+
+ private void skipCurveTo(final double x1, final double y1,
+ final double x2, final double y2,
+ final double x3, final double y3)
+ {
+ final double[] _curCurvepts = curCurvepts;
+ _curCurvepts[0] = cx0; _curCurvepts[1] = cy0;
+ _curCurvepts[2] = x1; _curCurvepts[3] = y1;
+ _curCurvepts[4] = x2; _curCurvepts[5] = y2;
+ _curCurvepts[6] = x3; _curCurvepts[7] = y3;
+
+ skipSomethingTo(8);
+
+ this.cx0 = x3;
+ this.cy0 = y3;
}
@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 outcode1 = DHelpers.outcode(x1, y1, clipRect);
+ final int outcode2 = DHelpers.outcode(x2, y2, clipRect);
+
+ // Should clip
+ final int orCode = (outcode0 | outcode1 | outcode2);
+ if (orCode != 0) {
+ final int sideCode = outcode0 & outcode1 & outcode2;
+
+ // basic rejection criteria:
+ if (sideCode == 0) {
+ // ovelap clip:
+ if (subdivide) {
+ // avoid reentrance
+ subdivide = false;
+ // subdivide curve => call lineTo() with subdivided curves:
+ boolean ret = curveSplitter.splitQuad(cx0, cy0, x1, y1,
+ x2, y2, orCode, this);
+ // reentrance is done:
+ subdivide = true;
+ if (ret) {
+ return;
+ }
+ }
+ // already subdivided so render it
+ } else {
+ this.cOutCode = outcode2;
+ skipQuadTo(x1, y1, x2, y2);
+ return;
+ }
+ }
+
+ this.cOutCode = outcode2;
+
+ if (this.outside) {
+ this.outside = false;
+ // Adjust current index, phase & dash:
+ skipLen();
+ }
+ }
+ _quadTo(x1, y1, x2, y2);
+ }
+
+ private 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;
- _curCurvepts[4] = x2; _curCurvepts[5] = y2;
- somethingTo(6);
+
+ // monotonize quad:
+ final CurveBasicMonotonizer monotonizer
+ = rdrCtx.monotonizer.quad(cx0, cy0, x1, y1, x2, y2);
+
+ final int nSplits = monotonizer.nbSplits;
+ final double[] mid = monotonizer.middle;
+
+ for (int i = 0, off = 0; i <= nSplits; i++, off += 4) {
+ // optimize arraycopy (8 values faster than 6 = type):
+ System.arraycopy(mid, off, _curCurvepts, 0, 8);
+
+ somethingTo(6);
+ }
+ }
+
+ private void skipQuadTo(final double x1, final double y1,
+ final double x2, final double y2)
+ {
+ final double[] _curCurvepts = curCurvepts;
+ _curCurvepts[0] = cx0; _curCurvepts[1] = cy0;
+ _curCurvepts[2] = x1; _curCurvepts[3] = y1;
+ _curCurvepts[4] = x2; _curCurvepts[5] = y2;
+
+ skipSomethingTo(6);
+
+ this.cx0 = x2;
+ this.cy0 = y2;
}
@Override
public void closePath() {
- lineTo(sx, sy);
+ if (cx0 != sx0 || cy0 != sy0) {
+ lineTo(sx0, sy0);
+ }
if (firstSegidx != 0) {
if (!dashOn || needsMoveTo) {
- out.moveTo(sx, sy);
+ out.moveTo(sx0, sy0);
}
emitFirstSegments();
}
- moveTo(sx, sy);
+ moveTo(sx0, sy0);
}
@Override
public void pathDone() {
if (firstSegidx != 0) {
- out.moveTo(sx, sy);
+ out.moveTo(sx0, sy0);
emitFirstSegments();
}
out.pathDone();
--- a/src/java.desktop/share/classes/sun/java2d/marlin/DHelpers.java Fri Mar 23 13:43:39 2018 -0700
+++ b/src/java.desktop/share/classes/sun/java2d/marlin/DHelpers.java Tue Mar 27 22:09:43 2018 +0200
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2007, 2017, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2007, 2018, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -25,7 +25,6 @@
package sun.java2d.marlin;
-import static java.lang.Math.PI;
import java.util.Arrays;
import sun.java2d.marlin.stats.Histogram;
import sun.java2d.marlin.stats.StatLong;
@@ -41,13 +40,25 @@
return (d <= err && d >= -err);
}
- static int quadraticRoots(final double a, final double b,
- final double c, double[] zeroes, final int off)
+ static double evalCubic(final double a, final double b,
+ final double c, final double d,
+ final double t)
+ {
+ return t * (t * (t * a + b) + c) + d;
+ }
+
+ static double evalQuad(final double a, final double b,
+ final double c, final double t)
+ {
+ return t * (t * a + b) + c;
+ }
+
+ static int quadraticRoots(final double a, final double b, final double c,
+ final double[] zeroes, final int off)
{
int ret = off;
- double t;
if (a != 0.0d) {
- final double dis = b*b - 4*a*c;
+ final double dis = b*b - 4.0d * a * c;
if (dis > 0.0d) {
final double sqrtDis = Math.sqrt(dis);
// depending on the sign of b we use a slightly different
@@ -62,34 +73,34 @@
zeroes[ret++] = (2.0d * c) / (-b + sqrtDis);
}
} else if (dis == 0.0d) {
- t = (-b) / (2.0d * a);
- zeroes[ret++] = t;
+ zeroes[ret++] = -b / (2.0d * a);
}
- } else {
- if (b != 0.0d) {
- t = (-c) / b;
- zeroes[ret++] = t;
- }
+ } else if (b != 0.0d) {
+ zeroes[ret++] = -c / b;
}
return ret - off;
}
// find the roots of g(t) = d*t^3 + a*t^2 + b*t + c in [A,B)
- static int cubicRootsInAB(double d, double a, double b, double c,
- double[] pts, final int off,
+ static int cubicRootsInAB(final double d, double a, double b, double c,
+ final double[] pts, final int off,
final double A, final double B)
{
if (d == 0.0d) {
- int num = quadraticRoots(a, b, c, pts, off);
+ final int num = quadraticRoots(a, b, c, pts, off);
return filterOutNotInAB(pts, off, num, A, B) - off;
}
// From Graphics Gems:
- // http://tog.acm.org/resources/GraphicsGems/gems/Roots3And4.c
+ // https://github.com/erich666/GraphicsGems/blob/master/gems/Roots3And4.c
// (also from awt.geom.CubicCurve2D. But here we don't need as
// much accuracy and we don't want to create arrays so we use
// our own customized version).
// normal form: x^3 + ax^2 + bx + c = 0
+
+ /*
+ * TODO: cleanup all that code after reading Roots3And4.c
+ */
a /= d;
b /= d;
c /= d;
@@ -102,63 +113,45 @@
// p = P/3
// q = Q/2
// instead and use those values for simplicity of the code.
- double sq_A = a * a;
- double p = (1.0d/3.0d) * ((-1.0d/3.0d) * sq_A + b);
- double q = (1.0d/2.0d) * ((2.0d/27.0d) * a * sq_A - (1.0d/3.0d) * a * b + c);
+ final double sub = (1.0d / 3.0d) * a;
+ final double sq_A = a * a;
+ final double p = (1.0d / 3.0d) * ((-1.0d / 3.0d) * sq_A + b);
+ final double q = (1.0d / 2.0d) * ((2.0d / 27.0d) * a * sq_A - sub * b + c);
// use Cardano's formula
- double cb_p = p * p * p;
- double D = q * q + cb_p;
+ final double cb_p = p * p * p;
+ final double D = q * q + cb_p;
int num;
if (D < 0.0d) {
// see: http://en.wikipedia.org/wiki/Cubic_function#Trigonometric_.28and_hyperbolic.29_method
- final double phi = (1.0d/3.0d) * Math.acos(-q / Math.sqrt(-cb_p));
+ final double phi = (1.0d / 3.0d) * Math.acos(-q / Math.sqrt(-cb_p));
final double t = 2.0d * Math.sqrt(-p);
- pts[ off+0 ] = ( t * Math.cos(phi));
- pts[ off+1 ] = (-t * Math.cos(phi + (PI / 3.0d)));
- pts[ off+2 ] = (-t * Math.cos(phi - (PI / 3.0d)));
+ pts[off ] = ( t * Math.cos(phi) - sub);
+ pts[off + 1] = (-t * Math.cos(phi + (Math.PI / 3.0d)) - sub);
+ pts[off + 2] = (-t * Math.cos(phi - (Math.PI / 3.0d)) - sub);
num = 3;
} else {
final double sqrt_D = Math.sqrt(D);
final double u = Math.cbrt(sqrt_D - q);
final double v = - Math.cbrt(sqrt_D + q);
- pts[ off ] = (u + v);
+ pts[off ] = (u + v - sub);
num = 1;
if (within(D, 0.0d, 1e-8d)) {
- pts[off+1] = -(pts[off] / 2.0d);
+ pts[off + 1] = ((-1.0d / 2.0d) * (u + v) - sub);
num = 2;
}
}
- final double sub = (1.0d/3.0d) * a;
-
- for (int i = 0; i < num; ++i) {
- pts[ off+i ] -= sub;
- }
-
return filterOutNotInAB(pts, off, num, A, B) - off;
}
- static double evalCubic(final double a, final double b,
- final double c, final double d,
- final double t)
- {
- return t * (t * (t * a + b) + c) + d;
- }
-
- static double evalQuad(final double a, final double b,
- final double c, final double t)
- {
- return t * (t * a + b) + c;
- }
-
// returns the index 1 past the last valid element remaining after filtering
- static int filterOutNotInAB(double[] nums, final int off, final int len,
+ static int filterOutNotInAB(final double[] nums, final int off, final int len,
final double a, final double b)
{
int ret = off;
@@ -170,35 +163,189 @@
return ret;
}
- static double linelen(double x1, double y1, double x2, double y2) {
- final double dx = x2 - x1;
- final double dy = y2 - y1;
- return Math.sqrt(dx*dx + dy*dy);
+ static double fastLineLen(final double x0, final double y0,
+ final double x1, final double y1)
+ {
+ final double dx = x1 - x0;
+ final double dy = y1 - y0;
+
+ // use manhattan norm:
+ return Math.abs(dx) + Math.abs(dy);
+ }
+
+ static double linelen(final double x0, final double y0,
+ final double x1, final double y1)
+ {
+ final double dx = x1 - x0;
+ final double dy = y1 - y0;
+ return Math.sqrt(dx * dx + dy * dy);
+ }
+
+ static double fastQuadLen(final double x0, final double y0,
+ final double x1, final double y1,
+ final double x2, final double y2)
+ {
+ final double dx1 = x1 - x0;
+ final double dx2 = x2 - x1;
+ final double dy1 = y1 - y0;
+ final double dy2 = y2 - y1;
+
+ // use manhattan norm:
+ return Math.abs(dx1) + Math.abs(dx2)
+ + Math.abs(dy1) + Math.abs(dy2);
+ }
+
+ static double quadlen(final double x0, final double y0,
+ final double x1, final double y1,
+ final double x2, final double y2)
+ {
+ return (linelen(x0, y0, x1, y1)
+ + linelen(x1, y1, x2, y2)
+ + linelen(x0, y0, x2, y2)) / 2.0d;
+ }
+
+ static double fastCurvelen(final double x0, final double y0,
+ final double x1, final double y1,
+ final double x2, final double y2,
+ final double x3, final double y3)
+ {
+ final double dx1 = x1 - x0;
+ final double dx2 = x2 - x1;
+ final double dx3 = x3 - x2;
+ final double dy1 = y1 - y0;
+ final double dy2 = y2 - y1;
+ final double dy3 = y3 - y2;
+
+ // use manhattan norm:
+ return Math.abs(dx1) + Math.abs(dx2) + Math.abs(dx3)
+ + Math.abs(dy1) + Math.abs(dy2) + Math.abs(dy3);
+ }
+
+ static double curvelen(final double x0, final double y0,
+ final double x1, final double y1,
+ final double x2, final double y2,
+ final double x3, final double y3)
+ {
+ return (linelen(x0, y0, x1, y1)
+ + linelen(x1, y1, x2, y2)
+ + linelen(x2, y2, x3, y3)
+ + linelen(x0, y0, x3, y3)) / 2.0d;
}
- static void subdivide(double[] src, int srcoff, double[] left, int leftoff,
- double[] right, int rightoff, int type)
+ // finds values of t where the curve in pts should be subdivided in order
+ // to get good offset curves a distance of w away from the middle curve.
+ // Stores the points in ts, and returns how many of them there were.
+ static int findSubdivPoints(final DCurve c, final double[] pts,
+ final double[] ts, final int type,
+ final double w2)
+ {
+ final double x12 = pts[2] - pts[0];
+ final double y12 = pts[3] - pts[1];
+ // if the curve is already parallel to either axis we gain nothing
+ // from rotating it.
+ if ((y12 != 0.0d && x12 != 0.0d)) {
+ // we rotate it so that the first vector in the control polygon is
+ // parallel to the x-axis. This will ensure that rotated quarter
+ // circles won't be subdivided.
+ final double hypot = Math.sqrt(x12 * x12 + y12 * y12);
+ final double cos = x12 / hypot;
+ final double sin = y12 / hypot;
+ final double x1 = cos * pts[0] + sin * pts[1];
+ final double y1 = cos * pts[1] - sin * pts[0];
+ final double x2 = cos * pts[2] + sin * pts[3];
+ final double y2 = cos * pts[3] - sin * pts[2];
+ final double x3 = cos * pts[4] + sin * pts[5];
+ final double y3 = cos * pts[5] - sin * pts[4];
+
+ switch(type) {
+ case 8:
+ final double x4 = cos * pts[6] + sin * pts[7];
+ final double y4 = cos * pts[7] - sin * pts[6];
+ c.set(x1, y1, x2, y2, x3, y3, x4, y4);
+ break;
+ case 6:
+ c.set(x1, y1, x2, y2, x3, y3);
+ break;
+ default:
+ }
+ } else {
+ c.set(pts, type);
+ }
+
+ int ret = 0;
+ // we subdivide at values of t such that the remaining rotated
+ // curves are monotonic in x and y.
+ ret += c.dxRoots(ts, ret);
+ ret += c.dyRoots(ts, ret);
+
+ // subdivide at inflection points.
+ if (type == 8) {
+ // quadratic curves can't have inflection points
+ ret += c.infPoints(ts, ret);
+ }
+
+ // now we must subdivide at points where one of the offset curves will have
+ // a cusp. This happens at ts where the radius of curvature is equal to w.
+ ret += c.rootsOfROCMinusW(ts, ret, w2, 0.0001d);
+
+ ret = filterOutNotInAB(ts, 0, ret, 0.0001d, 0.9999d);
+ isort(ts, ret);
+ return ret;
+ }
+
+ // finds values of t where the curve in pts should be subdivided in order
+ // to get intersections with the given clip rectangle.
+ // Stores the points in ts, and returns how many of them there were.
+ static int findClipPoints(final DCurve curve, final double[] pts,
+ final double[] ts, final int type,
+ final int outCodeOR,
+ final double[] clipRect)
+ {
+ curve.set(pts, type);
+
+ // clip rectangle (ymin, ymax, xmin, xmax)
+ int ret = 0;
+
+ if ((outCodeOR & OUTCODE_LEFT) != 0) {
+ ret += curve.xPoints(ts, ret, clipRect[2]);
+ }
+ if ((outCodeOR & OUTCODE_RIGHT) != 0) {
+ ret += curve.xPoints(ts, ret, clipRect[3]);
+ }
+ if ((outCodeOR & OUTCODE_TOP) != 0) {
+ ret += curve.yPoints(ts, ret, clipRect[0]);
+ }
+ if ((outCodeOR & OUTCODE_BOTTOM) != 0) {
+ ret += curve.yPoints(ts, ret, clipRect[1]);
+ }
+ isort(ts, ret);
+ return ret;
+ }
+
+ static void subdivide(final double[] src,
+ final double[] left, final double[] right,
+ final int type)
{
switch(type) {
- case 6:
- DHelpers.subdivideQuad(src, srcoff, left, leftoff, right, rightoff);
+ case 8:
+ subdivideCubic(src, left, right);
return;
- case 8:
- DHelpers.subdivideCubic(src, srcoff, left, leftoff, right, rightoff);
+ case 6:
+ subdivideQuad(src, left, right);
return;
default:
throw new InternalError("Unsupported curve type");
}
}
- static void isort(double[] a, int off, int len) {
- for (int i = off + 1, end = off + len; i < end; i++) {
- double ai = a[i];
- int j = i - 1;
- for (; j >= off && a[j] > ai; j--) {
- a[j+1] = a[j];
+ static void isort(final double[] a, final int len) {
+ for (int i = 1, j; i < len; i++) {
+ final double ai = a[i];
+ j = i - 1;
+ for (; j >= 0 && a[j] > ai; j--) {
+ a[j + 1] = a[j];
}
- a[j+1] = ai;
+ a[j + 1] = ai;
}
}
@@ -221,206 +368,216 @@
* equals (<code>leftoff</code> + 6), in order
* to avoid allocating extra storage for this common point.
* @param src the array holding the coordinates for the source curve
- * @param srcoff the offset into the array of the beginning of the
- * the 6 source coordinates
* @param left the array for storing the coordinates for the first
* half of the subdivided curve
- * @param leftoff the offset into the array of the beginning of the
- * the 6 left coordinates
* @param right the array for storing the coordinates for the second
* half of the subdivided curve
- * @param rightoff the offset into the array of the beginning of the
- * the 6 right coordinates
* @since 1.7
*/
- static void subdivideCubic(double[] src, int srcoff,
- double[] left, int leftoff,
- double[] right, int rightoff)
+ static void subdivideCubic(final double[] src,
+ final double[] left,
+ final double[] right)
{
- double x1 = src[srcoff + 0];
- double y1 = src[srcoff + 1];
- double ctrlx1 = src[srcoff + 2];
- double ctrly1 = src[srcoff + 3];
- double ctrlx2 = src[srcoff + 4];
- double ctrly2 = src[srcoff + 5];
- double x2 = src[srcoff + 6];
- double y2 = src[srcoff + 7];
- if (left != null) {
- left[leftoff + 0] = x1;
- left[leftoff + 1] = y1;
- }
- if (right != null) {
- right[rightoff + 6] = x2;
- right[rightoff + 7] = y2;
- }
- x1 = (x1 + ctrlx1) / 2.0d;
- y1 = (y1 + ctrly1) / 2.0d;
- x2 = (x2 + ctrlx2) / 2.0d;
- y2 = (y2 + ctrly2) / 2.0d;
- double centerx = (ctrlx1 + ctrlx2) / 2.0d;
- double centery = (ctrly1 + ctrly2) / 2.0d;
- ctrlx1 = (x1 + centerx) / 2.0d;
- ctrly1 = (y1 + centery) / 2.0d;
- ctrlx2 = (x2 + centerx) / 2.0d;
- ctrly2 = (y2 + centery) / 2.0d;
- centerx = (ctrlx1 + ctrlx2) / 2.0d;
- centery = (ctrly1 + ctrly2) / 2.0d;
- if (left != null) {
- left[leftoff + 2] = x1;
- left[leftoff + 3] = y1;
- left[leftoff + 4] = ctrlx1;
- left[leftoff + 5] = ctrly1;
- left[leftoff + 6] = centerx;
- left[leftoff + 7] = centery;
- }
- if (right != null) {
- right[rightoff + 0] = centerx;
- right[rightoff + 1] = centery;
- right[rightoff + 2] = ctrlx2;
- right[rightoff + 3] = ctrly2;
- right[rightoff + 4] = x2;
- right[rightoff + 5] = y2;
- }
+ double x1 = src[0];
+ double y1 = src[1];
+ double cx1 = src[2];
+ double cy1 = src[3];
+ double cx2 = src[4];
+ double cy2 = src[5];
+ double x2 = src[6];
+ double y2 = src[7];
+
+ left[0] = x1;
+ left[1] = y1;
+
+ right[6] = x2;
+ right[7] = y2;
+
+ x1 = (x1 + cx1) / 2.0d;
+ y1 = (y1 + cy1) / 2.0d;
+ x2 = (x2 + cx2) / 2.0d;
+ y2 = (y2 + cy2) / 2.0d;
+
+ double cx = (cx1 + cx2) / 2.0d;
+ double cy = (cy1 + cy2) / 2.0d;
+
+ cx1 = (x1 + cx) / 2.0d;
+ cy1 = (y1 + cy) / 2.0d;
+ cx2 = (x2 + cx) / 2.0d;
+ cy2 = (y2 + cy) / 2.0d;
+ cx = (cx1 + cx2) / 2.0d;
+ cy = (cy1 + cy2) / 2.0d;
+
+ left[2] = x1;
+ left[3] = y1;
+ left[4] = cx1;
+ left[5] = cy1;
+ left[6] = cx;
+ left[7] = cy;
+
+ right[0] = cx;
+ right[1] = cy;
+ right[2] = cx2;
+ right[3] = cy2;
+ right[4] = x2;
+ right[5] = y2;
+ }
+
+ static void subdivideCubicAt(final double t,
+ final double[] src, final int offS,
+ final double[] pts, final int offL, final int offR)
+ {
+ double x1 = src[offS ];
+ double y1 = src[offS + 1];
+ double cx1 = src[offS + 2];
+ double cy1 = src[offS + 3];
+ double cx2 = src[offS + 4];
+ double cy2 = src[offS + 5];
+ double x2 = src[offS + 6];
+ double y2 = src[offS + 7];
+
+ pts[offL ] = x1;
+ pts[offL + 1] = y1;
+
+ pts[offR + 6] = x2;
+ pts[offR + 7] = y2;
+
+ x1 = x1 + t * (cx1 - x1);
+ y1 = y1 + t * (cy1 - y1);
+ x2 = cx2 + t * (x2 - cx2);
+ y2 = cy2 + t * (y2 - cy2);
+
+ double cx = cx1 + t * (cx2 - cx1);
+ double cy = cy1 + t * (cy2 - cy1);
+
+ cx1 = x1 + t * (cx - x1);
+ cy1 = y1 + t * (cy - y1);
+ cx2 = cx + t * (x2 - cx);
+ cy2 = cy + t * (y2 - cy);
+ cx = cx1 + t * (cx2 - cx1);
+ cy = cy1 + t * (cy2 - cy1);
+
+ pts[offL + 2] = x1;
+ pts[offL + 3] = y1;
+ pts[offL + 4] = cx1;
+ pts[offL + 5] = cy1;
+ pts[offL + 6] = cx;
+ pts[offL + 7] = cy;
+
+ pts[offR ] = cx;
+ pts[offR + 1] = cy;
+ pts[offR + 2] = cx2;
+ pts[offR + 3] = cy2;
+ pts[offR + 4] = x2;
+ pts[offR + 5] = y2;
}
-
- static void subdivideCubicAt(double t, double[] src, int srcoff,
- double[] left, int leftoff,
- double[] right, int rightoff)
+ static void subdivideQuad(final double[] src,
+ final double[] left,
+ final double[] right)
{
- double x1 = src[srcoff + 0];
- double y1 = src[srcoff + 1];
- double ctrlx1 = src[srcoff + 2];
- double ctrly1 = src[srcoff + 3];
- double ctrlx2 = src[srcoff + 4];
- double ctrly2 = src[srcoff + 5];
- double x2 = src[srcoff + 6];
- double y2 = src[srcoff + 7];
- if (left != null) {
- left[leftoff + 0] = x1;
- left[leftoff + 1] = y1;
- }
- if (right != null) {
- right[rightoff + 6] = x2;
- right[rightoff + 7] = y2;
- }
- x1 = x1 + t * (ctrlx1 - x1);
- y1 = y1 + t * (ctrly1 - y1);
- x2 = ctrlx2 + t * (x2 - ctrlx2);
- y2 = ctrly2 + t * (y2 - ctrly2);
- double centerx = ctrlx1 + t * (ctrlx2 - ctrlx1);
- double centery = ctrly1 + t * (ctrly2 - ctrly1);
- ctrlx1 = x1 + t * (centerx - x1);
- ctrly1 = y1 + t * (centery - y1);
- ctrlx2 = centerx + t * (x2 - centerx);
- ctrly2 = centery + t * (y2 - centery);
- centerx = ctrlx1 + t * (ctrlx2 - ctrlx1);
- centery = ctrly1 + t * (ctrly2 - ctrly1);
- if (left != null) {
- left[leftoff + 2] = x1;
- left[leftoff + 3] = y1;
- left[leftoff + 4] = ctrlx1;
- left[leftoff + 5] = ctrly1;
- left[leftoff + 6] = centerx;
- left[leftoff + 7] = centery;
- }
- if (right != null) {
- right[rightoff + 0] = centerx;
- right[rightoff + 1] = centery;
- right[rightoff + 2] = ctrlx2;
- right[rightoff + 3] = ctrly2;
- right[rightoff + 4] = x2;
- right[rightoff + 5] = y2;
- }
+ double x1 = src[0];
+ double y1 = src[1];
+ double cx = src[2];
+ double cy = src[3];
+ double x2 = src[4];
+ double y2 = src[5];
+
+ left[0] = x1;
+ left[1] = y1;
+
+ right[4] = x2;
+ right[5] = y2;
+
+ x1 = (x1 + cx) / 2.0d;
+ y1 = (y1 + cy) / 2.0d;
+ x2 = (x2 + cx) / 2.0d;
+ y2 = (y2 + cy) / 2.0d;
+ cx = (x1 + x2) / 2.0d;
+ cy = (y1 + y2) / 2.0d;
+
+ left[2] = x1;
+ left[3] = y1;
+ left[4] = cx;
+ left[5] = cy;
+
+ right[0] = cx;
+ right[1] = cy;
+ right[2] = x2;
+ right[3] = y2;
}
- static void subdivideQuad(double[] src, int srcoff,
- double[] left, int leftoff,
- double[] right, int rightoff)
+ static void subdivideQuadAt(final double t,
+ final double[] src, final int offS,
+ final double[] pts, final int offL, final int offR)
{
- double x1 = src[srcoff + 0];
- double y1 = src[srcoff + 1];
- double ctrlx = src[srcoff + 2];
- double ctrly = src[srcoff + 3];
- double x2 = src[srcoff + 4];
- double y2 = src[srcoff + 5];
- if (left != null) {
- left[leftoff + 0] = x1;
- left[leftoff + 1] = y1;
- }
- if (right != null) {
- right[rightoff + 4] = x2;
- right[rightoff + 5] = y2;
- }
- x1 = (x1 + ctrlx) / 2.0d;
- y1 = (y1 + ctrly) / 2.0d;
- x2 = (x2 + ctrlx) / 2.0d;
- y2 = (y2 + ctrly) / 2.0d;
- ctrlx = (x1 + x2) / 2.0d;
- ctrly = (y1 + y2) / 2.0d;
- if (left != null) {
- left[leftoff + 2] = x1;
- left[leftoff + 3] = y1;
- left[leftoff + 4] = ctrlx;
- left[leftoff + 5] = ctrly;
- }
- if (right != null) {
- right[rightoff + 0] = ctrlx;
- right[rightoff + 1] = ctrly;
- right[rightoff + 2] = x2;
- right[rightoff + 3] = y2;
- }
+ double x1 = src[offS ];
+ double y1 = src[offS + 1];
+ double cx = src[offS + 2];
+ double cy = src[offS + 3];
+ double x2 = src[offS + 4];
+ double y2 = src[offS + 5];
+
+ pts[offL ] = x1;
+ pts[offL + 1] = y1;
+
+ pts[offR + 4] = x2;
+ pts[offR + 5] = y2;
+
+ x1 = x1 + t * (cx - x1);
+ y1 = y1 + t * (cy - y1);
+ x2 = cx + t * (x2 - cx);
+ y2 = cy + t * (y2 - cy);
+ cx = x1 + t * (x2 - x1);
+ cy = y1 + t * (y2 - y1);
+
+ pts[offL + 2] = x1;
+ pts[offL + 3] = y1;
+ pts[offL + 4] = cx;
+ pts[offL + 5] = cy;
+
+ pts[offR ] = cx;
+ pts[offR + 1] = cy;
+ pts[offR + 2] = x2;
+ pts[offR + 3] = y2;
}
- static void subdivideQuadAt(double t, double[] src, int srcoff,
- double[] left, int leftoff,
- double[] right, int rightoff)
+ static void subdivideLineAt(final double t,
+ final double[] src, final int offS,
+ final double[] pts, final int offL, final int offR)
{
- double x1 = src[srcoff + 0];
- double y1 = src[srcoff + 1];
- double ctrlx = src[srcoff + 2];
- double ctrly = src[srcoff + 3];
- double x2 = src[srcoff + 4];
- double y2 = src[srcoff + 5];
- if (left != null) {
- left[leftoff + 0] = x1;
- left[leftoff + 1] = y1;
- }
- if (right != null) {
- right[rightoff + 4] = x2;
- right[rightoff + 5] = y2;
- }
- x1 = x1 + t * (ctrlx - x1);
- y1 = y1 + t * (ctrly - y1);
- x2 = ctrlx + t * (x2 - ctrlx);
- y2 = ctrly + t * (y2 - ctrly);
- ctrlx = x1 + t * (x2 - x1);
- ctrly = y1 + t * (y2 - y1);
- if (left != null) {
- left[leftoff + 2] = x1;
- left[leftoff + 3] = y1;
- left[leftoff + 4] = ctrlx;
- left[leftoff + 5] = ctrly;
- }
- if (right != null) {
- right[rightoff + 0] = ctrlx;
- right[rightoff + 1] = ctrly;
- right[rightoff + 2] = x2;
- right[rightoff + 3] = y2;
- }
+ double x1 = src[offS ];
+ double y1 = src[offS + 1];
+ double x2 = src[offS + 2];
+ double y2 = src[offS + 3];
+
+ pts[offL ] = x1;
+ pts[offL + 1] = y1;
+
+ pts[offR + 2] = x2;
+ pts[offR + 3] = y2;
+
+ x1 = x1 + t * (x2 - x1);
+ y1 = y1 + t * (y2 - y1);
+
+ pts[offL + 2] = x1;
+ pts[offL + 3] = y1;
+
+ pts[offR ] = x1;
+ pts[offR + 1] = y1;
}
- static void subdivideAt(double t, double[] src, int srcoff,
- double[] left, int leftoff,
- double[] right, int rightoff, int size)
+ static void subdivideAt(final double t,
+ final double[] src, final int offS,
+ final double[] pts, final int offL, final int type)
{
- switch(size) {
- case 8:
- subdivideCubicAt(t, src, srcoff, left, leftoff, right, rightoff);
- return;
- case 6:
- subdivideQuadAt(t, src, srcoff, left, leftoff, right, rightoff);
- return;
+ // if instead of switch (perf + most probable cases first)
+ if (type == 8) {
+ subdivideCubicAt(t, src, offS, pts, offL, offL + type);
+ } else if (type == 4) {
+ subdivideLineAt(t, src, offS, pts, offL, offL + type);
+ } else {
+ subdivideQuadAt(t, src, offS, pts, offL, offL + type);
}
}
@@ -608,12 +765,12 @@
e += 2;
continue;
case TYPE_QUADTO:
- io.quadTo(_curves[e+0], _curves[e+1],
+ io.quadTo(_curves[e], _curves[e+1],
_curves[e+2], _curves[e+3]);
e += 4;
continue;
case TYPE_CUBICTO:
- io.curveTo(_curves[e+0], _curves[e+1],
+ io.curveTo(_curves[e], _curves[e+1],
_curves[e+2], _curves[e+3],
_curves[e+4], _curves[e+5]);
e += 6;
@@ -651,12 +808,12 @@
continue;
case TYPE_QUADTO:
e -= 4;
- io.quadTo(_curves[e+0], _curves[e+1],
+ io.quadTo(_curves[e], _curves[e+1],
_curves[e+2], _curves[e+3]);
continue;
case TYPE_CUBICTO:
e -= 6;
- io.curveTo(_curves[e+0], _curves[e+1],
+ io.curveTo(_curves[e], _curves[e+1],
_curves[e+2], _curves[e+3],
_curves[e+4], _curves[e+5]);
continue;
--- a/src/java.desktop/share/classes/sun/java2d/marlin/DMarlinRenderingEngine.java Fri Mar 23 13:43:39 2018 -0700
+++ b/src/java.desktop/share/classes/sun/java2d/marlin/DMarlinRenderingEngine.java Tue Mar 27 22:09:43 2018 +0200
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2007, 2017, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2007, 2018, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -31,6 +31,7 @@
import java.awt.geom.Path2D;
import java.awt.geom.PathIterator;
import java.security.AccessController;
+import sun.awt.geom.PathConsumer2D;
import static sun.java2d.marlin.MarlinUtils.logInfo;
import sun.java2d.ReentrantContextProvider;
import sun.java2d.ReentrantContextProviderCLQ;
@@ -46,7 +47,21 @@
public final class DMarlinRenderingEngine extends RenderingEngine
implements MarlinConst
{
- private static enum NormMode {
+ // slightly slower ~2% if enabled stroker clipping (lines) but skipping cap / join handling is few percents faster in specific cases
+ static final boolean DISABLE_2ND_STROKER_CLIPPING = true;
+
+ static final boolean DO_TRACE_PATH = false;
+
+ static final boolean DO_CLIP = MarlinProperties.isDoClip();
+ static final boolean DO_CLIP_FILL = true;
+ static final boolean DO_CLIP_RUNTIME_ENABLE = MarlinProperties.isDoClipRuntimeFlag();
+
+ private static final float MIN_PEN_SIZE = 1.0f / MIN_SUBPIXELS;
+
+ static final double UPPER_BND = Float.MAX_VALUE / 2.0d;
+ static final double LOWER_BND = -UPPER_BND;
+
+ private enum NormMode {
ON_WITH_AA {
@Override
PathIterator getNormalizingPathIterator(final DRendererContext rdrCtx,
@@ -79,18 +94,6 @@
PathIterator src);
}
- private static final float MIN_PEN_SIZE = 1.0f / NORM_SUBPIXELS;
-
- 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_CLIP_FILL = true;
-
- static final boolean DO_TRACE_PATH = false;
-
- static final boolean DO_CLIP_RUNTIME_ENABLE = MarlinProperties.isDoClipRuntimeFlag();
-
/**
* Public constructor
*/
@@ -186,7 +189,7 @@
boolean thin,
boolean normalize,
boolean antialias,
- final sun.awt.geom.PathConsumer2D consumer)
+ final PathConsumer2D consumer)
{
final NormMode norm = (normalize) ?
((antialias) ? NormMode.ON_WITH_AA : NormMode.ON_NO_AA)
@@ -424,11 +427,24 @@
pc2d = transformerPC2D.deltaTransformConsumer(pc2d, strokerat);
// stroker will adjust the clip rectangle (width / miter limit):
- pc2d = rdrCtx.stroker.init(pc2d, width, caps, join, miterlimit, scale);
+ pc2d = rdrCtx.stroker.init(pc2d, width, caps, join, miterlimit, scale,
+ (dashesD == null));
+
+ // Curve Monotizer:
+ rdrCtx.monotonizer.init(width);
if (dashesD != null) {
+ if (DO_TRACE_PATH) {
+ pc2d = transformerPC2D.traceDasher(pc2d);
+ }
pc2d = rdrCtx.dasher.init(pc2d, dashesD, dashLen, dashphase,
recycleDashes);
+
+ if (DISABLE_2ND_STROKER_CLIPPING) {
+ // disable stoker clipping:
+ rdrCtx.stroker.disableClipping();
+ }
+
} else if (rdrCtx.doClip && (caps != Stroker.CAP_BUTT)) {
if (DO_TRACE_PATH) {
pc2d = transformerPC2D.traceClosedPathDetector(pc2d);
@@ -627,6 +643,12 @@
private static void pathTo(final DRendererContext rdrCtx, final PathIterator pi,
DPathConsumer2D pc2d)
{
+ if (USE_PATH_SIMPLIFIER) {
+ // Use path simplifier at the first step
+ // to remove useless points
+ pc2d = rdrCtx.pathSimplifier.init(pc2d);
+ }
+
// mark context as DIRTY:
rdrCtx.dirty = true;
@@ -851,8 +873,6 @@
// trace Input:
pc2d = rdrCtx.transformerPC2D.traceInput(pc2d);
}
-
- // TODO: subdivide quad/cubic curves into monotonic curves ?
pathTo(rdrCtx, pi, pc2d);
} else {
@@ -1002,14 +1022,17 @@
final String refType = AccessController.doPrivileged(
new GetPropertyAction("sun.java2d.renderer.useRef",
"soft"));
-
- // Java 1.6 does not support strings in switch:
- if ("hard".equalsIgnoreCase(refType)) {
- REF_TYPE = ReentrantContextProvider.REF_HARD;
- } else if ("weak".equalsIgnoreCase(refType)) {
- REF_TYPE = ReentrantContextProvider.REF_WEAK;
- } else {
- REF_TYPE = ReentrantContextProvider.REF_SOFT;
+ switch (refType) {
+ default:
+ case "soft":
+ REF_TYPE = ReentrantContextProvider.REF_SOFT;
+ break;
+ case "weak":
+ REF_TYPE = ReentrantContextProvider.REF_WEAK;
+ break;
+ case "hard":
+ REF_TYPE = ReentrantContextProvider.REF_HARD;
+ break;
}
if (USE_THREAD_LOCAL) {
@@ -1069,8 +1092,10 @@
logInfo("sun.java2d.renderer.edges = "
+ MarlinConst.INITIAL_EDGES_COUNT);
- logInfo("sun.java2d.renderer.pixelsize = "
- + MarlinConst.INITIAL_PIXEL_DIM);
+ logInfo("sun.java2d.renderer.pixelWidth = "
+ + MarlinConst.INITIAL_PIXEL_WIDTH);
+ logInfo("sun.java2d.renderer.pixelHeight = "
+ + MarlinConst.INITIAL_PIXEL_HEIGHT);
logInfo("sun.java2d.renderer.subPixel_log2_X = "
+ MarlinConst.SUBPIXEL_LG_POSITIONS_X);
@@ -1100,12 +1125,21 @@
// optimisation parameters
logInfo("sun.java2d.renderer.useSimplifier = "
+ MarlinConst.USE_SIMPLIFIER);
+ logInfo("sun.java2d.renderer.usePathSimplifier= "
+ + MarlinConst.USE_PATH_SIMPLIFIER);
+ logInfo("sun.java2d.renderer.pathSimplifier.pixTol = "
+ + MarlinProperties.getPathSimplifierPixelTolerance());
logInfo("sun.java2d.renderer.clip = "
+ MarlinProperties.isDoClip());
logInfo("sun.java2d.renderer.clip.runtime.enable = "
+ MarlinProperties.isDoClipRuntimeFlag());
+ logInfo("sun.java2d.renderer.clip.subdivider = "
+ + MarlinProperties.isDoClipSubdivider());
+ logInfo("sun.java2d.renderer.clip.subdivider.minLength = "
+ + MarlinProperties.getSubdividerMinLength());
+
// debugging parameters
logInfo("sun.java2d.renderer.doStats = "
+ MarlinConst.DO_STATS);
@@ -1123,6 +1157,8 @@
+ MarlinConst.LOG_UNSAFE_MALLOC);
// quality settings
+ logInfo("sun.java2d.renderer.curve_len_err = "
+ + MarlinProperties.getCurveLengthError());
logInfo("sun.java2d.renderer.cubic_dec_d2 = "
+ MarlinProperties.getCubicDecD2());
logInfo("sun.java2d.renderer.cubic_inc_d1 = "
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/java.desktop/share/classes/sun/java2d/marlin/DPathSimplifier.java Tue Mar 27 22:09:43 2018 +0200
@@ -0,0 +1,136 @@
+/*
+ * Copyright (c) 2017, 2018, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+package sun.java2d.marlin;
+
+final class DPathSimplifier implements DPathConsumer2D {
+
+ // distance threshold in pixels (device)
+ private static final double PIX_THRESHOLD = MarlinProperties.getPathSimplifierPixelTolerance();
+
+ private static final double SQUARE_TOLERANCE = PIX_THRESHOLD * PIX_THRESHOLD;
+
+ // members:
+ private DPathConsumer2D delegate;
+ private double cx, cy;
+
+ DPathSimplifier() {
+ }
+
+ DPathSimplifier init(final DPathConsumer2D delegate) {
+ this.delegate = delegate;
+ return this; // fluent API
+ }
+
+ @Override
+ public void pathDone() {
+ delegate.pathDone();
+ }
+
+ @Override
+ public void closePath() {
+ delegate.closePath();
+ }
+
+ @Override
+ public long getNativeConsumer() {
+ return 0;
+ }
+
+ @Override
+ public void quadTo(final double x1, final double y1,
+ final double xe, final double ye)
+ {
+ // Test if curve is too small:
+ double dx = (xe - cx);
+ double dy = (ye - cy);
+
+ if ((dx * dx + dy * dy) <= SQUARE_TOLERANCE) {
+ // check control points P1:
+ dx = (x1 - cx);
+ dy = (y1 - cy);
+
+ if ((dx * dx + dy * dy) <= SQUARE_TOLERANCE) {
+ return;
+ }
+ }
+ delegate.quadTo(x1, y1, xe, ye);
+ // final end point:
+ cx = xe;
+ cy = ye;
+ }
+
+ @Override
+ public void curveTo(final double x1, final double y1,
+ final double x2, final double y2,
+ final double xe, final double ye)
+ {
+ // Test if curve is too small:
+ double dx = (xe - cx);
+ double dy = (ye - cy);
+
+ if ((dx * dx + dy * dy) <= SQUARE_TOLERANCE) {
+ // check control points P1:
+ dx = (x1 - cx);
+ dy = (y1 - cy);
+
+ if ((dx * dx + dy * dy) <= SQUARE_TOLERANCE) {
+ // check control points P2:
+ dx = (x2 - cx);
+ dy = (y2 - cy);
+
+ if ((dx * dx + dy * dy) <= SQUARE_TOLERANCE) {
+ return;
+ }
+ }
+ }
+ delegate.curveTo(x1, y1, x2, y2, xe, ye);
+ // final end point:
+ cx = xe;
+ cy = ye;
+ }
+
+ @Override
+ public void moveTo(final double xe, final double ye) {
+ delegate.moveTo(xe, ye);
+ // starting point:
+ cx = xe;
+ cy = ye;
+ }
+
+ @Override
+ public void lineTo(final double xe, final double ye) {
+ // Test if segment is too small:
+ double dx = (xe - cx);
+ double dy = (ye - cy);
+
+ if ((dx * dx + dy * dy) <= SQUARE_TOLERANCE) {
+ return;
+ }
+ delegate.lineTo(xe, ye);
+ // final end point:
+ cx = xe;
+ cy = ye;
+ }
+}
--- a/src/java.desktop/share/classes/sun/java2d/marlin/DRenderer.java Fri Mar 23 13:43:39 2018 -0700
+++ b/src/java.desktop/share/classes/sun/java2d/marlin/DRenderer.java Tue Mar 27 22:09:43 2018 +0200
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2007, 2017, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2007, 2018, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -53,9 +53,9 @@
private static final int SUBPIXEL_TILE
= TILE_H << SUBPIXEL_LG_POSITIONS_Y;
- // 2048 (pixelSize) pixels (height) x 8 subpixels = 64K
+ // 2176 pixels (height) x 8 subpixels = 68K
static final int INITIAL_BUCKET_ARRAY
- = INITIAL_PIXEL_DIM * SUBPIXEL_POSITIONS_Y;
+ = INITIAL_PIXEL_HEIGHT * SUBPIXEL_POSITIONS_Y;
// crossing capacity = edges count / 4 ~ 1024
static final int INITIAL_CROSSING_COUNT = INITIAL_EDGES_COUNT >> 2;
@@ -76,13 +76,17 @@
// curve break into lines
// cubic error in subpixels to decrement step
private static final double CUB_DEC_ERR_SUBPIX
- = MarlinProperties.getCubicDecD2() * (NORM_SUBPIXELS / 8.0d); // 1 pixel
+ = MarlinProperties.getCubicDecD2() * (SUBPIXEL_POSITIONS_X / 8.0d); // 1.0 / 8th pixel
// cubic error in subpixels to increment step
private static final double CUB_INC_ERR_SUBPIX
- = MarlinProperties.getCubicIncD1() * (NORM_SUBPIXELS / 8.0d); // 0.4 pixel
+ = MarlinProperties.getCubicIncD1() * (SUBPIXEL_POSITIONS_X / 8.0d); // 0.4 / 8th pixel
+ // scale factor for Y-axis contribution to quad / cubic errors:
+ public static final double SCALE_DY = ((double) SUBPIXEL_POSITIONS_X) / SUBPIXEL_POSITIONS_Y;
// TestNonAARasterization (JDK-8170879): cubics
// bad paths (59294/100000 == 59,29%, 94335 bad pixels (avg = 1,59), 3966 warnings (avg = 0,07)
+// 2018
+ // 1.0 / 0.2: bad paths (67194/100000 == 67,19%, 117394 bad pixels (avg = 1,75 - max = 9), 4042 warnings (avg = 0,06)
// cubic bind length to decrement step
public static final double CUB_DEC_BND
@@ -109,10 +113,12 @@
// quad break into lines
// quadratic error in subpixels
private static final double QUAD_DEC_ERR_SUBPIX
- = MarlinProperties.getQuadDecD2() * (NORM_SUBPIXELS / 8.0d); // 0.5 pixel
+ = MarlinProperties.getQuadDecD2() * (SUBPIXEL_POSITIONS_X / 8.0d); // 0.5 / 8th pixel
// TestNonAARasterization (JDK-8170879): quads
// bad paths (62916/100000 == 62,92%, 103818 bad pixels (avg = 1,65), 6514 warnings (avg = 0,10)
+// 2018
+ // 0.50px = bad paths (62915/100000 == 62,92%, 103810 bad pixels (avg = 1,65), 6512 warnings (avg = 0,10)
// quadratic bind length to decrement step
public static final double QUAD_DEC_BND
@@ -179,7 +185,7 @@
int count = 1; // dt = 1 / count
// maximum(ddX|Y) = norm(dbx, dby) * dt^2 (= 1)
- double maxDD = Math.abs(c.dbx) + Math.abs(c.dby);
+ double maxDD = Math.abs(c.dbx) + Math.abs(c.dby) * SCALE_DY;
final double _DEC_BND = QUAD_DEC_BND;
@@ -193,7 +199,8 @@
}
}
- int nL = 0; // line count
+ final int nL = count; // line count
+
if (count > 1) {
final double icount = 1.0d / count; // dt
final double icount2 = icount * icount; // dt^2
@@ -203,17 +210,12 @@
double dx = c.bx * icount2 + c.cx * icount;
double dy = c.by * icount2 + c.cy * icount;
- double x1, y1;
-
- while (--count > 0) {
- x1 = x0 + dx;
- dx += ddx;
- y1 = y0 + dy;
- dy += ddy;
+ // we use x0, y0 to walk the line
+ for (double x1 = x0, y1 = y0; --count > 0; dx += ddx, dy += ddy) {
+ x1 += dx;
+ y1 += dy;
addLine(x0, y0, x1, y1);
-
- if (DO_STATS) { nL++; }
x0 = x1;
y0 = y1;
}
@@ -221,7 +223,7 @@
addLine(x0, y0, x2, y2);
if (DO_STATS) {
- rdrCtx.stats.stat_rdr_quadBreak.add(nL + 1);
+ rdrCtx.stats.stat_rdr_quadBreak.add(nL);
}
}
@@ -234,7 +236,7 @@
final DCurve c,
final double x3, final double y3)
{
- int count = CUB_COUNT;
+ int count = CUB_COUNT;
final double icount = CUB_INV_COUNT; // dt
final double icount2 = CUB_INV_COUNT_2; // dt^2
final double icount3 = CUB_INV_COUNT_3; // dt^3
@@ -249,16 +251,35 @@
dx = c.ax * icount3 + c.bx * icount2 + c.cx * icount;
dy = c.ay * icount3 + c.by * icount2 + c.cy * icount;
- // we use x0, y0 to walk the line
- double x1 = x0, y1 = y0;
int nL = 0; // line count
final double _DEC_BND = CUB_DEC_BND;
final double _INC_BND = CUB_INC_BND;
+ final double _SCALE_DY = SCALE_DY;
- while (count > 0) {
+ // we use x0, y0 to walk the line
+ for (double x1 = x0, y1 = y0; count > 0; ) {
+ // inc / dec => ratio ~ 5 to minimize upscale / downscale but minimize edges
+
+ // double step:
+ // can only do this on even "count" values, because we must divide count by 2
+ while ((count % 2 == 0)
+ && ((Math.abs(ddx) + Math.abs(ddy) * _SCALE_DY) <= _INC_BND)) {
+ dx = 2.0d * dx + ddx;
+ dy = 2.0d * dy + ddy;
+ ddx = 4.0d * (ddx + dddx);
+ ddy = 4.0d * (ddy + dddy);
+ dddx *= 8.0d;
+ dddy *= 8.0d;
+
+ count >>= 1;
+ if (DO_STATS) {
+ rdrCtx.stats.stat_rdr_curveBreak_inc.add(count);
+ }
+ }
+
// divide step by half:
- while (Math.abs(ddx) + Math.abs(ddy) >= _DEC_BND) {
+ while ((Math.abs(ddx) + Math.abs(ddy) * _SCALE_DY) >= _DEC_BND) {
dddx /= 8.0d;
dddy /= 8.0d;
ddx = ddx / 4.0d - dddx;
@@ -271,44 +292,25 @@
rdrCtx.stats.stat_rdr_curveBreak_dec.add(count);
}
}
-
- // double step:
- // can only do this on even "count" values, because we must divide count by 2
- while (count % 2 == 0
- && Math.abs(dx) + Math.abs(dy) <= _INC_BND)
- {
- dx = 2.0d * dx + ddx;
- dy = 2.0d * dy + ddy;
- ddx = 4.0d * (ddx + dddx);
- ddy = 4.0d * (ddy + dddy);
- dddx *= 8.0d;
- dddy *= 8.0d;
-
- count >>= 1;
- if (DO_STATS) {
- rdrCtx.stats.stat_rdr_curveBreak_inc.add(count);
- }
- }
- if (--count > 0) {
- x1 += dx;
- dx += ddx;
- ddx += dddx;
- y1 += dy;
- dy += ddy;
- ddy += dddy;
- } else {
- x1 = x3;
- y1 = y3;
+ if (--count == 0) {
+ break;
}
- addLine(x0, y0, x1, y1);
+ x1 += dx;
+ y1 += dy;
+ dx += ddx;
+ dy += ddy;
+ ddx += dddx;
+ ddy += dddy;
- if (DO_STATS) { nL++; }
+ addLine(x0, y0, x1, y1);
x0 = x1;
y0 = y1;
}
+ addLine(x0, y0, x3, y3);
+
if (DO_STATS) {
- rdrCtx.stats.stat_rdr_curveBreak.add(nL);
+ rdrCtx.stats.stat_rdr_curveBreak.add(nL + 1);
}
}
@@ -533,8 +535,8 @@
edgeBuckets = edgeBuckets_ref.initial;
edgeBucketCounts = edgeBucketCounts_ref.initial;
- // 2048 (pixelsize) pixel large
- alphaLine_ref = rdrCtx.newCleanIntArrayRef(INITIAL_AA_ARRAY); // 8K
+ // 4096 pixels large
+ alphaLine_ref = rdrCtx.newCleanIntArrayRef(INITIAL_AA_ARRAY); // 16K
alphaLine = alphaLine_ref.initial;
crossings_ref = rdrCtx.newDirtyIntArrayRef(INITIAL_CROSSING_COUNT); // 2K
@@ -692,8 +694,10 @@
{
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);
+ 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;
@@ -705,7 +709,9 @@
{
final double xe = tosubpixx(pix_x2);
final double ye = tosubpixy(pix_y2);
- curve.set(x0, y0, tosubpixx(pix_x1), tosubpixy(pix_y1), xe, ye);
+ curve.set(x0, y0,
+ tosubpixx(pix_x1), tosubpixy(pix_y1),
+ xe, ye);
quadBreakIntoLinesAndAdd(x0, y0, curve, xe, ye);
x0 = xe;
y0 = ye;
--- a/src/java.desktop/share/classes/sun/java2d/marlin/DRendererContext.java Fri Mar 23 13:43:39 2018 -0700
+++ b/src/java.desktop/share/classes/sun/java2d/marlin/DRendererContext.java Tue Mar 27 22:09:43 2018 +0200
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2015, 2017, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2015, 2018, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -31,6 +31,8 @@
import sun.java2d.ReentrantContext;
import sun.java2d.marlin.ArrayCacheConst.CacheStats;
import sun.java2d.marlin.DMarlinRenderingEngine.NormalizingPathIterator;
+import sun.java2d.marlin.DTransformingPathConsumer2D.CurveBasicMonotonizer;
+import sun.java2d.marlin.DTransformingPathConsumer2D.CurveClipSplitter;
/**
* This class is a renderer context dedicated to a single thread
@@ -70,6 +72,8 @@
final DStroker stroker;
// Simplifies out collinear lines
final DCollinearSimplifier simplifier = new DCollinearSimplifier();
+ // Simplifies path
+ final DPathSimplifier pathSimplifier = new DPathSimplifier();
final DDasher dasher;
final MarlinTileGenerator ptg;
final MarlinCache cache;
@@ -81,6 +85,10 @@
boolean closedPath = false;
// clip rectangle (ymin, ymax, xmin, xmax):
final double[] clipRect = new double[4];
+ // CurveBasicMonotonizer instance
+ final CurveBasicMonotonizer monotonizer;
+ // CurveClipSplitter instance
+ final CurveClipSplitter curveClipSplitter;
// Array caches:
/* clean int[] cache (zero-filled) = 5 refs */
@@ -124,6 +132,10 @@
nPCPathIterator = new NormalizingPathIterator.NearestPixelCenter(double6);
nPQPathIterator = new NormalizingPathIterator.NearestPixelQuarter(double6);
+ // curve monotonizer & clip subdivider (before transformerPC2D init)
+ monotonizer = new CurveBasicMonotonizer(this);
+ curveClipSplitter = new CurveClipSplitter(this);
+
// MarlinRenderingEngine.TransformingPathConsumer2D
transformerPC2D = new DTransformingPathConsumer2D(this);
--- a/src/java.desktop/share/classes/sun/java2d/marlin/DStroker.java Fri Mar 23 13:43:39 2018 -0700
+++ b/src/java.desktop/share/classes/sun/java2d/marlin/DStroker.java Tue Mar 27 22:09:43 2018 +0200
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2007, 2017, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2007, 2018, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -27,6 +27,8 @@
import java.util.Arrays;
import sun.java2d.marlin.DHelpers.PolyStack;
+import sun.java2d.marlin.DTransformingPathConsumer2D.CurveBasicMonotonizer;
+import sun.java2d.marlin.DTransformingPathConsumer2D.CurveClipSplitter;
// 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
@@ -37,10 +39,9 @@
private static final int DRAWING_OP_TO = 1; // ie. curve, line, or quad
private static final int CLOSE = 2;
- // pisces used to use fixed point arithmetic with 16 decimal digits. I
- // didn't want to change the values of the constant below when I converted
- // 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;
+ // round join threshold = 1 subpixel
+ private static final double ERR_JOIN = (1.0f / MIN_SUBPIXELS);
+ private static final double ROUND_JOIN_THRESHOLD = ERR_JOIN * ERR_JOIN;
// kappa = (4/3) * (SQRT(2) - 1)
private static final double C = (4.0d * (Math.sqrt(2.0d) - 1.0d) / 3.0d);
@@ -48,8 +49,6 @@
// SQRT(2)
private static final double SQRT_2 = Math.sqrt(2.0d);
- private static final int MAX_N_CURVES = 11;
-
private DPathConsumer2D out;
private int capStyle;
@@ -80,12 +79,8 @@
private final PolyStack reverse;
- // This is where the curve to be processed is put. We give it
- // enough room to store all curves.
- private final double[] middle = new double[MAX_N_CURVES * 6 + 2];
private final double[] lp = new double[8];
private final double[] rp = new double[8];
- private final double[] subdivTs = new double[MAX_N_CURVES - 1];
// per-thread renderer context
final DRendererContext rdrCtx;
@@ -106,6 +101,11 @@
private boolean opened = false;
// flag indicating if the starting point's cap is done
private boolean capStart = false;
+ // flag indicating to monotonize curves
+ private boolean monotonize;
+
+ private boolean subdivide = false;
+ private final CurveClipSplitter curveSplitter;
/**
* Constructs a <code>DStroker</code>.
@@ -124,6 +124,7 @@
: new PolyStack(rdrCtx);
this.curve = rdrCtx.curve;
+ this.curveSplitter = rdrCtx.curveClipSplitter;
}
/**
@@ -139,6 +140,7 @@
* <code>JOIN_BEVEL</code>.
* @param miterLimit the desired miter limit
* @param scale scaling factor applied to clip boundaries
+ * @param subdivideCurves true to indicate to subdivide curves, false if dasher does
* @return this instance
*/
DStroker init(final DPathConsumer2D pc2d,
@@ -146,12 +148,15 @@
final int capStyle,
final int joinStyle,
final double miterLimit,
- final double scale)
+ final double scale,
+ final boolean subdivideCurves)
{
this.out = pc2d;
this.lineWidth2 = lineWidth / 2.0d;
this.invHalfLineWidth2Sq = 1.0d / (2.0d * lineWidth2 * lineWidth2);
+ this.monotonize = subdivideCurves;
+
this.capStyle = capStyle;
this.joinStyle = joinStyle;
@@ -189,6 +194,15 @@
_clipRect[2] -= margin - rdrOffX;
_clipRect[3] += margin + rdrOffX;
this.clipRect = _clipRect;
+
+ // initialize curve splitter here for stroker & dasher:
+ if (DO_CLIP_SUBDIVIDER) {
+ subdivide = subdivideCurves;
+ // adjust padded clip rectangle:
+ curveSplitter.init();
+ } else {
+ subdivide = false;
+ }
} else {
this.clipRect = null;
this.cOutCode = 0;
@@ -197,6 +211,12 @@
return this; // fluent API
}
+ void disableClipping() {
+ this.clipRect = null;
+ this.cOutCode = 0;
+ this.sOutCode = 0;
+ }
+
/**
* Disposes this stroker:
* clean up before reusing this instance
@@ -213,10 +233,8 @@
Arrays.fill(offset1, 0.0d);
Arrays.fill(offset2, 0.0d);
Arrays.fill(miter, 0.0d);
- Arrays.fill(middle, 0.0d);
Arrays.fill(lp, 0.0d);
Arrays.fill(rp, 0.0d);
- Arrays.fill(subdivTs, 0.0d);
}
}
@@ -248,19 +266,20 @@
return dx1 * dy2 <= dy1 * dx2;
}
- private void drawRoundJoin(double x, double y,
- double omx, double omy, double mx, double my,
- boolean rev,
- double threshold)
+ private void mayDrawRoundJoin(double cx, double cy,
+ double omx, double omy,
+ double mx, double my,
+ boolean rev)
{
if ((omx == 0.0d && omy == 0.0d) || (mx == 0.0d && my == 0.0d)) {
return;
}
- double domx = omx - mx;
- double domy = omy - my;
- double len = domx*domx + domy*domy;
- if (len < threshold) {
+ final double domx = omx - mx;
+ final double domy = omy - my;
+ final double lenSq = domx*domx + domy*domy;
+
+ if (lenSq < ROUND_JOIN_THRESHOLD) {
return;
}
@@ -270,7 +289,7 @@
mx = -mx;
my = -my;
}
- drawRoundJoin(x, y, omx, omy, mx, my, rev);
+ drawRoundJoin(cx, cy, omx, omy, mx, my, rev);
}
private void drawRoundJoin(double cx, double cy,
@@ -381,7 +400,7 @@
final double x1, final double y1,
final double x0p, final double y0p,
final double x1p, final double y1p,
- final double[] m, int off)
+ final double[] m)
{
double x10 = x1 - x0;
double y10 = y1 - y0;
@@ -400,8 +419,8 @@
double den = x10*y10p - x10p*y10;
double t = x10p*(y0-y0p) - y10p*(x0-x0p);
t /= den;
- m[off++] = x0 + t*x10;
- m[off] = y0 + t*y10;
+ m[0] = x0 + t*x10;
+ m[1] = y0 + t*y10;
}
// Return the intersection point of the lines (x0, y0) -> (x1, y1)
@@ -410,7 +429,7 @@
final double x1, final double y1,
final double x0p, final double y0p,
final double x1p, final double y1p,
- final double[] m, int off)
+ final double[] m)
{
double x10 = x1 - x0;
double y10 = y1 - y0;
@@ -428,20 +447,21 @@
// immediately).
double den = x10*y10p - x10p*y10;
if (den == 0.0d) {
- m[off++] = (x0 + x0p) / 2.0d;
- m[off] = (y0 + y0p) / 2.0d;
- return;
+ m[2] = (x0 + x0p) / 2.0d;
+ m[3] = (y0 + y0p) / 2.0d;
+ } else {
+ double t = x10p*(y0-y0p) - y10p*(x0-x0p);
+ t /= den;
+ m[2] = x0 + t*x10;
+ m[3] = y0 + t*y10;
}
- double t = x10p*(y0-y0p) - y10p*(x0-x0p);
- t /= den;
- m[off++] = x0 + t*x10;
- m[off] = y0 + t*y10;
}
private void drawMiter(final double pdx, final double pdy,
final double x0, final double y0,
final double dx, final double dy,
- double omx, double omy, double mx, double my,
+ double omx, double omy,
+ double mx, double my,
boolean rev)
{
if ((mx == omx && my == omy) ||
@@ -459,8 +479,7 @@
}
computeMiter((x0 - pdx) + omx, (y0 - pdy) + omy, x0 + omx, y0 + omy,
- (dx + x0) + mx, (dy + y0) + my, x0 + mx, y0 + my,
- miter, 0);
+ (dx + x0) + mx, (dy + y0) + my, x0 + mx, y0 + my, miter);
final double miterX = miter[0];
final double miterY = miter[1];
@@ -478,7 +497,7 @@
@Override
public void moveTo(final double x0, final double y0) {
- moveTo(x0, y0, cOutCode);
+ _moveTo(x0, y0, cOutCode);
// update starting point:
this.sx0 = x0;
this.sy0 = y0;
@@ -494,7 +513,7 @@
}
}
- private void moveTo(final double x0, final double y0,
+ private void _moveTo(final double x0, final double y0,
final int outcode)
{
if (prev == MOVE_TO) {
@@ -521,16 +540,40 @@
final boolean force)
{
final int outcode0 = this.cOutCode;
+
if (!force && clipRect != null) {
final int outcode1 = DHelpers.outcode(x1, y1, clipRect);
- this.cOutCode = outcode1;
+
+ // Should clip
+ final int orCode = (outcode0 | outcode1);
+ if (orCode != 0) {
+ final int sideCode = outcode0 & outcode1;
- // basic rejection criteria
- if ((outcode0 & outcode1) != 0) {
- moveTo(x1, y1, outcode0);
- opened = true;
- return;
+ // basic rejection criteria:
+ if (sideCode == 0) {
+ // ovelap clip:
+ if (subdivide) {
+ // avoid reentrance
+ subdivide = false;
+ // subdivide curve => callback with subdivided parts:
+ boolean ret = curveSplitter.splitLine(cx0, cy0, x1, y1,
+ orCode, this);
+ // reentrance is done:
+ subdivide = true;
+ if (ret) {
+ return;
+ }
+ }
+ // already subdivided so render it
+ } else {
+ this.cOutCode = outcode1;
+ _moveTo(x1, y1, outcode0);
+ opened = true;
+ return;
+ }
}
+
+ this.cOutCode = outcode1;
}
double dx = x1 - cx0;
@@ -752,10 +795,7 @@
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);
+ mayDrawRoundJoin(x0, y0, omx, omy, mx, my, cw);
}
}
emitLineTo(x0, y0, !cw);
@@ -765,18 +805,19 @@
private static boolean within(final double x1, final double y1,
final double x2, final double y2,
- final double ERR)
+ final double err)
{
- assert ERR > 0 : "";
+ assert err > 0 : "";
// compare taxicab distance. ERR will always be small, so using
// true distance won't give much benefit
- return (DHelpers.within(x1, x2, ERR) && // we want to avoid calling Math.abs
- DHelpers.within(y1, y2, ERR)); // this is just as good.
+ return (DHelpers.within(x1, x2, err) && // we want to avoid calling Math.abs
+ DHelpers.within(y1, y2, err)); // this is just as good.
}
- private void getLineOffsets(double x1, double y1,
- double x2, double y2,
- double[] left, double[] right) {
+ private void getLineOffsets(final double x1, final double y1,
+ final double x2, final double y2,
+ final double[] left, final double[] right)
+ {
computeOffset(x2 - x1, y2 - y1, lineWidth2, offset0);
final double mx = offset0[0];
final double my = offset0[1];
@@ -784,14 +825,16 @@
left[1] = y1 + my;
left[2] = x2 + mx;
left[3] = y2 + my;
+
right[0] = x1 - mx;
right[1] = y1 - my;
right[2] = x2 - mx;
right[3] = y2 - my;
}
- private int computeOffsetCubic(double[] pts, final int off,
- double[] leftOff, double[] rightOff)
+ private int computeOffsetCubic(final double[] pts, final int off,
+ final double[] leftOff,
+ final double[] rightOff)
{
// if p1=p2 or p3=p4 it means that the derivative at the endpoint
// vanishes, which creates problems with computeOffset. Usually
@@ -800,7 +843,7 @@
// the input curve at the cusp, and passes it to this function.
// because of inaccuracies in the splitting, we consider points
// equal if they're very close to each other.
- final double x1 = pts[off + 0], y1 = pts[off + 1];
+ final double x1 = pts[off ], y1 = pts[off + 1];
final double x2 = pts[off + 2], y2 = pts[off + 3];
final double x3 = pts[off + 4], y3 = pts[off + 5];
final double x4 = pts[off + 6], y4 = pts[off + 7];
@@ -814,6 +857,7 @@
// in which case ignore if p1 == p2
final boolean p1eqp2 = within(x1, y1, x2, y2, 6.0d * Math.ulp(y2));
final boolean p3eqp4 = within(x3, y3, x4, y4, 6.0d * Math.ulp(y4));
+
if (p1eqp2 && p3eqp4) {
getLineOffsets(x1, y1, x4, y4, leftOff, rightOff);
return 4;
@@ -829,6 +873,7 @@
double dotsq = (dx1 * dx4 + dy1 * dy4);
dotsq *= dotsq;
double l1sq = dx1 * dx1 + dy1 * dy1, l4sq = dx4 * dx4 + dy4 * dy4;
+
if (DHelpers.within(dotsq, l1sq * l4sq, 4.0d * Math.ulp(dotsq))) {
getLineOffsets(x1, y1, x4, y4, leftOff, rightOff);
return 4;
@@ -942,10 +987,11 @@
// compute offset curves using bezier spline through t=0.5 (i.e.
// ComputedCurve(0.5) == IdealParallelCurve(0.5))
// return the kind of curve in the right and left arrays.
- private int computeOffsetQuad(double[] pts, final int off,
- double[] leftOff, double[] rightOff)
+ private int computeOffsetQuad(final double[] pts, final int off,
+ final double[] leftOff,
+ final double[] rightOff)
{
- final double x1 = pts[off + 0], y1 = pts[off + 1];
+ final double x1 = pts[off ], y1 = pts[off + 1];
final double x2 = pts[off + 2], y2 = pts[off + 3];
final double x3 = pts[off + 4], y3 = pts[off + 5];
@@ -966,6 +1012,7 @@
// in which case ignore.
final boolean p1eqp2 = within(x1, y1, x2, y2, 6.0d * Math.ulp(y2));
final boolean p2eqp3 = within(x2, y2, x3, y3, 6.0d * Math.ulp(y3));
+
if (p1eqp2 || p2eqp3) {
getLineOffsets(x1, y1, x3, y3, leftOff, rightOff);
return 4;
@@ -975,6 +1022,7 @@
double dotsq = (dx1 * dx3 + dy1 * dy3);
dotsq *= dotsq;
double l1sq = dx1 * dx1 + dy1 * dy1, l3sq = dx3 * dx3 + dy3 * dy3;
+
if (DHelpers.within(dotsq, l1sq * l3sq, 4.0d * Math.ulp(dotsq))) {
getLineOffsets(x1, y1, x3, y3, leftOff, rightOff);
return 4;
@@ -990,151 +1038,111 @@
double y1p = y1 + offset0[1]; // point
double x3p = x3 + offset1[0]; // end
double y3p = y3 + offset1[1]; // point
- safeComputeMiter(x1p, y1p, x1p+dx1, y1p+dy1, x3p, y3p, x3p-dx3, y3p-dy3, leftOff, 2);
+ safeComputeMiter(x1p, y1p, x1p+dx1, y1p+dy1, x3p, y3p, x3p-dx3, y3p-dy3, leftOff);
leftOff[0] = x1p; leftOff[1] = y1p;
leftOff[4] = x3p; leftOff[5] = y3p;
x1p = x1 - offset0[0]; y1p = y1 - offset0[1];
x3p = x3 - offset1[0]; y3p = y3 - offset1[1];
- safeComputeMiter(x1p, y1p, x1p+dx1, y1p+dy1, x3p, y3p, x3p-dx3, y3p-dy3, rightOff, 2);
+ safeComputeMiter(x1p, y1p, x1p+dx1, y1p+dy1, x3p, y3p, x3p-dx3, y3p-dy3, rightOff);
rightOff[0] = x1p; rightOff[1] = y1p;
rightOff[4] = x3p; rightOff[5] = y3p;
return 6;
}
- // finds values of t where the curve in pts should be subdivided in order
- // to get good offset curves a distance of w away from the middle curve.
- // Stores the points in ts, and returns how many of them there were.
- private static int findSubdivPoints(final DCurve c, double[] pts, double[] ts,
- final int type, final double w)
- {
- final double x12 = pts[2] - pts[0];
- final double y12 = pts[3] - pts[1];
- // if the curve is already parallel to either axis we gain nothing
- // from rotating it.
- if (y12 != 0.0d && x12 != 0.0d) {
- // we rotate it so that the first vector in the control polygon is
- // parallel to the x-axis. This will ensure that rotated quarter
- // circles won't be subdivided.
- final double hypot = Math.sqrt(x12 * x12 + y12 * y12);
- final double cos = x12 / hypot;
- final double sin = y12 / hypot;
- final double x1 = cos * pts[0] + sin * pts[1];
- final double y1 = cos * pts[1] - sin * pts[0];
- final double x2 = cos * pts[2] + sin * pts[3];
- final double y2 = cos * pts[3] - sin * pts[2];
- final double x3 = cos * pts[4] + sin * pts[5];
- final double y3 = cos * pts[5] - sin * pts[4];
-
- switch(type) {
- case 8:
- final double x4 = cos * pts[6] + sin * pts[7];
- final double y4 = cos * pts[7] - sin * pts[6];
- c.set(x1, y1, x2, y2, x3, y3, x4, y4);
- break;
- case 6:
- c.set(x1, y1, x2, y2, x3, y3);
- break;
- default:
- }
- } else {
- c.set(pts, type);
- }
-
- int ret = 0;
- // we subdivide at values of t such that the remaining rotated
- // curves are monotonic in x and y.
- ret += c.dxRoots(ts, ret);
- ret += c.dyRoots(ts, ret);
- // subdivide at inflection points.
- if (type == 8) {
- // quadratic curves can't have inflection points
- ret += c.infPoints(ts, ret);
- }
-
- // now we must subdivide at points where one of the offset curves will have
- // a cusp. This happens at ts where the radius of curvature is equal to w.
- ret += c.rootsOfROCMinusW(ts, ret, w, 0.0001d);
-
- ret = DHelpers.filterOutNotInAB(ts, 0, ret, 0.0001d, 0.9999d);
- DHelpers.isort(ts, 0, ret);
- return ret;
- }
-
@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 outcode1 = DHelpers.outcode(x1, y1, clipRect);
+ final int outcode2 = DHelpers.outcode(x2, y2, clipRect);
final int outcode3 = DHelpers.outcode(x3, y3, clipRect);
- this.cOutCode = outcode3;
+
+ // Should clip
+ final int orCode = (outcode0 | outcode1 | outcode2 | outcode3);
+ if (orCode != 0) {
+ final int sideCode = outcode0 & outcode1 & outcode2 & outcode3;
- if ((outcode0 & 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);
+ // basic rejection criteria:
+ if (sideCode == 0) {
+ // ovelap clip:
+ if (subdivide) {
+ // avoid reentrance
+ subdivide = false;
+ // subdivide curve => callback with subdivided parts:
+ boolean ret = curveSplitter.splitCurve(cx0, cy0, x1, y1,
+ x2, y2, x3, y3,
+ orCode, this);
+ // reentrance is done:
+ subdivide = true;
+ if (ret) {
+ return;
+ }
+ }
+ // already subdivided so render it
+ } else {
+ this.cOutCode = outcode3;
+ _moveTo(x3, y3, outcode0);
opened = true;
return;
}
}
+
+ this.cOutCode = outcode3;
}
-
- final double[] mid = middle;
+ _curveTo(x1, y1, x2, y2, x3, y3, outcode0);
+ }
- mid[0] = cx0; mid[1] = cy0;
- mid[2] = x1; mid[3] = y1;
- mid[4] = x2; mid[5] = y2;
- mid[6] = x3; mid[7] = y3;
-
+ private void _curveTo(final double x1, final double y1,
+ final double x2, final double y2,
+ final double x3, final double y3,
+ final int outcode0)
+ {
// need these so we can update the state at the end of this method
- final double xf = x3, yf = y3;
- double dxs = mid[2] - mid[0];
- double dys = mid[3] - mid[1];
- double dxf = mid[6] - mid[4];
- double dyf = mid[7] - mid[5];
+ double dxs = x1 - cx0;
+ double dys = y1 - cy0;
+ double dxf = x3 - x2;
+ double dyf = y3 - y2;
- boolean p1eqp2 = (dxs == 0.0d && dys == 0.0d);
- boolean p3eqp4 = (dxf == 0.0d && dyf == 0.0d);
- if (p1eqp2) {
- dxs = mid[4] - mid[0];
- dys = mid[5] - mid[1];
- if (dxs == 0.0d && dys == 0.0d) {
- dxs = mid[6] - mid[0];
- dys = mid[7] - mid[1];
+ if ((dxs == 0.0d) && (dys == 0.0d)) {
+ dxs = x2 - cx0;
+ dys = y2 - cy0;
+ if ((dxs == 0.0d) && (dys == 0.0d)) {
+ dxs = x3 - cx0;
+ dys = y3 - cy0;
}
}
- if (p3eqp4) {
- dxf = mid[6] - mid[2];
- dyf = mid[7] - mid[3];
- if (dxf == 0.0d && dyf == 0.0d) {
- dxf = mid[6] - mid[0];
- dyf = mid[7] - mid[1];
+ if ((dxf == 0.0d) && (dyf == 0.0d)) {
+ dxf = x3 - x1;
+ dyf = y3 - y1;
+ if ((dxf == 0.0d) && (dyf == 0.0d)) {
+ dxf = x3 - cx0;
+ dyf = y3 - cy0;
}
}
- if (dxs == 0.0d && dys == 0.0d) {
+ 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]);
+ lineTo(cx0, cy0);
return;
}
// if these vectors are too small, normalize them, to avoid future
// precision problems.
if (Math.abs(dxs) < 0.1d && Math.abs(dys) < 0.1d) {
- double len = Math.sqrt(dxs*dxs + dys*dys);
+ final double len = Math.sqrt(dxs * dxs + dys * dys);
dxs /= len;
dys /= len;
}
if (Math.abs(dxf) < 0.1d && Math.abs(dyf) < 0.1d) {
- double len = Math.sqrt(dxf*dxf + dyf*dyf);
+ final double len = Math.sqrt(dxf * dxf + dyf * dyf);
dxf /= len;
dyf /= len;
}
@@ -1142,17 +1150,25 @@
computeOffset(dxs, dys, lineWidth2, offset0);
drawJoin(cdx, cdy, cx0, cy0, dxs, dys, cmx, cmy, offset0[0], offset0[1], outcode0);
- final int nSplits = findSubdivPoints(curve, mid, subdivTs, 8, lineWidth2);
+ int nSplits = 0;
+ final double[] mid;
+ final double[] l = lp;
+
+ if (monotonize) {
+ // monotonize curve:
+ final CurveBasicMonotonizer monotonizer
+ = rdrCtx.monotonizer.curve(cx0, cy0, x1, y1, x2, y2, x3, y3);
- double prevT = 0.0d;
- for (int i = 0, off = 0; i < nSplits; i++, off += 6) {
- final double t = subdivTs[i];
- DHelpers.subdivideCubicAt((t - prevT) / (1.0d - prevT),
- mid, off, mid, off, mid, off + 6);
- prevT = t;
+ nSplits = monotonizer.nbSplits;
+ mid = monotonizer.middle;
+ } else {
+ // use left instead:
+ mid = l;
+ mid[0] = cx0; mid[1] = cy0;
+ mid[2] = x1; mid[3] = y1;
+ mid[4] = x2; mid[5] = y2;
+ mid[6] = x3; mid[7] = y3;
}
-
- final double[] l = lp;
final double[] r = rp;
int kind = 0;
@@ -1176,8 +1192,8 @@
}
this.prev = DRAWING_OP_TO;
- this.cx0 = xf;
- this.cy0 = yf;
+ this.cx0 = x3;
+ this.cy0 = y3;
this.cdx = dxf;
this.cdy = dyf;
this.cmx = (l[kind - 2] - r[kind - 2]) / 2.0d;
@@ -1189,74 +1205,101 @@
final double x2, final double y2)
{
final int outcode0 = this.cOutCode;
+
if (clipRect != null) {
+ final int outcode1 = DHelpers.outcode(x1, y1, clipRect);
final int outcode2 = DHelpers.outcode(x2, y2, clipRect);
- this.cOutCode = outcode2;
+
+ // Should clip
+ final int orCode = (outcode0 | outcode1 | outcode2);
+ if (orCode != 0) {
+ final int sideCode = outcode0 & outcode1 & outcode2;
- if ((outcode0 & outcode2) != 0) {
- final int outcode1 = DHelpers.outcode(x1, y1, clipRect);
-
- // basic rejection criteria
- if ((outcode0 & outcode1 & outcode2) != 0) {
- moveTo(x2, y2, outcode0);
+ // basic rejection criteria:
+ if (sideCode == 0) {
+ // ovelap clip:
+ if (subdivide) {
+ // avoid reentrance
+ subdivide = false;
+ // subdivide curve => call lineTo() with subdivided curves:
+ boolean ret = curveSplitter.splitQuad(cx0, cy0, x1, y1,
+ x2, y2, orCode, this);
+ // reentrance is done:
+ subdivide = true;
+ if (ret) {
+ return;
+ }
+ }
+ // already subdivided so render it
+ } else {
+ this.cOutCode = outcode2;
+ _moveTo(x2, y2, outcode0);
opened = true;
return;
}
}
- }
-
- final double[] mid = middle;
- mid[0] = cx0; mid[1] = cy0;
- mid[2] = x1; mid[3] = y1;
- mid[4] = x2; mid[5] = y2;
+ this.cOutCode = outcode2;
+ }
+ _quadTo(x1, y1, x2, y2, outcode0);
+ }
+ private void _quadTo(final double x1, final double y1,
+ final double x2, final double y2,
+ final int outcode0)
+ {
// need these so we can update the state at the end of this method
- final double xf = x2, yf = y2;
- double dxs = mid[2] - mid[0];
- double dys = mid[3] - mid[1];
- double dxf = mid[4] - mid[2];
- double dyf = mid[5] - mid[3];
- if ((dxs == 0.0d && dys == 0.0d) || (dxf == 0.0d && dyf == 0.0d)) {
- dxs = dxf = mid[4] - mid[0];
- dys = dyf = mid[5] - mid[1];
+ double dxs = x1 - cx0;
+ double dys = y1 - cy0;
+ double dxf = x2 - x1;
+ double dyf = y2 - y1;
+
+ if (((dxs == 0.0d) && (dys == 0.0d)) || ((dxf == 0.0d) && (dyf == 0.0d))) {
+ dxs = dxf = x2 - cx0;
+ dys = dyf = y2 - cy0;
}
- if (dxs == 0.0d && dys == 0.0d) {
+ 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]);
+ lineTo(cx0, cy0);
return;
}
// if these vectors are too small, normalize them, to avoid future
// precision problems.
if (Math.abs(dxs) < 0.1d && Math.abs(dys) < 0.1d) {
- double len = Math.sqrt(dxs*dxs + dys*dys);
+ final double len = Math.sqrt(dxs * dxs + dys * dys);
dxs /= len;
dys /= len;
}
if (Math.abs(dxf) < 0.1d && Math.abs(dyf) < 0.1d) {
- double len = Math.sqrt(dxf*dxf + dyf*dyf);
+ final double len = Math.sqrt(dxf * dxf + dyf * dyf);
dxf /= len;
dyf /= len;
}
-
computeOffset(dxs, dys, lineWidth2, offset0);
drawJoin(cdx, cdy, cx0, cy0, dxs, dys, cmx, cmy, offset0[0], offset0[1], outcode0);
- int nSplits = findSubdivPoints(curve, mid, subdivTs, 6, lineWidth2);
+ int nSplits = 0;
+ final double[] mid;
+ final double[] l = lp;
+
+ if (monotonize) {
+ // monotonize quad:
+ final CurveBasicMonotonizer monotonizer
+ = rdrCtx.monotonizer.quad(cx0, cy0, x1, y1, x2, y2);
- double prevt = 0.0d;
- for (int i = 0, off = 0; i < nSplits; i++, off += 4) {
- final double t = subdivTs[i];
- DHelpers.subdivideQuadAt((t - prevt) / (1.0d - prevt),
- mid, off, mid, off, mid, off + 4);
- prevt = t;
+ nSplits = monotonizer.nbSplits;
+ mid = monotonizer.middle;
+ } else {
+ // use left instead:
+ mid = l;
+ mid[0] = cx0; mid[1] = cy0;
+ mid[2] = x1; mid[3] = y1;
+ mid[4] = x2; mid[5] = y2;
}
-
- final double[] l = lp;
final double[] r = rp;
int kind = 0;
@@ -1280,8 +1323,8 @@
}
this.prev = DRAWING_OP_TO;
- this.cx0 = xf;
- this.cy0 = yf;
+ this.cx0 = x2;
+ this.cy0 = y2;
this.cdx = dxf;
this.cdy = dyf;
this.cmx = (l[kind - 2] - r[kind - 2]) / 2.0d;
--- a/src/java.desktop/share/classes/sun/java2d/marlin/DTransformingPathConsumer2D.java Fri Mar 23 13:43:39 2018 -0700
+++ b/src/java.desktop/share/classes/sun/java2d/marlin/DTransformingPathConsumer2D.java Tue Mar 27 22:09:43 2018 +0200
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2007, 2017, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2007, 2018, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -27,11 +27,15 @@
import java.awt.geom.AffineTransform;
import java.awt.geom.Path2D;
+import java.util.Arrays;
import sun.java2d.marlin.DHelpers.IndexStack;
import sun.java2d.marlin.DHelpers.PolyStack;
final class DTransformingPathConsumer2D {
+ // smaller uncertainty in double variant
+ static final double CLIP_RECT_PADDING = 0.25d;
+
private final DRendererContext rdrCtx;
// recycled ClosedPathDetector instance from detectClosedPath()
@@ -56,6 +60,7 @@
private final PathTracer tracerCPDetector = new PathTracer("ClosedPathDetector");
private final PathTracer tracerFiller = new PathTracer("Filler");
private final PathTracer tracerStroker = new PathTracer("Stroker");
+ private final PathTracer tracerDasher = new PathTracer("Dasher");
DTransformingPathConsumer2D(final DRendererContext rdrCtx) {
// used by RendererContext
@@ -84,6 +89,10 @@
return tracerStroker.init(out);
}
+ DPathConsumer2D traceDasher(DPathConsumer2D out) {
+ return tracerDasher.init(out);
+ }
+
DPathConsumer2D detectClosedPath(DPathConsumer2D out) {
return cpDetector.init(out);
}
@@ -499,11 +508,19 @@
private boolean outside = false;
+ // The current point (TODO stupid repeated info)
+ private double cx0, cy0;
+
// The current point OUTSIDE
- private double cx0, cy0;
+ private double cox0, coy0;
+
+ private boolean subdivide = MarlinConst.DO_CLIP_SUBDIVIDER;
+ private final CurveClipSplitter curveSplitter;
PathClipFilter(final DRendererContext rdrCtx) {
this.clipRect = rdrCtx.clipRect;
+ this.curveSplitter = rdrCtx.curveClipSplitter;
+
this.stack = (rdrCtx.stats != null) ?
new IndexStack(rdrCtx,
rdrCtx.stats.stat_pcf_idxstack_indices,
@@ -528,6 +545,11 @@
_clipRect[2] -= margin - rdrOffX;
_clipRect[3] += margin + rdrOffX;
+ if (MarlinConst.DO_CLIP_SUBDIVIDER) {
+ // adjust padded clip rectangle:
+ curveSplitter.init();
+ }
+
this.init_corners = true;
this.gOutCode = MarlinConst.OUTCODE_MASK_T_B_L_R;
@@ -578,7 +600,9 @@
}
stack.pullAll(corners, out);
}
- out.lineTo(cx0, cy0);
+ out.lineTo(cox0, coy0);
+ this.cx0 = cox0;
+ this.cy0 = coy0;
}
@Override
@@ -603,38 +627,68 @@
public void moveTo(final double x0, final double y0) {
finishPath();
- final int outcode = DHelpers.outcode(x0, y0, clipRect);
- this.cOutCode = outcode;
+ this.cOutCode = DHelpers.outcode(x0, y0, clipRect);
this.outside = false;
out.moveTo(x0, y0);
+ this.cx0 = x0;
+ this.cy0 = y0;
}
@Override
public void lineTo(final double xe, final double ye) {
final int outcode0 = this.cOutCode;
final int outcode1 = DHelpers.outcode(xe, ye, clipRect);
- this.cOutCode = outcode1;
- final int sideCode = (outcode0 & outcode1);
+ // Should clip
+ final int orCode = (outcode0 | outcode1);
+ if (orCode != 0) {
+ final int sideCode = (outcode0 & outcode1);
- // basic rejection criteria:
- if (sideCode == 0) {
- this.gOutCode = 0;
- } else {
- this.gOutCode &= sideCode;
- // keep last point coordinate before entering the clip again:
- this.outside = true;
- this.cx0 = xe;
- this.cy0 = ye;
+ // basic rejection criteria:
+ if (sideCode == 0) {
+ // ovelap clip:
+ if (subdivide) {
+ // avoid reentrance
+ subdivide = false;
+ boolean ret;
+ // subdivide curve => callback with subdivided parts:
+ if (outside) {
+ ret = curveSplitter.splitLine(cox0, coy0, xe, ye,
+ orCode, this);
+ } else {
+ ret = curveSplitter.splitLine(cx0, cy0, xe, ye,
+ orCode, this);
+ }
+ // reentrance is done:
+ subdivide = true;
+ if (ret) {
+ return;
+ }
+ }
+ // already subdivided so render it
+ } else {
+ this.cOutCode = outcode1;
+ this.gOutCode &= sideCode;
+ // keep last point coordinate before entering the clip again:
+ this.outside = true;
+ this.cox0 = xe;
+ this.coy0 = ye;
- clip(sideCode, outcode0, outcode1);
- return;
+ clip(sideCode, outcode0, outcode1);
+ return;
+ }
}
+
+ this.cOutCode = outcode1;
+ this.gOutCode = 0;
+
if (outside) {
finish();
}
// clipping disabled:
out.lineTo(xe, ye);
+ this.cx0 = xe;
+ this.cy0 = ye;
}
private void clip(final int sideCode,
@@ -654,22 +708,18 @@
// add corners to outside stack:
switch (tbCode) {
case MarlinConst.OUTCODE_TOP:
-// System.out.println("TOP "+ ((off == 0) ? "LEFT" : "RIGHT"));
stack.push(off); // top
return;
case MarlinConst.OUTCODE_BOTTOM:
-// System.out.println("BOTTOM "+ ((off == 0) ? "LEFT" : "RIGHT"));
stack.push(off + 1); // bottom
return;
default:
// both TOP / BOTTOM:
if ((outcode0 & MarlinConst.OUTCODE_TOP) != 0) {
-// System.out.println("TOP + BOTTOM "+ ((off == 0) ? "LEFT" : "RIGHT"));
// top to bottom
stack.push(off); // top
stack.push(off + 1); // bottom
} else {
-// System.out.println("BOTTOM + TOP "+ ((off == 0) ? "LEFT" : "RIGHT"));
// bottom to top
stack.push(off + 1); // bottom
stack.push(off); // top
@@ -684,34 +734,62 @@
final double xe, final double ye)
{
final int outcode0 = this.cOutCode;
+ final int outcode1 = DHelpers.outcode(x1, y1, clipRect);
+ final int outcode2 = DHelpers.outcode(x2, y2, clipRect);
final int outcode3 = DHelpers.outcode(xe, ye, clipRect);
- this.cOutCode = outcode3;
-
- int sideCode = outcode0 & outcode3;
- if (sideCode == 0) {
- this.gOutCode = 0;
- } else {
- sideCode &= DHelpers.outcode(x1, y1, clipRect);
- sideCode &= DHelpers.outcode(x2, y2, clipRect);
- this.gOutCode &= sideCode;
+ // Should clip
+ final int orCode = (outcode0 | outcode1 | outcode2 | outcode3);
+ if (orCode != 0) {
+ final int sideCode = outcode0 & outcode1 & outcode2 & outcode3;
// basic rejection criteria:
- if (sideCode != 0) {
+ if (sideCode == 0) {
+ // ovelap clip:
+ if (subdivide) {
+ // avoid reentrance
+ subdivide = false;
+ // subdivide curve => callback with subdivided parts:
+ boolean ret;
+ if (outside) {
+ ret = curveSplitter.splitCurve(cox0, coy0, x1, y1,
+ x2, y2, xe, ye,
+ orCode, this);
+ } else {
+ ret = curveSplitter.splitCurve(cx0, cy0, x1, y1,
+ x2, y2, xe, ye,
+ orCode, this);
+ }
+ // reentrance is done:
+ subdivide = true;
+ if (ret) {
+ return;
+ }
+ }
+ // already subdivided so render it
+ } else {
+ this.cOutCode = outcode3;
+ this.gOutCode &= sideCode;
// keep last point coordinate before entering the clip again:
this.outside = true;
- this.cx0 = xe;
- this.cy0 = ye;
+ this.cox0 = xe;
+ this.coy0 = ye;
clip(sideCode, outcode0, outcode3);
return;
}
}
+
+ this.cOutCode = outcode3;
+ this.gOutCode = 0;
+
if (outside) {
finish();
}
// clipping disabled:
out.curveTo(x1, y1, x2, y2, xe, ye);
+ this.cx0 = xe;
+ this.cy0 = ye;
}
@Override
@@ -719,33 +797,59 @@
final double xe, final double ye)
{
final int outcode0 = this.cOutCode;
+ final int outcode1 = DHelpers.outcode(x1, y1, clipRect);
final int outcode2 = DHelpers.outcode(xe, ye, clipRect);
- this.cOutCode = outcode2;
-
- int sideCode = outcode0 & outcode2;
- if (sideCode == 0) {
- this.gOutCode = 0;
- } else {
- sideCode &= DHelpers.outcode(x1, y1, clipRect);
- this.gOutCode &= sideCode;
+ // Should clip
+ final int orCode = (outcode0 | outcode1 | outcode2);
+ if (orCode != 0) {
+ final int sideCode = outcode0 & outcode1 & outcode2;
// basic rejection criteria:
- if (sideCode != 0) {
+ if (sideCode == 0) {
+ // ovelap clip:
+ if (subdivide) {
+ // avoid reentrance
+ subdivide = false;
+ // subdivide curve => callback with subdivided parts:
+ boolean ret;
+ if (outside) {
+ ret = curveSplitter.splitQuad(cox0, coy0, x1, y1,
+ xe, ye, orCode, this);
+ } else {
+ ret = curveSplitter.splitQuad(cx0, cy0, x1, y1,
+ xe, ye, orCode, this);
+ }
+ // reentrance is done:
+ subdivide = true;
+ if (ret) {
+ return;
+ }
+ }
+ // already subdivided so render it
+ } else {
+ this.cOutCode = outcode2;
+ this.gOutCode &= sideCode;
// keep last point coordinate before entering the clip again:
this.outside = true;
- this.cx0 = xe;
- this.cy0 = ye;
+ this.cox0 = xe;
+ this.coy0 = ye;
clip(sideCode, outcode0, outcode2);
return;
}
}
+
+ this.cOutCode = outcode2;
+ this.gOutCode = 0;
+
if (outside) {
finish();
}
// clipping disabled:
out.quadTo(x1, y1, xe, ye);
+ this.cx0 = xe;
+ this.cy0 = ye;
}
@Override
@@ -754,6 +858,261 @@
}
}
+ static final class CurveClipSplitter {
+
+ static final double LEN_TH = MarlinProperties.getSubdividerMinLength();
+ static final boolean DO_CHECK_LENGTH = (LEN_TH > 0.0d);
+
+ private static final boolean TRACE = false;
+
+ private static final int MAX_N_CURVES = 3 * 4;
+
+ // clip rectangle (ymin, ymax, xmin, xmax):
+ final double[] clipRect;
+
+ // clip rectangle (ymin, ymax, xmin, xmax) including padding:
+ final double[] clipRectPad = new double[4];
+ private boolean init_clipRectPad = false;
+
+ // This is where the curve to be processed is put. We give it
+ // enough room to store all curves.
+ final double[] middle = new double[MAX_N_CURVES * 8 + 2];
+ // t values at subdivision points
+ private final double[] subdivTs = new double[MAX_N_CURVES];
+
+ // dirty curve
+ private final DCurve curve;
+
+ CurveClipSplitter(final DRendererContext rdrCtx) {
+ this.clipRect = rdrCtx.clipRect;
+ this.curve = rdrCtx.curve;
+ }
+
+ void init() {
+ this.init_clipRectPad = true;
+ }
+
+ private void initPaddedClip() {
+ // bounds as half-open intervals: minX <= x < maxX and minY <= y < maxY
+ // adjust padded clip rectangle (ymin, ymax, xmin, xmax):
+ // add a rounding error (curve subdivision ~ 0.1px):
+ final double[] _clipRect = clipRect;
+ final double[] _clipRectPad = clipRectPad;
+
+ _clipRectPad[0] = _clipRect[0] - CLIP_RECT_PADDING;
+ _clipRectPad[1] = _clipRect[1] + CLIP_RECT_PADDING;
+ _clipRectPad[2] = _clipRect[2] - CLIP_RECT_PADDING;
+ _clipRectPad[3] = _clipRect[3] + CLIP_RECT_PADDING;
+
+ if (TRACE) {
+ MarlinUtils.logInfo("clip: X [" + _clipRectPad[2] + " .. " + _clipRectPad[3] +"] "
+ + "Y ["+ _clipRectPad[0] + " .. " + _clipRectPad[1] +"]");
+ }
+ }
+
+ boolean splitLine(final double x0, final double y0,
+ final double x1, final double y1,
+ final int outCodeOR,
+ final DPathConsumer2D out)
+ {
+ if (TRACE) {
+ MarlinUtils.logInfo("divLine P0(" + x0 + ", " + y0 + ") P1(" + x1 + ", " + y1 + ")");
+ }
+
+ if (DO_CHECK_LENGTH && DHelpers.fastLineLen(x0, y0, x1, y1) <= LEN_TH) {
+ return false;
+ }
+
+ final double[] mid = middle;
+ mid[0] = x0; mid[1] = y0;
+ mid[2] = x1; mid[3] = y1;
+
+ return subdivideAtIntersections(4, outCodeOR, out);
+ }
+
+ boolean splitQuad(final double x0, final double y0,
+ final double x1, final double y1,
+ final double x2, final double y2,
+ final int outCodeOR,
+ final DPathConsumer2D out)
+ {
+ if (TRACE) {
+ MarlinUtils.logInfo("divQuad P0(" + x0 + ", " + y0 + ") P1(" + x1 + ", " + y1 + ") P2(" + x2 + ", " + y2 + ")");
+ }
+
+ if (DO_CHECK_LENGTH && DHelpers.fastQuadLen(x0, y0, x1, y1, x2, y2) <= LEN_TH) {
+ return false;
+ }
+
+ final double[] mid = middle;
+ mid[0] = x0; mid[1] = y0;
+ mid[2] = x1; mid[3] = y1;
+ mid[4] = x2; mid[5] = y2;
+
+ return subdivideAtIntersections(6, outCodeOR, out);
+ }
+
+ boolean splitCurve(final double x0, final double y0,
+ final double x1, final double y1,
+ final double x2, final double y2,
+ final double x3, final double y3,
+ final int outCodeOR,
+ final DPathConsumer2D out)
+ {
+ if (TRACE) {
+ MarlinUtils.logInfo("divCurve P0(" + x0 + ", " + y0 + ") P1(" + x1 + ", " + y1 + ") P2(" + x2 + ", " + y2 + ") P3(" + x3 + ", " + y3 + ")");
+ }
+
+ if (DO_CHECK_LENGTH && DHelpers.fastCurvelen(x0, y0, x1, y1, x2, y2, x3, y3) <= LEN_TH) {
+ return false;
+ }
+
+ final double[] mid = middle;
+ mid[0] = x0; mid[1] = y0;
+ mid[2] = x1; mid[3] = y1;
+ mid[4] = x2; mid[5] = y2;
+ mid[6] = x3; mid[7] = y3;
+
+ return subdivideAtIntersections(8, outCodeOR, out);
+ }
+
+ private boolean subdivideAtIntersections(final int type, final int outCodeOR,
+ final DPathConsumer2D out)
+ {
+ final double[] mid = middle;
+ final double[] subTs = subdivTs;
+
+ if (init_clipRectPad) {
+ init_clipRectPad = false;
+ initPaddedClip();
+ }
+
+ final int nSplits = DHelpers.findClipPoints(curve, mid, subTs, type,
+ outCodeOR, clipRectPad);
+
+ if (TRACE) {
+ MarlinUtils.logInfo("nSplits: "+ nSplits);
+ MarlinUtils.logInfo("subTs: "+Arrays.toString(Arrays.copyOfRange(subTs, 0, nSplits)));
+ }
+ if (nSplits == 0) {
+ // only curve support shortcut
+ return false;
+ }
+ double prevT = 0.0d;
+
+ for (int i = 0, off = 0; i < nSplits; i++, off += type) {
+ final double t = subTs[i];
+
+ DHelpers.subdivideAt((t - prevT) / (1.0d - prevT),
+ mid, off, mid, off, type);
+ prevT = t;
+ }
+
+ for (int i = 0, off = 0; i <= nSplits; i++, off += type) {
+ if (TRACE) {
+ MarlinUtils.logInfo("Part Curve "+Arrays.toString(Arrays.copyOfRange(mid, off, off + type)));
+ }
+ emitCurrent(type, mid, off, out);
+ }
+ return true;
+ }
+
+ static void emitCurrent(final int type, final double[] pts,
+ final int off, final DPathConsumer2D out)
+ {
+ // if instead of switch (perf + most probable cases first)
+ if (type == 8) {
+ out.curveTo(pts[off + 2], pts[off + 3],
+ pts[off + 4], pts[off + 5],
+ pts[off + 6], pts[off + 7]);
+ } else if (type == 4) {
+ out.lineTo(pts[off + 2], pts[off + 3]);
+ } else {
+ out.quadTo(pts[off + 2], pts[off + 3],
+ pts[off + 4], pts[off + 5]);
+ }
+ }
+ }
+
+ static final class CurveBasicMonotonizer {
+
+ private static final int MAX_N_CURVES = 11;
+
+ // squared half line width (for stroker)
+ private double lw2;
+
+ // number of splitted curves
+ int nbSplits;
+
+ // This is where the curve to be processed is put. We give it
+ // enough room to store all curves.
+ final double[] middle = new double[MAX_N_CURVES * 6 + 2];
+ // t values at subdivision points
+ private final double[] subdivTs = new double[MAX_N_CURVES - 1];
+
+ // dirty curve
+ private final DCurve curve;
+
+ CurveBasicMonotonizer(final DRendererContext rdrCtx) {
+ this.curve = rdrCtx.curve;
+ }
+
+ void init(final double lineWidth) {
+ this.lw2 = (lineWidth * lineWidth) / 4.0d;
+ }
+
+ CurveBasicMonotonizer curve(final double x0, final double y0,
+ final double x1, final double y1,
+ final double x2, final double y2,
+ final double x3, final double y3)
+ {
+ final double[] mid = middle;
+ mid[0] = x0; mid[1] = y0;
+ mid[2] = x1; mid[3] = y1;
+ mid[4] = x2; mid[5] = y2;
+ mid[6] = x3; mid[7] = y3;
+
+ final double[] subTs = subdivTs;
+ final int nSplits = DHelpers.findSubdivPoints(curve, mid, subTs, 8, lw2);
+
+ double prevT = 0.0d;
+ for (int i = 0, off = 0; i < nSplits; i++, off += 6) {
+ final double t = subTs[i];
+
+ DHelpers.subdivideCubicAt((t - prevT) / (1.0d - prevT),
+ mid, off, mid, off, off + 6);
+ prevT = t;
+ }
+
+ this.nbSplits = nSplits;
+ return this;
+ }
+
+ CurveBasicMonotonizer quad(final double x0, final double y0,
+ final double x1, final double y1,
+ final double x2, final double y2)
+ {
+ final double[] mid = middle;
+ mid[0] = x0; mid[1] = y0;
+ mid[2] = x1; mid[3] = y1;
+ mid[4] = x2; mid[5] = y2;
+
+ final double[] subTs = subdivTs;
+ final int nSplits = DHelpers.findSubdivPoints(curve, mid, subTs, 6, lw2);
+
+ double prevt = 0.0d;
+ for (int i = 0, off = 0; i < nSplits; i++, off += 4) {
+ final double t = subTs[i];
+ DHelpers.subdivideQuadAt((t - prevt) / (1.0d - prevt),
+ mid, off, mid, off, off + 4);
+ prevt = t;
+ }
+
+ this.nbSplits = nSplits;
+ return this;
+ }
+ }
+
static final class PathTracer implements DPathConsumer2D {
private final String prefix;
private DPathConsumer2D out;
@@ -807,7 +1166,7 @@
}
private void log(final String message) {
- System.out.println(prefix + message);
+ MarlinUtils.logInfo(prefix + message);
}
@Override
--- a/src/java.desktop/share/classes/sun/java2d/marlin/Dasher.java Fri Mar 23 13:43:39 2018 -0700
+++ b/src/java.desktop/share/classes/sun/java2d/marlin/Dasher.java Tue Mar 27 22:09:43 2018 +0200
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2007, 2017, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2007, 2018, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -27,6 +27,8 @@
import java.util.Arrays;
import sun.awt.geom.PathConsumer2D;
+import sun.java2d.marlin.TransformingPathConsumer2D.CurveBasicMonotonizer;
+import sun.java2d.marlin.TransformingPathConsumer2D.CurveClipSplitter;
/**
* The <code>Dasher</code> class takes a series of linear commands
@@ -41,8 +43,9 @@
*/
final class Dasher implements PathConsumer2D, MarlinConst {
- static final int REC_LIMIT = 4;
- static final float ERR = 0.01f;
+ /* huge circle with radius ~ 2E9 only needs 12 subdivision levels */
+ static final int REC_LIMIT = 16;
+ static final float CURVE_LEN_ERR = MarlinProperties.getCurveLengthError(); // 0.01
static final float MIN_T_INC = 1.0f / (1 << REC_LIMIT);
// More than 24 bits of mantissa means we can no longer accurately
@@ -64,8 +67,10 @@
private boolean dashOn;
private float phase;
- private float sx, sy;
- private float x0, y0;
+ // The starting point of the path
+ private float sx0, sy0;
+ // the current point
+ private float cx0, cy0;
// temporary storage for the current curve
private final float[] curCurvepts;
@@ -76,11 +81,34 @@
// flag to recycle dash array copy
boolean recycleDashes;
+ // We don't emit the first dash right away. If we did, caps would be
+ // drawn on it, but we need joins to be drawn if there's a closePath()
+ // So, we store the path elements that make up the first dash in the
+ // buffer below.
+ private float[] firstSegmentsBuffer; // dynamic array
+ private int firstSegidx;
+
// dashes ref (dirty)
final FloatArrayCache.Reference dashes_ref;
// firstSegmentsBuffer ref (dirty)
final FloatArrayCache.Reference firstSegmentsBuffer_ref;
+ // Bounds of the drawing region, at pixel precision.
+ private float[] clipRect;
+
+ // the outcode of the current point
+ private int cOutCode = 0;
+
+ private boolean subdivide = DO_CLIP_SUBDIVIDER;
+
+ private final LengthIterator li = new LengthIterator();
+
+ private final CurveClipSplitter curveSplitter;
+
+ private float cycleLen;
+ private boolean outside;
+ private float totalSkipLen;
+
/**
* Constructs a <code>Dasher</code>.
* @param rdrCtx per-thread renderer context
@@ -96,6 +124,8 @@
// we need curCurvepts to be able to contain 2 curves because when
// dashing curves, we need to subdivide it
curCurvepts = new float[8 * 2];
+
+ this.curveSplitter = rdrCtx.curveClipSplitter;
}
/**
@@ -116,10 +146,13 @@
// Normalize so 0 <= phase < dash[0]
int sidx = 0;
dashOn = true;
+
float sum = 0.0f;
for (float d : dash) {
sum += d;
}
+ this.cycleLen = sum;
+
float cycles = phase / sum;
if (phase < 0.0f) {
if (-cycles >= MAX_CYCLES) {
@@ -168,6 +201,12 @@
this.recycleDashes = recycleDashes;
+ if (rdrCtx.doClip) {
+ this.clipRect = rdrCtx.clipRect;
+ } else {
+ this.clipRect = null;
+ this.cOutCode = 0;
+ }
return this; // fluent API
}
@@ -205,33 +244,42 @@
@Override
public void moveTo(final float x0, final float y0) {
if (firstSegidx != 0) {
- out.moveTo(sx, sy);
+ out.moveTo(sx0, sy0);
emitFirstSegments();
}
- needsMoveTo = true;
+ this.needsMoveTo = true;
this.idx = startIdx;
this.dashOn = this.startDashOn;
this.phase = this.startPhase;
- this.sx = x0;
- this.sy = y0;
- this.x0 = x0;
- this.y0 = y0;
+ this.cx0 = x0;
+ this.cy0 = y0;
+
+ // update starting point:
+ this.sx0 = x0;
+ this.sy0 = y0;
this.starting = true;
+
+ if (clipRect != null) {
+ final int outcode = Helpers.outcode(x0, y0, clipRect);
+ this.cOutCode = outcode;
+ this.outside = false;
+ this.totalSkipLen = 0.0f;
+ }
}
private void emitSeg(float[] buf, int off, int type) {
switch (type) {
case 8:
- out.curveTo(buf[off+0], buf[off+1],
- buf[off+2], buf[off+3],
- buf[off+4], buf[off+5]);
+ out.curveTo(buf[off ], buf[off + 1],
+ buf[off + 2], buf[off + 3],
+ buf[off + 4], buf[off + 5]);
return;
case 6:
- out.quadTo(buf[off+0], buf[off+1],
- buf[off+2], buf[off+3]);
+ out.quadTo(buf[off ], buf[off + 1],
+ buf[off + 2], buf[off + 3]);
return;
case 4:
- out.lineTo(buf[off], buf[off+1]);
+ out.lineTo(buf[off], buf[off + 1]);
return;
default:
}
@@ -247,12 +295,6 @@
}
firstSegidx = 0;
}
- // We don't emit the first dash right away. If we did, caps would be
- // drawn on it, but we need joins to be drawn if there's a closePath()
- // So, we store the path elements that make up the first dash in the
- // buffer below.
- private float[] firstSegmentsBuffer; // dynamic array
- private int firstSegidx;
// precondition: pts must be in relative coordinates (relative to x0,y0)
private void goTo(final float[] pts, final int off, final int type,
@@ -268,7 +310,7 @@
} else {
if (needsMoveTo) {
needsMoveTo = false;
- out.moveTo(x0, y0);
+ out.moveTo(cx0, cy0);
}
emitSeg(pts, off, type);
}
@@ -279,8 +321,8 @@
}
needsMoveTo = true;
}
- this.x0 = x;
- this.y0 = y;
+ this.cx0 = x;
+ this.cy0 = y;
}
private void goTo_starting(final float[] pts, final int off, final int type) {
@@ -306,10 +348,56 @@
@Override
public void lineTo(final float x1, final float y1) {
- final float dx = x1 - x0;
- final float dy = y1 - y0;
+ final int outcode0 = this.cOutCode;
+
+ if (clipRect != null) {
+ final int outcode1 = Helpers.outcode(x1, y1, clipRect);
+
+ // Should clip
+ final int orCode = (outcode0 | outcode1);
+
+ if (orCode != 0) {
+ final int sideCode = outcode0 & outcode1;
- float len = dx*dx + dy*dy;
+ // basic rejection criteria:
+ if (sideCode == 0) {
+ // ovelap clip:
+ if (subdivide) {
+ // avoid reentrance
+ subdivide = false;
+ // subdivide curve => callback with subdivided parts:
+ boolean ret = curveSplitter.splitLine(cx0, cy0, x1, y1,
+ orCode, this);
+ // reentrance is done:
+ subdivide = true;
+ if (ret) {
+ return;
+ }
+ }
+ // already subdivided so render it
+ } else {
+ this.cOutCode = outcode1;
+ skipLineTo(x1, y1);
+ return;
+ }
+ }
+
+ this.cOutCode = outcode1;
+
+ if (this.outside) {
+ this.outside = false;
+ // Adjust current index, phase & dash:
+ skipLen();
+ }
+ }
+ _lineTo(x1, y1);
+ }
+
+ private void _lineTo(final float x1, final float y1) {
+ final float dx = x1 - cx0;
+ final float dy = y1 - cy0;
+
+ float len = dx * dx + dy * dy;
if (len == 0.0f) {
return;
}
@@ -328,8 +416,7 @@
boolean _dashOn = dashOn;
float _phase = phase;
- float leftInThisDashSegment;
- float d, dashdx, dashdy, p;
+ float leftInThisDashSegment, d;
while (true) {
d = _dash[_idx];
@@ -350,24 +437,15 @@
_idx = (_idx + 1) % _dashLen;
_dashOn = !_dashOn;
}
-
- // Save local state:
- idx = _idx;
- dashOn = _dashOn;
- phase = _phase;
- return;
+ break;
}
- dashdx = d * cx;
- dashdy = d * cy;
-
if (_phase == 0.0f) {
- _curCurvepts[0] = x0 + dashdx;
- _curCurvepts[1] = y0 + dashdy;
+ _curCurvepts[0] = cx0 + d * cx;
+ _curCurvepts[1] = cy0 + d * cy;
} else {
- p = leftInThisDashSegment / d;
- _curCurvepts[0] = x0 + p * dashdx;
- _curCurvepts[1] = y0 + p * dashdy;
+ _curCurvepts[0] = cx0 + leftInThisDashSegment * cx;
+ _curCurvepts[1] = cy0 + leftInThisDashSegment * cy;
}
goTo(_curCurvepts, 0, 4, _dashOn);
@@ -378,19 +456,95 @@
_dashOn = !_dashOn;
_phase = 0.0f;
}
+ // Save local state:
+ idx = _idx;
+ dashOn = _dashOn;
+ phase = _phase;
+ }
+
+ private void skipLineTo(final float x1, final float y1) {
+ final float dx = x1 - cx0;
+ final float dy = y1 - cy0;
+
+ float len = dx * dx + dy * dy;
+ if (len != 0.0f) {
+ len = (float)Math.sqrt(len);
+ }
+
+ // Accumulate skipped length:
+ this.outside = true;
+ this.totalSkipLen += len;
+
+ // Fix initial move:
+ this.needsMoveTo = true;
+ this.starting = false;
+
+ this.cx0 = x1;
+ this.cy0 = y1;
}
- // shared instance in Dasher
- private final LengthIterator li = new LengthIterator();
+ public void skipLen() {
+ float len = this.totalSkipLen;
+ this.totalSkipLen = 0.0f;
+
+ final float[] _dash = dash;
+ final int _dashLen = this.dashLen;
+
+ int _idx = idx;
+ boolean _dashOn = dashOn;
+ float _phase = phase;
+
+ // -2 to ensure having 2 iterations of the post-loop
+ // to compensate the remaining phase
+ final long fullcycles = (long)Math.floor(len / cycleLen) - 2L;
+
+ if (fullcycles > 0L) {
+ len -= cycleLen * fullcycles;
+
+ final long iterations = fullcycles * _dashLen;
+ _idx = (int) (iterations + _idx) % _dashLen;
+ _dashOn = (iterations + (_dashOn ? 1L : 0L) & 1L) == 1L;
+ }
+
+ float leftInThisDashSegment, d;
+
+ while (true) {
+ d = _dash[_idx];
+ leftInThisDashSegment = d - _phase;
+
+ if (len <= leftInThisDashSegment) {
+ // Advance phase within current dash segment
+ _phase += len;
+
+ // TODO: compare float values using epsilon:
+ if (len == leftInThisDashSegment) {
+ _phase = 0.0f;
+ _idx = (_idx + 1) % _dashLen;
+ _dashOn = !_dashOn;
+ }
+ break;
+ }
+
+ len -= leftInThisDashSegment;
+ // Advance to next dash segment
+ _idx = (_idx + 1) % _dashLen;
+ _dashOn = !_dashOn;
+ _phase = 0.0f;
+ }
+ // Save local state:
+ idx = _idx;
+ dashOn = _dashOn;
+ phase = _phase;
+ }
// preconditions: curCurvepts must be an array of length at least 2 * type,
// that contains the curve we want to dash in the first type elements
private void somethingTo(final int type) {
- if (pointCurve(curCurvepts, type)) {
+ final float[] _curCurvepts = curCurvepts;
+ if (pointCurve(_curCurvepts, type)) {
return;
}
final LengthIterator _li = li;
- final float[] _curCurvepts = curCurvepts;
final float[] _dash = dash;
final int _dashLen = this.dashLen;
@@ -402,17 +556,16 @@
// initially the current curve is at curCurvepts[0...type]
int curCurveoff = 0;
- float lastSplitT = 0.0f;
+ float prevT = 0.0f;
float t;
float leftInThisDashSegment = _dash[_idx] - _phase;
while ((t = _li.next(leftInThisDashSegment)) < 1.0f) {
if (t != 0.0f) {
- Helpers.subdivideAt((t - lastSplitT) / (1.0f - lastSplitT),
+ Helpers.subdivideAt((t - prevT) / (1.0f - prevT),
_curCurvepts, curCurveoff,
- _curCurvepts, 0,
- _curCurvepts, type, type);
- lastSplitT = t;
+ _curCurvepts, 0, type);
+ prevT = t;
goTo(_curCurvepts, 2, type, _dashOn);
curCurveoff = type;
}
@@ -440,7 +593,29 @@
_li.reset();
}
- private static boolean pointCurve(float[] curve, int type) {
+ private void skipSomethingTo(final int type) {
+ final float[] _curCurvepts = curCurvepts;
+ if (pointCurve(_curCurvepts, type)) {
+ return;
+ }
+ final LengthIterator _li = li;
+
+ _li.initializeIterationOnCurve(_curCurvepts, type);
+
+ // In contrary to somethingTo(),
+ // just estimate properly the curve length:
+ final float len = _li.totalLength();
+
+ // Accumulate skipped length:
+ this.outside = true;
+ this.totalSkipLen += len;
+
+ // Fix initial move:
+ this.needsMoveTo = true;
+ this.starting = false;
+ }
+
+ private static boolean pointCurve(final float[] curve, final int type) {
for (int i = 2; i < type; i++) {
if (curve[i] != curve[i-2]) {
return false;
@@ -463,15 +638,14 @@
// 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}
// 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
// only the right half of the original curve is at 0)
private final float[][] recCurveStack; // dirty
- // sides[i] indicates whether the node at level i+1 in the path from
+ // sidesRight[i] indicates whether the node at level i+1 in the path from
// the root to the current leaf is a left or right child of its parent.
- private final Side[] sides; // dirty
+ private final boolean[] sidesRight; // dirty
private int curveType;
// lastT and nextT delimit the current leaf.
private float nextT;
@@ -492,7 +666,7 @@
LengthIterator() {
this.recCurveStack = new float[REC_LIMIT + 1][8];
- this.sides = new Side[REC_LIMIT];
+ this.sidesRight = new boolean[REC_LIMIT];
// if any methods are called without first initializing this object
// on a curve, we want it to fail ASAP.
this.nextT = Float.MAX_VALUE;
@@ -514,7 +688,7 @@
for (int i = recLimit; i >= 0; i--) {
Arrays.fill(recCurveStack[i], 0.0f);
}
- Arrays.fill(sides, Side.LEFT);
+ Arrays.fill(sidesRight, false);
Arrays.fill(curLeafCtrlPolyLengths, 0.0f);
Arrays.fill(nextRoots, 0.0f);
Arrays.fill(flatLeafCoefCache, 0.0f);
@@ -522,7 +696,7 @@
}
}
- void initializeIterationOnCurve(float[] pts, int type) {
+ void initializeIterationOnCurve(final float[] pts, final int type) {
// optimize arraycopy (8 values faster than 6 = type):
System.arraycopy(pts, 0, recCurveStack[0], 0, 8);
this.curveType = type;
@@ -534,11 +708,11 @@
goLeft(); // initializes nextT and lenAtNextT properly
this.lenAtLastSplit = 0.0f;
if (recLevel > 0) {
- this.sides[0] = Side.LEFT;
+ this.sidesRight[0] = false;
this.done = false;
} else {
// the root of the tree is a leaf so we're done.
- this.sides[0] = Side.RIGHT;
+ this.sidesRight[0] = true;
this.done = true;
}
this.lastSegLen = 0.0f;
@@ -547,7 +721,7 @@
// 0 == false, 1 == true, -1 == invalid cached value.
private int cachedHaveLowAcceleration = -1;
- private boolean haveLowAcceleration(float err) {
+ private boolean haveLowAcceleration(final float err) {
if (cachedHaveLowAcceleration == -1) {
final float len1 = curLeafCtrlPolyLengths[0];
final float len2 = curLeafCtrlPolyLengths[1];
@@ -636,7 +810,7 @@
// we use cubicRootsInAB here, because we want only roots in 0, 1,
// and our quadratic root finder doesn't filter, so it's just a
// matter of convenience.
- int n = Helpers.cubicRootsInAB(a, b, c, d, nextRoots, 0, 0.0f, 1.0f);
+ final int n = Helpers.cubicRootsInAB(a, b, c, d, nextRoots, 0, 0.0f, 1.0f);
if (n == 1 && !Float.isNaN(nextRoots[0])) {
t = nextRoots[0];
}
@@ -657,6 +831,16 @@
return t;
}
+ float totalLength() {
+ while (!done) {
+ goToNextLeaf();
+ }
+ // reset LengthIterator:
+ reset();
+
+ return lenAtNextT;
+ }
+
float lastSegLen() {
return lastSegLen;
}
@@ -666,11 +850,11 @@
private void goToNextLeaf() {
// We must go to the first ancestor node that has an unvisited
// right child.
+ final boolean[] _sides = sidesRight;
int _recLevel = recLevel;
- final Side[] _sides = sides;
+ _recLevel--;
- _recLevel--;
- while(_sides[_recLevel] == Side.RIGHT) {
+ while(_sides[_recLevel]) {
if (_recLevel == 0) {
recLevel = 0;
done = true;
@@ -679,19 +863,17 @@
_recLevel--;
}
- _sides[_recLevel] = Side.RIGHT;
+ _sides[_recLevel] = true;
// optimize arraycopy (8 values faster than 6 = type):
- System.arraycopy(recCurveStack[_recLevel], 0,
- recCurveStack[_recLevel+1], 0, 8);
- _recLevel++;
-
+ System.arraycopy(recCurveStack[_recLevel++], 0,
+ recCurveStack[_recLevel], 0, 8);
recLevel = _recLevel;
goLeft();
}
// go to the leftmost node from the current node. Return its length.
private void goLeft() {
- float len = onLeaf();
+ final float len = onLeaf();
if (len >= 0.0f) {
lastT = nextT;
lenAtLastT = lenAtNextT;
@@ -701,10 +883,11 @@
flatLeafCoefCache[2] = -1.0f;
cachedHaveLowAcceleration = -1;
} else {
- Helpers.subdivide(recCurveStack[recLevel], 0,
- recCurveStack[recLevel+1], 0,
- recCurveStack[recLevel], 0, curveType);
- sides[recLevel] = Side.LEFT;
+ Helpers.subdivide(recCurveStack[recLevel],
+ recCurveStack[recLevel + 1],
+ recCurveStack[recLevel], curveType);
+
+ sidesRight[recLevel] = false;
recLevel++;
goLeft();
}
@@ -719,7 +902,7 @@
float x0 = curve[0], y0 = curve[1];
for (int i = 2; i < _curveType; i += 2) {
- final float x1 = curve[i], y1 = curve[i+1];
+ final float x1 = curve[i], y1 = curve[i + 1];
final float len = Helpers.linelen(x0, y0, x1, y1);
polyLen += len;
curLeafCtrlPolyLengths[(i >> 1) - 1] = len;
@@ -727,10 +910,9 @@
y0 = y1;
}
- final float lineLen = Helpers.linelen(curve[0], curve[1],
- curve[_curveType-2],
- curve[_curveType-1]);
- if ((polyLen - lineLen) < ERR || recLevel == REC_LIMIT) {
+ final float lineLen = Helpers.linelen(curve[0], curve[1], x0, y0);
+
+ if ((polyLen - lineLen) < CURVE_LEN_ERR || recLevel == REC_LIMIT) {
return (polyLen + lineLen) / 2.0f;
}
return -1.0f;
@@ -742,41 +924,190 @@
final float x2, final float y2,
final float x3, final float y3)
{
+ final int outcode0 = this.cOutCode;
+
+ if (clipRect != null) {
+ final int outcode1 = Helpers.outcode(x1, y1, clipRect);
+ final int outcode2 = Helpers.outcode(x2, y2, clipRect);
+ final int outcode3 = Helpers.outcode(x3, y3, clipRect);
+
+ // Should clip
+ final int orCode = (outcode0 | outcode1 | outcode2 | outcode3);
+ if (orCode != 0) {
+ final int sideCode = outcode0 & outcode1 & outcode2 & outcode3;
+
+ // basic rejection criteria:
+ if (sideCode == 0) {
+ // ovelap clip:
+ if (subdivide) {
+ // avoid reentrance
+ subdivide = false;
+ // subdivide curve => callback with subdivided parts:
+ boolean ret = curveSplitter.splitCurve(cx0, cy0, x1, y1, x2, y2, x3, y3,
+ orCode, this);
+ // reentrance is done:
+ subdivide = true;
+ if (ret) {
+ return;
+ }
+ }
+ // already subdivided so render it
+ } else {
+ this.cOutCode = outcode3;
+ skipCurveTo(x1, y1, x2, y2, x3, y3);
+ return;
+ }
+ }
+
+ this.cOutCode = outcode3;
+
+ if (this.outside) {
+ this.outside = false;
+ // Adjust current index, phase & dash:
+ skipLen();
+ }
+ }
+ _curveTo(x1, y1, x2, y2, x3, y3);
+ }
+
+ private 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;
- _curCurvepts[2] = x1; _curCurvepts[3] = y1;
- _curCurvepts[4] = x2; _curCurvepts[5] = y2;
- _curCurvepts[6] = x3; _curCurvepts[7] = y3;
- somethingTo(8);
+
+ // monotonize curve:
+ final CurveBasicMonotonizer monotonizer
+ = rdrCtx.monotonizer.curve(cx0, cy0, x1, y1, x2, y2, x3, y3);
+
+ final int nSplits = monotonizer.nbSplits;
+ final float[] mid = monotonizer.middle;
+
+ for (int i = 0, off = 0; i <= nSplits; i++, off += 6) {
+ // optimize arraycopy (8 values faster than 6 = type):
+ System.arraycopy(mid, off, _curCurvepts, 0, 8);
+
+ somethingTo(8);
+ }
+ }
+
+ private void skipCurveTo(final float x1, final float y1,
+ final float x2, final float y2,
+ final float x3, final float y3)
+ {
+ final float[] _curCurvepts = curCurvepts;
+ _curCurvepts[0] = cx0; _curCurvepts[1] = cy0;
+ _curCurvepts[2] = x1; _curCurvepts[3] = y1;
+ _curCurvepts[4] = x2; _curCurvepts[5] = y2;
+ _curCurvepts[6] = x3; _curCurvepts[7] = y3;
+
+ skipSomethingTo(8);
+
+ this.cx0 = x3;
+ this.cy0 = y3;
}
@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 outcode1 = Helpers.outcode(x1, y1, clipRect);
+ final int outcode2 = Helpers.outcode(x2, y2, clipRect);
+
+ // Should clip
+ final int orCode = (outcode0 | outcode1 | outcode2);
+ if (orCode != 0) {
+ final int sideCode = outcode0 & outcode1 & outcode2;
+
+ // basic rejection criteria:
+ if (sideCode == 0) {
+ // ovelap clip:
+ if (subdivide) {
+ // avoid reentrance
+ subdivide = false;
+ // subdivide curve => call lineTo() with subdivided curves:
+ boolean ret = curveSplitter.splitQuad(cx0, cy0, x1, y1,
+ x2, y2, orCode, this);
+ // reentrance is done:
+ subdivide = true;
+ if (ret) {
+ return;
+ }
+ }
+ // already subdivided so render it
+ } else {
+ this.cOutCode = outcode2;
+ skipQuadTo(x1, y1, x2, y2);
+ return;
+ }
+ }
+
+ this.cOutCode = outcode2;
+
+ if (this.outside) {
+ this.outside = false;
+ // Adjust current index, phase & dash:
+ skipLen();
+ }
+ }
+ _quadTo(x1, y1, x2, y2);
+ }
+
+ private 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;
- _curCurvepts[4] = x2; _curCurvepts[5] = y2;
- somethingTo(6);
+
+ // monotonize quad:
+ final CurveBasicMonotonizer monotonizer
+ = rdrCtx.monotonizer.quad(cx0, cy0, x1, y1, x2, y2);
+
+ final int nSplits = monotonizer.nbSplits;
+ final float[] mid = monotonizer.middle;
+
+ for (int i = 0, off = 0; i <= nSplits; i++, off += 4) {
+ // optimize arraycopy (8 values faster than 6 = type):
+ System.arraycopy(mid, off, _curCurvepts, 0, 8);
+
+ somethingTo(6);
+ }
+ }
+
+ private void skipQuadTo(final float x1, final float y1,
+ final float x2, final float y2)
+ {
+ final float[] _curCurvepts = curCurvepts;
+ _curCurvepts[0] = cx0; _curCurvepts[1] = cy0;
+ _curCurvepts[2] = x1; _curCurvepts[3] = y1;
+ _curCurvepts[4] = x2; _curCurvepts[5] = y2;
+
+ skipSomethingTo(6);
+
+ this.cx0 = x2;
+ this.cy0 = y2;
}
@Override
public void closePath() {
- lineTo(sx, sy);
+ if (cx0 != sx0 || cy0 != sy0) {
+ lineTo(sx0, sy0);
+ }
if (firstSegidx != 0) {
if (!dashOn || needsMoveTo) {
- out.moveTo(sx, sy);
+ out.moveTo(sx0, sy0);
}
emitFirstSegments();
}
- moveTo(sx, sy);
+ moveTo(sx0, sy0);
}
@Override
public void pathDone() {
if (firstSegidx != 0) {
- out.moveTo(sx, sy);
+ out.moveTo(sx0, sy0);
emitFirstSegments();
}
out.pathDone();
--- a/src/java.desktop/share/classes/sun/java2d/marlin/DoubleArrayCache.java Fri Mar 23 13:43:39 2018 -0700
+++ b/src/java.desktop/share/classes/sun/java2d/marlin/DoubleArrayCache.java Tue Mar 27 22:09:43 2018 +0200
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2015, 2017, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2015, 2018, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -99,7 +99,7 @@
Reference(final DoubleArrayCache cache, final int initialSize) {
this.cache = cache;
this.clean = cache.clean;
- this.initial = createArray(initialSize, clean);
+ this.initial = createArray(initialSize);
if (DO_STATS) {
cache.stats.totalInitial += initialSize;
}
@@ -116,7 +116,7 @@
logInfo(getLogPrefix(clean) + "DoubleArrayCache: "
+ "getArray[oversize]: length=\t" + length);
}
- return createArray(length, clean);
+ return createArray(length);
}
double[] widenArray(final double[] array, final int usedSize,
@@ -202,7 +202,7 @@
if (DO_STATS) {
stats.createOp++;
}
- return createArray(arraySize, clean);
+ return createArray(arraySize);
}
void putArray(final double[] array)
@@ -229,12 +229,8 @@
}
}
- static double[] createArray(final int length, final boolean clean) {
- if (clean) {
- return new double[length];
- }
- // use JDK9 Unsafe.allocateUninitializedArray(class, length):
- return (double[]) OffHeapArray.UNSAFE.allocateUninitializedArray(double.class, length);
+ static double[] createArray(final int length) {
+ return new double[length];
}
static void fill(final double[] array, final int fromIndex,
--- a/src/java.desktop/share/classes/sun/java2d/marlin/FloatArrayCache.java Fri Mar 23 13:43:39 2018 -0700
+++ b/src/java.desktop/share/classes/sun/java2d/marlin/FloatArrayCache.java Tue Mar 27 22:09:43 2018 +0200
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2015, 2017, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2015, 2018, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -99,7 +99,7 @@
Reference(final FloatArrayCache cache, final int initialSize) {
this.cache = cache;
this.clean = cache.clean;
- this.initial = createArray(initialSize, clean);
+ this.initial = createArray(initialSize);
if (DO_STATS) {
cache.stats.totalInitial += initialSize;
}
@@ -116,7 +116,7 @@
logInfo(getLogPrefix(clean) + "FloatArrayCache: "
+ "getArray[oversize]: length=\t" + length);
}
- return createArray(length, clean);
+ return createArray(length);
}
float[] widenArray(final float[] array, final int usedSize,
@@ -202,7 +202,7 @@
if (DO_STATS) {
stats.createOp++;
}
- return createArray(arraySize, clean);
+ return createArray(arraySize);
}
void putArray(final float[] array)
@@ -229,12 +229,8 @@
}
}
- static float[] createArray(final int length, final boolean clean) {
- if (clean) {
- return new float[length];
- }
- // use JDK9 Unsafe.allocateUninitializedArray(class, length):
- return (float[]) OffHeapArray.UNSAFE.allocateUninitializedArray(float.class, length);
+ static float[] createArray(final int length) {
+ return new float[length];
}
static void fill(final float[] array, final int fromIndex,
--- a/src/java.desktop/share/classes/sun/java2d/marlin/Helpers.java Fri Mar 23 13:43:39 2018 -0700
+++ b/src/java.desktop/share/classes/sun/java2d/marlin/Helpers.java Tue Mar 27 22:09:43 2018 +0200
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2007, 2017, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2007, 2018, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -25,7 +25,6 @@
package sun.java2d.marlin;
-import static java.lang.Math.PI;
import java.util.Arrays;
import sun.awt.geom.PathConsumer2D;
import sun.java2d.marlin.stats.Histogram;
@@ -47,13 +46,25 @@
return (d <= err && d >= -err);
}
- static int quadraticRoots(final float a, final float b,
- final float c, float[] zeroes, final int off)
+ static float evalCubic(final float a, final float b,
+ final float c, final float d,
+ final float t)
+ {
+ return t * (t * (t * a + b) + c) + d;
+ }
+
+ static float evalQuad(final float a, final float b,
+ final float c, final float t)
+ {
+ return t * (t * a + b) + c;
+ }
+
+ static int quadraticRoots(final float a, final float b, final float c,
+ final float[] zeroes, final int off)
{
int ret = off;
- float t;
if (a != 0.0f) {
- final float dis = b*b - 4*a*c;
+ final float dis = b*b - 4.0f * a * c;
if (dis > 0.0f) {
final float sqrtDis = (float) Math.sqrt(dis);
// depending on the sign of b we use a slightly different
@@ -68,37 +79,38 @@
zeroes[ret++] = (2.0f * c) / (-b + sqrtDis);
}
} else if (dis == 0.0f) {
- t = (-b) / (2.0f * a);
- zeroes[ret++] = t;
+ zeroes[ret++] = -b / (2.0f * a);
}
- } else {
- if (b != 0.0f) {
- t = (-c) / b;
- zeroes[ret++] = t;
- }
+ } else if (b != 0.0f) {
+ zeroes[ret++] = -c / b;
}
return ret - off;
}
// find the roots of g(t) = d*t^3 + a*t^2 + b*t + c in [A,B)
- static int cubicRootsInAB(float d, float a, float b, float c,
- float[] pts, final int off,
+ static int cubicRootsInAB(final float d0, float a0, float b0, float c0,
+ final float[] pts, final int off,
final float A, final float B)
{
- if (d == 0.0f) {
- int num = quadraticRoots(a, b, c, pts, off);
+ if (d0 == 0.0f) {
+ final int num = quadraticRoots(a0, b0, c0, pts, off);
return filterOutNotInAB(pts, off, num, A, B) - off;
}
// From Graphics Gems:
- // http://tog.acm.org/resources/GraphicsGems/gems/Roots3And4.c
+ // https://github.com/erich666/GraphicsGems/blob/master/gems/Roots3And4.c
// (also from awt.geom.CubicCurve2D. But here we don't need as
// much accuracy and we don't want to create arrays so we use
// our own customized version).
// normal form: x^3 + ax^2 + bx + c = 0
- a /= d;
- b /= d;
- c /= d;
+
+ // 2018.1: Need double precision if d is very small (flat curve) !
+ /*
+ * TODO: cleanup all that code after reading Roots3And4.c
+ */
+ final double a = ((double)a0) / d0;
+ final double b = ((double)b0) / d0;
+ final double c = ((double)c0) / d0;
// substitute x = y - A/3 to eliminate quadratic term:
// x^3 +Px + Q = 0
@@ -108,63 +120,45 @@
// p = P/3
// q = Q/2
// instead and use those values for simplicity of the code.
- double sq_A = a * a;
- double p = (1.0d/3.0d) * ((-1.0d/3.0d) * sq_A + b);
- double q = (1.0d/2.0d) * ((2.0d/27.0d) * a * sq_A - (1.0d/3.0d) * a * b + c);
+ final double sub = (1.0d / 3.0d) * a;
+ final double sq_A = a * a;
+ final double p = (1.0d / 3.0d) * ((-1.0d / 3.0d) * sq_A + b);
+ final double q = (1.0d / 2.0d) * ((2.0d / 27.0d) * a * sq_A - sub * b + c);
// use Cardano's formula
- double cb_p = p * p * p;
- double D = q * q + cb_p;
+ final double cb_p = p * p * p;
+ final double D = q * q + cb_p;
int num;
if (D < 0.0d) {
// see: http://en.wikipedia.org/wiki/Cubic_function#Trigonometric_.28and_hyperbolic.29_method
- final double phi = (1.0d/3.0d) * Math.acos(-q / Math.sqrt(-cb_p));
+ final double phi = (1.0d / 3.0d) * Math.acos(-q / Math.sqrt(-cb_p));
final double t = 2.0d * Math.sqrt(-p);
- pts[ off+0 ] = (float) ( t * Math.cos(phi));
- pts[ off+1 ] = (float) (-t * Math.cos(phi + (PI / 3.0d)));
- pts[ off+2 ] = (float) (-t * Math.cos(phi - (PI / 3.0d)));
+ pts[off ] = (float) ( t * Math.cos(phi) - sub);
+ pts[off + 1] = (float) (-t * Math.cos(phi + (Math.PI / 3.0d)) - sub);
+ pts[off + 2] = (float) (-t * Math.cos(phi - (Math.PI / 3.0d)) - sub);
num = 3;
} else {
final double sqrt_D = Math.sqrt(D);
final double u = Math.cbrt(sqrt_D - q);
final double v = - Math.cbrt(sqrt_D + q);
- pts[ off ] = (float) (u + v);
+ pts[off ] = (float) (u + v - sub);
num = 1;
if (within(D, 0.0d, 1e-8d)) {
- pts[off+1] = -(pts[off] / 2.0f);
+ pts[off + 1] = (float)((-1.0d / 2.0d) * (u + v) - sub);
num = 2;
}
}
- final float sub = (1.0f/3.0f) * a;
-
- for (int i = 0; i < num; ++i) {
- pts[ off+i ] -= sub;
- }
-
return filterOutNotInAB(pts, off, num, A, B) - off;
}
- static float evalCubic(final float a, final float b,
- final float c, final float d,
- final float t)
- {
- return t * (t * (t * a + b) + c) + d;
- }
-
- static float evalQuad(final float a, final float b,
- final float c, final float t)
- {
- return t * (t * a + b) + c;
- }
-
// returns the index 1 past the last valid element remaining after filtering
- static int filterOutNotInAB(float[] nums, final int off, final int len,
+ static int filterOutNotInAB(final float[] nums, final int off, final int len,
final float a, final float b)
{
int ret = off;
@@ -176,35 +170,190 @@
return ret;
}
- static float linelen(float x1, float y1, float x2, float y2) {
- final float dx = x2 - x1;
- final float dy = y2 - y1;
- return (float) Math.sqrt(dx*dx + dy*dy);
+ static float fastLineLen(final float x0, final float y0,
+ final float x1, final float y1)
+ {
+ final float dx = x1 - x0;
+ final float dy = y1 - y0;
+
+ // use manhattan norm:
+ return Math.abs(dx) + Math.abs(dy);
+ }
+
+ static float linelen(final float x0, final float y0,
+ final float x1, final float y1)
+ {
+ final float dx = x1 - x0;
+ final float dy = y1 - y0;
+ return (float) Math.sqrt(dx * dx + dy * dy);
+ }
+
+ static float fastQuadLen(final float x0, final float y0,
+ final float x1, final float y1,
+ final float x2, final float y2)
+ {
+ final float dx1 = x1 - x0;
+ final float dx2 = x2 - x1;
+ final float dy1 = y1 - y0;
+ final float dy2 = y2 - y1;
+
+ // use manhattan norm:
+ return Math.abs(dx1) + Math.abs(dx2)
+ + Math.abs(dy1) + Math.abs(dy2);
+ }
+
+ static float quadlen(final float x0, final float y0,
+ final float x1, final float y1,
+ final float x2, final float y2)
+ {
+ return (linelen(x0, y0, x1, y1)
+ + linelen(x1, y1, x2, y2)
+ + linelen(x0, y0, x2, y2)) / 2.0f;
+ }
+
+
+ static float fastCurvelen(final float x0, final float y0,
+ final float x1, final float y1,
+ final float x2, final float y2,
+ final float x3, final float y3)
+ {
+ final float dx1 = x1 - x0;
+ final float dx2 = x2 - x1;
+ final float dx3 = x3 - x2;
+ final float dy1 = y1 - y0;
+ final float dy2 = y2 - y1;
+ final float dy3 = y3 - y2;
+
+ // use manhattan norm:
+ return Math.abs(dx1) + Math.abs(dx2) + Math.abs(dx3)
+ + Math.abs(dy1) + Math.abs(dy2) + Math.abs(dy3);
+ }
+
+ static float curvelen(final float x0, final float y0,
+ final float x1, final float y1,
+ final float x2, final float y2,
+ final float x3, final float y3)
+ {
+ return (linelen(x0, y0, x1, y1)
+ + linelen(x1, y1, x2, y2)
+ + linelen(x2, y2, x3, y3)
+ + linelen(x0, y0, x3, y3)) / 2.0f;
}
- static void subdivide(float[] src, int srcoff, float[] left, int leftoff,
- float[] right, int rightoff, int type)
+ // finds values of t where the curve in pts should be subdivided in order
+ // to get good offset curves a distance of w away from the middle curve.
+ // Stores the points in ts, and returns how many of them there were.
+ static int findSubdivPoints(final Curve c, final float[] pts,
+ final float[] ts, final int type,
+ final float w2)
+ {
+ final float x12 = pts[2] - pts[0];
+ final float y12 = pts[3] - pts[1];
+ // if the curve is already parallel to either axis we gain nothing
+ // from rotating it.
+ if ((y12 != 0.0f && x12 != 0.0f)) {
+ // we rotate it so that the first vector in the control polygon is
+ // parallel to the x-axis. This will ensure that rotated quarter
+ // circles won't be subdivided.
+ final float hypot = (float)Math.sqrt(x12 * x12 + y12 * y12);
+ final float cos = x12 / hypot;
+ final float sin = y12 / hypot;
+ final float x1 = cos * pts[0] + sin * pts[1];
+ final float y1 = cos * pts[1] - sin * pts[0];
+ final float x2 = cos * pts[2] + sin * pts[3];
+ final float y2 = cos * pts[3] - sin * pts[2];
+ final float x3 = cos * pts[4] + sin * pts[5];
+ final float y3 = cos * pts[5] - sin * pts[4];
+
+ switch(type) {
+ case 8:
+ final float x4 = cos * pts[6] + sin * pts[7];
+ final float y4 = cos * pts[7] - sin * pts[6];
+ c.set(x1, y1, x2, y2, x3, y3, x4, y4);
+ break;
+ case 6:
+ c.set(x1, y1, x2, y2, x3, y3);
+ break;
+ default:
+ }
+ } else {
+ c.set(pts, type);
+ }
+
+ int ret = 0;
+ // we subdivide at values of t such that the remaining rotated
+ // curves are monotonic in x and y.
+ ret += c.dxRoots(ts, ret);
+ ret += c.dyRoots(ts, ret);
+
+ // subdivide at inflection points.
+ if (type == 8) {
+ // quadratic curves can't have inflection points
+ ret += c.infPoints(ts, ret);
+ }
+
+ // now we must subdivide at points where one of the offset curves will have
+ // a cusp. This happens at ts where the radius of curvature is equal to w.
+ ret += c.rootsOfROCMinusW(ts, ret, w2, 0.0001f);
+
+ ret = filterOutNotInAB(ts, 0, ret, 0.0001f, 0.9999f);
+ isort(ts, ret);
+ return ret;
+ }
+
+ // finds values of t where the curve in pts should be subdivided in order
+ // to get intersections with the given clip rectangle.
+ // Stores the points in ts, and returns how many of them there were.
+ static int findClipPoints(final Curve curve, final float[] pts,
+ final float[] ts, final int type,
+ final int outCodeOR,
+ final float[] clipRect)
+ {
+ curve.set(pts, type);
+
+ // clip rectangle (ymin, ymax, xmin, xmax)
+ int ret = 0;
+
+ if ((outCodeOR & OUTCODE_LEFT) != 0) {
+ ret += curve.xPoints(ts, ret, clipRect[2]);
+ }
+ if ((outCodeOR & OUTCODE_RIGHT) != 0) {
+ ret += curve.xPoints(ts, ret, clipRect[3]);
+ }
+ if ((outCodeOR & OUTCODE_TOP) != 0) {
+ ret += curve.yPoints(ts, ret, clipRect[0]);
+ }
+ if ((outCodeOR & OUTCODE_BOTTOM) != 0) {
+ ret += curve.yPoints(ts, ret, clipRect[1]);
+ }
+ isort(ts, ret);
+ return ret;
+ }
+
+ static void subdivide(final float[] src,
+ final float[] left, final float[] right,
+ final int type)
{
switch(type) {
- case 6:
- Helpers.subdivideQuad(src, srcoff, left, leftoff, right, rightoff);
+ case 8:
+ subdivideCubic(src, left, right);
return;
- case 8:
- Helpers.subdivideCubic(src, srcoff, left, leftoff, right, rightoff);
+ case 6:
+ subdivideQuad(src, left, right);
return;
default:
throw new InternalError("Unsupported curve type");
}
}
- static void isort(float[] a, int off, int len) {
- for (int i = off + 1, end = off + len; i < end; i++) {
- float ai = a[i];
- int j = i - 1;
- for (; j >= off && a[j] > ai; j--) {
- a[j+1] = a[j];
+ static void isort(final float[] a, final int len) {
+ for (int i = 1, j; i < len; i++) {
+ final float ai = a[i];
+ j = i - 1;
+ for (; j >= 0 && a[j] > ai; j--) {
+ a[j + 1] = a[j];
}
- a[j+1] = ai;
+ a[j + 1] = ai;
}
}
@@ -227,206 +376,216 @@
* equals (<code>leftoff</code> + 6), in order
* to avoid allocating extra storage for this common point.
* @param src the array holding the coordinates for the source curve
- * @param srcoff the offset into the array of the beginning of the
- * the 6 source coordinates
* @param left the array for storing the coordinates for the first
* half of the subdivided curve
- * @param leftoff the offset into the array of the beginning of the
- * the 6 left coordinates
* @param right the array for storing the coordinates for the second
* half of the subdivided curve
- * @param rightoff the offset into the array of the beginning of the
- * the 6 right coordinates
* @since 1.7
*/
- static void subdivideCubic(float[] src, int srcoff,
- float[] left, int leftoff,
- float[] right, int rightoff)
+ static void subdivideCubic(final float[] src,
+ final float[] left,
+ final float[] right)
{
- float x1 = src[srcoff + 0];
- float y1 = src[srcoff + 1];
- float ctrlx1 = src[srcoff + 2];
- float ctrly1 = src[srcoff + 3];
- float ctrlx2 = src[srcoff + 4];
- float ctrly2 = src[srcoff + 5];
- float x2 = src[srcoff + 6];
- float y2 = src[srcoff + 7];
- if (left != null) {
- left[leftoff + 0] = x1;
- left[leftoff + 1] = y1;
- }
- if (right != null) {
- right[rightoff + 6] = x2;
- right[rightoff + 7] = y2;
- }
- x1 = (x1 + ctrlx1) / 2.0f;
- y1 = (y1 + ctrly1) / 2.0f;
- x2 = (x2 + ctrlx2) / 2.0f;
- y2 = (y2 + ctrly2) / 2.0f;
- float centerx = (ctrlx1 + ctrlx2) / 2.0f;
- float centery = (ctrly1 + ctrly2) / 2.0f;
- ctrlx1 = (x1 + centerx) / 2.0f;
- ctrly1 = (y1 + centery) / 2.0f;
- ctrlx2 = (x2 + centerx) / 2.0f;
- ctrly2 = (y2 + centery) / 2.0f;
- centerx = (ctrlx1 + ctrlx2) / 2.0f;
- centery = (ctrly1 + ctrly2) / 2.0f;
- if (left != null) {
- left[leftoff + 2] = x1;
- left[leftoff + 3] = y1;
- left[leftoff + 4] = ctrlx1;
- left[leftoff + 5] = ctrly1;
- left[leftoff + 6] = centerx;
- left[leftoff + 7] = centery;
- }
- if (right != null) {
- right[rightoff + 0] = centerx;
- right[rightoff + 1] = centery;
- right[rightoff + 2] = ctrlx2;
- right[rightoff + 3] = ctrly2;
- right[rightoff + 4] = x2;
- right[rightoff + 5] = y2;
- }
+ float x1 = src[0];
+ float y1 = src[1];
+ float cx1 = src[2];
+ float cy1 = src[3];
+ float cx2 = src[4];
+ float cy2 = src[5];
+ float x2 = src[6];
+ float y2 = src[7];
+
+ left[0] = x1;
+ left[1] = y1;
+
+ right[6] = x2;
+ right[7] = y2;
+
+ x1 = (x1 + cx1) / 2.0f;
+ y1 = (y1 + cy1) / 2.0f;
+ x2 = (x2 + cx2) / 2.0f;
+ y2 = (y2 + cy2) / 2.0f;
+
+ float cx = (cx1 + cx2) / 2.0f;
+ float cy = (cy1 + cy2) / 2.0f;
+
+ cx1 = (x1 + cx) / 2.0f;
+ cy1 = (y1 + cy) / 2.0f;
+ cx2 = (x2 + cx) / 2.0f;
+ cy2 = (y2 + cy) / 2.0f;
+ cx = (cx1 + cx2) / 2.0f;
+ cy = (cy1 + cy2) / 2.0f;
+
+ left[2] = x1;
+ left[3] = y1;
+ left[4] = cx1;
+ left[5] = cy1;
+ left[6] = cx;
+ left[7] = cy;
+
+ right[0] = cx;
+ right[1] = cy;
+ right[2] = cx2;
+ right[3] = cy2;
+ right[4] = x2;
+ right[5] = y2;
+ }
+
+ static void subdivideCubicAt(final float t,
+ final float[] src, final int offS,
+ final float[] pts, final int offL, final int offR)
+ {
+ float x1 = src[offS ];
+ float y1 = src[offS + 1];
+ float cx1 = src[offS + 2];
+ float cy1 = src[offS + 3];
+ float cx2 = src[offS + 4];
+ float cy2 = src[offS + 5];
+ float x2 = src[offS + 6];
+ float y2 = src[offS + 7];
+
+ pts[offL ] = x1;
+ pts[offL + 1] = y1;
+
+ pts[offR + 6] = x2;
+ pts[offR + 7] = y2;
+
+ x1 = x1 + t * (cx1 - x1);
+ y1 = y1 + t * (cy1 - y1);
+ x2 = cx2 + t * (x2 - cx2);
+ y2 = cy2 + t * (y2 - cy2);
+
+ float cx = cx1 + t * (cx2 - cx1);
+ float cy = cy1 + t * (cy2 - cy1);
+
+ cx1 = x1 + t * (cx - x1);
+ cy1 = y1 + t * (cy - y1);
+ cx2 = cx + t * (x2 - cx);
+ cy2 = cy + t * (y2 - cy);
+ cx = cx1 + t * (cx2 - cx1);
+ cy = cy1 + t * (cy2 - cy1);
+
+ pts[offL + 2] = x1;
+ pts[offL + 3] = y1;
+ pts[offL + 4] = cx1;
+ pts[offL + 5] = cy1;
+ pts[offL + 6] = cx;
+ pts[offL + 7] = cy;
+
+ pts[offR ] = cx;
+ pts[offR + 1] = cy;
+ pts[offR + 2] = cx2;
+ pts[offR + 3] = cy2;
+ pts[offR + 4] = x2;
+ pts[offR + 5] = y2;
}
-
- static void subdivideCubicAt(float t, float[] src, int srcoff,
- float[] left, int leftoff,
- float[] right, int rightoff)
+ static void subdivideQuad(final float[] src,
+ final float[] left,
+ final float[] right)
{
- float x1 = src[srcoff + 0];
- float y1 = src[srcoff + 1];
- float ctrlx1 = src[srcoff + 2];
- float ctrly1 = src[srcoff + 3];
- float ctrlx2 = src[srcoff + 4];
- float ctrly2 = src[srcoff + 5];
- float x2 = src[srcoff + 6];
- float y2 = src[srcoff + 7];
- if (left != null) {
- left[leftoff + 0] = x1;
- left[leftoff + 1] = y1;
- }
- if (right != null) {
- right[rightoff + 6] = x2;
- right[rightoff + 7] = y2;
- }
- x1 = x1 + t * (ctrlx1 - x1);
- y1 = y1 + t * (ctrly1 - y1);
- x2 = ctrlx2 + t * (x2 - ctrlx2);
- y2 = ctrly2 + t * (y2 - ctrly2);
- float centerx = ctrlx1 + t * (ctrlx2 - ctrlx1);
- float centery = ctrly1 + t * (ctrly2 - ctrly1);
- ctrlx1 = x1 + t * (centerx - x1);
- ctrly1 = y1 + t * (centery - y1);
- ctrlx2 = centerx + t * (x2 - centerx);
- ctrly2 = centery + t * (y2 - centery);
- centerx = ctrlx1 + t * (ctrlx2 - ctrlx1);
- centery = ctrly1 + t * (ctrly2 - ctrly1);
- if (left != null) {
- left[leftoff + 2] = x1;
- left[leftoff + 3] = y1;
- left[leftoff + 4] = ctrlx1;
- left[leftoff + 5] = ctrly1;
- left[leftoff + 6] = centerx;
- left[leftoff + 7] = centery;
- }
- if (right != null) {
- right[rightoff + 0] = centerx;
- right[rightoff + 1] = centery;
- right[rightoff + 2] = ctrlx2;
- right[rightoff + 3] = ctrly2;
- right[rightoff + 4] = x2;
- right[rightoff + 5] = y2;
- }
+ float x1 = src[0];
+ float y1 = src[1];
+ float cx = src[2];
+ float cy = src[3];
+ float x2 = src[4];
+ float y2 = src[5];
+
+ left[0] = x1;
+ left[1] = y1;
+
+ right[4] = x2;
+ right[5] = y2;
+
+ x1 = (x1 + cx) / 2.0f;
+ y1 = (y1 + cy) / 2.0f;
+ x2 = (x2 + cx) / 2.0f;
+ y2 = (y2 + cy) / 2.0f;
+ cx = (x1 + x2) / 2.0f;
+ cy = (y1 + y2) / 2.0f;
+
+ left[2] = x1;
+ left[3] = y1;
+ left[4] = cx;
+ left[5] = cy;
+
+ right[0] = cx;
+ right[1] = cy;
+ right[2] = x2;
+ right[3] = y2;
}
- static void subdivideQuad(float[] src, int srcoff,
- float[] left, int leftoff,
- float[] right, int rightoff)
+ static void subdivideQuadAt(final float t,
+ final float[] src, final int offS,
+ final float[] pts, final int offL, final int offR)
{
- float x1 = src[srcoff + 0];
- float y1 = src[srcoff + 1];
- float ctrlx = src[srcoff + 2];
- float ctrly = src[srcoff + 3];
- float x2 = src[srcoff + 4];
- float y2 = src[srcoff + 5];
- if (left != null) {
- left[leftoff + 0] = x1;
- left[leftoff + 1] = y1;
- }
- if (right != null) {
- right[rightoff + 4] = x2;
- right[rightoff + 5] = y2;
- }
- x1 = (x1 + ctrlx) / 2.0f;
- y1 = (y1 + ctrly) / 2.0f;
- x2 = (x2 + ctrlx) / 2.0f;
- y2 = (y2 + ctrly) / 2.0f;
- ctrlx = (x1 + x2) / 2.0f;
- ctrly = (y1 + y2) / 2.0f;
- if (left != null) {
- left[leftoff + 2] = x1;
- left[leftoff + 3] = y1;
- left[leftoff + 4] = ctrlx;
- left[leftoff + 5] = ctrly;
- }
- if (right != null) {
- right[rightoff + 0] = ctrlx;
- right[rightoff + 1] = ctrly;
- right[rightoff + 2] = x2;
- right[rightoff + 3] = y2;
- }
+ float x1 = src[offS ];
+ float y1 = src[offS + 1];
+ float cx = src[offS + 2];
+ float cy = src[offS + 3];
+ float x2 = src[offS + 4];
+ float y2 = src[offS + 5];
+
+ pts[offL ] = x1;
+ pts[offL + 1] = y1;
+
+ pts[offR + 4] = x2;
+ pts[offR + 5] = y2;
+
+ x1 = x1 + t * (cx - x1);
+ y1 = y1 + t * (cy - y1);
+ x2 = cx + t * (x2 - cx);
+ y2 = cy + t * (y2 - cy);
+ cx = x1 + t * (x2 - x1);
+ cy = y1 + t * (y2 - y1);
+
+ pts[offL + 2] = x1;
+ pts[offL + 3] = y1;
+ pts[offL + 4] = cx;
+ pts[offL + 5] = cy;
+
+ pts[offR ] = cx;
+ pts[offR + 1] = cy;
+ pts[offR + 2] = x2;
+ pts[offR + 3] = y2;
}
- static void subdivideQuadAt(float t, float[] src, int srcoff,
- float[] left, int leftoff,
- float[] right, int rightoff)
+ static void subdivideLineAt(final float t,
+ final float[] src, final int offS,
+ final float[] pts, final int offL, final int offR)
{
- float x1 = src[srcoff + 0];
- float y1 = src[srcoff + 1];
- float ctrlx = src[srcoff + 2];
- float ctrly = src[srcoff + 3];
- float x2 = src[srcoff + 4];
- float y2 = src[srcoff + 5];
- if (left != null) {
- left[leftoff + 0] = x1;
- left[leftoff + 1] = y1;
- }
- if (right != null) {
- right[rightoff + 4] = x2;
- right[rightoff + 5] = y2;
- }
- x1 = x1 + t * (ctrlx - x1);
- y1 = y1 + t * (ctrly - y1);
- x2 = ctrlx + t * (x2 - ctrlx);
- y2 = ctrly + t * (y2 - ctrly);
- ctrlx = x1 + t * (x2 - x1);
- ctrly = y1 + t * (y2 - y1);
- if (left != null) {
- left[leftoff + 2] = x1;
- left[leftoff + 3] = y1;
- left[leftoff + 4] = ctrlx;
- left[leftoff + 5] = ctrly;
- }
- if (right != null) {
- right[rightoff + 0] = ctrlx;
- right[rightoff + 1] = ctrly;
- right[rightoff + 2] = x2;
- right[rightoff + 3] = y2;
- }
+ float x1 = src[offS ];
+ float y1 = src[offS + 1];
+ float x2 = src[offS + 2];
+ float y2 = src[offS + 3];
+
+ pts[offL ] = x1;
+ pts[offL + 1] = y1;
+
+ pts[offR + 2] = x2;
+ pts[offR + 3] = y2;
+
+ x1 = x1 + t * (x2 - x1);
+ y1 = y1 + t * (y2 - y1);
+
+ pts[offL + 2] = x1;
+ pts[offL + 3] = y1;
+
+ pts[offR ] = x1;
+ pts[offR + 1] = y1;
}
- static void subdivideAt(float t, float[] src, int srcoff,
- float[] left, int leftoff,
- float[] right, int rightoff, int size)
+ static void subdivideAt(final float t,
+ final float[] src, final int offS,
+ final float[] pts, final int offL, final int type)
{
- switch(size) {
- case 8:
- subdivideCubicAt(t, src, srcoff, left, leftoff, right, rightoff);
- return;
- case 6:
- subdivideQuadAt(t, src, srcoff, left, leftoff, right, rightoff);
- return;
+ // if instead of switch (perf + most probable cases first)
+ if (type == 8) {
+ subdivideCubicAt(t, src, offS, pts, offL, offL + type);
+ } else if (type == 4) {
+ subdivideLineAt(t, src, offS, pts, offL, offL + type);
+ } else {
+ subdivideQuadAt(t, src, offS, pts, offL, offL + type);
}
}
@@ -614,12 +773,12 @@
e += 2;
continue;
case TYPE_QUADTO:
- io.quadTo(_curves[e+0], _curves[e+1],
+ io.quadTo(_curves[e], _curves[e+1],
_curves[e+2], _curves[e+3]);
e += 4;
continue;
case TYPE_CUBICTO:
- io.curveTo(_curves[e+0], _curves[e+1],
+ io.curveTo(_curves[e], _curves[e+1],
_curves[e+2], _curves[e+3],
_curves[e+4], _curves[e+5]);
e += 6;
@@ -657,12 +816,12 @@
continue;
case TYPE_QUADTO:
e -= 4;
- io.quadTo(_curves[e+0], _curves[e+1],
+ io.quadTo(_curves[e], _curves[e+1],
_curves[e+2], _curves[e+3]);
continue;
case TYPE_CUBICTO:
e -= 6;
- io.curveTo(_curves[e+0], _curves[e+1],
+ io.curveTo(_curves[e], _curves[e+1],
_curves[e+2], _curves[e+3],
_curves[e+4], _curves[e+5]);
continue;
--- a/src/java.desktop/share/classes/sun/java2d/marlin/IntArrayCache.java Fri Mar 23 13:43:39 2018 -0700
+++ b/src/java.desktop/share/classes/sun/java2d/marlin/IntArrayCache.java Tue Mar 27 22:09:43 2018 +0200
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2015, 2017, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2015, 2018, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -99,7 +99,7 @@
Reference(final IntArrayCache cache, final int initialSize) {
this.cache = cache;
this.clean = cache.clean;
- this.initial = createArray(initialSize, clean);
+ this.initial = createArray(initialSize);
if (DO_STATS) {
cache.stats.totalInitial += initialSize;
}
@@ -116,7 +116,7 @@
logInfo(getLogPrefix(clean) + "IntArrayCache: "
+ "getArray[oversize]: length=\t" + length);
}
- return createArray(length, clean);
+ return createArray(length);
}
int[] widenArray(final int[] array, final int usedSize,
@@ -202,7 +202,7 @@
if (DO_STATS) {
stats.createOp++;
}
- return createArray(arraySize, clean);
+ return createArray(arraySize);
}
void putArray(final int[] array)
@@ -229,12 +229,8 @@
}
}
- static int[] createArray(final int length, final boolean clean) {
- if (clean) {
- return new int[length];
- }
- // use JDK9 Unsafe.allocateUninitializedArray(class, length):
- return (int[]) OffHeapArray.UNSAFE.allocateUninitializedArray(int.class, length);
+ static int[] createArray(final int length) {
+ return new int[length];
}
static void fill(final int[] array, final int fromIndex,
--- a/src/java.desktop/share/classes/sun/java2d/marlin/MarlinCache.java Fri Mar 23 13:43:39 2018 -0700
+++ b/src/java.desktop/share/classes/sun/java2d/marlin/MarlinCache.java Tue Mar 27 22:09:43 2018 +0200
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2007, 2017, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2007, 2018, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -43,9 +43,9 @@
// values are stored as int [x|alpha] where alpha is 8 bits
static final int RLE_MAX_WIDTH = 1 << (24 - 1);
- // 2048 (pixelSize) alpha values (width) x 32 rows (tile) = 64K bytes
+ // 4096 (pixels) alpha values (width) x 64 rows / 4 (tile) = 64K bytes
// x1 instead of 4 bytes (RLE) ie 1/4 capacity or average good RLE compression
- static final long INITIAL_CHUNK_ARRAY = TILE_H * INITIAL_PIXEL_DIM; // 64K
+ static final long INITIAL_CHUNK_ARRAY = TILE_H * INITIAL_PIXEL_WIDTH >> 2; // 64K
// The alpha map used by this object (taken out of our map cache) to convert
// pixel coverage counts gotten from MarlinCache (which are in the range
@@ -292,11 +292,11 @@
// ensure values are in [0; MAX_AA_ALPHA] range
if (DO_AA_RANGE_CHECK) {
if (val < 0) {
- System.out.println("Invalid coverage = " + val);
+ MarlinUtils.logInfo("Invalid coverage = " + val);
val = 0;
}
if (val > MAX_AA_ALPHA) {
- System.out.println("Invalid coverage = " + val);
+ MarlinUtils.logInfo("Invalid coverage = " + val);
val = MAX_AA_ALPHA;
}
}
@@ -460,11 +460,11 @@
// ensure values are in [0; MAX_AA_ALPHA] range
if (DO_AA_RANGE_CHECK) {
if (val < 0) {
- System.out.println("Invalid coverage = " + val);
+ MarlinUtils.logInfo("Invalid coverage = " + val);
val = 0;
}
if (val > MAX_AA_ALPHA) {
- System.out.println("Invalid coverage = " + val);
+ MarlinUtils.logInfo("Invalid coverage = " + val);
val = MAX_AA_ALPHA;
}
}
@@ -630,8 +630,6 @@
final int halfmaxalpha = maxalpha >> 2;
for (int i = 0; i <= maxalpha; i++) {
alMap[i] = (byte) ((i * 255 + halfmaxalpha) / maxalpha);
-// System.out.println("alphaMap[" + i + "] = "
-// + Byte.toUnsignedInt(alMap[i]));
}
return alMap;
}
--- a/src/java.desktop/share/classes/sun/java2d/marlin/MarlinConst.java Fri Mar 23 13:43:39 2018 -0700
+++ b/src/java.desktop/share/classes/sun/java2d/marlin/MarlinConst.java Tue Mar 27 22:09:43 2018 +0200
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2015, 2017, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2015, 2018, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -74,23 +74,34 @@
// do clean dirty array
static final boolean DO_CLEAN_DIRTY = false;
- // flag to use line simplifier
+ // flag to use collinear simplifier
static final boolean USE_SIMPLIFIER = MarlinProperties.isUseSimplifier();
+ // flag to use path simplifier
+ static final boolean USE_PATH_SIMPLIFIER = MarlinProperties.isUsePathSimplifier();
+
+ static final boolean DO_CLIP_SUBDIVIDER = MarlinProperties.isDoClipSubdivider();
+
// flag to enable logs related bounds checks
static final boolean DO_LOG_BOUNDS = ENABLE_LOGS && false;
+ // flag to enable float precision correction
+ static final boolean DO_FIX_FLOAT_PREC = true;
+
// Initial Array sizing (initial context capacity) ~ 450K
- // 2048 pixel (width x height) for initial capacity
- static final int INITIAL_PIXEL_DIM
- = MarlinProperties.getInitialImageSize();
+ // 4096 pixels (width) for initial capacity
+ static final int INITIAL_PIXEL_WIDTH
+ = MarlinProperties.getInitialPixelWidth();
+ // 2176 pixels (height) for initial capacity
+ static final int INITIAL_PIXEL_HEIGHT
+ = MarlinProperties.getInitialPixelHeight();
// typical array sizes: only odd numbers allowed below
static final int INITIAL_ARRAY = 256;
// alpha row dimension
- static final int INITIAL_AA_ARRAY = INITIAL_PIXEL_DIM;
+ static final int INITIAL_AA_ARRAY = INITIAL_PIXEL_WIDTH;
// 4096 edges for initial capacity
static final int INITIAL_EDGES_COUNT = MarlinProperties.getInitialEdges();
@@ -109,16 +120,17 @@
public static final int SUBPIXEL_LG_POSITIONS_Y
= MarlinProperties.getSubPixel_Log2_Y();
+ public static final int MIN_SUBPIXEL_LG_POSITIONS
+ = Math.min(SUBPIXEL_LG_POSITIONS_X, SUBPIXEL_LG_POSITIONS_Y);
+
// number of subpixels
public static final int SUBPIXEL_POSITIONS_X = 1 << (SUBPIXEL_LG_POSITIONS_X);
public static final int SUBPIXEL_POSITIONS_Y = 1 << (SUBPIXEL_LG_POSITIONS_Y);
- public static final float NORM_SUBPIXELS
- = (float) Math.sqrt(( SUBPIXEL_POSITIONS_X * SUBPIXEL_POSITIONS_X
- + SUBPIXEL_POSITIONS_Y * SUBPIXEL_POSITIONS_Y) / 2.0d);
+ public static final float MIN_SUBPIXELS = 1 << MIN_SUBPIXEL_LG_POSITIONS;
public static final int MAX_AA_ALPHA
- = SUBPIXEL_POSITIONS_X * SUBPIXEL_POSITIONS_Y;
+ = (SUBPIXEL_POSITIONS_X * SUBPIXEL_POSITIONS_Y);
public static final int TILE_H_LG = MarlinProperties.getTileSize_Log2();
public static final int TILE_H = 1 << TILE_H_LG; // 32 by default
--- a/src/java.desktop/share/classes/sun/java2d/marlin/MarlinProperties.java Fri Mar 23 13:43:39 2018 -0700
+++ b/src/java.desktop/share/classes/sun/java2d/marlin/MarlinProperties.java Tue Mar 27 22:09:43 2018 +0200
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2007, 2017, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2007, 2018, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -54,29 +54,41 @@
}
/**
- * Return the initial pixel size used to define initial arrays
- * (tile AA chunk, alpha line, buckets)
+ * Return the initial pixel width used to define initial arrays
+ * (tile AA chunk, alpha line)
*
- * @return 64 < initial pixel size < 32768 (2048 by default)
+ * @return 64 < initial pixel size < 32768 (4096 by default)
*/
- public static int getInitialImageSize() {
+ public static int getInitialPixelWidth() {
return align(
- getInteger("sun.java2d.renderer.pixelsize", 2048, 64, 32 * 1024),
+ getInteger("sun.java2d.renderer.pixelWidth", 4096, 64, 32 * 1024),
64);
}
/**
- * Return the log(2) corresponding to subpixel on x-axis (
+ * Return the initial pixel height used to define initial arrays
+ * (buckets)
*
- * @return 0 (1 subpixels) < initial pixel size < 8 (256 subpixels)
- * (3 by default ie 8 subpixels)
+ * @return 64 < initial pixel size < 32768 (2176 by default)
*/
- public static int getSubPixel_Log2_X() {
- return getInteger("sun.java2d.renderer.subPixel_log2_X", 3, 0, 8);
+ public static int getInitialPixelHeight() {
+ return align(
+ getInteger("sun.java2d.renderer.pixelHeight", 2176, 64, 32 * 1024),
+ 64);
}
/**
- * Return the log(2) corresponding to subpixel on y-axis (
+ * Return the log(2) corresponding to subpixel on x-axis
+ *
+ * @return 0 (1 subpixels) < initial pixel size < 8 (256 subpixels)
+ * (8 by default ie 256 subpixels)
+ */
+ public static int getSubPixel_Log2_X() {
+ return getInteger("sun.java2d.renderer.subPixel_log2_X", 8, 0, 8);
+ }
+
+ /**
+ * Return the log(2) corresponding to subpixel on y-axis
*
* @return 0 (1 subpixels) < initial pixel size < 8 (256 subpixels)
* (3 by default ie 8 subpixels)
@@ -88,7 +100,7 @@
/**
* Return the log(2) corresponding to the square tile size in pixels
*
- * @return 3 (8x8 pixels) < tile size < 8 (256x256 pixels)
+ * @return 3 (8x8 pixels) < tile size < 10 (1024x1024 pixels)
* (5 by default ie 32x32 pixels)
*/
public static int getTileSize_Log2() {
@@ -98,12 +110,11 @@
/**
* Return the log(2) corresponding to the tile width in pixels
*
- * @return 3 (8 pixels) < tile with < 8 (256 pixels)
- * (by default is given by the square tile size)
+ * @return 3 (8 pixels) < tile width < 10 (1024 pixels)
+ * (5 by default ie 32x32 pixels)
*/
public static int getTileWidth_Log2() {
- final int tileSize = getTileSize_Log2();
- return getInteger("sun.java2d.renderer.tileWidth_log2", tileSize, 3, 10);
+ return getInteger("sun.java2d.renderer.tileWidth_log2", 5, 3, 10);
}
/**
@@ -145,6 +156,18 @@
return getBoolean("sun.java2d.renderer.useSimplifier", "false");
}
+ public static boolean isUsePathSimplifier() {
+ return getBoolean("sun.java2d.renderer.usePathSimplifier", "false");
+ }
+
+ public static float getPathSimplifierPixelTolerance() {
+ // default: MIN_PEN_SIZE or less ?
+ return getFloat("sun.java2d.renderer.pathSimplifier.pixTol",
+ (1.0f / MarlinConst.MIN_SUBPIXELS),
+ 1e-3f,
+ 10.0f);
+ }
+
public static boolean isDoClip() {
return getBoolean("sun.java2d.renderer.clip", "true");
}
@@ -157,6 +180,14 @@
return getBoolean("sun.java2d.renderer.clip.runtime", "true");
}
+ public static boolean isDoClipSubdivider() {
+ return getBoolean("sun.java2d.renderer.clip.subdivider", "true");
+ }
+
+ public static float getSubdividerMinLength() {
+ return getFloat("sun.java2d.renderer.clip.subdivider.minLength", 100.0f, Float.NEGATIVE_INFINITY, Float.POSITIVE_INFINITY);
+ }
+
// debugging parameters
public static boolean isDoStats() {
@@ -191,16 +222,20 @@
// quality settings
+ public static float getCurveLengthError() {
+ return getFloat("sun.java2d.renderer.curve_len_err", 0.01f, 1e-6f, 1.0f);
+ }
+
public static float getCubicDecD2() {
- return getFloat("sun.java2d.renderer.cubic_dec_d2", 1.0f, 0.01f, 4.0f);
+ return getFloat("sun.java2d.renderer.cubic_dec_d2", 1.0f, 1e-5f, 4.0f);
}
public static float getCubicIncD1() {
- return getFloat("sun.java2d.renderer.cubic_inc_d1", 0.4f, 0.01f, 2.0f);
+ return getFloat("sun.java2d.renderer.cubic_inc_d1", 0.2f, 1e-6f, 1.0f);
}
public static float getQuadDecD2() {
- return getFloat("sun.java2d.renderer.quad_dec_d2", 0.5f, 0.01f, 4.0f);
+ return getFloat("sun.java2d.renderer.quad_dec_d2", 0.5f, 1e-5f, 4.0f);
}
// system property utilities
--- a/src/java.desktop/share/classes/sun/java2d/marlin/MarlinRenderingEngine.java Fri Mar 23 13:43:39 2018 -0700
+++ b/src/java.desktop/share/classes/sun/java2d/marlin/MarlinRenderingEngine.java Tue Mar 27 22:09:43 2018 +0200
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2007, 2017, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2007, 2018, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -47,7 +47,21 @@
public final class MarlinRenderingEngine extends RenderingEngine
implements MarlinConst
{
- private static enum NormMode {
+ // slightly slower ~2% if enabled stroker clipping (lines) but skipping cap / join handling is few percents faster in specific cases
+ static final boolean DISABLE_2ND_STROKER_CLIPPING = true;
+
+ static final boolean DO_TRACE_PATH = false;
+
+ static final boolean DO_CLIP = MarlinProperties.isDoClip();
+ static final boolean DO_CLIP_FILL = true;
+ static final boolean DO_CLIP_RUNTIME_ENABLE = MarlinProperties.isDoClipRuntimeFlag();
+
+ private static final float MIN_PEN_SIZE = 1.0f / MIN_SUBPIXELS;
+
+ static final float UPPER_BND = Float.MAX_VALUE / 2.0f;
+ static final float LOWER_BND = -UPPER_BND;
+
+ private enum NormMode {
ON_WITH_AA {
@Override
PathIterator getNormalizingPathIterator(final RendererContext rdrCtx,
@@ -80,18 +94,6 @@
PathIterator src);
}
- private static final float MIN_PEN_SIZE = 1.0f / NORM_SUBPIXELS;
-
- 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_CLIP_FILL = true;
-
- static final boolean DO_TRACE_PATH = false;
-
- static final boolean DO_CLIP_RUNTIME_ENABLE = MarlinProperties.isDoClipRuntimeFlag();
-
/**
* Public constructor
*/
@@ -419,14 +421,27 @@
pc2d = transformerPC2D.deltaTransformConsumer(pc2d, strokerat);
// stroker will adjust the clip rectangle (width / miter limit):
- pc2d = rdrCtx.stroker.init(pc2d, width, caps, join, miterlimit, scale);
+ pc2d = rdrCtx.stroker.init(pc2d, width, caps, join, miterlimit, scale,
+ (dashes == null));
+
+ // Curve Monotizer:
+ rdrCtx.monotonizer.init(width);
if (dashes != null) {
if (!recycleDashes) {
dashLen = dashes.length;
}
+ if (DO_TRACE_PATH) {
+ pc2d = transformerPC2D.traceDasher(pc2d);
+ }
pc2d = rdrCtx.dasher.init(pc2d, dashes, dashLen, dashphase,
recycleDashes);
+
+ if (DISABLE_2ND_STROKER_CLIPPING) {
+ // disable stoker clipping
+ rdrCtx.stroker.disableClipping();
+ }
+
} else if (rdrCtx.doClip && (caps != Stroker.CAP_BUTT)) {
if (DO_TRACE_PATH) {
pc2d = transformerPC2D.traceClosedPathDetector(pc2d);
@@ -625,6 +640,12 @@
private static void pathTo(final RendererContext rdrCtx, final PathIterator pi,
PathConsumer2D pc2d)
{
+ if (USE_PATH_SIMPLIFIER) {
+ // Use path simplifier at the first step
+ // to remove useless points
+ pc2d = rdrCtx.pathSimplifier.init(pc2d);
+ }
+
// mark context as DIRTY:
rdrCtx.dirty = true;
@@ -849,8 +870,6 @@
// trace Input:
pc2d = rdrCtx.transformerPC2D.traceInput(pc2d);
}
-
- // TODO: subdivide quad/cubic curves into monotonic curves ?
pathTo(rdrCtx, pi, pc2d);
} else {
@@ -1070,8 +1089,10 @@
logInfo("sun.java2d.renderer.edges = "
+ MarlinConst.INITIAL_EDGES_COUNT);
- logInfo("sun.java2d.renderer.pixelsize = "
- + MarlinConst.INITIAL_PIXEL_DIM);
+ logInfo("sun.java2d.renderer.pixelWidth = "
+ + MarlinConst.INITIAL_PIXEL_WIDTH);
+ logInfo("sun.java2d.renderer.pixelHeight = "
+ + MarlinConst.INITIAL_PIXEL_HEIGHT);
logInfo("sun.java2d.renderer.subPixel_log2_X = "
+ MarlinConst.SUBPIXEL_LG_POSITIONS_X);
@@ -1101,12 +1122,21 @@
// optimisation parameters
logInfo("sun.java2d.renderer.useSimplifier = "
+ MarlinConst.USE_SIMPLIFIER);
+ logInfo("sun.java2d.renderer.usePathSimplifier= "
+ + MarlinConst.USE_PATH_SIMPLIFIER);
+ logInfo("sun.java2d.renderer.pathSimplifier.pixTol = "
+ + MarlinProperties.getPathSimplifierPixelTolerance());
logInfo("sun.java2d.renderer.clip = "
+ MarlinProperties.isDoClip());
logInfo("sun.java2d.renderer.clip.runtime.enable = "
+ MarlinProperties.isDoClipRuntimeFlag());
+ logInfo("sun.java2d.renderer.clip.subdivider = "
+ + MarlinProperties.isDoClipSubdivider());
+ logInfo("sun.java2d.renderer.clip.subdivider.minLength = "
+ + MarlinProperties.getSubdividerMinLength());
+
// debugging parameters
logInfo("sun.java2d.renderer.doStats = "
+ MarlinConst.DO_STATS);
@@ -1124,6 +1154,8 @@
+ MarlinConst.LOG_UNSAFE_MALLOC);
// quality settings
+ logInfo("sun.java2d.renderer.curve_len_err = "
+ + MarlinProperties.getCurveLengthError());
logInfo("sun.java2d.renderer.cubic_dec_d2 = "
+ MarlinProperties.getCubicDecD2());
logInfo("sun.java2d.renderer.cubic_inc_d1 = "
--- a/src/java.desktop/share/classes/sun/java2d/marlin/MarlinTileGenerator.java Fri Mar 23 13:43:39 2018 -0700
+++ b/src/java.desktop/share/classes/sun/java2d/marlin/MarlinTileGenerator.java Tue Mar 27 22:09:43 2018 +0200
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2007, 2017, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2007, 2018, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -31,6 +31,8 @@
final class MarlinTileGenerator implements AATileGenerator, MarlinConst {
+ private static final boolean DISABLE_BLEND = false;
+
private static final int MAX_TILE_ALPHA_SUM = TILE_W * TILE_H * MAX_AA_ALPHA;
private static final int TH_AA_ALPHA_FILL_EMPTY = ((MAX_AA_ALPHA + 1) / 3); // 33%
@@ -43,10 +45,10 @@
throw new IllegalStateException("Invalid MAX_TILE_ALPHA_SUM: " + MAX_TILE_ALPHA_SUM);
}
if (DO_TRACE) {
- System.out.println("MAX_AA_ALPHA : " + MAX_AA_ALPHA);
- System.out.println("TH_AA_ALPHA_FILL_EMPTY : " + TH_AA_ALPHA_FILL_EMPTY);
- System.out.println("TH_AA_ALPHA_FILL_FULL : " + TH_AA_ALPHA_FILL_FULL);
- System.out.println("FILL_TILE_W : " + FILL_TILE_W);
+ MarlinUtils.logInfo("MAX_AA_ALPHA : " + MAX_AA_ALPHA);
+ MarlinUtils.logInfo("TH_AA_ALPHA_FILL_EMPTY : " + TH_AA_ALPHA_FILL_EMPTY);
+ MarlinUtils.logInfo("TH_AA_ALPHA_FILL_FULL : " + TH_AA_ALPHA_FILL_FULL);
+ MarlinUtils.logInfo("FILL_TILE_W : " + FILL_TILE_W);
}
}
@@ -141,6 +143,10 @@
*/
@Override
public int getTypicalAlpha() {
+ if (DISABLE_BLEND) {
+ // always return empty tiles to disable blending operations
+ return 0x00;
+ }
int al = cache.alphaSumInTile(x);
// Note: if we have a filled rectangle that doesn't end on a tile
// border, we could still return 0xff, even though al!=maxTileAlphaSum
--- a/src/java.desktop/share/classes/sun/java2d/marlin/MarlinUtils.java Fri Mar 23 13:43:39 2018 -0700
+++ b/src/java.desktop/share/classes/sun/java2d/marlin/MarlinUtils.java Tue Mar 27 22:09:43 2018 +0200
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2015, 2016, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2015, 2018, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -60,4 +60,22 @@
th.printStackTrace(System.err);
}
}
+
+ // From sun.awt.util.ThreadGroupUtils
+
+ /**
+ * Returns a root thread group.
+ * Should be called with {@link sun.security.util.SecurityConstants#MODIFY_THREADGROUP_PERMISSION}
+ *
+ * @return a root {@code ThreadGroup}
+ */
+ public static ThreadGroup getRootThreadGroup() {
+ ThreadGroup currentTG = Thread.currentThread().getThreadGroup();
+ ThreadGroup parentTG = currentTG.getParent();
+ while (parentTG != null) {
+ currentTG = parentTG;
+ parentTG = currentTG.getParent();
+ }
+ return currentTG;
+ }
}
--- a/src/java.desktop/share/classes/sun/java2d/marlin/MergeSort.java Fri Mar 23 13:43:39 2018 -0700
+++ b/src/java.desktop/share/classes/sun/java2d/marlin/MergeSort.java Tue Mar 27 22:09:43 2018 +0200
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2009, 2015, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2009, 2018, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -61,7 +61,6 @@
// Merge sorted parts (auxX/auxY) into x/y arrays
if ((insertionSortIndex == 0)
|| (auxX[insertionSortIndex - 1] <= auxX[insertionSortIndex])) {
-// System.out.println("mergeSortNoCopy: ordered");
// 34 occurences
// no initial left part or both sublists (auxX, auxY) are sorted:
// copy back data into (x, y):
@@ -135,7 +134,6 @@
// If arrays are inverted ie all(A) > all(B) do swap A and B to dst
if (srcX[high - 1] <= srcX[low]) {
-// System.out.println("mergeSort: inverse ordered");
// 1561 occurences
final int left = mid - low;
final int right = high - mid;
@@ -151,7 +149,6 @@
// If arrays are already sorted, just copy from src to dest. This is an
// optimization that results in faster sorts for nearly ordered lists.
if (srcX[mid - 1] <= srcX[mid]) {
-// System.out.println("mergeSort: ordered");
// 14 occurences
System.arraycopy(srcX, low, dstX, low, length);
System.arraycopy(srcY, low, dstY, low, length);
--- a/src/java.desktop/share/classes/sun/java2d/marlin/OffHeapArray.java Fri Mar 23 13:43:39 2018 -0700
+++ b/src/java.desktop/share/classes/sun/java2d/marlin/OffHeapArray.java Tue Mar 27 22:09:43 2018 +0200
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2007, 2017, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2007, 2018, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -31,7 +31,6 @@
/**
*
- * @author bourgesl
*/
final class OffHeapArray {
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/java.desktop/share/classes/sun/java2d/marlin/PathSimplifier.java Tue Mar 27 22:09:43 2018 +0200
@@ -0,0 +1,138 @@
+/*
+ * Copyright (c) 2017, 2018, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+package sun.java2d.marlin;
+
+import sun.awt.geom.PathConsumer2D;
+
+final class PathSimplifier implements PathConsumer2D {
+
+ // distance threshold in pixels (device)
+ private static final float PIX_THRESHOLD = MarlinProperties.getPathSimplifierPixelTolerance();
+
+ private static final float SQUARE_TOLERANCE = PIX_THRESHOLD * PIX_THRESHOLD;
+
+ // members:
+ private PathConsumer2D delegate;
+ private float cx, cy;
+
+ PathSimplifier() {
+ }
+
+ PathSimplifier init(final PathConsumer2D delegate) {
+ this.delegate = delegate;
+ return this; // fluent API
+ }
+
+ @Override
+ public void pathDone() {
+ delegate.pathDone();
+ }
+
+ @Override
+ public void closePath() {
+ delegate.closePath();
+ }
+
+ @Override
+ public long getNativeConsumer() {
+ return 0;
+ }
+
+ @Override
+ public void quadTo(final float x1, final float y1,
+ final float xe, final float ye)
+ {
+ // Test if curve is too small:
+ float dx = (xe - cx);
+ float dy = (ye - cy);
+
+ if ((dx * dx + dy * dy) <= SQUARE_TOLERANCE) {
+ // check control points P1:
+ dx = (x1 - cx);
+ dy = (y1 - cy);
+
+ if ((dx * dx + dy * dy) <= SQUARE_TOLERANCE) {
+ return;
+ }
+ }
+ delegate.quadTo(x1, y1, xe, ye);
+ // final end point:
+ cx = xe;
+ cy = ye;
+ }
+
+ @Override
+ public void curveTo(final float x1, final float y1,
+ final float x2, final float y2,
+ final float xe, final float ye)
+ {
+ // Test if curve is too small:
+ float dx = (xe - cx);
+ float dy = (ye - cy);
+
+ if ((dx * dx + dy * dy) <= SQUARE_TOLERANCE) {
+ // check control points P1:
+ dx = (x1 - cx);
+ dy = (y1 - cy);
+
+ if ((dx * dx + dy * dy) <= SQUARE_TOLERANCE) {
+ // check control points P2:
+ dx = (x2 - cx);
+ dy = (y2 - cy);
+
+ if ((dx * dx + dy * dy) <= SQUARE_TOLERANCE) {
+ return;
+ }
+ }
+ }
+ delegate.curveTo(x1, y1, x2, y2, xe, ye);
+ // final end point:
+ cx = xe;
+ cy = ye;
+ }
+
+ @Override
+ public void moveTo(final float xe, final float ye) {
+ delegate.moveTo(xe, ye);
+ // starting point:
+ cx = xe;
+ cy = ye;
+ }
+
+ @Override
+ public void lineTo(final float xe, final float ye) {
+ // Test if segment is too small:
+ float dx = (xe - cx);
+ float dy = (ye - cy);
+
+ if ((dx * dx + dy * dy) <= SQUARE_TOLERANCE) {
+ return;
+ }
+ delegate.lineTo(xe, ye);
+ // final end point:
+ cx = xe;
+ cy = ye;
+ }
+}
--- a/src/java.desktop/share/classes/sun/java2d/marlin/Renderer.java Fri Mar 23 13:43:39 2018 -0700
+++ b/src/java.desktop/share/classes/sun/java2d/marlin/Renderer.java Tue Mar 27 22:09:43 2018 +0200
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2007, 2017, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2007, 2018, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -54,9 +54,9 @@
private static final int SUBPIXEL_TILE
= TILE_H << SUBPIXEL_LG_POSITIONS_Y;
- // 2048 (pixelSize) pixels (height) x 8 subpixels = 64K
+ // 2176 pixels (height) x 8 subpixels = 68K
static final int INITIAL_BUCKET_ARRAY
- = INITIAL_PIXEL_DIM * SUBPIXEL_POSITIONS_Y;
+ = INITIAL_PIXEL_HEIGHT * SUBPIXEL_POSITIONS_Y;
// crossing capacity = edges count / 4 ~ 1024
static final int INITIAL_CROSSING_COUNT = INITIAL_EDGES_COUNT >> 2;
@@ -77,13 +77,17 @@
// curve break into lines
// cubic error in subpixels to decrement step
private static final float CUB_DEC_ERR_SUBPIX
- = MarlinProperties.getCubicDecD2() * (NORM_SUBPIXELS / 8.0f); // 1 pixel
+ = MarlinProperties.getCubicDecD2() * (SUBPIXEL_POSITIONS_X / 8.0f); // 1.0 / 8th pixel
// cubic error in subpixels to increment step
private static final float CUB_INC_ERR_SUBPIX
- = MarlinProperties.getCubicIncD1() * (NORM_SUBPIXELS / 8.0f); // 0.4 pixel
+ = MarlinProperties.getCubicIncD1() * (SUBPIXEL_POSITIONS_X / 8.0f); // 0.4 / 8th pixel
+ // scale factor for Y-axis contribution to quad / cubic errors:
+ public static final float SCALE_DY = ((float) SUBPIXEL_POSITIONS_X) / SUBPIXEL_POSITIONS_Y;
// TestNonAARasterization (JDK-8170879): cubics
// bad paths (59294/100000 == 59,29%, 94335 bad pixels (avg = 1,59), 3966 warnings (avg = 0,07)
+// 2018
+ // 1.0 / 0.2: bad paths (67194/100000 == 67,19%, 117394 bad pixels (avg = 1,75 - max = 9), 4042 warnings (avg = 0,06)
// cubic bind length to decrement step
public static final float CUB_DEC_BND
@@ -110,10 +114,12 @@
// quad break into lines
// quadratic error in subpixels
private static final float QUAD_DEC_ERR_SUBPIX
- = MarlinProperties.getQuadDecD2() * (NORM_SUBPIXELS / 8.0f); // 0.5 pixel
+ = MarlinProperties.getQuadDecD2() * (SUBPIXEL_POSITIONS_X / 8.0f); // 0.5 / 8th pixel
// TestNonAARasterization (JDK-8170879): quads
// bad paths (62916/100000 == 62,92%, 103818 bad pixels (avg = 1,65), 6514 warnings (avg = 0,10)
+// 2018
+ // 0.50px = bad paths (62915/100000 == 62,92%, 103810 bad pixels (avg = 1,65), 6512 warnings (avg = 0,10)
// quadratic bind length to decrement step
public static final float QUAD_DEC_BND
@@ -180,7 +186,7 @@
int count = 1; // dt = 1 / count
// maximum(ddX|Y) = norm(dbx, dby) * dt^2 (= 1)
- float maxDD = Math.abs(c.dbx) + Math.abs(c.dby);
+ float maxDD = Math.abs(c.dbx) + Math.abs(c.dby) * SCALE_DY;
final float _DEC_BND = QUAD_DEC_BND;
@@ -194,7 +200,8 @@
}
}
- int nL = 0; // line count
+ final int nL = count; // line count
+
if (count > 1) {
final float icount = 1.0f / count; // dt
final float icount2 = icount * icount; // dt^2
@@ -204,17 +211,12 @@
float dx = c.bx * icount2 + c.cx * icount;
float dy = c.by * icount2 + c.cy * icount;
- float x1, y1;
-
- while (--count > 0) {
- x1 = x0 + dx;
- dx += ddx;
- y1 = y0 + dy;
- dy += ddy;
+ // we use x0, y0 to walk the line
+ for (float x1 = x0, y1 = y0; --count > 0; dx += ddx, dy += ddy) {
+ x1 += dx;
+ y1 += dy;
addLine(x0, y0, x1, y1);
-
- if (DO_STATS) { nL++; }
x0 = x1;
y0 = y1;
}
@@ -222,7 +224,7 @@
addLine(x0, y0, x2, y2);
if (DO_STATS) {
- rdrCtx.stats.stat_rdr_quadBreak.add(nL + 1);
+ rdrCtx.stats.stat_rdr_quadBreak.add(nL);
}
}
@@ -250,16 +252,35 @@
dx = c.ax * icount3 + c.bx * icount2 + c.cx * icount;
dy = c.ay * icount3 + c.by * icount2 + c.cy * icount;
- // we use x0, y0 to walk the line
- float x1 = x0, y1 = y0;
int nL = 0; // line count
final float _DEC_BND = CUB_DEC_BND;
final float _INC_BND = CUB_INC_BND;
+ final float _SCALE_DY = SCALE_DY;
- while (count > 0) {
+ // we use x0, y0 to walk the line
+ for (float x1 = x0, y1 = y0; count > 0; ) {
+ // inc / dec => ratio ~ 5 to minimize upscale / downscale but minimize edges
+
+ // float step:
+ // can only do this on even "count" values, because we must divide count by 2
+ while ((count % 2 == 0)
+ && ((Math.abs(ddx) + Math.abs(ddy) * _SCALE_DY) <= _INC_BND)) {
+ dx = 2.0f * dx + ddx;
+ dy = 2.0f * dy + ddy;
+ ddx = 4.0f * (ddx + dddx);
+ ddy = 4.0f * (ddy + dddy);
+ dddx *= 8.0f;
+ dddy *= 8.0f;
+
+ count >>= 1;
+ if (DO_STATS) {
+ rdrCtx.stats.stat_rdr_curveBreak_inc.add(count);
+ }
+ }
+
// divide step by half:
- while (Math.abs(ddx) + Math.abs(ddy) >= _DEC_BND) {
+ while ((Math.abs(ddx) + Math.abs(ddy) * _SCALE_DY) >= _DEC_BND) {
dddx /= 8.0f;
dddy /= 8.0f;
ddx = ddx / 4.0f - dddx;
@@ -272,44 +293,25 @@
rdrCtx.stats.stat_rdr_curveBreak_dec.add(count);
}
}
-
- // double step:
- // can only do this on even "count" values, because we must divide count by 2
- while (count % 2 == 0
- && Math.abs(dx) + Math.abs(dy) <= _INC_BND)
- {
- dx = 2.0f * dx + ddx;
- dy = 2.0f * dy + ddy;
- ddx = 4.0f * (ddx + dddx);
- ddy = 4.0f * (ddy + dddy);
- dddx *= 8.0f;
- dddy *= 8.0f;
-
- count >>= 1;
- if (DO_STATS) {
- rdrCtx.stats.stat_rdr_curveBreak_inc.add(count);
- }
- }
- if (--count > 0) {
- x1 += dx;
- dx += ddx;
- ddx += dddx;
- y1 += dy;
- dy += ddy;
- ddy += dddy;
- } else {
- x1 = x3;
- y1 = y3;
+ if (--count == 0) {
+ break;
}
- addLine(x0, y0, x1, y1);
+ x1 += dx;
+ y1 += dy;
+ dx += ddx;
+ dy += ddy;
+ ddx += dddx;
+ ddy += dddy;
- if (DO_STATS) { nL++; }
+ addLine(x0, y0, x1, y1);
x0 = x1;
y0 = y1;
}
+ addLine(x0, y0, x3, y3);
+
if (DO_STATS) {
- rdrCtx.stats.stat_rdr_curveBreak.add(nL);
+ rdrCtx.stats.stat_rdr_curveBreak.add(nL + 1);
}
}
@@ -537,8 +539,8 @@
edgeBuckets = edgeBuckets_ref.initial;
edgeBucketCounts = edgeBucketCounts_ref.initial;
- // 2048 (pixelsize) pixel large
- alphaLine_ref = rdrCtx.newCleanIntArrayRef(INITIAL_AA_ARRAY); // 8K
+ // 4096 pixels large
+ alphaLine_ref = rdrCtx.newCleanIntArrayRef(INITIAL_AA_ARRAY); // 16K
alphaLine = alphaLine_ref.initial;
crossings_ref = rdrCtx.newDirtyIntArrayRef(INITIAL_CROSSING_COUNT); // 2K
@@ -696,8 +698,10 @@
{
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);
+ 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;
@@ -709,7 +713,9 @@
{
final float xe = tosubpixx(pix_x2);
final float ye = tosubpixy(pix_y2);
- curve.set(x0, y0, tosubpixx(pix_x1), tosubpixy(pix_y1), xe, ye);
+ curve.set(x0, y0,
+ tosubpixx(pix_x1), tosubpixy(pix_y1),
+ xe, ye);
quadBreakIntoLinesAndAdd(x0, y0, curve, xe, ye);
x0 = xe;
y0 = ye;
--- a/src/java.desktop/share/classes/sun/java2d/marlin/RendererContext.java Fri Mar 23 13:43:39 2018 -0700
+++ b/src/java.desktop/share/classes/sun/java2d/marlin/RendererContext.java Tue Mar 27 22:09:43 2018 +0200
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2015, 2017, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2015, 2018, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -31,6 +31,8 @@
import sun.java2d.ReentrantContext;
import sun.java2d.marlin.ArrayCacheConst.CacheStats;
import sun.java2d.marlin.MarlinRenderingEngine.NormalizingPathIterator;
+import sun.java2d.marlin.TransformingPathConsumer2D.CurveBasicMonotonizer;
+import sun.java2d.marlin.TransformingPathConsumer2D.CurveClipSplitter;
/**
* This class is a renderer context dedicated to a single thread
@@ -70,6 +72,8 @@
final Stroker stroker;
// Simplifies out collinear lines
final CollinearSimplifier simplifier = new CollinearSimplifier();
+ // Simplifies path
+ final PathSimplifier pathSimplifier = new PathSimplifier();
final Dasher dasher;
final MarlinTileGenerator ptg;
final MarlinCache cache;
@@ -81,6 +85,10 @@
boolean closedPath = false;
// clip rectangle (ymin, ymax, xmin, xmax):
final float[] clipRect = new float[4];
+ // CurveBasicMonotonizer instance
+ final CurveBasicMonotonizer monotonizer;
+ // CurveClipSplitter instance
+ final CurveClipSplitter curveClipSplitter;
// Array caches:
/* clean int[] cache (zero-filled) = 5 refs */
@@ -121,6 +129,10 @@
nPCPathIterator = new NormalizingPathIterator.NearestPixelCenter(float6);
nPQPathIterator = new NormalizingPathIterator.NearestPixelQuarter(float6);
+ // curve monotonizer & clip subdivider (before transformerPC2D init)
+ monotonizer = new CurveBasicMonotonizer(this);
+ curveClipSplitter = new CurveClipSplitter(this);
+
// MarlinRenderingEngine.TransformingPathConsumer2D
transformerPC2D = new TransformingPathConsumer2D(this);
--- a/src/java.desktop/share/classes/sun/java2d/marlin/RendererStats.java Fri Mar 23 13:43:39 2018 -0700
+++ b/src/java.desktop/share/classes/sun/java2d/marlin/RendererStats.java Tue Mar 27 22:09:43 2018 +0200
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2015, 2017, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2015, 2018, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -36,7 +36,6 @@
import sun.java2d.marlin.stats.Histogram;
import sun.java2d.marlin.stats.Monitor;
import sun.java2d.marlin.stats.StatLong;
-import sun.awt.util.ThreadGroupUtils;
/**
* This class gathers global rendering statistics for debugging purposes only
@@ -359,7 +358,7 @@
AccessController.doPrivileged(
(PrivilegedAction<Void>) () -> {
final Thread hook = new Thread(
- ThreadGroupUtils.getRootThreadGroup(),
+ MarlinUtils.getRootThreadGroup(),
new Runnable() {
@Override
public void run() {
--- a/src/java.desktop/share/classes/sun/java2d/marlin/Stroker.java Fri Mar 23 13:43:39 2018 -0700
+++ b/src/java.desktop/share/classes/sun/java2d/marlin/Stroker.java Tue Mar 27 22:09:43 2018 +0200
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2007, 2017, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2007, 2018, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -29,6 +29,8 @@
import sun.awt.geom.PathConsumer2D;
import sun.java2d.marlin.Helpers.PolyStack;
+import sun.java2d.marlin.TransformingPathConsumer2D.CurveBasicMonotonizer;
+import sun.java2d.marlin.TransformingPathConsumer2D.CurveClipSplitter;
// 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
@@ -39,10 +41,9 @@
private static final int DRAWING_OP_TO = 1; // ie. curve, line, or quad
private static final int CLOSE = 2;
- // pisces used to use fixed point arithmetic with 16 decimal digits. I
- // didn't want to change the values of the constant below when I converted
- // 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;
+ // round join threshold = 1 subpixel
+ private static final float ERR_JOIN = (1.0f / MIN_SUBPIXELS);
+ private static final float ROUND_JOIN_THRESHOLD = ERR_JOIN * ERR_JOIN;
// kappa = (4/3) * (SQRT(2) - 1)
private static final float C = (float)(4.0d * (Math.sqrt(2.0d) - 1.0d) / 3.0d);
@@ -50,8 +51,6 @@
// SQRT(2)
private static final float SQRT_2 = (float)Math.sqrt(2.0d);
- private static final int MAX_N_CURVES = 11;
-
private PathConsumer2D out;
private int capStyle;
@@ -82,12 +81,8 @@
private final PolyStack reverse;
- // This is where the curve to be processed is put. We give it
- // enough room to store all curves.
- private final float[] middle = new float[MAX_N_CURVES * 6 + 2];
private final float[] lp = new float[8];
private final float[] rp = new float[8];
- private final float[] subdivTs = new float[MAX_N_CURVES - 1];
// per-thread renderer context
final RendererContext rdrCtx;
@@ -108,6 +103,11 @@
private boolean opened = false;
// flag indicating if the starting point's cap is done
private boolean capStart = false;
+ // flag indicating to monotonize curves
+ private boolean monotonize;
+
+ private boolean subdivide = DO_CLIP_SUBDIVIDER;
+ private final CurveClipSplitter curveSplitter;
/**
* Constructs a <code>Stroker</code>.
@@ -126,6 +126,7 @@
: new PolyStack(rdrCtx);
this.curve = rdrCtx.curve;
+ this.curveSplitter = rdrCtx.curveClipSplitter;
}
/**
@@ -141,6 +142,7 @@
* <code>JOIN_BEVEL</code>.
* @param miterLimit the desired miter limit
* @param scale scaling factor applied to clip boundaries
+ * @param subdivideCurves true to indicate to subdivide curves, false if dasher does
* @return this instance
*/
Stroker init(final PathConsumer2D pc2d,
@@ -148,12 +150,15 @@
final int capStyle,
final int joinStyle,
final float miterLimit,
- final float scale)
+ final float scale,
+ final boolean subdivideCurves)
{
this.out = pc2d;
this.lineWidth2 = lineWidth / 2.0f;
this.invHalfLineWidth2Sq = 1.0f / (2.0f * lineWidth2 * lineWidth2);
+ this.monotonize = subdivideCurves;
+
this.capStyle = capStyle;
this.joinStyle = joinStyle;
@@ -191,6 +196,15 @@
_clipRect[2] -= margin - rdrOffX;
_clipRect[3] += margin + rdrOffX;
this.clipRect = _clipRect;
+
+ // initialize curve splitter here for stroker & dasher:
+ if (DO_CLIP_SUBDIVIDER) {
+ subdivide = subdivideCurves;
+ // adjust padded clip rectangle:
+ curveSplitter.init();
+ } else {
+ subdivide = false;
+ }
} else {
this.clipRect = null;
this.cOutCode = 0;
@@ -199,6 +213,12 @@
return this; // fluent API
}
+ void disableClipping() {
+ this.clipRect = null;
+ this.cOutCode = 0;
+ this.sOutCode = 0;
+ }
+
/**
* Disposes this stroker:
* clean up before reusing this instance
@@ -215,10 +235,8 @@
Arrays.fill(offset1, 0.0f);
Arrays.fill(offset2, 0.0f);
Arrays.fill(miter, 0.0f);
- Arrays.fill(middle, 0.0f);
Arrays.fill(lp, 0.0f);
Arrays.fill(rp, 0.0f);
- Arrays.fill(subdivTs, 0.0f);
}
}
@@ -250,19 +268,20 @@
return dx1 * dy2 <= dy1 * dx2;
}
- private void drawRoundJoin(float x, float y,
- float omx, float omy, float mx, float my,
- boolean rev,
- float threshold)
+ private void mayDrawRoundJoin(float cx, float cy,
+ float omx, float omy,
+ float mx, float my,
+ boolean rev)
{
if ((omx == 0.0f && omy == 0.0f) || (mx == 0.0f && my == 0.0f)) {
return;
}
- float domx = omx - mx;
- float domy = omy - my;
- float len = domx*domx + domy*domy;
- if (len < threshold) {
+ final float domx = omx - mx;
+ final float domy = omy - my;
+ final float lenSq = domx*domx + domy*domy;
+
+ if (lenSq < ROUND_JOIN_THRESHOLD) {
return;
}
@@ -272,7 +291,7 @@
mx = -mx;
my = -my;
}
- drawRoundJoin(x, y, omx, omy, mx, my, rev);
+ drawRoundJoin(cx, cy, omx, omy, mx, my, rev);
}
private void drawRoundJoin(float cx, float cy,
@@ -383,7 +402,7 @@
final float x1, final float y1,
final float x0p, final float y0p,
final float x1p, final float y1p,
- final float[] m, int off)
+ final float[] m)
{
float x10 = x1 - x0;
float y10 = y1 - y0;
@@ -402,8 +421,8 @@
float den = x10*y10p - x10p*y10;
float t = x10p*(y0-y0p) - y10p*(x0-x0p);
t /= den;
- m[off++] = x0 + t*x10;
- m[off] = y0 + t*y10;
+ m[0] = x0 + t*x10;
+ m[1] = y0 + t*y10;
}
// Return the intersection point of the lines (x0, y0) -> (x1, y1)
@@ -412,7 +431,7 @@
final float x1, final float y1,
final float x0p, final float y0p,
final float x1p, final float y1p,
- final float[] m, int off)
+ final float[] m)
{
float x10 = x1 - x0;
float y10 = y1 - y0;
@@ -430,20 +449,21 @@
// immediately).
float den = x10*y10p - x10p*y10;
if (den == 0.0f) {
- m[off++] = (x0 + x0p) / 2.0f;
- m[off] = (y0 + y0p) / 2.0f;
- return;
+ m[2] = (x0 + x0p) / 2.0f;
+ m[3] = (y0 + y0p) / 2.0f;
+ } else {
+ float t = x10p*(y0-y0p) - y10p*(x0-x0p);
+ t /= den;
+ m[2] = x0 + t*x10;
+ m[3] = y0 + t*y10;
}
- float t = x10p*(y0-y0p) - y10p*(x0-x0p);
- t /= den;
- m[off++] = x0 + t*x10;
- m[off] = y0 + t*y10;
}
private void drawMiter(final float pdx, final float pdy,
final float x0, final float y0,
final float dx, final float dy,
- float omx, float omy, float mx, float my,
+ float omx, float omy,
+ float mx, float my,
boolean rev)
{
if ((mx == omx && my == omy) ||
@@ -461,8 +481,7 @@
}
computeMiter((x0 - pdx) + omx, (y0 - pdy) + omy, x0 + omx, y0 + omy,
- (dx + x0) + mx, (dy + y0) + my, x0 + mx, y0 + my,
- miter, 0);
+ (dx + x0) + mx, (dy + y0) + my, x0 + mx, y0 + my, miter);
final float miterX = miter[0];
final float miterY = miter[1];
@@ -480,7 +499,7 @@
@Override
public void moveTo(final float x0, final float y0) {
- moveTo(x0, y0, cOutCode);
+ _moveTo(x0, y0, cOutCode);
// update starting point:
this.sx0 = x0;
this.sy0 = y0;
@@ -496,7 +515,7 @@
}
}
- private void moveTo(final float x0, final float y0,
+ private void _moveTo(final float x0, final float y0,
final int outcode)
{
if (prev == MOVE_TO) {
@@ -523,16 +542,40 @@
final boolean force)
{
final int outcode0 = this.cOutCode;
+
if (!force && clipRect != null) {
final int outcode1 = Helpers.outcode(x1, y1, clipRect);
- this.cOutCode = outcode1;
+
+ // Should clip
+ final int orCode = (outcode0 | outcode1);
+ if (orCode != 0) {
+ final int sideCode = outcode0 & outcode1;
- // basic rejection criteria
- if ((outcode0 & outcode1) != 0) {
- moveTo(x1, y1, outcode0);
- opened = true;
- return;
+ // basic rejection criteria:
+ if (sideCode == 0) {
+ // ovelap clip:
+ if (subdivide) {
+ // avoid reentrance
+ subdivide = false;
+ // subdivide curve => callback with subdivided parts:
+ boolean ret = curveSplitter.splitLine(cx0, cy0, x1, y1,
+ orCode, this);
+ // reentrance is done:
+ subdivide = true;
+ if (ret) {
+ return;
+ }
+ }
+ // already subdivided so render it
+ } else {
+ this.cOutCode = outcode1;
+ _moveTo(x1, y1, outcode0);
+ opened = true;
+ return;
+ }
}
+
+ this.cOutCode = outcode1;
}
float dx = x1 - cx0;
@@ -754,10 +797,7 @@
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);
+ mayDrawRoundJoin(x0, y0, omx, omy, mx, my, cw);
}
}
emitLineTo(x0, y0, !cw);
@@ -767,18 +807,19 @@
private static boolean within(final float x1, final float y1,
final float x2, final float y2,
- final float ERR)
+ final float err)
{
- assert ERR > 0 : "";
+ assert err > 0 : "";
// compare taxicab distance. ERR will always be small, so using
// true distance won't give much benefit
- return (Helpers.within(x1, x2, ERR) && // we want to avoid calling Math.abs
- Helpers.within(y1, y2, ERR)); // this is just as good.
+ return (Helpers.within(x1, x2, err) && // we want to avoid calling Math.abs
+ Helpers.within(y1, y2, err)); // this is just as good.
}
- private void getLineOffsets(float x1, float y1,
- float x2, float y2,
- float[] left, float[] right) {
+ private void getLineOffsets(final float x1, final float y1,
+ final float x2, final float y2,
+ final float[] left, final float[] right)
+ {
computeOffset(x2 - x1, y2 - y1, lineWidth2, offset0);
final float mx = offset0[0];
final float my = offset0[1];
@@ -786,14 +827,16 @@
left[1] = y1 + my;
left[2] = x2 + mx;
left[3] = y2 + my;
+
right[0] = x1 - mx;
right[1] = y1 - my;
right[2] = x2 - mx;
right[3] = y2 - my;
}
- private int computeOffsetCubic(float[] pts, final int off,
- float[] leftOff, float[] rightOff)
+ private int computeOffsetCubic(final float[] pts, final int off,
+ final float[] leftOff,
+ final float[] rightOff)
{
// if p1=p2 or p3=p4 it means that the derivative at the endpoint
// vanishes, which creates problems with computeOffset. Usually
@@ -802,7 +845,7 @@
// the input curve at the cusp, and passes it to this function.
// because of inaccuracies in the splitting, we consider points
// equal if they're very close to each other.
- final float x1 = pts[off + 0], y1 = pts[off + 1];
+ final float x1 = pts[off ], y1 = pts[off + 1];
final float x2 = pts[off + 2], y2 = pts[off + 3];
final float x3 = pts[off + 4], y3 = pts[off + 5];
final float x4 = pts[off + 6], y4 = pts[off + 7];
@@ -816,6 +859,7 @@
// in which case ignore if p1 == p2
final boolean p1eqp2 = within(x1, y1, x2, y2, 6.0f * Math.ulp(y2));
final boolean p3eqp4 = within(x3, y3, x4, y4, 6.0f * Math.ulp(y4));
+
if (p1eqp2 && p3eqp4) {
getLineOffsets(x1, y1, x4, y4, leftOff, rightOff);
return 4;
@@ -831,6 +875,7 @@
float dotsq = (dx1 * dx4 + dy1 * dy4);
dotsq *= dotsq;
float l1sq = dx1 * dx1 + dy1 * dy1, l4sq = dx4 * dx4 + dy4 * dy4;
+
if (Helpers.within(dotsq, l1sq * l4sq, 4.0f * Math.ulp(dotsq))) {
getLineOffsets(x1, y1, x4, y4, leftOff, rightOff);
return 4;
@@ -944,10 +989,11 @@
// compute offset curves using bezier spline through t=0.5 (i.e.
// ComputedCurve(0.5) == IdealParallelCurve(0.5))
// return the kind of curve in the right and left arrays.
- private int computeOffsetQuad(float[] pts, final int off,
- float[] leftOff, float[] rightOff)
+ private int computeOffsetQuad(final float[] pts, final int off,
+ final float[] leftOff,
+ final float[] rightOff)
{
- final float x1 = pts[off + 0], y1 = pts[off + 1];
+ final float x1 = pts[off ], y1 = pts[off + 1];
final float x2 = pts[off + 2], y2 = pts[off + 3];
final float x3 = pts[off + 4], y3 = pts[off + 5];
@@ -968,6 +1014,7 @@
// in which case ignore.
final boolean p1eqp2 = within(x1, y1, x2, y2, 6.0f * Math.ulp(y2));
final boolean p2eqp3 = within(x2, y2, x3, y3, 6.0f * Math.ulp(y3));
+
if (p1eqp2 || p2eqp3) {
getLineOffsets(x1, y1, x3, y3, leftOff, rightOff);
return 4;
@@ -977,6 +1024,7 @@
float dotsq = (dx1 * dx3 + dy1 * dy3);
dotsq *= dotsq;
float l1sq = dx1 * dx1 + dy1 * dy1, l3sq = dx3 * dx3 + dy3 * dy3;
+
if (Helpers.within(dotsq, l1sq * l3sq, 4.0f * Math.ulp(dotsq))) {
getLineOffsets(x1, y1, x3, y3, leftOff, rightOff);
return 4;
@@ -992,151 +1040,111 @@
float y1p = y1 + offset0[1]; // point
float x3p = x3 + offset1[0]; // end
float y3p = y3 + offset1[1]; // point
- safeComputeMiter(x1p, y1p, x1p+dx1, y1p+dy1, x3p, y3p, x3p-dx3, y3p-dy3, leftOff, 2);
+ safeComputeMiter(x1p, y1p, x1p+dx1, y1p+dy1, x3p, y3p, x3p-dx3, y3p-dy3, leftOff);
leftOff[0] = x1p; leftOff[1] = y1p;
leftOff[4] = x3p; leftOff[5] = y3p;
x1p = x1 - offset0[0]; y1p = y1 - offset0[1];
x3p = x3 - offset1[0]; y3p = y3 - offset1[1];
- safeComputeMiter(x1p, y1p, x1p+dx1, y1p+dy1, x3p, y3p, x3p-dx3, y3p-dy3, rightOff, 2);
+ safeComputeMiter(x1p, y1p, x1p+dx1, y1p+dy1, x3p, y3p, x3p-dx3, y3p-dy3, rightOff);
rightOff[0] = x1p; rightOff[1] = y1p;
rightOff[4] = x3p; rightOff[5] = y3p;
return 6;
}
- // finds values of t where the curve in pts should be subdivided in order
- // to get good offset curves a distance of w away from the middle curve.
- // Stores the points in ts, and returns how many of them there were.
- private static int findSubdivPoints(final Curve c, float[] pts, float[] ts,
- final int type, final float w)
- {
- final float x12 = pts[2] - pts[0];
- final float y12 = pts[3] - pts[1];
- // if the curve is already parallel to either axis we gain nothing
- // from rotating it.
- if (y12 != 0.0f && x12 != 0.0f) {
- // we rotate it so that the first vector in the control polygon is
- // parallel to the x-axis. This will ensure that rotated quarter
- // circles won't be subdivided.
- final float hypot = (float) Math.sqrt(x12 * x12 + y12 * y12);
- final float cos = x12 / hypot;
- final float sin = y12 / hypot;
- final float x1 = cos * pts[0] + sin * pts[1];
- final float y1 = cos * pts[1] - sin * pts[0];
- final float x2 = cos * pts[2] + sin * pts[3];
- final float y2 = cos * pts[3] - sin * pts[2];
- final float x3 = cos * pts[4] + sin * pts[5];
- final float y3 = cos * pts[5] - sin * pts[4];
-
- switch(type) {
- case 8:
- final float x4 = cos * pts[6] + sin * pts[7];
- final float y4 = cos * pts[7] - sin * pts[6];
- c.set(x1, y1, x2, y2, x3, y3, x4, y4);
- break;
- case 6:
- c.set(x1, y1, x2, y2, x3, y3);
- break;
- default:
- }
- } else {
- c.set(pts, type);
- }
-
- int ret = 0;
- // we subdivide at values of t such that the remaining rotated
- // curves are monotonic in x and y.
- ret += c.dxRoots(ts, ret);
- ret += c.dyRoots(ts, ret);
- // subdivide at inflection points.
- if (type == 8) {
- // quadratic curves can't have inflection points
- ret += c.infPoints(ts, ret);
- }
-
- // now we must subdivide at points where one of the offset curves will have
- // a cusp. This happens at ts where the radius of curvature is equal to w.
- ret += c.rootsOfROCMinusW(ts, ret, w, 0.0001f);
-
- ret = Helpers.filterOutNotInAB(ts, 0, ret, 0.0001f, 0.9999f);
- Helpers.isort(ts, 0, ret);
- return ret;
- }
-
@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 outcode1 = Helpers.outcode(x1, y1, clipRect);
+ final int outcode2 = Helpers.outcode(x2, y2, clipRect);
final int outcode3 = Helpers.outcode(x3, y3, clipRect);
- this.cOutCode = outcode3;
+
+ // Should clip
+ final int orCode = (outcode0 | outcode1 | outcode2 | outcode3);
+ if (orCode != 0) {
+ final int sideCode = outcode0 & outcode1 & outcode2 & outcode3;
- if ((outcode0 & 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);
+ // basic rejection criteria:
+ if (sideCode == 0) {
+ // ovelap clip:
+ if (subdivide) {
+ // avoid reentrance
+ subdivide = false;
+ // subdivide curve => callback with subdivided parts:
+ boolean ret = curveSplitter.splitCurve(cx0, cy0, x1, y1,
+ x2, y2, x3, y3,
+ orCode, this);
+ // reentrance is done:
+ subdivide = true;
+ if (ret) {
+ return;
+ }
+ }
+ // already subdivided so render it
+ } else {
+ this.cOutCode = outcode3;
+ _moveTo(x3, y3, outcode0);
opened = true;
return;
}
}
+
+ this.cOutCode = outcode3;
}
-
- final float[] mid = middle;
+ _curveTo(x1, y1, x2, y2, x3, y3, outcode0);
+ }
- mid[0] = cx0; mid[1] = cy0;
- mid[2] = x1; mid[3] = y1;
- mid[4] = x2; mid[5] = y2;
- mid[6] = x3; mid[7] = y3;
-
+ private void _curveTo(final float x1, final float y1,
+ final float x2, final float y2,
+ final float x3, final float y3,
+ final int outcode0)
+ {
// need these so we can update the state at the end of this method
- final float xf = x3, yf = y3;
- float dxs = mid[2] - mid[0];
- float dys = mid[3] - mid[1];
- float dxf = mid[6] - mid[4];
- float dyf = mid[7] - mid[5];
+ float dxs = x1 - cx0;
+ float dys = y1 - cy0;
+ float dxf = x3 - x2;
+ float dyf = y3 - y2;
- boolean p1eqp2 = (dxs == 0.0f && dys == 0.0f);
- boolean p3eqp4 = (dxf == 0.0f && dyf == 0.0f);
- if (p1eqp2) {
- dxs = mid[4] - mid[0];
- dys = mid[5] - mid[1];
- if (dxs == 0.0f && dys == 0.0f) {
- dxs = mid[6] - mid[0];
- dys = mid[7] - mid[1];
+ if ((dxs == 0.0f) && (dys == 0.0f)) {
+ dxs = x2 - cx0;
+ dys = y2 - cy0;
+ if ((dxs == 0.0f) && (dys == 0.0f)) {
+ dxs = x3 - cx0;
+ dys = y3 - cy0;
}
}
- if (p3eqp4) {
- dxf = mid[6] - mid[2];
- dyf = mid[7] - mid[3];
- if (dxf == 0.0f && dyf == 0.0f) {
- dxf = mid[6] - mid[0];
- dyf = mid[7] - mid[1];
+ if ((dxf == 0.0f) && (dyf == 0.0f)) {
+ dxf = x3 - x1;
+ dyf = y3 - y1;
+ if ((dxf == 0.0f) && (dyf == 0.0f)) {
+ dxf = x3 - cx0;
+ dyf = y3 - cy0;
}
}
- if (dxs == 0.0f && dys == 0.0f) {
+ 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]);
+ lineTo(cx0, cy0);
return;
}
// if these vectors are too small, normalize them, to avoid future
// precision problems.
if (Math.abs(dxs) < 0.1f && Math.abs(dys) < 0.1f) {
- float len = (float) Math.sqrt(dxs*dxs + dys*dys);
+ final float len = (float)Math.sqrt(dxs * dxs + dys * dys);
dxs /= len;
dys /= len;
}
if (Math.abs(dxf) < 0.1f && Math.abs(dyf) < 0.1f) {
- float len = (float) Math.sqrt(dxf*dxf + dyf*dyf);
+ final float len = (float)Math.sqrt(dxf * dxf + dyf * dyf);
dxf /= len;
dyf /= len;
}
@@ -1144,17 +1152,25 @@
computeOffset(dxs, dys, lineWidth2, offset0);
drawJoin(cdx, cdy, cx0, cy0, dxs, dys, cmx, cmy, offset0[0], offset0[1], outcode0);
- final int nSplits = findSubdivPoints(curve, mid, subdivTs, 8, lineWidth2);
+ int nSplits = 0;
+ final float[] mid;
+ final float[] l = lp;
+
+ if (monotonize) {
+ // monotonize curve:
+ final CurveBasicMonotonizer monotonizer
+ = rdrCtx.monotonizer.curve(cx0, cy0, x1, y1, x2, y2, x3, y3);
- float prevT = 0.0f;
- for (int i = 0, off = 0; i < nSplits; i++, off += 6) {
- final float t = subdivTs[i];
- Helpers.subdivideCubicAt((t - prevT) / (1.0f - prevT),
- mid, off, mid, off, mid, off + 6);
- prevT = t;
+ nSplits = monotonizer.nbSplits;
+ mid = monotonizer.middle;
+ } else {
+ // use left instead:
+ mid = l;
+ mid[0] = cx0; mid[1] = cy0;
+ mid[2] = x1; mid[3] = y1;
+ mid[4] = x2; mid[5] = y2;
+ mid[6] = x3; mid[7] = y3;
}
-
- final float[] l = lp;
final float[] r = rp;
int kind = 0;
@@ -1178,8 +1194,8 @@
}
this.prev = DRAWING_OP_TO;
- this.cx0 = xf;
- this.cy0 = yf;
+ this.cx0 = x3;
+ this.cy0 = y3;
this.cdx = dxf;
this.cdy = dyf;
this.cmx = (l[kind - 2] - r[kind - 2]) / 2.0f;
@@ -1191,74 +1207,101 @@
final float x2, final float y2)
{
final int outcode0 = this.cOutCode;
+
if (clipRect != null) {
+ final int outcode1 = Helpers.outcode(x1, y1, clipRect);
final int outcode2 = Helpers.outcode(x2, y2, clipRect);
- this.cOutCode = outcode2;
+
+ // Should clip
+ final int orCode = (outcode0 | outcode1 | outcode2);
+ if (orCode != 0) {
+ final int sideCode = outcode0 & outcode1 & outcode2;
- if ((outcode0 & outcode2) != 0) {
- final int outcode1 = Helpers.outcode(x1, y1, clipRect);
-
- // basic rejection criteria
- if ((outcode0 & outcode1 & outcode2) != 0) {
- moveTo(x2, y2, outcode0);
+ // basic rejection criteria:
+ if (sideCode == 0) {
+ // ovelap clip:
+ if (subdivide) {
+ // avoid reentrance
+ subdivide = false;
+ // subdivide curve => call lineTo() with subdivided curves:
+ boolean ret = curveSplitter.splitQuad(cx0, cy0, x1, y1,
+ x2, y2, orCode, this);
+ // reentrance is done:
+ subdivide = true;
+ if (ret) {
+ return;
+ }
+ }
+ // already subdivided so render it
+ } else {
+ this.cOutCode = outcode2;
+ _moveTo(x2, y2, outcode0);
opened = true;
return;
}
}
- }
-
- final float[] mid = middle;
- mid[0] = cx0; mid[1] = cy0;
- mid[2] = x1; mid[3] = y1;
- mid[4] = x2; mid[5] = y2;
+ this.cOutCode = outcode2;
+ }
+ _quadTo(x1, y1, x2, y2, outcode0);
+ }
+ private void _quadTo(final float x1, final float y1,
+ final float x2, final float y2,
+ final int outcode0)
+ {
// need these so we can update the state at the end of this method
- final float xf = x2, yf = y2;
- float dxs = mid[2] - mid[0];
- float dys = mid[3] - mid[1];
- float dxf = mid[4] - mid[2];
- float dyf = mid[5] - mid[3];
- if ((dxs == 0.0f && dys == 0.0f) || (dxf == 0.0f && dyf == 0.0f)) {
- dxs = dxf = mid[4] - mid[0];
- dys = dyf = mid[5] - mid[1];
+ float dxs = x1 - cx0;
+ float dys = y1 - cy0;
+ float dxf = x2 - x1;
+ float dyf = y2 - y1;
+
+ if (((dxs == 0.0f) && (dys == 0.0f)) || ((dxf == 0.0f) && (dyf == 0.0f))) {
+ dxs = dxf = x2 - cx0;
+ dys = dyf = y2 - cy0;
}
- if (dxs == 0.0f && dys == 0.0f) {
+ 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]);
+ lineTo(cx0, cy0);
return;
}
// if these vectors are too small, normalize them, to avoid future
// precision problems.
if (Math.abs(dxs) < 0.1f && Math.abs(dys) < 0.1f) {
- float len = (float) Math.sqrt(dxs*dxs + dys*dys);
+ final float len = (float)Math.sqrt(dxs * dxs + dys * dys);
dxs /= len;
dys /= len;
}
if (Math.abs(dxf) < 0.1f && Math.abs(dyf) < 0.1f) {
- float len = (float) Math.sqrt(dxf*dxf + dyf*dyf);
+ final float len = (float)Math.sqrt(dxf * dxf + dyf * dyf);
dxf /= len;
dyf /= len;
}
-
computeOffset(dxs, dys, lineWidth2, offset0);
drawJoin(cdx, cdy, cx0, cy0, dxs, dys, cmx, cmy, offset0[0], offset0[1], outcode0);
- int nSplits = findSubdivPoints(curve, mid, subdivTs, 6, lineWidth2);
+ int nSplits = 0;
+ final float[] mid;
+ final float[] l = lp;
+
+ if (monotonize) {
+ // monotonize quad:
+ final CurveBasicMonotonizer monotonizer
+ = rdrCtx.monotonizer.quad(cx0, cy0, x1, y1, x2, y2);
- float prevt = 0.0f;
- for (int i = 0, off = 0; i < nSplits; i++, off += 4) {
- final float t = subdivTs[i];
- Helpers.subdivideQuadAt((t - prevt) / (1.0f - prevt),
- mid, off, mid, off, mid, off + 4);
- prevt = t;
+ nSplits = monotonizer.nbSplits;
+ mid = monotonizer.middle;
+ } else {
+ // use left instead:
+ mid = l;
+ mid[0] = cx0; mid[1] = cy0;
+ mid[2] = x1; mid[3] = y1;
+ mid[4] = x2; mid[5] = y2;
}
-
- final float[] l = lp;
final float[] r = rp;
int kind = 0;
@@ -1282,8 +1325,8 @@
}
this.prev = DRAWING_OP_TO;
- this.cx0 = xf;
- this.cy0 = yf;
+ this.cx0 = x2;
+ this.cy0 = y2;
this.cdx = dxf;
this.cdy = dyf;
this.cmx = (l[kind - 2] - r[kind - 2]) / 2.0f;
--- a/src/java.desktop/share/classes/sun/java2d/marlin/TransformingPathConsumer2D.java Fri Mar 23 13:43:39 2018 -0700
+++ b/src/java.desktop/share/classes/sun/java2d/marlin/TransformingPathConsumer2D.java Tue Mar 27 22:09:43 2018 +0200
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2007, 2017, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2007, 2018, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -28,11 +28,15 @@
import sun.awt.geom.PathConsumer2D;
import java.awt.geom.AffineTransform;
import java.awt.geom.Path2D;
+import java.util.Arrays;
import sun.java2d.marlin.Helpers.IndexStack;
import sun.java2d.marlin.Helpers.PolyStack;
final class TransformingPathConsumer2D {
+ // higher uncertainty in float variant for huge shapes > 10^7
+ static final float CLIP_RECT_PADDING = 1.0f;
+
private final RendererContext rdrCtx;
// recycled ClosedPathDetector instance from detectClosedPath()
@@ -57,6 +61,7 @@
private final PathTracer tracerCPDetector = new PathTracer("ClosedPathDetector");
private final PathTracer tracerFiller = new PathTracer("Filler");
private final PathTracer tracerStroker = new PathTracer("Stroker");
+ private final PathTracer tracerDasher = new PathTracer("Dasher");
TransformingPathConsumer2D(final RendererContext rdrCtx) {
// used by RendererContext
@@ -85,6 +90,10 @@
return tracerStroker.init(out);
}
+ PathConsumer2D traceDasher(PathConsumer2D out) {
+ return tracerDasher.init(out);
+ }
+
PathConsumer2D detectClosedPath(PathConsumer2D out) {
return cpDetector.init(out);
}
@@ -500,11 +509,19 @@
private boolean outside = false;
+ // The current point (TODO stupid repeated info)
+ private float cx0, cy0;
+
// The current point OUTSIDE
- private float cx0, cy0;
+ private float cox0, coy0;
+
+ private boolean subdivide = MarlinConst.DO_CLIP_SUBDIVIDER;
+ private final CurveClipSplitter curveSplitter;
PathClipFilter(final RendererContext rdrCtx) {
this.clipRect = rdrCtx.clipRect;
+ this.curveSplitter = rdrCtx.curveClipSplitter;
+
this.stack = (rdrCtx.stats != null) ?
new IndexStack(rdrCtx,
rdrCtx.stats.stat_pcf_idxstack_indices,
@@ -529,6 +546,11 @@
_clipRect[2] -= margin - rdrOffX;
_clipRect[3] += margin + rdrOffX;
+ if (MarlinConst.DO_CLIP_SUBDIVIDER) {
+ // adjust padded clip rectangle:
+ curveSplitter.init();
+ }
+
this.init_corners = true;
this.gOutCode = MarlinConst.OUTCODE_MASK_T_B_L_R;
@@ -579,7 +601,9 @@
}
stack.pullAll(corners, out);
}
- out.lineTo(cx0, cy0);
+ out.lineTo(cox0, coy0);
+ this.cx0 = cox0;
+ this.cy0 = coy0;
}
@Override
@@ -604,38 +628,68 @@
public void moveTo(final float x0, final float y0) {
finishPath();
- final int outcode = Helpers.outcode(x0, y0, clipRect);
- this.cOutCode = outcode;
+ this.cOutCode = Helpers.outcode(x0, y0, clipRect);
this.outside = false;
out.moveTo(x0, y0);
+ this.cx0 = x0;
+ this.cy0 = y0;
}
@Override
public void lineTo(final float xe, final float ye) {
final int outcode0 = this.cOutCode;
final int outcode1 = Helpers.outcode(xe, ye, clipRect);
- this.cOutCode = outcode1;
- final int sideCode = (outcode0 & outcode1);
+ // Should clip
+ final int orCode = (outcode0 | outcode1);
+ if (orCode != 0) {
+ final int sideCode = (outcode0 & outcode1);
- // basic rejection criteria:
- if (sideCode == 0) {
- this.gOutCode = 0;
- } else {
- this.gOutCode &= sideCode;
- // keep last point coordinate before entering the clip again:
- this.outside = true;
- this.cx0 = xe;
- this.cy0 = ye;
+ // basic rejection criteria:
+ if (sideCode == 0) {
+ // ovelap clip:
+ if (subdivide) {
+ // avoid reentrance
+ subdivide = false;
+ boolean ret;
+ // subdivide curve => callback with subdivided parts:
+ if (outside) {
+ ret = curveSplitter.splitLine(cox0, coy0, xe, ye,
+ orCode, this);
+ } else {
+ ret = curveSplitter.splitLine(cx0, cy0, xe, ye,
+ orCode, this);
+ }
+ // reentrance is done:
+ subdivide = true;
+ if (ret) {
+ return;
+ }
+ }
+ // already subdivided so render it
+ } else {
+ this.cOutCode = outcode1;
+ this.gOutCode &= sideCode;
+ // keep last point coordinate before entering the clip again:
+ this.outside = true;
+ this.cox0 = xe;
+ this.coy0 = ye;
- clip(sideCode, outcode0, outcode1);
- return;
+ clip(sideCode, outcode0, outcode1);
+ return;
+ }
}
+
+ this.cOutCode = outcode1;
+ this.gOutCode = 0;
+
if (outside) {
finish();
}
// clipping disabled:
out.lineTo(xe, ye);
+ this.cx0 = xe;
+ this.cy0 = ye;
}
private void clip(final int sideCode,
@@ -655,22 +709,18 @@
// add corners to outside stack:
switch (tbCode) {
case MarlinConst.OUTCODE_TOP:
-// System.out.println("TOP "+ ((off == 0) ? "LEFT" : "RIGHT"));
stack.push(off); // top
return;
case MarlinConst.OUTCODE_BOTTOM:
-// System.out.println("BOTTOM "+ ((off == 0) ? "LEFT" : "RIGHT"));
stack.push(off + 1); // bottom
return;
default:
// both TOP / BOTTOM:
if ((outcode0 & MarlinConst.OUTCODE_TOP) != 0) {
-// System.out.println("TOP + BOTTOM "+ ((off == 0) ? "LEFT" : "RIGHT"));
// top to bottom
stack.push(off); // top
stack.push(off + 1); // bottom
} else {
-// System.out.println("BOTTOM + TOP "+ ((off == 0) ? "LEFT" : "RIGHT"));
// bottom to top
stack.push(off + 1); // bottom
stack.push(off); // top
@@ -685,34 +735,62 @@
final float xe, final float ye)
{
final int outcode0 = this.cOutCode;
+ final int outcode1 = Helpers.outcode(x1, y1, clipRect);
+ final int outcode2 = Helpers.outcode(x2, y2, clipRect);
final int outcode3 = Helpers.outcode(xe, ye, clipRect);
- this.cOutCode = outcode3;
-
- int sideCode = outcode0 & outcode3;
- if (sideCode == 0) {
- this.gOutCode = 0;
- } else {
- sideCode &= Helpers.outcode(x1, y1, clipRect);
- sideCode &= Helpers.outcode(x2, y2, clipRect);
- this.gOutCode &= sideCode;
+ // Should clip
+ final int orCode = (outcode0 | outcode1 | outcode2 | outcode3);
+ if (orCode != 0) {
+ final int sideCode = outcode0 & outcode1 & outcode2 & outcode3;
// basic rejection criteria:
- if (sideCode != 0) {
+ if (sideCode == 0) {
+ // ovelap clip:
+ if (subdivide) {
+ // avoid reentrance
+ subdivide = false;
+ // subdivide curve => callback with subdivided parts:
+ boolean ret;
+ if (outside) {
+ ret = curveSplitter.splitCurve(cox0, coy0, x1, y1,
+ x2, y2, xe, ye,
+ orCode, this);
+ } else {
+ ret = curveSplitter.splitCurve(cx0, cy0, x1, y1,
+ x2, y2, xe, ye,
+ orCode, this);
+ }
+ // reentrance is done:
+ subdivide = true;
+ if (ret) {
+ return;
+ }
+ }
+ // already subdivided so render it
+ } else {
+ this.cOutCode = outcode3;
+ this.gOutCode &= sideCode;
// keep last point coordinate before entering the clip again:
this.outside = true;
- this.cx0 = xe;
- this.cy0 = ye;
+ this.cox0 = xe;
+ this.coy0 = ye;
clip(sideCode, outcode0, outcode3);
return;
}
}
+
+ this.cOutCode = outcode3;
+ this.gOutCode = 0;
+
if (outside) {
finish();
}
// clipping disabled:
out.curveTo(x1, y1, x2, y2, xe, ye);
+ this.cx0 = xe;
+ this.cy0 = ye;
}
@Override
@@ -720,33 +798,59 @@
final float xe, final float ye)
{
final int outcode0 = this.cOutCode;
+ final int outcode1 = Helpers.outcode(x1, y1, clipRect);
final int outcode2 = Helpers.outcode(xe, ye, clipRect);
- this.cOutCode = outcode2;
-
- int sideCode = outcode0 & outcode2;
- if (sideCode == 0) {
- this.gOutCode = 0;
- } else {
- sideCode &= Helpers.outcode(x1, y1, clipRect);
- this.gOutCode &= sideCode;
+ // Should clip
+ final int orCode = (outcode0 | outcode1 | outcode2);
+ if (orCode != 0) {
+ final int sideCode = outcode0 & outcode1 & outcode2;
// basic rejection criteria:
- if (sideCode != 0) {
+ if (sideCode == 0) {
+ // ovelap clip:
+ if (subdivide) {
+ // avoid reentrance
+ subdivide = false;
+ // subdivide curve => callback with subdivided parts:
+ boolean ret;
+ if (outside) {
+ ret = curveSplitter.splitQuad(cox0, coy0, x1, y1,
+ xe, ye, orCode, this);
+ } else {
+ ret = curveSplitter.splitQuad(cx0, cy0, x1, y1,
+ xe, ye, orCode, this);
+ }
+ // reentrance is done:
+ subdivide = true;
+ if (ret) {
+ return;
+ }
+ }
+ // already subdivided so render it
+ } else {
+ this.cOutCode = outcode2;
+ this.gOutCode &= sideCode;
// keep last point coordinate before entering the clip again:
this.outside = true;
- this.cx0 = xe;
- this.cy0 = ye;
+ this.cox0 = xe;
+ this.coy0 = ye;
clip(sideCode, outcode0, outcode2);
return;
}
}
+
+ this.cOutCode = outcode2;
+ this.gOutCode = 0;
+
if (outside) {
finish();
}
// clipping disabled:
out.quadTo(x1, y1, xe, ye);
+ this.cx0 = xe;
+ this.cy0 = ye;
}
@Override
@@ -755,6 +859,261 @@
}
}
+ static final class CurveClipSplitter {
+
+ static final float LEN_TH = MarlinProperties.getSubdividerMinLength();
+ static final boolean DO_CHECK_LENGTH = (LEN_TH > 0.0f);
+
+ private static final boolean TRACE = false;
+
+ private static final int MAX_N_CURVES = 3 * 4;
+
+ // clip rectangle (ymin, ymax, xmin, xmax):
+ final float[] clipRect;
+
+ // clip rectangle (ymin, ymax, xmin, xmax) including padding:
+ final float[] clipRectPad = new float[4];
+ private boolean init_clipRectPad = false;
+
+ // This is where the curve to be processed is put. We give it
+ // enough room to store all curves.
+ final float[] middle = new float[MAX_N_CURVES * 8 + 2];
+ // t values at subdivision points
+ private final float[] subdivTs = new float[MAX_N_CURVES];
+
+ // dirty curve
+ private final Curve curve;
+
+ CurveClipSplitter(final RendererContext rdrCtx) {
+ this.clipRect = rdrCtx.clipRect;
+ this.curve = rdrCtx.curve;
+ }
+
+ void init() {
+ this.init_clipRectPad = true;
+ }
+
+ private void initPaddedClip() {
+ // bounds as half-open intervals: minX <= x < maxX and minY <= y < maxY
+ // adjust padded clip rectangle (ymin, ymax, xmin, xmax):
+ // add a rounding error (curve subdivision ~ 0.1px):
+ final float[] _clipRect = clipRect;
+ final float[] _clipRectPad = clipRectPad;
+
+ _clipRectPad[0] = _clipRect[0] - CLIP_RECT_PADDING;
+ _clipRectPad[1] = _clipRect[1] + CLIP_RECT_PADDING;
+ _clipRectPad[2] = _clipRect[2] - CLIP_RECT_PADDING;
+ _clipRectPad[3] = _clipRect[3] + CLIP_RECT_PADDING;
+
+ if (TRACE) {
+ MarlinUtils.logInfo("clip: X [" + _clipRectPad[2] + " .. " + _clipRectPad[3] +"] "
+ + "Y ["+ _clipRectPad[0] + " .. " + _clipRectPad[1] +"]");
+ }
+ }
+
+ boolean splitLine(final float x0, final float y0,
+ final float x1, final float y1,
+ final int outCodeOR,
+ final PathConsumer2D out)
+ {
+ if (TRACE) {
+ MarlinUtils.logInfo("divLine P0(" + x0 + ", " + y0 + ") P1(" + x1 + ", " + y1 + ")");
+ }
+
+ if (DO_CHECK_LENGTH && Helpers.fastLineLen(x0, y0, x1, y1) <= LEN_TH) {
+ return false;
+ }
+
+ final float[] mid = middle;
+ mid[0] = x0; mid[1] = y0;
+ mid[2] = x1; mid[3] = y1;
+
+ return subdivideAtIntersections(4, outCodeOR, out);
+ }
+
+ boolean splitQuad(final float x0, final float y0,
+ final float x1, final float y1,
+ final float x2, final float y2,
+ final int outCodeOR,
+ final PathConsumer2D out)
+ {
+ if (TRACE) {
+ MarlinUtils.logInfo("divQuad P0(" + x0 + ", " + y0 + ") P1(" + x1 + ", " + y1 + ") P2(" + x2 + ", " + y2 + ")");
+ }
+
+ if (DO_CHECK_LENGTH && Helpers.fastQuadLen(x0, y0, x1, y1, x2, y2) <= LEN_TH) {
+ return false;
+ }
+
+ final float[] mid = middle;
+ mid[0] = x0; mid[1] = y0;
+ mid[2] = x1; mid[3] = y1;
+ mid[4] = x2; mid[5] = y2;
+
+ return subdivideAtIntersections(6, outCodeOR, out);
+ }
+
+ boolean splitCurve(final float x0, final float y0,
+ final float x1, final float y1,
+ final float x2, final float y2,
+ final float x3, final float y3,
+ final int outCodeOR,
+ final PathConsumer2D out)
+ {
+ if (TRACE) {
+ MarlinUtils.logInfo("divCurve P0(" + x0 + ", " + y0 + ") P1(" + x1 + ", " + y1 + ") P2(" + x2 + ", " + y2 + ") P3(" + x3 + ", " + y3 + ")");
+ }
+
+ if (DO_CHECK_LENGTH && Helpers.fastCurvelen(x0, y0, x1, y1, x2, y2, x3, y3) <= LEN_TH) {
+ return false;
+ }
+
+ final float[] mid = middle;
+ mid[0] = x0; mid[1] = y0;
+ mid[2] = x1; mid[3] = y1;
+ mid[4] = x2; mid[5] = y2;
+ mid[6] = x3; mid[7] = y3;
+
+ return subdivideAtIntersections(8, outCodeOR, out);
+ }
+
+ private boolean subdivideAtIntersections(final int type, final int outCodeOR,
+ final PathConsumer2D out)
+ {
+ final float[] mid = middle;
+ final float[] subTs = subdivTs;
+
+ if (init_clipRectPad) {
+ init_clipRectPad = false;
+ initPaddedClip();
+ }
+
+ final int nSplits = Helpers.findClipPoints(curve, mid, subTs, type,
+ outCodeOR, clipRectPad);
+
+ if (TRACE) {
+ MarlinUtils.logInfo("nSplits: "+ nSplits);
+ MarlinUtils.logInfo("subTs: "+Arrays.toString(Arrays.copyOfRange(subTs, 0, nSplits)));
+ }
+ if (nSplits == 0) {
+ // only curve support shortcut
+ return false;
+ }
+ float prevT = 0.0f;
+
+ for (int i = 0, off = 0; i < nSplits; i++, off += type) {
+ final float t = subTs[i];
+
+ Helpers.subdivideAt((t - prevT) / (1.0f - prevT),
+ mid, off, mid, off, type);
+ prevT = t;
+ }
+
+ for (int i = 0, off = 0; i <= nSplits; i++, off += type) {
+ if (TRACE) {
+ MarlinUtils.logInfo("Part Curve "+Arrays.toString(Arrays.copyOfRange(mid, off, off + type)));
+ }
+ emitCurrent(type, mid, off, out);
+ }
+ return true;
+ }
+
+ static void emitCurrent(final int type, final float[] pts,
+ final int off, final PathConsumer2D out)
+ {
+ // if instead of switch (perf + most probable cases first)
+ if (type == 8) {
+ out.curveTo(pts[off + 2], pts[off + 3],
+ pts[off + 4], pts[off + 5],
+ pts[off + 6], pts[off + 7]);
+ } else if (type == 4) {
+ out.lineTo(pts[off + 2], pts[off + 3]);
+ } else {
+ out.quadTo(pts[off + 2], pts[off + 3],
+ pts[off + 4], pts[off + 5]);
+ }
+ }
+ }
+
+ static final class CurveBasicMonotonizer {
+
+ private static final int MAX_N_CURVES = 11;
+
+ // squared half line width (for stroker)
+ private float lw2;
+
+ // number of splitted curves
+ int nbSplits;
+
+ // This is where the curve to be processed is put. We give it
+ // enough room to store all curves.
+ final float[] middle = new float[MAX_N_CURVES * 6 + 2];
+ // t values at subdivision points
+ private final float[] subdivTs = new float[MAX_N_CURVES - 1];
+
+ // dirty curve
+ private final Curve curve;
+
+ CurveBasicMonotonizer(final RendererContext rdrCtx) {
+ this.curve = rdrCtx.curve;
+ }
+
+ void init(final float lineWidth) {
+ this.lw2 = (lineWidth * lineWidth) / 4.0f;
+ }
+
+ CurveBasicMonotonizer curve(final float x0, final float y0,
+ final float x1, final float y1,
+ final float x2, final float y2,
+ final float x3, final float y3)
+ {
+ final float[] mid = middle;
+ mid[0] = x0; mid[1] = y0;
+ mid[2] = x1; mid[3] = y1;
+ mid[4] = x2; mid[5] = y2;
+ mid[6] = x3; mid[7] = y3;
+
+ final float[] subTs = subdivTs;
+ final int nSplits = Helpers.findSubdivPoints(curve, mid, subTs, 8, lw2);
+
+ float prevT = 0.0f;
+ for (int i = 0, off = 0; i < nSplits; i++, off += 6) {
+ final float t = subTs[i];
+
+ Helpers.subdivideCubicAt((t - prevT) / (1.0f - prevT),
+ mid, off, mid, off, off + 6);
+ prevT = t;
+ }
+
+ this.nbSplits = nSplits;
+ return this;
+ }
+
+ CurveBasicMonotonizer quad(final float x0, final float y0,
+ final float x1, final float y1,
+ final float x2, final float y2)
+ {
+ final float[] mid = middle;
+ mid[0] = x0; mid[1] = y0;
+ mid[2] = x1; mid[3] = y1;
+ mid[4] = x2; mid[5] = y2;
+
+ final float[] subTs = subdivTs;
+ final int nSplits = Helpers.findSubdivPoints(curve, mid, subTs, 6, lw2);
+
+ float prevt = 0.0f;
+ for (int i = 0, off = 0; i < nSplits; i++, off += 4) {
+ final float t = subTs[i];
+ Helpers.subdivideQuadAt((t - prevt) / (1.0f - prevt),
+ mid, off, mid, off, off + 4);
+ prevt = t;
+ }
+
+ this.nbSplits = nSplits;
+ return this;
+ }
+ }
+
static final class PathTracer implements PathConsumer2D {
private final String prefix;
private PathConsumer2D out;
@@ -808,7 +1167,7 @@
}
private void log(final String message) {
- System.out.println(prefix + message);
+ MarlinUtils.logInfo(prefix + message);
}
@Override
--- a/src/java.desktop/share/classes/sun/java2d/marlin/Version.java Fri Mar 23 13:43:39 2018 -0700
+++ b/src/java.desktop/share/classes/sun/java2d/marlin/Version.java Tue Mar 27 22:09:43 2018 +0200
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2015, 2017, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2015, 2018, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -27,7 +27,7 @@
public final class Version {
- private static final String VERSION = "marlin-0.8.2-Unsafe-OpenJDK";
+ private static final String VERSION = "marlin-0.9.1-Unsafe-OpenJDK";
public static String getVersion() {
return VERSION;
--- a/src/java.desktop/share/classes/sun/java2d/marlin/stats/Histogram.java Fri Mar 23 13:43:39 2018 -0700
+++ b/src/java.desktop/share/classes/sun/java2d/marlin/stats/Histogram.java Tue Mar 27 22:09:43 2018 +0200
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2015, 2016, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2015, 2018, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -42,7 +42,6 @@
for (int i = 2; i < MAX; i++) {
STEPS[i] = STEPS[i - 1] * BUCKET;
}
-// System.out.println("Histogram.STEPS = " + Arrays.toString(STEPS));
}
static int bucket(int val) {
--- a/src/java.desktop/share/classes/sun/java2d/marlin/stats/StatLong.java Fri Mar 23 13:43:39 2018 -0700
+++ b/src/java.desktop/share/classes/sun/java2d/marlin/stats/StatLong.java Tue Mar 27 22:09:43 2018 +0200
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2015, 2016, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -71,9 +71,7 @@
@Override
public String toString() {
- final StringBuilder sb = new StringBuilder(128);
- toString(sb);
- return sb.toString();
+ return toString(new StringBuilder(128)).toString();
}
public final StringBuilder toString(final StringBuilder sb) {
--- a/src/java.desktop/share/classes/sun/java2d/pipe/RenderingEngine.java Fri Mar 23 13:43:39 2018 -0700
+++ b/src/java.desktop/share/classes/sun/java2d/pipe/RenderingEngine.java Tue Mar 27 22:09:43 2018 +0200
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2007, 2017, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2007, 2018, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -30,7 +30,6 @@
import java.awt.geom.PathIterator;
import java.awt.geom.AffineTransform;
-import java.security.PrivilegedAction;
import java.security.AccessController;
import sun.security.action.GetPropertyAction;
--- a/test/jdk/sun/java2d/marlin/ClipShapeTest.java Fri Mar 23 13:43:39 2018 -0700
+++ b/test/jdk/sun/java2d/marlin/ClipShapeTest.java Tue Mar 27 22:09:43 2018 +0200
@@ -54,13 +54,24 @@
* @bug 8191814
* @summary Verifies that Marlin rendering generates the same
* images with and without clipping optimization with all possible
- * stroke (cap/join) and fill modes (EO rules)
+ * stroke (cap/join) and/or dashes or fill modes (EO rules)
+ * for paths made of either 9 lines, 4 quads, 2 cubics (random)
* Note: Use the argument -slow to run more intensive tests (too much time)
- * @run main/othervm/timeout=120 -Dsun.java2d.renderer=sun.java2d.marlin.MarlinRenderingEngine ClipShapeTest
- * @run main/othervm/timeout=120 -Dsun.java2d.renderer=sun.java2d.marlin.DMarlinRenderingEngine ClipShapeTest
- */
+ *
+ * @run main/othervm/timeout=120 -Dsun.java2d.renderer=sun.java2d.marlin.MarlinRenderingEngine ClipShapeTest -poly
+ * @run main/othervm/timeout=240 -Dsun.java2d.renderer=sun.java2d.marlin.MarlinRenderingEngine ClipShapeTest -poly -doDash
+ * @run main/othervm/timeout=120 -Dsun.java2d.renderer=sun.java2d.marlin.MarlinRenderingEngine ClipShapeTest -cubic
+ * @run main/othervm/timeout=240 -Dsun.java2d.renderer=sun.java2d.marlin.MarlinRenderingEngine ClipShapeTest -cubic -doDash
+ * @run main/othervm/timeout=120 -Dsun.java2d.renderer=sun.java2d.marlin.DMarlinRenderingEngine ClipShapeTest -poly
+ * @run main/othervm/timeout=240 -Dsun.java2d.renderer=sun.java2d.marlin.DMarlinRenderingEngine ClipShapeTest -poly -doDash
+ * @run main/othervm/timeout=120 -Dsun.java2d.renderer=sun.java2d.marlin.DMarlinRenderingEngine ClipShapeTest -cubic
+ * @run main/othervm/timeout=240 -Dsun.java2d.renderer=sun.java2d.marlin.DMarlinRenderingEngine ClipShapeTest -cubic -doDash
+*/
public final class ClipShapeTest {
+ static boolean TX_SCALE = false;
+ static boolean TX_SHEAR = false;
+
static final boolean TEST_STROKER = true;
static final boolean TEST_FILLER = true;
@@ -73,18 +84,23 @@
static final int TESTH = 100;
// shape settings:
- static final ShapeMode SHAPE_MODE = ShapeMode.NINE_LINE_POLYS;
+ static ShapeMode SHAPE_MODE = ShapeMode.NINE_LINE_POLYS;
+
+ static int THRESHOLD_DELTA;
+ static long THRESHOLD_NBPIX;
+
static final boolean SHAPE_REPEAT = true;
// dump path on console:
static final boolean DUMP_SHAPE = true;
- static final boolean SHOW_DETAILS = true;
+ static final boolean SHOW_DETAILS = false; // disabled
static final boolean SHOW_OUTLINE = true;
static final boolean SHOW_POINTS = true;
static final boolean SHOW_INFO = false;
static final int MAX_SHOW_FRAMES = 10;
+ static final int MAX_SAVE_FRAMES = 100;
// use fixed seed to reproduce always same polygons between tests
static final boolean FIXED_SEED = false;
@@ -109,24 +125,13 @@
static final File OUTPUT_DIR = new File(".");
- /**
- * Test
- * @param args
- */
- public static void main(String[] args) {
- boolean runSlowTests = (args.length != 0 && "-slow".equals(args[0]));
+ static final AtomicBoolean isMarlin = new AtomicBoolean();
+ static final AtomicBoolean isClipRuntime = new AtomicBoolean();
- if (runSlowTests) {
- NUM_TESTS = 20000; // or 100000 (very slow)
- USE_DASHES = true;
- USE_VAR_STROKE = true;
- }
-
+ static {
Locale.setDefault(Locale.US);
- // Get Marlin runtime state from its log:
- final AtomicBoolean isMarlin = new AtomicBoolean();
- final AtomicBoolean isClipRuntime = new AtomicBoolean();
+ // FIRST: Get Marlin runtime state from its log:
// initialize j.u.l Looger:
final Logger log = Logger.getLogger("sun.java2d.marlin");
@@ -171,6 +176,95 @@
System.setProperty("sun.java2d.renderer.clip", "false");
System.setProperty("sun.java2d.renderer.clip.runtime.enable", "true");
+ // enable subdivider:
+ System.setProperty("sun.java2d.renderer.clip.subdivider", "true");
+
+ // disable min length check: always subdivide curves at clip edges
+ System.setProperty("sun.java2d.renderer.clip.subdivider.minLength", "-1");
+
+ // If any curve, increase curve accuracy:
+ // curve length max error:
+ System.setProperty("sun.java2d.renderer.curve_len_err", "1e-4");
+
+ // quad max error:
+ System.setProperty("sun.java2d.renderer.quad_dec_d2", "5e-4");
+
+ // cubic min/max error:
+ System.setProperty("sun.java2d.renderer.cubic_dec_d2", "1e-3");
+ System.setProperty("sun.java2d.renderer.cubic_inc_d1", "1e-4"); // or disabled ~ 1e-6
+ }
+
+ /**
+ * Test
+ * @param args
+ */
+ public static void main(String[] args) {
+ boolean runSlowTests = false;
+
+ for (String arg : args) {
+ if ("-slow".equals(arg)) {
+ System.out.println("slow: enabled.");
+ runSlowTests = true;
+ } else if ("-doScale".equals(arg)) {
+ System.out.println("doScale: enabled.");
+ TX_SCALE = true;
+ } else if ("-doShear".equals(arg)) {
+ System.out.println("doShear: enabled.");
+ TX_SHEAR = true;
+ } else if ("-doDash".equals(arg)) {
+ System.out.println("doDash: enabled.");
+ USE_DASHES = true;
+ } else if ("-doVarStroke".equals(arg)) {
+ System.out.println("doVarStroke: enabled.");
+ USE_VAR_STROKE = true;
+ }
+ // shape mode:
+ else if (arg.equalsIgnoreCase("-poly")) {
+ SHAPE_MODE = ShapeMode.NINE_LINE_POLYS;
+ } else if (arg.equalsIgnoreCase("-bigpoly")) {
+ SHAPE_MODE = ShapeMode.FIFTY_LINE_POLYS;
+ } else if (arg.equalsIgnoreCase("-quad")) {
+ SHAPE_MODE = ShapeMode.FOUR_QUADS;
+ } else if (arg.equalsIgnoreCase("-cubic")) {
+ SHAPE_MODE = ShapeMode.TWO_CUBICS;
+ } else if (arg.equalsIgnoreCase("-mixed")) {
+ SHAPE_MODE = ShapeMode.MIXED;
+ }
+ }
+
+ System.out.println("Shape mode: " + SHAPE_MODE);
+
+ // adjust image comparison thresholds:
+ switch(SHAPE_MODE) {
+ case TWO_CUBICS:
+ // Define uncertainty for curves:
+ THRESHOLD_DELTA = 32; // / 256
+ THRESHOLD_NBPIX = 128; // / 10000
+ break;
+ case FOUR_QUADS:
+ case MIXED:
+ // Define uncertainty for quads:
+ // curve subdivision causes curves to be smaller
+ // then curve offsets are different (more accurate)
+ THRESHOLD_DELTA = 64; // 64 / 256
+ THRESHOLD_NBPIX = 256; // 256 / 10000
+ break;
+ default:
+ // Define uncertainty for lines:
+ // float variant have higher uncertainty
+ THRESHOLD_DELTA = 8;
+ THRESHOLD_NBPIX = 8;
+ }
+
+ System.out.println("THRESHOLD_DELTA: "+THRESHOLD_DELTA);
+ System.out.println("THRESHOLD_NBPIX: "+THRESHOLD_NBPIX);
+
+ if (runSlowTests) {
+ NUM_TESTS = 10000; // or 100000 (very slow)
+ USE_DASHES = true;
+ USE_VAR_STROKE = true;
+ }
+
System.out.println("ClipShapeTests: image = " + TESTW + " x " + TESTH);
int failures = 0;
@@ -179,14 +273,21 @@
// TODO: test affine transforms ?
if (TEST_STROKER) {
- final float[][] dashArrays = (USE_DASHES)
- ? new float[][]{null, new float[]{1f, 2f}}
+ final float[][] dashArrays = (USE_DASHES) ?
+// small
+// new float[][]{new float[]{1f, 2f}}
+// normal
+ new float[][]{new float[]{13f, 7f}}
+// large (prime)
+// new float[][]{new float[]{41f, 7f}}
+// none
: new float[][]{null};
- System.out.println("dashes: " + Arrays.toString(dashArrays));
+ System.out.println("dashes: " + Arrays.deepToString(dashArrays));
final float[] strokeWidths = (USE_VAR_STROKE)
- ? new float[5] : new float[]{8f};
+ ? new float[5] :
+ new float[]{10f};
int nsw = 0;
if (USE_VAR_STROKE) {
@@ -290,22 +391,20 @@
final double ratio = (100.0 * testCtx.histPix.count) / testCtx.histAll.count;
System.out.println("Diff ratio: " + testName + " = " + trimTo3Digits(ratio) + " %");
- if (false) {
- saveImage(diffImage, OUTPUT_DIR, testName + "-diff.png");
- }
-
- if (DUMP_SHAPE) {
- dumpShape(p2d);
- }
if (nd < MAX_SHOW_FRAMES) {
if (SHOW_DETAILS) {
paintShapeDetails(g2dOff, p2d);
paintShapeDetails(g2dOn, p2d);
}
- saveImage(imgOff, OUTPUT_DIR, testName + "-off.png");
- saveImage(imgOn, OUTPUT_DIR, testName + "-on.png");
- saveImage(diffImage, OUTPUT_DIR, testName + "-diff.png");
+ if (nd < MAX_SAVE_FRAMES) {
+ if (DUMP_SHAPE) {
+ dumpShape(p2d);
+ }
+ saveImage(imgOff, OUTPUT_DIR, testName + "-off.png");
+ saveImage(imgOn, OUTPUT_DIR, testName + "-on.png");
+ saveImage(diffImage, OUTPUT_DIR, testName + "-diff.png");
+ }
}
}
}
@@ -351,6 +450,15 @@
}
g2d.setColor(Color.GRAY);
+ // Test scale
+ if (TX_SCALE) {
+ g2d.scale(1.2, 1.2);
+ }
+ // Test shear
+ if (TX_SHEAR) {
+ g2d.shear(0.1, 0.2);
+ }
+
return g2d;
}
@@ -470,6 +578,8 @@
}
break;
case PathIterator.SEG_LINETO:
+ case PathIterator.SEG_QUADTO:
+ case PathIterator.SEG_CUBICTO:
if (SHOW_POINTS) {
g2d.setColor((nLine % 2 == 0) ? COLOR_LINETO_ODD : COLOR_LINETO_EVEN);
}
@@ -515,6 +625,12 @@
case PathIterator.SEG_LINETO:
System.out.println("p2d.lineTo(" + coords[0] + ", " + coords[1] + ");");
break;
+ case PathIterator.SEG_QUADTO:
+ System.out.println("p2d.quadTo(" + coords[0] + ", " + coords[1] + ", " + coords[2] + ", " + coords[3] + ");");
+ break;
+ case PathIterator.SEG_CUBICTO:
+ System.out.println("p2d.curveTo(" + coords[0] + ", " + coords[1] + ", " + coords[2] + ", " + coords[3] + ", " + coords[4] + ", " + coords[5] + ");");
+ break;
case PathIterator.SEG_CLOSE:
System.out.println("p2d.closePath();");
break;
@@ -580,10 +696,54 @@
@Override
public String toString() {
+ if (isStroke()) {
+ return "TestSetup{id=" + id + ", shapeMode=" + shapeMode + ", closed=" + closed
+ + ", strokeWidth=" + strokeWidth + ", strokeCap=" + getCap(strokeCap) + ", strokeJoin=" + getJoin(strokeJoin)
+ + ((dashes != null) ? ", dashes: " + Arrays.toString(dashes) : "")
+ + '}';
+ }
return "TestSetup{id=" + id + ", shapeMode=" + shapeMode + ", closed=" + closed
- + ", strokeWidth=" + strokeWidth + ", strokeCap=" + strokeCap + ", strokeJoin=" + strokeJoin
- + ((dashes != null) ? ", dashes: " + Arrays.toString(dashes) : "")
- + ", windingRule=" + windingRule + '}';
+ + ", fill"
+ + ", windingRule=" + getWindingRule(windingRule) + '}';
+ }
+
+ private static String getCap(final int cap) {
+ switch (cap) {
+ case BasicStroke.CAP_BUTT:
+ return "CAP_BUTT";
+ case BasicStroke.CAP_ROUND:
+ return "CAP_ROUND";
+ case BasicStroke.CAP_SQUARE:
+ return "CAP_SQUARE";
+ default:
+ return "";
+ }
+
+ }
+
+ private static String getJoin(final int join) {
+ switch (join) {
+ case BasicStroke.JOIN_MITER:
+ return "JOIN_MITER";
+ case BasicStroke.JOIN_ROUND:
+ return "JOIN_ROUND";
+ case BasicStroke.JOIN_BEVEL:
+ return "JOIN_BEVEL";
+ default:
+ return "";
+ }
+
+ }
+
+ private static String getWindingRule(final int rule) {
+ switch (rule) {
+ case PathIterator.WIND_EVEN_ODD:
+ return "WIND_EVEN_ODD";
+ case PathIterator.WIND_NON_ZERO:
+ return "WIND_NON_ZERO";
+ default:
+ return "";
+ }
}
}
@@ -618,16 +778,23 @@
// max difference on grayscale values:
v = (int) Math.ceil(Math.abs(dg / 3.0));
- aDifPix[i] = toInt(v, v, v);
+// TODO: count warnings
+ if (v <= THRESHOLD_DELTA) {
+ aDifPix[i] = 0;
+ } else {
+ aDifPix[i] = toInt(v, v, v);
- localCtx.add(v);
+ localCtx.add(v);
+ }
globalCtx.add(v);
}
- if (!localCtx.isDiff()) {
+ if (!localCtx.isDiff() || (localCtx.histPix.count <= THRESHOLD_NBPIX)) {
return null;
}
+ localCtx.dump();
+
return diffImage;
}