--- old/modules/javafx.graphics/src/main/java/com/sun/marlin/DRendererContext.java 2017-11-08 22:06:38.740421569 +0100 +++ new/modules/javafx.graphics/src/main/java/com/sun/marlin/DRendererContext.java 2017-11-08 22:06:38.584417884 +0100 @@ -25,10 +25,12 @@ package com.sun.marlin; +import com.sun.javafx.geom.Path2D; import java.util.concurrent.atomic.AtomicInteger; import com.sun.util.reentrant.ReentrantContext; import com.sun.javafx.geom.Rectangle; import com.sun.marlin.ArrayCacheConst.CacheStats; +import java.lang.ref.WeakReference; /** * This class is a renderer context dedicated to a single thread @@ -58,6 +60,8 @@ final DCurve curve = new DCurve(); // MarlinRenderingEngine.TransformingPathConsumer2D public final DTransformingPathConsumer2D transformerPC2D; + // recycled Path2D instance (weak) + private WeakReference refPath2D = null; // shared memory between renderer instances: final DRendererSharedMemory rdrMem; public final DRenderer renderer; @@ -150,6 +154,22 @@ } } + public Path2D getPath2D() { + // resolve reference: + Path2D p2d = (refPath2D != null) ? refPath2D.get() : null; + + // create a new Path2D ? + if (p2d == null) { + p2d = new Path2D(Path2D.WIND_NON_ZERO, INITIAL_EDGES_COUNT); // 32K + + // update weak reference: + refPath2D = new WeakReference(p2d); + } + // reset the path anyway: + p2d.reset(); + return p2d; + } + public DRendererNoAA getRendererNoAA() { if (rendererNoAA == null) { rendererNoAA = new DRendererNoAA(this); --- old/modules/javafx.graphics/src/main/java/com/sun/marlin/DTransformingPathConsumer2D.java 2017-11-08 22:06:39.076429501 +0100 +++ new/modules/javafx.graphics/src/main/java/com/sun/marlin/DTransformingPathConsumer2D.java 2017-11-08 22:06:38.920425820 +0100 @@ -25,7 +25,7 @@ package com.sun.marlin; - +import com.sun.javafx.geom.Path2D; import com.sun.javafx.geom.transform.BaseTransform; public final class DTransformingPathConsumer2D { @@ -34,10 +34,17 @@ // used by DRendererContext } + // recycled DPathConsumer2D instance from wrapPath2d() + private final Path2DWrapper wp_Path2DWrapper = new Path2DWrapper(); + // recycled DPathConsumer2D instances from deltaTransformConsumer() private final DeltaScaleFilter dt_DeltaScaleFilter = new DeltaScaleFilter(); private final DeltaTransformFilter dt_DeltaTransformFilter = new DeltaTransformFilter(); + public DPathConsumer2D wrapPath2D(Path2D p2d) { + return wp_Path2DWrapper.init(p2d); + } + public DPathConsumer2D deltaTransformConsumer(DPathConsumer2D out, BaseTransform at) { @@ -91,7 +98,6 @@ } } - static final class DeltaScaleFilter implements DPathConsumer2D { private DPathConsumer2D out; private double sx, sy; @@ -209,4 +215,47 @@ out.pathDone(); } } + + static final class Path2DWrapper implements DPathConsumer2D { + private Path2D p2d; + + Path2DWrapper() {} + + Path2DWrapper init(Path2D p2d) { + this.p2d = p2d; + return this; + } + + @Override + public void moveTo(double x0, double y0) { + p2d.moveTo((float)x0, (float)y0); + } + + @Override + public void lineTo(double x1, double y1) { + p2d.lineTo((float)x1, (float)y1); + } + + @Override + public void closePath() { + p2d.closePath(); + } + + @Override + public void pathDone() {} + + @Override + public void curveTo(double x1, double y1, + double x2, double y2, + double x3, double y3) + { + p2d.curveTo((float)x1, (float)y1, (float)x2, (float)y2, + (float)x3, (float)y3); + } + + @Override + public void quadTo(double x1, double y1, double x2, double y2) { + p2d.quadTo((float)x1, (float)y1, (float)x2, (float)y2); + } + } } --- old/modules/javafx.graphics/src/main/java/com/sun/marlin/MaskMarlinAlphaConsumer.java 2017-11-08 22:06:39.516439876 +0100 +++ new/modules/javafx.graphics/src/main/java/com/sun/marlin/MaskMarlinAlphaConsumer.java 2017-11-08 22:06:39.360436198 +0100 @@ -159,7 +159,7 @@ // System.out.println("setting row "+(pix_y - y)+ // " out of "+width+" x "+height); - final byte out[] = this.alphas; + final byte[] out = this.alphas; final int w = width; final int off = (pix_y - y) * w; @@ -223,7 +223,7 @@ // System.out.println("setting row "+(pix_y - y)+ // " out of "+width+" x "+height); - final byte out[] = this.alphas; + final byte[] out = this.alphas; final int w = width; final int off = (pix_y - y) * w; --- old/modules/javafx.graphics/src/main/java/com/sun/marlin/RendererContext.java 2017-11-08 22:06:39.952450156 +0100 +++ new/modules/javafx.graphics/src/main/java/com/sun/marlin/RendererContext.java 2017-11-08 22:06:39.800446572 +0100 @@ -25,10 +25,12 @@ package com.sun.marlin; +import com.sun.javafx.geom.Path2D; import java.util.concurrent.atomic.AtomicInteger; import com.sun.util.reentrant.ReentrantContext; import com.sun.javafx.geom.Rectangle; import com.sun.marlin.ArrayCacheConst.CacheStats; +import java.lang.ref.WeakReference; /** * This class is a renderer context dedicated to a single thread @@ -58,6 +60,8 @@ final Curve curve = new Curve(); // MarlinRenderingEngine.TransformingPathConsumer2D public final TransformingPathConsumer2D transformerPC2D; + // recycled Path2D instance (weak) + private WeakReference refPath2D = null; // shared memory between renderer instances: final RendererSharedMemory rdrMem; public final Renderer renderer; @@ -150,6 +154,22 @@ } } + public Path2D getPath2D() { + // resolve reference: + Path2D p2d = (refPath2D != null) ? refPath2D.get() : null; + + // create a new Path2D ? + if (p2d == null) { + p2d = new Path2D(Path2D.WIND_NON_ZERO, INITIAL_EDGES_COUNT); // 32K + + // update weak reference: + refPath2D = new WeakReference(p2d); + } + // reset the path anyway: + p2d.reset(); + return p2d; + } + public RendererNoAA getRendererNoAA() { if (rendererNoAA == null) { rendererNoAA = new RendererNoAA(this); --- old/modules/javafx.graphics/src/main/java/com/sun/marlin/TransformingPathConsumer2D.java 2017-11-08 22:06:40.280457878 +0100 +++ new/modules/javafx.graphics/src/main/java/com/sun/marlin/TransformingPathConsumer2D.java 2017-11-08 22:06:40.124454206 +0100 @@ -25,6 +25,7 @@ package com.sun.marlin; +import com.sun.javafx.geom.Path2D; import com.sun.javafx.geom.PathConsumer2D; import com.sun.javafx.geom.transform.BaseTransform; @@ -34,10 +35,17 @@ // used by RendererContext } + // recycled PathConsumer2D instance from wrapPath2d() + private final Path2DWrapper wp_Path2DWrapper = new Path2DWrapper(); + // recycled PathConsumer2D instances from deltaTransformConsumer() private final DeltaScaleFilter dt_DeltaScaleFilter = new DeltaScaleFilter(); private final DeltaTransformFilter dt_DeltaTransformFilter = new DeltaTransformFilter(); + public PathConsumer2D wrapPath2D(Path2D p2d) { + return wp_Path2DWrapper.init(p2d); + } + public PathConsumer2D deltaTransformConsumer(PathConsumer2D out, BaseTransform at) { @@ -91,7 +99,6 @@ } } - static final class DeltaScaleFilter implements PathConsumer2D { private PathConsumer2D out; private float sx, sy; @@ -209,4 +216,46 @@ out.pathDone(); } } + + static final class Path2DWrapper implements PathConsumer2D { + private Path2D p2d; + + Path2DWrapper() {} + + Path2DWrapper init(Path2D p2d) { + this.p2d = p2d; + return this; + } + + @Override + public void moveTo(float x0, float y0) { + p2d.moveTo(x0, y0); + } + + @Override + public void lineTo(float x1, float y1) { + p2d.lineTo(x1, y1); + } + + @Override + public void closePath() { + p2d.closePath(); + } + + @Override + public void pathDone() {} + + @Override + public void curveTo(float x1, float y1, + float x2, float y2, + float x3, float y3) + { + p2d.curveTo(x1, y1, x2, y2, x3, y3); + } + + @Override + public void quadTo(float x1, float y1, float x2, float y2) { + p2d.quadTo(x1, y1, x2, y2); + } + } } --- old/modules/javafx.graphics/src/main/java/com/sun/prism/BasicStroke.java 2017-11-08 22:06:40.604465503 +0100 +++ new/modules/javafx.graphics/src/main/java/com/sun/prism/BasicStroke.java 2017-11-08 22:06:40.452461926 +0100 @@ -28,24 +28,28 @@ import com.sun.javafx.geom.Area; import com.sun.javafx.geom.GeneralShapePair; import com.sun.javafx.geom.Path2D; -import com.sun.javafx.geom.PathConsumer2D; import com.sun.javafx.geom.PathIterator; import com.sun.javafx.geom.RoundRectangle2D; import com.sun.javafx.geom.Shape; import com.sun.javafx.geom.ShapePair; import com.sun.javafx.geom.transform.BaseTransform; -import com.sun.openpisces.Dasher; -import com.sun.openpisces.Stroker; -import com.sun.prism.impl.shape.OpenPiscesPrismUtils; +import com.sun.prism.impl.shape.ShapeUtil; public final class BasicStroke { - public static final int CAP_BUTT = Stroker.CAP_BUTT; - public static final int CAP_ROUND = Stroker.CAP_ROUND; - public static final int CAP_SQUARE = Stroker.CAP_SQUARE; - - public static final int JOIN_BEVEL = Stroker.JOIN_BEVEL; - public static final int JOIN_MITER = Stroker.JOIN_MITER; - public static final int JOIN_ROUND = Stroker.JOIN_ROUND; + + /** Constant value for end cap style. */ + public static final int CAP_BUTT = 0; + /** Constant value for end cap style. */ + public static final int CAP_ROUND = 1; + /** Constant value for end cap style. */ + public static final int CAP_SQUARE = 2; + + /** Constant value for join style. */ + public static final int JOIN_MITER = 0; + /** Constant value for join style. */ + public static final int JOIN_ROUND = 1; + /** Constant value for join style. */ + public static final int JOIN_BEVEL = 2; public static final int TYPE_CENTERED = 0; public static final int TYPE_INNER = 1; @@ -679,16 +683,8 @@ accumulate(x0 + ox, y0 + oy, x0 - ox, y0 - oy, bbox); } - public Shape createCenteredStrokedShape(Shape s) { - Path2D p2d = new Path2D(Path2D.WIND_NON_ZERO); - float lw = (type == TYPE_CENTERED) ? width : width * 2.0f; - PathConsumer2D pc2d = - new Stroker(p2d, lw, cap, join, miterLimit); - if (dash != null) { - pc2d = new Dasher(pc2d, dash, dashPhase); - } - OpenPiscesPrismUtils.feedConsumer(s.getPathIterator(null), pc2d); - return p2d; + public Shape createCenteredStrokedShape(final Shape s) { + return ShapeUtil.createCenteredStrokedShape(s, this); } static final float SQRT_2 = (float) Math.sqrt(2); --- old/modules/javafx.graphics/src/main/java/com/sun/prism/impl/shape/DMarlinPrismUtils.java 2017-11-08 22:06:40.936473317 +0100 +++ new/modules/javafx.graphics/src/main/java/com/sun/prism/impl/shape/DMarlinPrismUtils.java 2017-11-08 22:06:40.784469740 +0100 @@ -51,17 +51,17 @@ private DMarlinPrismUtils() { } - private static DPathConsumer2D initRenderer( + private static boolean nearZero(final double num) { + return Math.abs(num) < 2.0d * Math.ulp(num); + } + + private static DPathConsumer2D initPipeline( final DRendererContext rdrCtx, final BasicStroke stroke, + final float lineWidth, final BaseTransform tx, - final Rectangle clip, - final int pirule, - final DMarlinRenderer renderer) + final DPathConsumer2D out) { - 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 @@ -79,12 +79,12 @@ int dashLen = -1; boolean recycleDashes = false; - + double scale = 1.0d; double width = 0.0f, dashphase = 0.0f; double[] dashesD = null; if (stroke != null) { - width = stroke.getLineWidth(); + width = lineWidth; final float[] dashes = stroke.getDashArray(); dashphase = stroke.getDashPhase(); @@ -95,7 +95,7 @@ dashesD = rdrCtx.dasher.copyDashArray(dashes); } - if (tx != null && !tx.isIdentity()) { + if ((tx != null) && !tx.isIdentity()) { final double a = tx.getMxx(); final double b = tx.getMxy(); final double c = tx.getMyx(); @@ -108,7 +108,7 @@ // a*b == -c*d && a*a+c*c == b*b+d*d. In the actual check below, we // leave a bit of room for error. if (nearZero(a*b + c*d) && nearZero(a*a + c*c - (b*b + d*d))) { - final double scale = Math.sqrt(a*a + c*c); + scale = Math.sqrt(a*a + c*c); if (dashesD != null) { for (int i = 0; i < dashLen; i++) { @@ -140,7 +140,8 @@ } } - DPathConsumer2D pc = renderer.init(clip.x, clip.y, clip.width, clip.height, oprule); + // Prepare the pipeline: + DPathConsumer2D pc = out; if (MarlinConst.USE_SIMPLIFIER) { // Use simplifier after stroker before Renderer @@ -149,19 +150,19 @@ } final DTransformingPathConsumer2D transformerPC2D = rdrCtx.transformerPC2D; - pc = transformerPC2D.deltaTransformConsumer(pc, strokerTx); if (stroke != null) { + pc = transformerPC2D.deltaTransformConsumer(pc, strokerTx); + 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); } - pc = transformerPC2D.inverseDeltaTransformConsumer(pc, strokerTx); - /* * Pipeline seems to be: * shape.getPathIterator(tx) @@ -177,52 +178,68 @@ return pc; } - private static boolean nearZero(final double num) { - return Math.abs(num) < 2.0d * Math.ulp(num); - } - - public static DMarlinRenderer setupRenderer( + private static DPathConsumer2D initRenderer( final DRendererContext rdrCtx, - final Shape shape, final BasicStroke stroke, - final BaseTransform xform, - final Rectangle rclip, - final boolean antialiasedShape) + final BaseTransform tx, + final Rectangle clip, + final int piRule, + final DMarlinRenderer renderer) { - // Test if transform is identity: - final BaseTransform tf = (xform != null && !xform.isIdentity()) ? xform : null; + final int oprule = ((stroke == null) && (piRule == PathIterator.WIND_EVEN_ODD)) ? + DMarlinRenderer.WIND_EVEN_ODD : DMarlinRenderer.WIND_NON_ZERO; - final PathIterator pi = shape.getPathIterator(tf); + renderer.init(clip.x, clip.y, clip.width, clip.height, oprule); - final DMarlinRenderer r = (!FORCE_NO_AA && antialiasedShape) ? - rdrCtx.renderer : rdrCtx.getRendererNoAA(); - - final DPathConsumer2D pc2d = initRenderer(rdrCtx, stroke, tf, rclip, pi.getWindingRule(), r); + float lw = 0.0f; - feedConsumer(rdrCtx, pi, pc2d); + if (stroke != null) { + lw = stroke.getLineWidth(); + } - return r; + return initPipeline(rdrCtx, stroke, lw, tx, renderer); } public static DMarlinRenderer setupRenderer( final DRendererContext rdrCtx, - final Path2D p2d, + 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 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); + if (shape instanceof Path2D) { + final Path2D p2d = (Path2D)shape; + final DPathConsumer2D pc2d = initRenderer(rdrCtx, stroke, tf, rclip, p2d.getWindingRule(), r); + feedConsumer(rdrCtx, p2d, tf, pc2d); + } else { + final PathIterator pi = shape.getPathIterator(tf); + final DPathConsumer2D pc2d = initRenderer(rdrCtx, stroke, tf, rclip, pi.getWindingRule(), r); + feedConsumer(rdrCtx, pi, pc2d); + } + return r; + } - feedConsumer(rdrCtx, p2d, tf, pc2d); + public static void strokeTo( + final DRendererContext rdrCtx, + final Shape shape, + final BasicStroke stroke, + final float lineWidth, + final DPathConsumer2D out) + { + final DPathConsumer2D pc2d = initPipeline(rdrCtx, stroke, lineWidth, null, out); - return r; + if (shape instanceof Path2D) { + feedConsumer(rdrCtx, (Path2D)shape, null, pc2d); + } else { + feedConsumer(rdrCtx, shape.getPathIterator(null), pc2d); + } } private static void feedConsumer(final DRendererContext rdrCtx, final PathIterator pi, @@ -359,8 +376,8 @@ // - removed pathClosed (ie subpathStarted not set to false) boolean subpathStarted = false; - final float pCoords[] = p2d.getFloatCoordsNoClone(); - final byte pTypes[] = p2d.getCommandsNoClone(); + final float[] pCoords = p2d.getFloatCoordsNoClone(); + final byte[] pTypes = p2d.getCommandsNoClone(); final int nsegs = p2d.getNumCommands(); for (int i = 0, coff = 0; i < nsegs; i++) { --- old/modules/javafx.graphics/src/main/java/com/sun/prism/impl/shape/DMarlinRasterizer.java 2017-11-08 22:06:41.276481309 +0100 +++ new/modules/javafx.graphics/src/main/java/com/sun/prism/impl/shape/DMarlinRasterizer.java 2017-11-08 22:06:41.120477644 +0100 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2010, 2016, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2010, 2017, 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 @@ -83,14 +83,9 @@ final Rectangle rclip = rdrCtx.clip; rclip.setBounds(xformBounds); - if (shape instanceof Path2D) { - renderer = DMarlinPrismUtils.setupRenderer(rdrCtx, (Path2D) shape, stroke, xform, rclip, - antialiasedShape); - } - if (renderer == null) { - renderer = DMarlinPrismUtils.setupRenderer(rdrCtx, shape, stroke, xform, rclip, - antialiasedShape); - } + renderer = DMarlinPrismUtils.setupRenderer(rdrCtx, shape, stroke, xform, rclip, + antialiasedShape); + final int outpix_xmin = renderer.getOutpixMinX(); final int outpix_xmax = renderer.getOutpixMaxX(); final int outpix_ymin = renderer.getOutpixMinY(); @@ -111,12 +106,36 @@ } consumer.setBoundsNoClone(outpix_xmin, outpix_ymin, w, h); renderer.produceAlphas(consumer); + return consumer.getMaskData(); } finally { if (renderer != null) { renderer.dispose(); } - // recycle the RendererContext instance + // recycle the DRendererContext instance + DMarlinRenderingEngine.returnRendererContext(rdrCtx); + } + } + + static Shape createCenteredStrokedShape(Shape s, BasicStroke stroke) + { + final float lw = (stroke.getType() == BasicStroke.TYPE_CENTERED) ? + stroke.getLineWidth() : stroke.getLineWidth() * 2.0f; + + final DRendererContext rdrCtx = DMarlinRenderingEngine.getRendererContext(); + try { + // initialize a large copyable Path2D to avoid a lot of array growing: + final Path2D p2d = rdrCtx.getPath2D(); + + DMarlinPrismUtils.strokeTo(rdrCtx, s, stroke, lw, + rdrCtx.transformerPC2D.wrapPath2D(p2d) + ); + + // Use Path2D copy constructor (trim) + return new Path2D(p2d); + + } finally { + // recycle the DRendererContext instance DMarlinRenderingEngine.returnRendererContext(rdrCtx); } } --- old/modules/javafx.graphics/src/main/java/com/sun/prism/impl/shape/MarlinPrismUtils.java 2017-11-08 22:06:41.604489014 +0100 +++ new/modules/javafx.graphics/src/main/java/com/sun/prism/impl/shape/MarlinPrismUtils.java 2017-11-08 22:06:41.448485350 +0100 @@ -51,17 +51,17 @@ private MarlinPrismUtils() { } - private static PathConsumer2D initRenderer( + private static boolean nearZero(final double num) { + return Math.abs(num) < 2.0d * Math.ulp(num); + } + + private static PathConsumer2D initPipeline( final RendererContext rdrCtx, final BasicStroke stroke, + final float lineWidth, final BaseTransform tx, - final Rectangle clip, - final int pirule, - final MarlinRenderer renderer) + final PathConsumer2D out) { - 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 @@ -79,16 +79,16 @@ int dashLen = -1; boolean recycleDashes = false; - + float scale = 1.0f; float width = 0.0f, dashphase = 0.0f; float[] dashes = null; if (stroke != null) { - width = stroke.getLineWidth(); + width = lineWidth; dashes = stroke.getDashArray(); dashphase = stroke.getDashPhase(); - if (tx != null && !tx.isIdentity()) { + if ((tx != null) && !tx.isIdentity()) { final double a = tx.getMxx(); final double b = tx.getMxy(); final double c = tx.getMyx(); @@ -101,7 +101,7 @@ // a*b == -c*d && a*a+c*c == b*b+d*d. In the actual check below, we // leave a bit of room for error. if (nearZero(a*b + c*d) && nearZero(a*a + c*c - (b*b + d*d))) { - final float scale = (float) Math.sqrt(a*a + c*c); + scale = (float) Math.sqrt(a*a + c*c); if (dashes != null) { recycleDashes = true; @@ -136,7 +136,8 @@ } } - PathConsumer2D pc = renderer.init(clip.x, clip.y, clip.width, clip.height, oprule); + // Prepare the pipeline: + PathConsumer2D pc = out; if (MarlinConst.USE_SIMPLIFIER) { // Use simplifier after stroker before Renderer @@ -145,9 +146,10 @@ } final TransformingPathConsumer2D transformerPC2D = rdrCtx.transformerPC2D; - pc = transformerPC2D.deltaTransformConsumer(pc, strokerTx); if (stroke != null) { + pc = transformerPC2D.deltaTransformConsumer(pc, strokerTx); + pc = rdrCtx.stroker.init(pc, width, stroke.getEndCap(), stroke.getLineJoin(), stroke.getMiterLimit()); @@ -157,10 +159,9 @@ } pc = rdrCtx.dasher.init(pc, dashes, dashLen, dashphase, recycleDashes); } + pc = transformerPC2D.inverseDeltaTransformConsumer(pc, strokerTx); } - pc = transformerPC2D.inverseDeltaTransformConsumer(pc, strokerTx); - /* * Pipeline seems to be: * shape.getPathIterator(tx) @@ -176,52 +177,68 @@ return pc; } - private static boolean nearZero(final double num) { - return Math.abs(num) < 2.0d * Math.ulp(num); - } - - public static MarlinRenderer setupRenderer( + private static PathConsumer2D initRenderer( final RendererContext rdrCtx, - final Shape shape, final BasicStroke stroke, - final BaseTransform xform, - final Rectangle rclip, - final boolean antialiasedShape) + final BaseTransform tx, + final Rectangle clip, + final int piRule, + final MarlinRenderer renderer) { - // Test if transform is identity: - final BaseTransform tf = (xform != null && !xform.isIdentity()) ? xform : null; + final int oprule = ((stroke == null) && (piRule == PathIterator.WIND_EVEN_ODD)) ? + MarlinRenderer.WIND_EVEN_ODD : MarlinRenderer.WIND_NON_ZERO; - final PathIterator pi = shape.getPathIterator(tf); + renderer.init(clip.x, clip.y, clip.width, clip.height, oprule); - final MarlinRenderer r = (!FORCE_NO_AA && antialiasedShape) ? - rdrCtx.renderer : rdrCtx.getRendererNoAA(); - - final PathConsumer2D pc2d = initRenderer(rdrCtx, stroke, tf, rclip, pi.getWindingRule(), r); + float lw = 0.0f; - feedConsumer(rdrCtx, pi, pc2d); + if (stroke != null) { + lw = stroke.getLineWidth(); + } - return r; + return initPipeline(rdrCtx, stroke, lw, tx, renderer); } public static MarlinRenderer setupRenderer( final RendererContext rdrCtx, - final Path2D p2d, + 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 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); + if (shape instanceof Path2D) { + final Path2D p2d = (Path2D)shape; + final PathConsumer2D pc2d = initRenderer(rdrCtx, stroke, tf, rclip, p2d.getWindingRule(), r); + feedConsumer(rdrCtx, p2d, tf, pc2d); + } else { + final PathIterator pi = shape.getPathIterator(tf); + final PathConsumer2D pc2d = initRenderer(rdrCtx, stroke, tf, rclip, pi.getWindingRule(), r); + feedConsumer(rdrCtx, pi, pc2d); + } + return r; + } - feedConsumer(rdrCtx, p2d, tf, pc2d); + public static void strokeTo( + final RendererContext rdrCtx, + final Shape shape, + final BasicStroke stroke, + final float lineWidth, + final PathConsumer2D out) + { + final PathConsumer2D pc2d = initPipeline(rdrCtx, stroke, lineWidth, null, out); - return r; + if (shape instanceof Path2D) { + feedConsumer(rdrCtx, (Path2D)shape, null, pc2d); + } else { + feedConsumer(rdrCtx, shape.getPathIterator(null), pc2d); + } } private static void feedConsumer(final RendererContext rdrCtx, final PathIterator pi, @@ -358,8 +375,8 @@ // - removed pathClosed (ie subpathStarted not set to false) boolean subpathStarted = false; - final float pCoords[] = p2d.getFloatCoordsNoClone(); - final byte pTypes[] = p2d.getCommandsNoClone(); + final float[] pCoords = p2d.getFloatCoordsNoClone(); + final byte[] pTypes = p2d.getCommandsNoClone(); final int nsegs = p2d.getNumCommands(); for (int i = 0, coff = 0; i < nsegs; i++) { --- old/modules/javafx.graphics/src/main/java/com/sun/prism/impl/shape/MarlinRasterizer.java 2017-11-08 22:06:41.936496814 +0100 +++ new/modules/javafx.graphics/src/main/java/com/sun/prism/impl/shape/MarlinRasterizer.java 2017-11-08 22:06:41.780493149 +0100 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2010, 2016, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2010, 2017, 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 @@ -83,14 +83,9 @@ final Rectangle rclip = rdrCtx.clip; rclip.setBounds(xformBounds); - if (shape instanceof Path2D) { - renderer = MarlinPrismUtils.setupRenderer(rdrCtx, (Path2D) shape, stroke, xform, rclip, - antialiasedShape); - } - if (renderer == null) { - renderer = MarlinPrismUtils.setupRenderer(rdrCtx, shape, stroke, xform, rclip, - antialiasedShape); - } + renderer = MarlinPrismUtils.setupRenderer(rdrCtx, shape, stroke, xform, rclip, + antialiasedShape); + final int outpix_xmin = renderer.getOutpixMinX(); final int outpix_xmax = renderer.getOutpixMaxX(); final int outpix_ymin = renderer.getOutpixMinY(); @@ -111,6 +106,7 @@ } consumer.setBoundsNoClone(outpix_xmin, outpix_ymin, w, h); renderer.produceAlphas(consumer); + return consumer.getMaskData(); } finally { if (renderer != null) { @@ -119,5 +115,28 @@ // recycle the RendererContext instance MarlinRenderingEngine.returnRendererContext(rdrCtx); } + } + + static Shape createCenteredStrokedShape(Shape s, BasicStroke stroke) + { + final float lw = (stroke.getType() == BasicStroke.TYPE_CENTERED) ? + stroke.getLineWidth() : stroke.getLineWidth() * 2.0f; + + final RendererContext rdrCtx = MarlinRenderingEngine.getRendererContext(); + try { + // initialize a large copyable Path2D to avoid a lot of array growing: + final Path2D p2d = rdrCtx.getPath2D(); + + MarlinPrismUtils.strokeTo(rdrCtx, s, stroke, lw, + rdrCtx.transformerPC2D.wrapPath2D(p2d) + ); + + // Use Path2D copy constructor (trim) + return new Path2D(p2d); + + } finally { + // recycle the RendererContext instance + MarlinRenderingEngine.returnRendererContext(rdrCtx); + } } } --- old/modules/javafx.graphics/src/main/java/com/sun/prism/impl/shape/ShapeUtil.java 2017-11-08 22:06:42.260504416 +0100 +++ new/modules/javafx.graphics/src/main/java/com/sun/prism/impl/shape/ShapeUtil.java 2017-11-08 22:06:42.108500851 +0100 @@ -25,11 +25,14 @@ package com.sun.prism.impl.shape; +import com.sun.javafx.geom.Path2D; +import com.sun.javafx.geom.PathConsumer2D; import com.sun.javafx.geom.RectBounds; import com.sun.javafx.geom.Shape; import com.sun.javafx.geom.transform.BaseTransform; import com.sun.prism.BasicStroke; import com.sun.prism.impl.PrismSettings; +import com.sun.prism.impl.PrismSettings.RasterizerType; public class ShapeUtil { @@ -61,6 +64,40 @@ return shapeRasterizer.getMaskData(shape, stroke, xformBounds, xform, close, antialiasedShape); } + public static Shape createCenteredStrokedShape(Shape s, BasicStroke stroke) + { + if (PrismSettings.rasterizerSpec == RasterizerType.DoubleMarlin) { + return DMarlinRasterizer.createCenteredStrokedShape(s, stroke); + } + if (PrismSettings.rasterizerSpec == RasterizerType.FloatMarlin) { + return MarlinRasterizer.createCenteredStrokedShape(s, stroke); + } + // JavaPisces fallback: + return createCenteredStrokedShapeOpenPisces(s, stroke); + } + + private static Shape createCenteredStrokedShapeOpenPisces(Shape s, BasicStroke stroke) + { + final float lw = (stroke.getType() == BasicStroke.TYPE_CENTERED) ? + stroke.getLineWidth() : stroke.getLineWidth() * 2.0f; + + final Path2D p2d = new Path2D(Path2D.WIND_NON_ZERO); + + PathConsumer2D pc2d = + new com.sun.openpisces.Stroker(p2d, lw, stroke.getEndCap(), + stroke.getLineJoin(), + stroke.getMiterLimit()); + + if (stroke.isDashed()) { + pc2d = new com.sun.openpisces.Dasher(pc2d, stroke.getDashArray(), + stroke.getDashPhase()); + } + com.sun.prism.impl.shape.OpenPiscesPrismUtils.feedConsumer( + s.getPathIterator(null), pc2d); + + return p2d; + } + /** * Private constructor to prevent instantiation. */