changeset 49503:1ea202af7a97

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
author lbourges
date Tue, 27 Mar 2018 22:09:43 +0200
parents f46bfa7a2956
children 1f87bf473c9a
files src/java.desktop/share/classes/sun/java2d/marlin/ByteArrayCache.java src/java.desktop/share/classes/sun/java2d/marlin/Curve.java src/java.desktop/share/classes/sun/java2d/marlin/DCurve.java src/java.desktop/share/classes/sun/java2d/marlin/DDasher.java src/java.desktop/share/classes/sun/java2d/marlin/DHelpers.java src/java.desktop/share/classes/sun/java2d/marlin/DMarlinRenderingEngine.java src/java.desktop/share/classes/sun/java2d/marlin/DPathSimplifier.java src/java.desktop/share/classes/sun/java2d/marlin/DRenderer.java src/java.desktop/share/classes/sun/java2d/marlin/DRendererContext.java src/java.desktop/share/classes/sun/java2d/marlin/DStroker.java src/java.desktop/share/classes/sun/java2d/marlin/DTransformingPathConsumer2D.java src/java.desktop/share/classes/sun/java2d/marlin/Dasher.java src/java.desktop/share/classes/sun/java2d/marlin/DoubleArrayCache.java src/java.desktop/share/classes/sun/java2d/marlin/FloatArrayCache.java src/java.desktop/share/classes/sun/java2d/marlin/Helpers.java src/java.desktop/share/classes/sun/java2d/marlin/IntArrayCache.java src/java.desktop/share/classes/sun/java2d/marlin/MarlinCache.java src/java.desktop/share/classes/sun/java2d/marlin/MarlinConst.java src/java.desktop/share/classes/sun/java2d/marlin/MarlinProperties.java src/java.desktop/share/classes/sun/java2d/marlin/MarlinRenderingEngine.java src/java.desktop/share/classes/sun/java2d/marlin/MarlinTileGenerator.java src/java.desktop/share/classes/sun/java2d/marlin/MarlinUtils.java src/java.desktop/share/classes/sun/java2d/marlin/MergeSort.java src/java.desktop/share/classes/sun/java2d/marlin/OffHeapArray.java src/java.desktop/share/classes/sun/java2d/marlin/PathSimplifier.java src/java.desktop/share/classes/sun/java2d/marlin/Renderer.java src/java.desktop/share/classes/sun/java2d/marlin/RendererContext.java src/java.desktop/share/classes/sun/java2d/marlin/RendererStats.java src/java.desktop/share/classes/sun/java2d/marlin/Stroker.java src/java.desktop/share/classes/sun/java2d/marlin/TransformingPathConsumer2D.java src/java.desktop/share/classes/sun/java2d/marlin/Version.java src/java.desktop/share/classes/sun/java2d/marlin/stats/Histogram.java src/java.desktop/share/classes/sun/java2d/marlin/stats/StatLong.java src/java.desktop/share/classes/sun/java2d/pipe/RenderingEngine.java test/jdk/sun/java2d/marlin/ClipShapeTest.java
diffstat 35 files changed, 4077 insertions(+), 1664 deletions(-) [+]
line wrap: on
line diff
--- 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;
 
-        double len = dx*dx + dy*dy;
+        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;
+
+                // 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;
     }
 
-    // shared instance in DDasher
-    private final LengthIterator li = new LengthIterator();
+    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;
+    }
+
+    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 void subdivide(double[] src, int srcoff, double[] left, int leftoff,
-                          double[] right, int rightoff, int type)
+    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;
+    }
+
+    // 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 8:
+            subdivideCubic(src, left, right);
+            return;
         case 6:
-            DHelpers.subdivideQuad(src, srcoff, left, leftoff, right, rightoff);
-            return;
-        case 8:
-            DHelpers.subdivideCubic(src, srcoff, left, leftoff, right, rightoff);
+            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];
 
