< prev index next >

openjfx9/modules/javafx.graphics/src/main/java/com/sun/prism/impl/shape/MarlinPrismUtils.java

Print this page

        

*** 1,7 **** /* ! * Copyright (c) 2011, 2014, 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 --- 1,7 ---- /* ! * 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
*** 30,157 **** 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.openpisces.Dasher; ! import com.sun.openpisces.Renderer; ! import com.sun.openpisces.Stroker; ! import com.sun.openpisces.TransformingPathConsumer2D; import com.sun.prism.BasicStroke; ! public class OpenPiscesPrismUtils { ! private static final Renderer savedAARenderer = new Renderer(3, 3); ! private static final Renderer savedRenderer = new Renderer(0, 0); ! private static final Stroker savedStroker = new Stroker(savedRenderer); ! private static final Dasher savedDasher = new Dasher(savedStroker); ! ! private static TransformingPathConsumer2D.FilterSet transformer = ! new TransformingPathConsumer2D.FilterSet(); ! ! private static PathConsumer2D initRenderer(BasicStroke stroke, ! BaseTransform tx, ! Rectangle clip, ! int pirule, ! Renderer renderer) { ! int oprule = (stroke == null && pirule == PathIterator.WIND_EVEN_ODD) ? ! Renderer.WIND_EVEN_ODD : Renderer.WIND_NON_ZERO; ! renderer.reset(clip.x, clip.y, clip.width, clip.height, oprule); ! PathConsumer2D ret = transformer.getConsumer(renderer, tx); if (stroke != null) { ! savedStroker.reset(stroke.getLineWidth(), stroke.getEndCap(), ! stroke.getLineJoin(), stroke.getMiterLimit()); ! savedStroker.setConsumer(ret); ! ret = savedStroker; ! float dashes[] = stroke.getDashArray(); ! if (dashes != null) { ! savedDasher.reset(dashes, stroke.getDashPhase()); ! ret = savedDasher; } } - return ret; - } ! public static void feedConsumer(PathIterator pi, PathConsumer2D pc) { ! float[] coords = new float[6]; ! while (!pi.isDone()) { ! int type = pi.currentSegment(coords); ! switch (type) { ! case PathIterator.SEG_MOVETO: ! pc.moveTo(coords[0], coords[1]); ! break; ! case PathIterator.SEG_LINETO: ! pc.lineTo(coords[0], coords[1]); ! break; ! case PathIterator.SEG_QUADTO: ! pc.quadTo(coords[0], coords[1], ! coords[2], coords[3]); ! break; ! case PathIterator.SEG_CUBICTO: ! pc.curveTo(coords[0], coords[1], ! coords[2], coords[3], ! coords[4], coords[5]); ! break; ! case PathIterator.SEG_CLOSE: ! pc.closePath(); ! break; } - pi.next(); } ! pc.pathDone(); } ! public static Renderer setupRenderer(Shape shape, ! BasicStroke stroke, ! BaseTransform xform, ! Rectangle rclip, ! boolean antialiasedShape) { ! PathIterator pi = shape.getPathIterator(null); ! Renderer r = antialiasedShape ? savedAARenderer : savedRenderer; ! feedConsumer(pi, initRenderer(stroke, xform, rclip, pi.getWindingRule(), r)); return r; } ! public static Renderer setupRenderer(Path2D p2d, ! BasicStroke stroke, ! BaseTransform xform, ! Rectangle rclip, ! boolean antialiasedShape) { ! Renderer r = antialiasedShape ? savedAARenderer : savedRenderer; ! PathConsumer2D pc2d = initRenderer(stroke, xform, rclip, p2d.getWindingRule(), r); ! float coords[] = p2d.getFloatCoordsNoClone(); ! byte types[] = p2d.getCommandsNoClone(); ! int nsegs = p2d.getNumCommands(); ! int coff = 0; ! for (int i = 0; i < nsegs; i++) { ! switch (types[i]) { ! case PathIterator.SEG_MOVETO: ! pc2d.moveTo(coords[coff+0], coords[coff+1]); ! coff += 2; ! break; ! case PathIterator.SEG_LINETO: ! pc2d.lineTo(coords[coff+0], coords[coff+1]); ! coff += 2; ! break; ! case PathIterator.SEG_QUADTO: ! pc2d.quadTo(coords[coff+0], coords[coff+1], ! coords[coff+2], coords[coff+3]); ! coff += 4; ! break; ! case PathIterator.SEG_CUBICTO: ! pc2d.curveTo(coords[coff+0], coords[coff+1], ! coords[coff+2], coords[coff+3], ! coords[coff+4], coords[coff+5]); ! coff += 6; ! break; ! case PathIterator.SEG_CLOSE: pc2d.closePath(); ! break; } } pc2d.pathDone(); ! return r; } } --- 30,504 ---- 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; } }
< prev index next >