--- old/modules/javafx.graphics/src/main/java/com/sun/prism/impl/shape/MarlinPrismUtils.java	2016-11-30 22:45:22.010404728 +0100
+++ /dev/null	2016-11-30 21:27:13.355352085 +0100
@@ -1,504 +0,0 @@
-/*
- * Copyright (c) 2011, 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
- * 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 com.sun.prism.impl.shape;
-
-
-import com.sun.javafx.geom.PathConsumer2D;
-import com.sun.javafx.geom.PathIterator;
-import com.sun.javafx.geom.Path2D;
-import com.sun.javafx.geom.Rectangle;
-import com.sun.javafx.geom.Shape;
-import com.sun.javafx.geom.transform.BaseTransform;
-import com.sun.marlin.MarlinConst;
-import com.sun.marlin.MarlinRenderer;
-import com.sun.marlin.RendererContext;
-import com.sun.marlin.TransformingPathConsumer2D;
-import com.sun.prism.BasicStroke;
-
-public final class MarlinPrismUtils {
-
-    private static final boolean FORCE_NO_AA = false;
-
-    static final float UPPER_BND = Float.MAX_VALUE / 2.0f;
-    static final float LOWER_BND = -UPPER_BND;
-
-    /**
-     * Private constructor to prevent instantiation.
-     */
-    private MarlinPrismUtils() {
-    }
-
-    private static PathConsumer2D initRenderer(
-            final RendererContext rdrCtx,
-            final BasicStroke stroke,
-            final BaseTransform tx,
-            final Rectangle clip,
-            final int pirule,
-            final MarlinRenderer renderer)
-    {
-        final int oprule = (stroke == null && pirule == PathIterator.WIND_EVEN_ODD) ?
-            MarlinRenderer.WIND_EVEN_ODD : MarlinRenderer.WIND_NON_ZERO;
-
-        // We use strokerat so that in Stroker and Dasher we can work only
-        // with the pre-transformation coordinates. This will repeat a lot of
-        // computations done in the path iterator, but the alternative is to
-        // work with transformed paths and compute untransformed coordinates
-        // as needed. This would be faster but I do not think the complexity
-        // of working with both untransformed and transformed coordinates in
-        // the same code is worth it.
-        // However, if a path's width is constant after a transformation,
-        // we can skip all this untransforming.
-
-        // As pathTo() will check transformed coordinates for invalid values
-        // (NaN / Infinity) to ignore such points, it is necessary to apply the
-        // transformation before the path processing.
-        BaseTransform strokerTx = null;
-
-        int dashLen = -1;
-        boolean recycleDashes = false;
-
-        float width = 0f, dashphase = 0f;
-        float[] dashes = null;
-
-        if (stroke != null) {
-            width = stroke.getLineWidth();
-            dashes = stroke.getDashArray();
-            dashphase = stroke.getDashPhase();
-
-            if (tx != null && !tx.isIdentity()) {
-                final double a = tx.getMxx();
-                final double b = tx.getMxy();
-                final double c = tx.getMyx();
-                final double d = tx.getMyy();
-
-                // If the transform is a constant multiple of an orthogonal transformation
-                // then every length is just multiplied by a constant, so we just
-                // need to transform input paths to stroker and tell stroker
-                // the scaled width. This condition is satisfied if
-                // a*b == -c*d && a*a+c*c == b*b+d*d. In the actual check below, we
-                // leave a bit of room for error.
-                if (nearZero(a*b + c*d) && nearZero(a*a + c*c - (b*b + d*d))) {
-                    final float scale = (float) Math.sqrt(a*a + c*c);
-
-                    if (dashes != null) {
-                        recycleDashes = true;
-                        dashLen = dashes.length;
-                        dashes = rdrCtx.dasher.copyDashArray(dashes);
-                        for (int i = 0; i < dashLen; i++) {
-                            dashes[i] *= scale;
-                        }
-                        dashphase *= scale;
-                    }
-                    width *= scale;
-
-                    // by now strokerat == null. Input paths to
-                    // stroker (and maybe dasher) will have the full transform tx
-                    // applied to them and nothing will happen to the output paths.
-                } else {
-                    strokerTx = tx;
-
-                    // by now strokerat == tx. Input paths to
-                    // stroker (and maybe dasher) will have the full transform tx
-                    // applied to them, then they will be normalized, and then
-                    // the inverse of *only the non translation part of tx* will
-                    // be applied to the normalized paths. This won't cause problems
-                    // in stroker, because, suppose tx = T*A, where T is just the
-                    // translation part of tx, and A is the rest. T*A has already
-                    // been applied to Stroker/Dasher's input. Then Ainv will be
-                    // applied. Ainv*T*A is not equal to T, but it is a translation,
-                    // which means that none of stroker's assumptions about its
-                    // input will be violated. After all this, A will be applied
-                    // to stroker's output.
-                }
-            }
-        }
-
-        PathConsumer2D pc = renderer.init(clip.x, clip.y, clip.width, clip.height, oprule);
-
-        if (MarlinConst.USE_SIMPLIFIER) {
-            // Use simplifier after stroker before Renderer
-            // to remove collinear segments (notably due to cap square)
-            pc = rdrCtx.simplifier.init(pc);
-        }
-
-        final TransformingPathConsumer2D transformerPC2D = rdrCtx.transformerPC2D;
-        pc = transformerPC2D.deltaTransformConsumer(pc, strokerTx);
-
-        if (stroke != null) {
-            pc = rdrCtx.stroker.init(pc, width, stroke.getEndCap(),
-                    stroke.getLineJoin(), stroke.getMiterLimit());
-
-            if (dashes != null) {
-                if (!recycleDashes) {
-                    dashLen = dashes.length;
-                }
-                pc = rdrCtx.dasher.init(pc, dashes, dashLen, dashphase, recycleDashes);
-            }
-        }
-
-        pc = transformerPC2D.inverseDeltaTransformConsumer(pc, strokerTx);
-
-        /*
-         * Pipeline seems to be:
-         * shape.getPathIterator(tx)
-         * -> (inverseDeltaTransformConsumer)
-         * -> (Dasher)
-         * -> Stroker
-         * -> (deltaTransformConsumer)
-         *
-         * -> (CollinearSimplifier) to remove redundant segments
-         *
-         * -> pc2d = Renderer (bounding box)
-         */
-        return pc;
-    }
-
-    private static boolean nearZero(final double num) {
-        return Math.abs(num) < 2.0 * Math.ulp(num);
-    }
-
-    public static MarlinRenderer setupRenderer(
-            final RendererContext rdrCtx,
-            final Shape shape,
-            final BasicStroke stroke,
-            final BaseTransform xform,
-            final Rectangle rclip,
-            final boolean antialiasedShape)
-    {
-        // Test if transform is identity:
-        final BaseTransform tf = (xform != null && !xform.isIdentity()) ? xform : null;
-
-        final PathIterator pi = shape.getPathIterator(tf);
-
-        final MarlinRenderer r =  (!FORCE_NO_AA && antialiasedShape) ?
-                rdrCtx.renderer : rdrCtx.getRendererNoAA();
-
-        final PathConsumer2D pc2d = initRenderer(rdrCtx, stroke, tf, rclip, pi.getWindingRule(), r);
-
-        feedConsumer(rdrCtx, pi, pc2d);
-
-        return r;
-    }
-
-    public static MarlinRenderer setupRenderer(
-            final RendererContext rdrCtx,
-            final Path2D p2d,
-            final BasicStroke stroke,
-            final BaseTransform xform,
-            final Rectangle rclip,
-            final boolean antialiasedShape)
-    {
-        // Test if transform is identity:
-        final BaseTransform tf = (xform != null && !xform.isIdentity()) ? xform : null;
-
-        final MarlinRenderer r =  (!FORCE_NO_AA && antialiasedShape) ?
-                rdrCtx.renderer : rdrCtx.getRendererNoAA();
-
-        final PathConsumer2D pc2d = initRenderer(rdrCtx, stroke, tf, rclip, p2d.getWindingRule(), r);
-
-        feedConsumer(rdrCtx, p2d, tf, pc2d);
-
-        return r;
-    }
-
-    private static void feedConsumer(final RendererContext rdrCtx, final PathIterator pi,
-                                     final PathConsumer2D pc2d)
-    {
-        // mark context as DIRTY:
-        rdrCtx.dirty = true;
-
-        final float[] coords = rdrCtx.float6;
-
-        // ported from DuctusRenderingEngine.feedConsumer() but simplified:
-        // - removed skip flag = !subpathStarted
-        // - removed pathClosed (ie subpathStarted not set to false)
-        boolean subpathStarted = false;
-
-        for (; !pi.isDone(); pi.next()) {
-            switch (pi.currentSegment(coords)) {
-            case PathIterator.SEG_MOVETO:
-                /* Checking SEG_MOVETO coordinates if they are out of the
-                 * [LOWER_BND, UPPER_BND] range. This check also handles NaN
-                 * and Infinity values. Skipping next path segment in case of
-                 * invalid data.
-                 */
-                if (coords[0] < UPPER_BND && coords[0] > LOWER_BND &&
-                    coords[1] < UPPER_BND && coords[1] > LOWER_BND)
-                {
-                    pc2d.moveTo(coords[0], coords[1]);
-                    subpathStarted = true;
-                }
-                break;
-            case PathIterator.SEG_LINETO:
-                /* Checking SEG_LINETO coordinates if they are out of the
-                 * [LOWER_BND, UPPER_BND] range. This check also handles NaN
-                 * and Infinity values. Ignoring current path segment in case
-                 * of invalid data. If segment is skipped its endpoint
-                 * (if valid) is used to begin new subpath.
-                 */
-                if (coords[0] < UPPER_BND && coords[0] > LOWER_BND &&
-                    coords[1] < UPPER_BND && coords[1] > LOWER_BND)
-                {
-                    if (subpathStarted) {
-                        pc2d.lineTo(coords[0], coords[1]);
-                    } else {
-                        pc2d.moveTo(coords[0], coords[1]);
-                        subpathStarted = true;
-                    }
-                }
-                break;
-            case PathIterator.SEG_QUADTO:
-                // Quadratic curves take two points
-                /* Checking SEG_QUADTO coordinates if they are out of the
-                 * [LOWER_BND, UPPER_BND] range. This check also handles NaN
-                 * and Infinity values. Ignoring current path segment in case
-                 * of invalid endpoints's data. Equivalent to the SEG_LINETO
-                 * if endpoint coordinates are valid but there are invalid data
-                 * among other coordinates
-                 */
-                if (coords[2] < UPPER_BND && coords[2] > LOWER_BND &&
-                    coords[3] < UPPER_BND && coords[3] > LOWER_BND)
-                {
-                    if (subpathStarted) {
-                        if (coords[0] < UPPER_BND && coords[0] > LOWER_BND &&
-                            coords[1] < UPPER_BND && coords[1] > LOWER_BND)
-                        {
-                            pc2d.quadTo(coords[0], coords[1],
-                                        coords[2], coords[3]);
-                        } else {
-                            pc2d.lineTo(coords[2], coords[3]);
-                        }
-                    } else {
-                        pc2d.moveTo(coords[2], coords[3]);
-                        subpathStarted = true;
-                    }
-                }
-                break;
-            case PathIterator.SEG_CUBICTO:
-                // Cubic curves take three points
-                /* Checking SEG_CUBICTO coordinates if they are out of the
-                 * [LOWER_BND, UPPER_BND] range. This check also handles NaN
-                 * and Infinity values. Ignoring current path segment in case
-                 * of invalid endpoints's data. Equivalent to the SEG_LINETO
-                 * if endpoint coordinates are valid but there are invalid data
-                 * among other coordinates
-                 */
-                if (coords[4] < UPPER_BND && coords[4] > LOWER_BND &&
-                    coords[5] < UPPER_BND && coords[5] > LOWER_BND)
-                {
-                    if (subpathStarted) {
-                        if (coords[0] < UPPER_BND && coords[0] > LOWER_BND &&
-                            coords[1] < UPPER_BND && coords[1] > LOWER_BND &&
-                            coords[2] < UPPER_BND && coords[2] > LOWER_BND &&
-                            coords[3] < UPPER_BND && coords[3] > LOWER_BND)
-                        {
-                            pc2d.curveTo(coords[0], coords[1],
-                                         coords[2], coords[3],
-                                         coords[4], coords[5]);
-                        } else {
-                            pc2d.lineTo(coords[4], coords[5]);
-                        }
-                    } else {
-                        pc2d.moveTo(coords[4], coords[5]);
-                        subpathStarted = true;
-                    }
-                }
-                break;
-            case PathIterator.SEG_CLOSE:
-                if (subpathStarted) {
-                    pc2d.closePath();
-                    // do not set subpathStarted to false
-                    // in case of missing moveTo() after close()
-                }
-                break;
-            default:
-            }
-        }
-        pc2d.pathDone();
-
-        // mark context as CLEAN:
-        rdrCtx.dirty = false;
-    }
-
-    private static void feedConsumer(final RendererContext rdrCtx,
-                                     final Path2D p2d,
-                                     final BaseTransform xform,
-                                     final PathConsumer2D pc2d)
-    {
-        // mark context as DIRTY:
-        rdrCtx.dirty = true;
-
-        final float[] coords = rdrCtx.float6;
-
-        // ported from DuctusRenderingEngine.feedConsumer() but simplified:
-        // - removed skip flag = !subpathStarted
-        // - removed pathClosed (ie subpathStarted not set to false)
-        boolean subpathStarted = false;
-
-        final float pCoords[] = p2d.getFloatCoordsNoClone();
-        final byte pTypes[] = p2d.getCommandsNoClone();
-        final int nsegs = p2d.getNumCommands();
-
-        for (int i = 0, coff = 0; i < nsegs; i++) {
-            switch (pTypes[i]) {
-            case PathIterator.SEG_MOVETO:
-                if (xform == null) {
-                    coords[0] = pCoords[coff];
-                    coords[1] = pCoords[coff+1];
-                } else {
-                    xform.transform(pCoords, coff, coords, 0, 1);
-                }
-                coff += 2;
-                /* Checking SEG_MOVETO coordinates if they are out of the
-                 * [LOWER_BND, UPPER_BND] range. This check also handles NaN
-                 * and Infinity values. Skipping next path segment in case of
-                 * invalid data.
-                 */
-                if (coords[0] < UPPER_BND && coords[0] > LOWER_BND &&
-                    coords[1] < UPPER_BND && coords[1] > LOWER_BND)
-                {
-                    pc2d.moveTo(coords[0], coords[1]);
-                    subpathStarted = true;
-                }
-                break;
-            case PathIterator.SEG_LINETO:
-                if (xform == null) {
-                    coords[0] = pCoords[coff];
-                    coords[1] = pCoords[coff+1];
-                } else {
-                    xform.transform(pCoords, coff, coords, 0, 1);
-                }
-                coff += 2;
-                /* Checking SEG_LINETO coordinates if they are out of the
-                 * [LOWER_BND, UPPER_BND] range. This check also handles NaN
-                 * and Infinity values. Ignoring current path segment in case
-                 * of invalid data. If segment is skipped its endpoint
-                 * (if valid) is used to begin new subpath.
-                 */
-                if (coords[0] < UPPER_BND && coords[0] > LOWER_BND &&
-                    coords[1] < UPPER_BND && coords[1] > LOWER_BND)
-                {
-                    if (subpathStarted) {
-                        pc2d.lineTo(coords[0], coords[1]);
-                    } else {
-                        pc2d.moveTo(coords[0], coords[1]);
-                        subpathStarted = true;
-                    }
-                }
-                break;
-            case PathIterator.SEG_QUADTO:
-                if (xform == null) {
-                    coords[0] = pCoords[coff];
-                    coords[1] = pCoords[coff+1];
-                    coords[2] = pCoords[coff+2];
-                    coords[3] = pCoords[coff+3];
-                } else {
-                    xform.transform(pCoords, coff, coords, 0, 2);
-                }
-                coff += 4;
-                // Quadratic curves take two points
-                /* Checking SEG_QUADTO coordinates if they are out of the
-                 * [LOWER_BND, UPPER_BND] range. This check also handles NaN
-                 * and Infinity values. Ignoring current path segment in case
-                 * of invalid endpoints's data. Equivalent to the SEG_LINETO
-                 * if endpoint coordinates are valid but there are invalid data
-                 * among other coordinates
-                 */
-                if (coords[2] < UPPER_BND && coords[2] > LOWER_BND &&
-                    coords[3] < UPPER_BND && coords[3] > LOWER_BND)
-                {
-                    if (subpathStarted) {
-                        if (coords[0] < UPPER_BND && coords[0] > LOWER_BND &&
-                            coords[1] < UPPER_BND && coords[1] > LOWER_BND)
-                        {
-                            pc2d.quadTo(coords[0], coords[1],
-                                        coords[2], coords[3]);
-                        } else {
-                            pc2d.lineTo(coords[2], coords[3]);
-                        }
-                    } else {
-                        pc2d.moveTo(coords[2], coords[3]);
-                        subpathStarted = true;
-                    }
-                }
-                break;
-            case PathIterator.SEG_CUBICTO:
-                if (xform == null) {
-                    coords[0] = pCoords[coff];
-                    coords[1] = pCoords[coff+1];
-                    coords[2] = pCoords[coff+2];
-                    coords[3] = pCoords[coff+3];
-                    coords[4] = pCoords[coff+4];
-                    coords[5] = pCoords[coff+5];
-                } else {
-                    xform.transform(pCoords, coff, coords, 0, 3);
-                }
-                coff += 6;
-                // Cubic curves take three points
-                /* Checking SEG_CUBICTO coordinates if they are out of the
-                 * [LOWER_BND, UPPER_BND] range. This check also handles NaN
-                 * and Infinity values. Ignoring current path segment in case
-                 * of invalid endpoints's data. Equivalent to the SEG_LINETO
-                 * if endpoint coordinates are valid but there are invalid data
-                 * among other coordinates
-                 */
-                if (coords[4] < UPPER_BND && coords[4] > LOWER_BND &&
-                    coords[5] < UPPER_BND && coords[5] > LOWER_BND)
-                {
-                    if (subpathStarted) {
-                        if (coords[0] < UPPER_BND && coords[0] > LOWER_BND &&
-                            coords[1] < UPPER_BND && coords[1] > LOWER_BND &&
-                            coords[2] < UPPER_BND && coords[2] > LOWER_BND &&
-                            coords[3] < UPPER_BND && coords[3] > LOWER_BND)
-                        {
-                            pc2d.curveTo(coords[0], coords[1],
-                                         coords[2], coords[3],
-                                         coords[4], coords[5]);
-                        } else {
-                            pc2d.lineTo(coords[4], coords[5]);
-                        }
-                    } else {
-                        pc2d.moveTo(coords[4], coords[5]);
-                        subpathStarted = true;
-                    }
-                }
-                break;
-            case PathIterator.SEG_CLOSE:
-                if (subpathStarted) {
-                    pc2d.closePath();
-                    // do not set subpathStarted to false
-                    // in case of missing moveTo() after close()
-                }
-                break;
-            default:
-            }
-        }
-        pc2d.pathDone();
-
-        // mark context as CLEAN:
-        rdrCtx.dirty = false;
-    }
-}
--- /dev/null	2016-11-30 21:27:13.355352085 +0100
+++ new/modules/javafx.graphics/src/main/java/com/sun/prism/impl/shape/DMarlinPrismUtils.java	2016-11-30 22:45:21.718404707 +0100
@@ -0,0 +1,505 @@
+/*
+ * Copyright (c) 2011, 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
+ * 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 com.sun.prism.impl.shape;
+
+
+import com.sun.javafx.geom.PathIterator;
+import com.sun.javafx.geom.Path2D;
+import com.sun.javafx.geom.Rectangle;
+import com.sun.javafx.geom.Shape;
+import com.sun.javafx.geom.transform.BaseTransform;
+import com.sun.marlin.MarlinConst;
+import com.sun.marlin.DMarlinRenderer;
+import com.sun.marlin.DPathConsumer2D;
+import com.sun.marlin.DRendererContext;
+import com.sun.marlin.DTransformingPathConsumer2D;
+import com.sun.prism.BasicStroke;
+
+public final class DMarlinPrismUtils {
+
+    private static final boolean FORCE_NO_AA = false;
+
+    static final float UPPER_BND = Float.MAX_VALUE / 2.0f;
+    static final float LOWER_BND = -UPPER_BND;
+
+    /**
+     * Private constructor to prevent instantiation.
+     */
+    private DMarlinPrismUtils() {
+    }
+
+    private static DPathConsumer2D initRenderer(
+            final DRendererContext rdrCtx,
+            final BasicStroke stroke,
+            final BaseTransform tx,
+            final Rectangle clip,
+            final int pirule,
+            final DMarlinRenderer renderer)
+    {
+        final int oprule = (stroke == null && pirule == PathIterator.WIND_EVEN_ODD) ?
+            DMarlinRenderer.WIND_EVEN_ODD : DMarlinRenderer.WIND_NON_ZERO;
+
+        // We use strokerat so that in Stroker and Dasher we can work only
+        // with the pre-transformation coordinates. This will repeat a lot of
+        // computations done in the path iterator, but the alternative is to
+        // work with transformed paths and compute untransformed coordinates
+        // as needed. This would be faster but I do not think the complexity
+        // of working with both untransformed and transformed coordinates in
+        // the same code is worth it.
+        // However, if a path's width is constant after a transformation,
+        // we can skip all this untransforming.
+
+        // As pathTo() will check transformed coordinates for invalid values
+        // (NaN / Infinity) to ignore such points, it is necessary to apply the
+        // transformation before the path processing.
+        BaseTransform strokerTx = null;
+
+        int dashLen = -1;
+        boolean recycleDashes = false;
+
+        double width = 0f, dashphase = 0f;
+        double[] dashesD = null;
+
+        if (stroke != null) {
+            width = stroke.getLineWidth();
+            final float[] dashes = stroke.getDashArray();
+            dashphase = stroke.getDashPhase();
+            
+            // Ensure converting dashes to double precision:
+            if (dashes != null) {
+                recycleDashes = true;
+                dashLen = dashes.length;
+                dashesD = rdrCtx.dasher.copyDashArray(dashes);
+            }
+
+            if (tx != null && !tx.isIdentity()) {
+                final double a = tx.getMxx();
+                final double b = tx.getMxy();
+                final double c = tx.getMyx();
+                final double d = tx.getMyy();
+
+                // If the transform is a constant multiple of an orthogonal transformation
+                // then every length is just multiplied by a constant, so we just
+                // need to transform input paths to stroker and tell stroker
+                // the scaled width. This condition is satisfied if
+                // a*b == -c*d && a*a+c*c == b*b+d*d. In the actual check below, we
+                // leave a bit of room for error.
+                if (nearZero(a*b + c*d) && nearZero(a*a + c*c - (b*b + d*d))) {
+                    final double scale = Math.sqrt(a*a + c*c);
+
+                    if (dashesD != null) {
+                        for (int i = 0; i < dashLen; i++) {
+                            dashesD[i] *= scale;
+                        }
+                        dashphase *= scale;
+                    }
+                    width *= scale;
+
+                    // by now strokerat == null. Input paths to
+                    // stroker (and maybe dasher) will have the full transform tx
+                    // applied to them and nothing will happen to the output paths.
+                } else {
+                    strokerTx = tx;
+
+                    // by now strokerat == tx. Input paths to
+                    // stroker (and maybe dasher) will have the full transform tx
+                    // applied to them, then they will be normalized, and then
+                    // the inverse of *only the non translation part of tx* will
+                    // be applied to the normalized paths. This won't cause problems
+                    // in stroker, because, suppose tx = T*A, where T is just the
+                    // translation part of tx, and A is the rest. T*A has already
+                    // been applied to Stroker/Dasher's input. Then Ainv will be
+                    // applied. Ainv*T*A is not equal to T, but it is a translation,
+                    // which means that none of stroker's assumptions about its
+                    // input will be violated. After all this, A will be applied
+                    // to stroker's output.
+                }
+            }
+        }
+
+        DPathConsumer2D pc = renderer.init(clip.x, clip.y, clip.width, clip.height, oprule);
+
+        if (MarlinConst.USE_SIMPLIFIER) {
+            // Use simplifier after stroker before Renderer
+            // to remove collinear segments (notably due to cap square)
+            pc = rdrCtx.simplifier.init(pc);
+        }
+
+        final DTransformingPathConsumer2D transformerPC2D = rdrCtx.transformerPC2D;
+        pc = transformerPC2D.deltaTransformConsumer(pc, strokerTx);
+
+        if (stroke != null) {
+            pc = rdrCtx.stroker.init(pc, width, stroke.getEndCap(),
+                    stroke.getLineJoin(), stroke.getMiterLimit());
+
+            if (dashesD != null) {
+                pc = rdrCtx.dasher.init(pc, dashesD, dashLen, dashphase, recycleDashes);
+            }
+        }
+
+        pc = transformerPC2D.inverseDeltaTransformConsumer(pc, strokerTx);
+
+        /*
+         * Pipeline seems to be:
+         * shape.getPathIterator(tx)
+         * -> (inverseDeltaTransformConsumer)
+         * -> (Dasher)
+         * -> Stroker
+         * -> (deltaTransformConsumer)
+         *
+         * -> (CollinearSimplifier) to remove redundant segments
+         *
+         * -> pc2d = Renderer (bounding box)
+         */
+        return pc;
+    }
+
+    private static boolean nearZero(final double num) {
+        return Math.abs(num) < 2.0 * Math.ulp(num);
+    }
+
+    public static DMarlinRenderer setupRenderer(
+            final DRendererContext rdrCtx,
+            final Shape shape,
+            final BasicStroke stroke,
+            final BaseTransform xform,
+            final Rectangle rclip,
+            final boolean antialiasedShape)
+    {
+        // Test if transform is identity:
+        final BaseTransform tf = (xform != null && !xform.isIdentity()) ? xform : null;
+
+        final PathIterator pi = shape.getPathIterator(tf);
+
+        final DMarlinRenderer r =  (!FORCE_NO_AA && antialiasedShape) ?
+                rdrCtx.renderer : rdrCtx.getRendererNoAA();
+
+        final DPathConsumer2D pc2d = initRenderer(rdrCtx, stroke, tf, rclip, pi.getWindingRule(), r);
+
+        feedConsumer(rdrCtx, pi, pc2d);
+
+        return r;
+    }
+
+    public static DMarlinRenderer setupRenderer(
+            final DRendererContext rdrCtx,
+            final Path2D p2d,
+            final BasicStroke stroke,
+            final BaseTransform xform,
+            final Rectangle rclip,
+            final boolean antialiasedShape)
+    {
+        // Test if transform is identity:
+        final BaseTransform tf = (xform != null && !xform.isIdentity()) ? xform : null;
+
+        final DMarlinRenderer r =  (!FORCE_NO_AA && antialiasedShape) ?
+                rdrCtx.renderer : rdrCtx.getRendererNoAA();
+
+        final DPathConsumer2D pc2d = initRenderer(rdrCtx, stroke, tf, rclip, p2d.getWindingRule(), r);
+
+        feedConsumer(rdrCtx, p2d, tf, pc2d);
+
+        return r;
+    }
+
+    private static void feedConsumer(final DRendererContext rdrCtx, final PathIterator pi,
+                                     final DPathConsumer2D pc2d)
+    {
+        // mark context as DIRTY:
+        rdrCtx.dirty = true;
+
+        final float[] coords = rdrCtx.float6;
+
+        // ported from DuctusRenderingEngine.feedConsumer() but simplified:
+        // - removed skip flag = !subpathStarted
+        // - removed pathClosed (ie subpathStarted not set to false)
+        boolean subpathStarted = false;
+
+        for (; !pi.isDone(); pi.next()) {
+            switch (pi.currentSegment(coords)) {
+            case PathIterator.SEG_MOVETO:
+                /* Checking SEG_MOVETO coordinates if they are out of the
+                 * [LOWER_BND, UPPER_BND] range. This check also handles NaN
+                 * and Infinity values. Skipping next path segment in case of
+                 * invalid data.
+                 */
+                if (coords[0] < UPPER_BND && coords[0] > LOWER_BND &&
+                    coords[1] < UPPER_BND && coords[1] > LOWER_BND)
+                {
+                    pc2d.moveTo(coords[0], coords[1]);
+                    subpathStarted = true;
+                }
+                break;
+            case PathIterator.SEG_LINETO:
+                /* Checking SEG_LINETO coordinates if they are out of the
+                 * [LOWER_BND, UPPER_BND] range. This check also handles NaN
+                 * and Infinity values. Ignoring current path segment in case
+                 * of invalid data. If segment is skipped its endpoint
+                 * (if valid) is used to begin new subpath.
+                 */
+                if (coords[0] < UPPER_BND && coords[0] > LOWER_BND &&
+                    coords[1] < UPPER_BND && coords[1] > LOWER_BND)
+                {
+                    if (subpathStarted) {
+                        pc2d.lineTo(coords[0], coords[1]);
+                    } else {
+                        pc2d.moveTo(coords[0], coords[1]);
+                        subpathStarted = true;
+                    }
+                }
+                break;
+            case PathIterator.SEG_QUADTO:
+                // Quadratic curves take two points
+                /* Checking SEG_QUADTO coordinates if they are out of the
+                 * [LOWER_BND, UPPER_BND] range. This check also handles NaN
+                 * and Infinity values. Ignoring current path segment in case
+                 * of invalid endpoints's data. Equivalent to the SEG_LINETO
+                 * if endpoint coordinates are valid but there are invalid data
+                 * among other coordinates
+                 */
+                if (coords[2] < UPPER_BND && coords[2] > LOWER_BND &&
+                    coords[3] < UPPER_BND && coords[3] > LOWER_BND)
+                {
+                    if (subpathStarted) {
+                        if (coords[0] < UPPER_BND && coords[0] > LOWER_BND &&
+                            coords[1] < UPPER_BND && coords[1] > LOWER_BND)
+                        {
+                            pc2d.quadTo(coords[0], coords[1],
+                                        coords[2], coords[3]);
+                        } else {
+                            pc2d.lineTo(coords[2], coords[3]);
+                        }
+                    } else {
+                        pc2d.moveTo(coords[2], coords[3]);
+                        subpathStarted = true;
+                    }
+                }
+                break;
+            case PathIterator.SEG_CUBICTO:
+                // Cubic curves take three points
+                /* Checking SEG_CUBICTO coordinates if they are out of the
+                 * [LOWER_BND, UPPER_BND] range. This check also handles NaN
+                 * and Infinity values. Ignoring current path segment in case
+                 * of invalid endpoints's data. Equivalent to the SEG_LINETO
+                 * if endpoint coordinates are valid but there are invalid data
+                 * among other coordinates
+                 */
+                if (coords[4] < UPPER_BND && coords[4] > LOWER_BND &&
+                    coords[5] < UPPER_BND && coords[5] > LOWER_BND)
+                {
+                    if (subpathStarted) {
+                        if (coords[0] < UPPER_BND && coords[0] > LOWER_BND &&
+                            coords[1] < UPPER_BND && coords[1] > LOWER_BND &&
+                            coords[2] < UPPER_BND && coords[2] > LOWER_BND &&
+                            coords[3] < UPPER_BND && coords[3] > LOWER_BND)
+                        {
+                            pc2d.curveTo(coords[0], coords[1],
+                                         coords[2], coords[3],
+                                         coords[4], coords[5]);
+                        } else {
+                            pc2d.lineTo(coords[4], coords[5]);
+                        }
+                    } else {
+                        pc2d.moveTo(coords[4], coords[5]);
+                        subpathStarted = true;
+                    }
+                }
+                break;
+            case PathIterator.SEG_CLOSE:
+                if (subpathStarted) {
+                    pc2d.closePath();
+                    // do not set subpathStarted to false
+                    // in case of missing moveTo() after close()
+                }
+                break;
+            default:
+            }
+        }
+        pc2d.pathDone();
+
+        // mark context as CLEAN:
+        rdrCtx.dirty = false;
+    }
+
+    private static void feedConsumer(final DRendererContext rdrCtx,
+                                     final Path2D p2d,
+                                     final BaseTransform xform,
+                                     final DPathConsumer2D pc2d)
+    {
+        // mark context as DIRTY:
+        rdrCtx.dirty = true;
+
+        final float[] coords = rdrCtx.float6;
+
+        // ported from DuctusRenderingEngine.feedConsumer() but simplified:
+        // - removed skip flag = !subpathStarted
+        // - removed pathClosed (ie subpathStarted not set to false)
+        boolean subpathStarted = false;
+
+        final float pCoords[] = p2d.getFloatCoordsNoClone();
+        final byte pTypes[] = p2d.getCommandsNoClone();
+        final int nsegs = p2d.getNumCommands();
+
+        for (int i = 0, coff = 0; i < nsegs; i++) {
+            switch (pTypes[i]) {
+            case PathIterator.SEG_MOVETO:
+                if (xform == null) {
+                    coords[0] = pCoords[coff];
+                    coords[1] = pCoords[coff+1];
+                } else {
+                    xform.transform(pCoords, coff, coords, 0, 1);
+                }
+                coff += 2;
+                /* Checking SEG_MOVETO coordinates if they are out of the
+                 * [LOWER_BND, UPPER_BND] range. This check also handles NaN
+                 * and Infinity values. Skipping next path segment in case of
+                 * invalid data.
+                 */
+                if (coords[0] < UPPER_BND && coords[0] > LOWER_BND &&
+                    coords[1] < UPPER_BND && coords[1] > LOWER_BND)
+                {
+                    pc2d.moveTo(coords[0], coords[1]);
+                    subpathStarted = true;
+                }
+                break;
+            case PathIterator.SEG_LINETO:
+                if (xform == null) {
+                    coords[0] = pCoords[coff];
+                    coords[1] = pCoords[coff+1];
+                } else {
+                    xform.transform(pCoords, coff, coords, 0, 1);
+                }
+                coff += 2;
+                /* Checking SEG_LINETO coordinates if they are out of the
+                 * [LOWER_BND, UPPER_BND] range. This check also handles NaN
+                 * and Infinity values. Ignoring current path segment in case
+                 * of invalid data. If segment is skipped its endpoint
+                 * (if valid) is used to begin new subpath.
+                 */
+                if (coords[0] < UPPER_BND && coords[0] > LOWER_BND &&
+                    coords[1] < UPPER_BND && coords[1] > LOWER_BND)
+                {
+                    if (subpathStarted) {
+                        pc2d.lineTo(coords[0], coords[1]);
+                    } else {
+                        pc2d.moveTo(coords[0], coords[1]);
+                        subpathStarted = true;
+                    }
+                }
+                break;
+            case PathIterator.SEG_QUADTO:
+                if (xform == null) {
+                    coords[0] = pCoords[coff];
+                    coords[1] = pCoords[coff+1];
+                    coords[2] = pCoords[coff+2];
+                    coords[3] = pCoords[coff+3];
+                } else {
+                    xform.transform(pCoords, coff, coords, 0, 2);
+                }
+                coff += 4;
+                // Quadratic curves take two points
+                /* Checking SEG_QUADTO coordinates if they are out of the
+                 * [LOWER_BND, UPPER_BND] range. This check also handles NaN
+                 * and Infinity values. Ignoring current path segment in case
+                 * of invalid endpoints's data. Equivalent to the SEG_LINETO
+                 * if endpoint coordinates are valid but there are invalid data
+                 * among other coordinates
+                 */
+                if (coords[2] < UPPER_BND && coords[2] > LOWER_BND &&
+                    coords[3] < UPPER_BND && coords[3] > LOWER_BND)
+                {
+                    if (subpathStarted) {
+                        if (coords[0] < UPPER_BND && coords[0] > LOWER_BND &&
+                            coords[1] < UPPER_BND && coords[1] > LOWER_BND)
+                        {
+                            pc2d.quadTo(coords[0], coords[1],
+                                        coords[2], coords[3]);
+                        } else {
+                            pc2d.lineTo(coords[2], coords[3]);
+                        }
+                    } else {
+                        pc2d.moveTo(coords[2], coords[3]);
+                        subpathStarted = true;
+                    }
+                }
+                break;
+            case PathIterator.SEG_CUBICTO:
+                if (xform == null) {
+                    coords[0] = pCoords[coff];
+                    coords[1] = pCoords[coff+1];
+                    coords[2] = pCoords[coff+2];
+                    coords[3] = pCoords[coff+3];
+                    coords[4] = pCoords[coff+4];
+                    coords[5] = pCoords[coff+5];
+                } else {
+                    xform.transform(pCoords, coff, coords, 0, 3);
+                }
+                coff += 6;
+                // Cubic curves take three points
+                /* Checking SEG_CUBICTO coordinates if they are out of the
+                 * [LOWER_BND, UPPER_BND] range. This check also handles NaN
+                 * and Infinity values. Ignoring current path segment in case
+                 * of invalid endpoints's data. Equivalent to the SEG_LINETO
+                 * if endpoint coordinates are valid but there are invalid data
+                 * among other coordinates
+                 */
+                if (coords[4] < UPPER_BND && coords[4] > LOWER_BND &&
+                    coords[5] < UPPER_BND && coords[5] > LOWER_BND)
+                {
+                    if (subpathStarted) {
+                        if (coords[0] < UPPER_BND && coords[0] > LOWER_BND &&
+                            coords[1] < UPPER_BND && coords[1] > LOWER_BND &&
+                            coords[2] < UPPER_BND && coords[2] > LOWER_BND &&
+                            coords[3] < UPPER_BND && coords[3] > LOWER_BND)
+                        {
+                            pc2d.curveTo(coords[0], coords[1],
+                                         coords[2], coords[3],
+                                         coords[4], coords[5]);
+                        } else {
+                            pc2d.lineTo(coords[4], coords[5]);
+                        }
+                    } else {
+                        pc2d.moveTo(coords[4], coords[5]);
+                        subpathStarted = true;
+                    }
+                }
+                break;
+            case PathIterator.SEG_CLOSE:
+                if (subpathStarted) {
+                    pc2d.closePath();
+                    // do not set subpathStarted to false
+                    // in case of missing moveTo() after close()
+                }
+                break;
+            default:
+            }
+        }
+        pc2d.pathDone();
+
+        // mark context as CLEAN:
+        rdrCtx.dirty = false;
+    }
+}