--- 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; + } +}