-    static void subdivideCubicAt(double t, double[] src, int srcoff,
-                                 double[] left, int leftoff,
-                                 double[] right, int rightoff)
-    {
-        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;
-        }
+        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 subdivideQuad(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 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[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 subdivideQuadAt(double t, 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 + 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 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 subdivideAt(double t, double[] src, int srcoff,
-                            double[] left, int leftoff,
-                            double[] right, int rightoff, int size)
+    static void subdivideLineAt(final double t,
+                                final double[] src, final int offS,
+                                final double[] pts, final int offL, final int offR)
     {
-        switch(size) {
-        case 8:
-            subdivideCubicAt(t, src, srcoff, left, leftoff, right, rightoff);
-            return;
-        case 6:
-            subdivideQuadAt(t, src, srcoff, left, leftoff, right, rightoff);
-            return;
+        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(final double t,
+                            final double[] src, final int offS,
+                            final double[] pts, final int offL, final int type)
+    {
+        // 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;
             }
 
+            x1 += dx;
+            y1 += dy;
+            dx += ddx;
+            dy += ddy;
+            ddx += dddx;
+            ddy += dddy;
+
             addLine(x0, y0, x1, y1);
-
-            if (DO_STATS) { nL++; }
             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);
+
+            // Should clip
+            final int orCode = (outcode0 | outcode1);
+            if (orCode != 0) {
+                final int sideCode = outcode0 & outcode1;
+
+                // 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;
-
-            // basic rejection criteria
-            if ((outcode0 & outcode1) != 0) {
-                moveTo(x1, y1, outcode0);
-                opened = true;
-                return;
-            }
         }
 
         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;
 
-            if ((outcode0 & outcode3) != 0) {
-                final int outcode1 = DHelpers.outcode(x1, y1, clipRect);
-                final int outcode2 = DHelpers.outcode(x2, y2, clipRect);
+            // Should clip
+            final int orCode = (outcode0 | outcode1 | outcode2 | outcode3);
+            if (orCode != 0) {
+                final int sideCode = outcode0 & outcode1 & outcode2 & outcode3;
 
-                // 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;
         }
+        _curveTo(x1, y1, x2, y2, x3, y3, outcode0);
+    }
 
-        final double[] mid = middle;
+    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
+        double dxs = x1 - cx0;
+        double dys = y1 - cy0;
+        double dxf = x3 - x2;
+        double dyf = y3 - y2;
 
-        mid[0] = cx0; mid[1] = cy0;
-        mid[2] = x1;  mid[3] = y1;
-        mid[4] = x2;  mid[5] = y2;
-        mid[6] = x3;  mid[7] = y3;
-
-        // 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];
-
-        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;
 
-        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;
+        if (monotonize) {
+            // monotonize curve:
+            final CurveBasicMonotonizer monotonizer
+                = rdrCtx.monotonizer.curve(cx0, cy0, x1, y1, x2, y2, x3, y3);
+
+            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;
 
-            if ((outcode0 & outcode2) != 0) {
-                final int outcode1 = DHelpers.outcode(x1, y1, clipRect);
+            // Should clip
+            final int orCode = (outcode0 | outcode1 | outcode2);
+            if (orCode != 0) {
+                final int sideCode = outcode0 & outcode1 & outcode2;
 
-                // 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;
                 }
             }
+
+            this.cOutCode = outcode2;
         }
+        _quadTo(x1, y1, x2, y2, outcode0);
+    }
 
-        final double[] mid = middle;
+    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
+        double dxs = x1 - cx0;
+        double dys = y1 - cy0;
+        double dxf = x2 - x1;
+        double dyf = y2 - y1;
 
-        mid[0] = cx0; mid[1] = cy0;
-        mid[2] = x1;  mid[3] = y1;
-        mid[4] = x2;  mid[5] = y2;
-
-        // 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];
+        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;
 
-        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;
+        if (monotonize) {
+            // monotonize quad:
+            final CurveBasicMonotonizer monotonizer
+                = rdrCtx.monotonizer.quad(cx0, cy0, x1, y1, x2, y2);
+
+            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);
+
+            // Should clip
+            final int orCode = (outcode0 | outcode1);
+            if (orCode != 0) {
+                final int sideCode = (outcode0 & outcode1);
+
+                // 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;
+                }
+            }
+
             this.cOutCode = outcode1;
+            this.gOutCode = 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;
-
-                clip(sideCode, outcode0, outcode1);
-                return;
-            }
             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;
 
-        float len = dx*dx + dy*dy;
+        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;
+
+                // 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;
     }
 
-    // shared instance in Dasher
-    private final LengthIterator li = new LengthIterator();
+    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;
+    }
+
+    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 void subdivide(float[] src, int srcoff, float[] left, int leftoff,
-                          float[] right, int rightoff, int type)
+    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;
+    }
+
+    // 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 8:
+            subdivideCubic(src, left, right);
+            return;
         case 6:
-            Helpers.subdivideQuad(src, srcoff, left, leftoff, right, rightoff);
-            return;
-        case 8:
-            Helpers.subdivideCubic(src, srcoff, left, leftoff, right, rightoff);
+            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];
 
