< prev index next >

src/java.desktop/share/classes/sun/java2d/marlin/MarlinRenderingEngine.java

Print this page

        

@@ -49,10 +49,13 @@
 {
     private static enum NormMode {ON_WITH_AA, ON_NO_AA, OFF}
 
     private static final float MIN_PEN_SIZE = 1f / NORM_SUBPIXELS;
 
+    static final float UPPER_BND = Float.MAX_VALUE / 2.0f;
+    static final float LOWER_BND = -UPPER_BND;
+
     /**
      * Public constructor
      */
     public MarlinRenderingEngine() {
         super();

@@ -277,29 +280,25 @@
                         float miterlimit,
                         float dashes[],
                         float dashphase,
                         PathConsumer2D pc2d)
     {
-        // We use strokerat and outat so that in Stroker and Dasher we can work only
+        // 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.
 
-        // If normalization is off we save some transformations by not
-        // transforming the input to pisces. Instead, we apply the
-        // transformation after the path processing has been done.
-        // We can't do this if normalization is on, because it isn't a good
-        // idea to normalize before the transformation is applied.
+        // 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.
         AffineTransform strokerat = null;
-        AffineTransform outat = null;
 
-        PathIterator pi;
         int dashLen = -1;
         boolean recycleDashes = false;
 
         if (at != null && !at.isIdentity()) {
             final double a = at.getScaleX();

@@ -331,10 +330,11 @@
             // 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;
                     final float[] newDashes;
                     if (dashLen <= INITIAL_ARRAY) {

@@ -347,66 +347,48 @@
                         newDashes = rdrCtx.getDirtyFloatArray(dashLen);
                     }
                     System.arraycopy(dashes, 0, newDashes, 0, dashLen);
                     dashes = newDashes;
                     for (int i = 0; i < dashLen; i++) {
-                        dashes[i] = scale * dashes[i];
+                        dashes[i] *= scale;
                     }
-                    dashphase = scale * dashphase;
+                    dashphase *= scale;
                 }
-                width = scale * width;
-                pi = getNormalizingPathIterator(rdrCtx, normalize,
-                                                src.getPathIterator(at));
+                width *= scale;
 
-                // by now strokerat == null && outat == null. Input paths to
+                // by now strokerat == null. Input paths to
                 // stroker (and maybe dasher) will have the full transform at
                 // applied to them and nothing will happen to the output paths.
             } else {
-                if (normalize != NormMode.OFF) {
-                    strokerat = at;
-                    pi = getNormalizingPathIterator(rdrCtx, normalize,
-                                                    src.getPathIterator(at));
-
-                    // by now strokerat == at && outat == null. Input paths to
-                    // stroker (and maybe dasher) will have the full transform at
-                    // applied to them, then they will be normalized, and then
-                    // the inverse of *only the non translation part of at* will
-                    // be applied to the normalized paths. This won't cause problems
-                    // in stroker, because, suppose at = T*A, where T is just the
-                    // translation part of at, 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.
-                } else {
-                    outat = at;
-                    pi = src.getPathIterator(null);
-                    // outat == at && strokerat == null. This is because if no
-                    // normalization is done, we can just apply all our
-                    // transformations to stroker's output.
-                }
+                strokerat = at;
+
+                // by now strokerat == at. Input paths to
+                // stroker (and maybe dasher) will have the full transform at
+                // applied to them, then they will be normalized, and then
+                // the inverse of *only the non translation part of at* will
+                // be applied to the normalized paths. This won't cause problems
+                // in stroker, because, suppose at = T*A, where T is just the
+                // translation part of at, 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.
             }
         } else {
             // either at is null or it's the identity. In either case
             // we don't transform the path.
-            pi = getNormalizingPathIterator(rdrCtx, normalize,
-                                            src.getPathIterator(null));
+            at = null;
         }
 
         if (useSimplifier) {
             // Use simplifier after stroker before Renderer
             // to remove collinear segments (notably due to cap square)
             pc2d = rdrCtx.simplifier.init(pc2d);
         }
 
-        // by now, at least one of outat and strokerat will be null. Unless at is not
-        // a constant multiple of an orthogonal transformation, they will both be
-        // null. In other cases, outat == at if normalization is off, and if
-        // normalization is on, strokerat == at.
         final TransformingPathConsumer2D transformerPC2D = rdrCtx.transformerPC2D;
-        pc2d = transformerPC2D.transformConsumer(pc2d, outat);
         pc2d = transformerPC2D.deltaTransformConsumer(pc2d, strokerat);
 
         pc2d = rdrCtx.stroker.init(pc2d, width, caps, join, miterlimit);
 
         if (dashes != null) {

@@ -415,22 +397,26 @@
             }
             pc2d = rdrCtx.dasher.init(pc2d, dashes, dashLen, dashphase,
                                       recycleDashes);
         }
         pc2d = transformerPC2D.inverseDeltaTransformConsumer(pc2d, strokerat);
+
+        final PathIterator pi = getNormalizingPathIterator(rdrCtx, normalize,
+                                    src.getPathIterator(at));
+
         pathTo(rdrCtx, pi, pc2d);
 
         /*
          * Pipeline seems to be:
-         *    shape.getPathIterator
-         * -> NormalizingPathIterator
-         * -> inverseDeltaTransformConsumer
-         * -> Dasher
+         * shape.getPathIterator(at)
+         * -> (NormalizingPathIterator)
+         * -> (inverseDeltaTransformConsumer)
+         * -> (Dasher)
          * -> Stroker
-         * -> deltaTransformConsumer OR transformConsumer
+         * -> (deltaTransformConsumer)
          *
-         * -> CollinearSimplifier to remove redundant segments
+         * -> (CollinearSimplifier) to remove redundant segments
          *
          * -> pc2d = Renderer (bounding box)
          */
     }
 

@@ -640,31 +626,113 @@
     }
 
     private static void pathToLoop(final float[] coords, final PathIterator pi,
                                    final PathConsumer2D pc2d)
     {
+        // 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:
+            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]);
-                    continue;
-                case PathIterator.SEG_LINETO:
-                    pc2d.lineTo(coords[0], coords[1]);
-                    continue;
-                case PathIterator.SEG_QUADTO:
-                    pc2d.quadTo(coords[0], coords[1],
-                                coords[2], coords[3]);
-                    continue;
-                case PathIterator.SEG_CUBICTO:
-                    pc2d.curveTo(coords[0], coords[1],
-                                 coords[2], coords[3],
-                                 coords[4], coords[5]);
-                    continue;
-                case PathIterator.SEG_CLOSE:
+                    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();
-                    continue;
-                default:
+                    // do not set subpathStarted to false
+                    // in case of missing moveTo() after close()
+                }
+                break;
+            default:
             }
         }
         pc2d.pathDone();
     }
 
< prev index next >