-    static void subdivideCubicAt(float t, float[] src, int srcoff,
-                                 float[] left, int leftoff,
-                                 float[] right, int rightoff)
-    {
-        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;
-        }
+        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 subdivideQuad(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 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[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 subdivideQuadAt(float t, 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 + 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 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 subdivideAt(float t, float[] src, int srcoff,
-                            float[] left, int leftoff,
-                            float[] right, int rightoff, int size)
+    static void subdivideLineAt(final float t,
+                                final float[] src, final int offS,
+                                final float[] pts, final int offL, final int offR)
     {
-        switch(size) {
-        case 8:
-            subdivideCubicAt(t, src, srcoff, left, leftoff, right, rightoff);
-            return;
-        case 6:
-            subdivideQuadAt(t, src, srcoff, left, leftoff, right, rightoff);
-            return;
+        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(final float t,
+                            final float[] src, final int offS,
+                            final float[] pts, final int offL, final int type)
+    {
+        // 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;
             }
 
+            x1 += dx;
+            y1 += dy;
+            dx += ddx;
+            dy += ddy;
+            ddx += dddx;
+            ddy += dddy;
+
             addLine(x0, y0, x1, y1);
-
-            if (DO_STATS) { nL++; }
             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);
+
+            // Should clip
+            final int orCode = (outcode0 | outcode1);
+            if (orCode != 0) {
+                final int sideCode = outcode0 & outcode1;
+
+                // 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;
-
-            // basic rejection criteria
-            if ((outcode0 & outcode1) != 0) {
-                moveTo(x1, y1, outcode0);
-                opened = true;
-                return;
-            }
         }
 
         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;
 
-            if ((outcode0 & outcode3) != 0) {
-                final int outcode1 = Helpers.outcode(x1, y1, clipRect);
-                final int outcode2 = Helpers.outcode(x2, y2, clipRect);
+            // Should clip
+            final int orCode = (outcode0 | outcode1 | outcode2 | outcode3);
+            if (orCode != 0) {
+                final int sideCode = outcode0 & outcode1 & outcode2 & outcode3;
 
-                // 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;
         }
+        _curveTo(x1, y1, x2, y2, x3, y3, outcode0);
+    }
 
-        final float[] mid = middle;
+    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
+        float dxs = x1 - cx0;
+        float dys = y1 - cy0;
+        float dxf = x3 - x2;
+        float dyf = y3 - y2;
 
-        mid[0] = cx0; mid[1] = cy0;
-        mid[2] = x1;  mid[3] = y1;
-        mid[4] = x2;  mid[5] = y2;
-        mid[6] = x3;  mid[7] = y3;
-
-        // 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];
-
-        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;
 
-        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;
+        if (monotonize) {
+            // monotonize curve:
+            final CurveBasicMonotonizer monotonizer
+                = rdrCtx.monotonizer.curve(cx0, cy0, x1, y1, x2, y2, x3, y3);
+
+            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;
 
-            if ((outcode0 & outcode2) != 0) {
-                final int outcode1 = Helpers.outcode(x1, y1, clipRect);
+            // Should clip
+            final int orCode = (outcode0 | outcode1 | outcode2);
+            if (orCode != 0) {
+                final int sideCode = outcode0 & outcode1 & outcode2;
 
-                // 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;
                 }
             }
+
+            this.cOutCode = outcode2;
         }
+        _quadTo(x1, y1, x2, y2, outcode0);
+    }
 
-        final float[] mid = middle;
+    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
+        float dxs = x1 - cx0;
+        float dys = y1 - cy0;
+        float dxf = x2 - x1;
+        float dyf = y2 - y1;
 
-        mid[0] = cx0; mid[1] = cy0;
-        mid[2] = x1;  mid[3] = y1;
-        mid[4] = x2;  mid[5] = y2;
-
-        // 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];
+        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;
 
-        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;
+        if (monotonize) {
+            // monotonize quad:
+            final CurveBasicMonotonizer monotonizer
+                = rdrCtx.monotonizer.quad(cx0, cy0, x1, y1, x2, y2);
+
+            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);
+
+            // Should clip
+            final int orCode = (outcode0 | outcode1);
+            if (orCode != 0) {
+                final int sideCode = (outcode0 & outcode1);
+
+                // 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;
+                }
+            }
+
             this.cOutCode = outcode1;
+            this.gOutCode = 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;
-
-                clip(sideCode, outcode0, outcode1);
-                return;
-            }
             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;
     }