< prev index next >

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

Print this page

        

@@ -24,10 +24,11 @@
  */
 
 package sun.java2d.marlin;
 
 import java.util.Arrays;
+import sun.java2d.marlin.DHelpers.PolyStack;
 
 // TODO: some of the arithmetic here is too verbose and prone to hard to
 // debug typos. We should consider making a small Point/Vector class that
 // has methods like plus(Point), minus(Point), dot(Point), cross(Point)and such
 final class DStroker implements DPathConsumer2D, MarlinConst {

@@ -69,11 +70,15 @@
     // pisces used to use fixed point arithmetic with 16 decimal digits. I
     // didn't want to change the values of the constant below when I converted
     // it to floating point, so that's why the divisions by 2^16 are there.
     private static final double ROUND_JOIN_THRESHOLD = 1000.0d/65536.0d;
 
-    private static final double C = 0.5522847498307933d;
+    // kappa = (4/3) * (SQRT(2) - 1)
+    private static final double C = (4.0d * (Math.sqrt(2.0d) - 1.0d) / 3.0d);
+
+    // SQRT(2)
+    private static final double SQRT_2 = Math.sqrt(2.0d);
 
     private static final int MAX_N_CURVES = 11;
 
     private DPathConsumer2D out;
 

@@ -116,18 +121,40 @@
     final DRendererContext rdrCtx;
 
     // dirty curve
     final DCurve curve;
 
+    // Bounds of the drawing region, at pixel precision.
+    private double[] clipRect;
+
+    // the outcode of the current point
+    private int cOutCode = 0;
+
+    // the outcode of the starting point
+    private int sOutCode = 0;
+
+    // flag indicating if the path is opened (clipped)
+    private boolean opened = false;
+    // flag indicating if the starting point's cap is done
+    private boolean capStart = false;
+
     /**
      * Constructs a <code>DStroker</code>.
      * @param rdrCtx per-thread renderer context
      */
     DStroker(final DRendererContext rdrCtx) {
         this.rdrCtx = rdrCtx;
 
-        this.reverse = new PolyStack(rdrCtx);
+        this.reverse = (rdrCtx.stats != null) ?
+            new PolyStack(rdrCtx,
+                    rdrCtx.stats.stat_str_polystack_types,
+                    rdrCtx.stats.stat_str_polystack_curves,
+                    rdrCtx.stats.hist_str_polystack_curves,
+                    rdrCtx.stats.stat_array_str_polystack_curves,
+                    rdrCtx.stats.stat_array_str_polystack_types)
+            : new PolyStack(rdrCtx);
+
         this.curve = rdrCtx.curve;
     }
 
     /**
      * Inits the <code>DStroker</code>.

@@ -139,42 +166,75 @@
      * <code>CAP_SQUARE</code>.
      * @param joinStyle the desired line join style, one of
      * <code>JOIN_MITER</code>, <code>JOIN_ROUND</code> or
      * <code>JOIN_BEVEL</code>.
      * @param miterLimit the desired miter limit
+     * @param scale scaling factor applied to clip boundaries
      * @return this instance
      */
-    DStroker init(DPathConsumer2D pc2d,
-              double lineWidth,
-              int capStyle,
-              int joinStyle,
-              double miterLimit)
+    DStroker init(final DPathConsumer2D pc2d,
+                  final double lineWidth,
+                  final int capStyle,
+                  final int joinStyle,
+                  final double miterLimit,
+                  final double scale)
     {
         this.out = pc2d;
 
         this.lineWidth2 = lineWidth / 2.0d;
         this.invHalfLineWidth2Sq = 1.0d / (2.0d * lineWidth2 * lineWidth2);
         this.capStyle = capStyle;
         this.joinStyle = joinStyle;
 
-        double limit = miterLimit * lineWidth2;
+        final double limit = miterLimit * lineWidth2;
         this.miterLimitSq = limit * limit;
 
         this.prev = CLOSE;
 
         rdrCtx.stroking = 1;
 
+        if (rdrCtx.doClip) {
+            // Adjust the clipping rectangle with the stroker margin (miter limit, width)
+            double rdrOffX = 0.0d, rdrOffY = 0.0d;
+            double margin = lineWidth2;
+
+            if (capStyle == CAP_SQUARE) {
+                margin *= SQRT_2;
+            }
+            if ((joinStyle == JOIN_MITER) && (margin < limit)) {
+                margin = limit;
+            }
+            if (scale != 1.0d) {
+                margin *= scale;
+                rdrOffX = scale * DRenderer.RDR_OFFSET_X;
+                rdrOffY = scale * DRenderer.RDR_OFFSET_Y;
+            }
+
+            // bounds as half-open intervals: minX <= x < maxX and minY <= y < maxY
+            // adjust clip rectangle (ymin, ymax, xmin, xmax):
+            final double[] _clipRect = rdrCtx.clipRect;
+            _clipRect[0] -= margin - rdrOffY;
+            _clipRect[1] += margin + rdrOffY;
+            _clipRect[2] -= margin - rdrOffX;
+            _clipRect[3] += margin + rdrOffX;
+            this.clipRect = _clipRect;
+        } else {
+            this.clipRect = null;
+        }
         return this; // fluent API
     }
 
     /**
      * Disposes this stroker:
      * clean up before reusing this instance
      */
     void dispose() {
         reverse.dispose();
 
+        opened   = false;
+        capStart = false;
+
         if (DO_CLEAN_DIRTY) {
             // Force zero-fill dirty arrays:
             Arrays.fill(offset0, 0.0d);
             Arrays.fill(offset1, 0.0d);
             Arrays.fill(offset2, 0.0d);

@@ -441,87 +501,151 @@
             emitLineTo(miterX, miterY, rev);
         }
     }
 
     @Override
-    public void moveTo(double x0, double y0) {
-        if (prev == DRAWING_OP_TO) {
-            finish();
+    public void moveTo(final double x0, final double y0) {
+        moveTo(x0, y0, cOutCode);
+        // update starting point:
+        this.sx0 = x0;
+        this.sy0 = y0;
+        this.sdx = 1.0d;
+        this.sdy = 0.0d;
+        this.opened   = false;
+        this.capStart = false;
+
+        if (clipRect != null) {
+            final int outcode = DHelpers.outcode(x0, y0, clipRect);
+            this.cOutCode = outcode;
+            this.sOutCode = outcode;
+        }
+    }
+
+    private void moveTo(final double x0, final double y0,
+                        final int outcode)
+    {
+        if (prev == MOVE_TO) {
+            this.cx0 = x0;
+            this.cy0 = y0;
+        } else {
+            if (prev == DRAWING_OP_TO) {
+                finish(outcode);
+            }
+            this.prev = MOVE_TO;
+            this.cx0 = x0;
+            this.cy0 = y0;
+            this.cdx = 1.0d;
+            this.cdy = 0.0d;
         }
-        this.sx0 = this.cx0 = x0;
-        this.sy0 = this.cy0 = y0;
-        this.cdx = this.sdx = 1.0d;
-        this.cdy = this.sdy = 0.0d;
-        this.prev = MOVE_TO;
     }
 
     @Override
-    public void lineTo(double x1, double y1) {
+    public void lineTo(final double x1, final double y1) {
+        lineTo(x1, y1, false);
+    }
+
+    private void lineTo(final double x1, final double y1,
+                        final boolean force)
+    {
+        final int outcode0 = this.cOutCode;
+        if (!force && clipRect != null) {
+            final int outcode1 = DHelpers.outcode(x1, y1, clipRect);
+            this.cOutCode = outcode1;
+
+            // basic rejection criteria
+            if ((outcode0 & outcode1) != 0) {
+                moveTo(x1, y1, outcode0);
+                opened = true;
+                return;
+            }
+        }
+
         double dx = x1 - cx0;
         double dy = y1 - cy0;
         if (dx == 0.0d && dy == 0.0d) {
             dx = 1.0d;
         }
         computeOffset(dx, dy, lineWidth2, offset0);
         final double mx = offset0[0];
         final double my = offset0[1];
 
-        drawJoin(cdx, cdy, cx0, cy0, dx, dy, cmx, cmy, mx, my);
+        drawJoin(cdx, cdy, cx0, cy0, dx, dy, cmx, cmy, mx, my, outcode0);
 
         emitLineTo(cx0 + mx, cy0 + my);
         emitLineTo( x1 + mx,  y1 + my);
 
         emitLineToRev(cx0 - mx, cy0 - my);
         emitLineToRev( x1 - mx,  y1 - my);
 
-        this.cmx = mx;
-        this.cmy = my;
-        this.cdx = dx;
-        this.cdy = dy;
+        this.prev = DRAWING_OP_TO;
         this.cx0 = x1;
         this.cy0 = y1;
-        this.prev = DRAWING_OP_TO;
+        this.cdx = dx;
+        this.cdy = dy;
+        this.cmx = mx;
+        this.cmy = my;
     }
 
     @Override
     public void closePath() {
-        if (prev != DRAWING_OP_TO) {
+        // distinguish empty path at all vs opened path ?
+        if (prev != DRAWING_OP_TO && !opened) {
             if (prev == CLOSE) {
                 return;
             }
             emitMoveTo(cx0, cy0 - lineWidth2);
-            this.cmx = this.smx = 0.0d;
-            this.cmy = this.smy = -lineWidth2;
-            this.cdx = this.sdx = 1.0d;
-            this.cdy = this.sdy = 0.0d;
-            finish();
+
+            this.sdx = 1.0d;
+            this.sdy = 0.0d;
+            this.cdx = 1.0d;
+            this.cdy = 0.0d;
+
+            this.smx = 0.0d;
+            this.smy = -lineWidth2;
+            this.cmx = 0.0d;
+            this.cmy = -lineWidth2;
+
+            finish(cOutCode);
             return;
         }
 
-        if (cx0 != sx0 || cy0 != sy0) {
-            lineTo(sx0, sy0);
-        }
+        if (sOutCode == 0) {
+            if (cx0 != sx0 || cy0 != sy0) {
+                lineTo(sx0, sy0, true);
+            }
 
-        drawJoin(cdx, cdy, cx0, cy0, sdx, sdy, cmx, cmy, smx, smy);
+            drawJoin(cdx, cdy, cx0, cy0, sdx, sdy, cmx, cmy, smx, smy, cOutCode);
 
-        emitLineTo(sx0 + smx, sy0 + smy);
+            emitLineTo(sx0 + smx, sy0 + smy);
 
-        emitMoveTo(sx0 - smx, sy0 - smy);
+            if (opened) {
+                emitLineTo(sx0 - smx, sy0 - smy);
+            } else {
+                emitMoveTo(sx0 - smx, sy0 - smy);
+            }
+        }
+        // Ignore caps like finish(false)
         emitReverse();
 
         this.prev = CLOSE;
-        emitClose();
+
+        if (opened) {
+            // do not emit close
+            opened = false;
+        } else {
+            emitClose();
+        }
     }
 
     private void emitReverse() {
         reverse.popAll(out);
     }
 
     @Override
     public void pathDone() {
         if (prev == DRAWING_OP_TO) {
-            finish();
+            finish(cOutCode);
         }
 
         out.pathDone();
 
         // this shouldn't matter since this object won't be used

@@ -530,27 +654,43 @@
 
         // Dispose this instance:
         dispose();
     }
 
-    private void finish() {
-        if (capStyle == CAP_ROUND) {
-            drawRoundCap(cx0, cy0, cmx, cmy);
-        } else if (capStyle == CAP_SQUARE) {
-            emitLineTo(cx0 - cmy + cmx, cy0 + cmx + cmy);
-            emitLineTo(cx0 - cmy - cmx, cy0 + cmx - cmy);
-        }
+    private void finish(final int outcode) {
+        // Problem: impossible to guess if the path will be closed in advance
+        //          i.e. if caps must be drawn or not ?
+        // Solution: use the ClosedPathDetector before Stroker to determine
+        // if the path is a closed path or not
+        if (!rdrCtx.closedPath) {
+            if (outcode == 0) {
+                // current point = end's cap:
+                if (capStyle == CAP_ROUND) {
+                    drawRoundCap(cx0, cy0, cmx, cmy);
+                } else if (capStyle == CAP_SQUARE) {
+                    emitLineTo(cx0 - cmy + cmx, cy0 + cmx + cmy);
+                    emitLineTo(cx0 - cmy - cmx, cy0 + cmx - cmy);
+                }
+            }
+            emitReverse();
 
-        emitReverse();
+            if (!capStart) {
+                capStart = true;
 
-        if (capStyle == CAP_ROUND) {
-            drawRoundCap(sx0, sy0, -smx, -smy);
-        } else if (capStyle == CAP_SQUARE) {
-            emitLineTo(sx0 + smy - smx, sy0 - smx - smy);
-            emitLineTo(sx0 + smy + smx, sy0 - smx + smy);
+                if (sOutCode == 0) {
+                    // starting point = initial cap:
+                    if (capStyle == CAP_ROUND) {
+                        drawRoundCap(sx0, sy0, -smx, -smy);
+                    } else if (capStyle == CAP_SQUARE) {
+                        emitLineTo(sx0 + smy - smx, sy0 - smx - smy);
+                        emitLineTo(sx0 + smy + smx, sy0 - smx + smy);
+                    }
+                }
+            }
+        } else {
+            emitReverse();
         }
-
         emitClose();
     }
 
     private void emitMoveTo(final double x0, final double y0) {
         out.moveTo(x0, y0);

@@ -618,27 +758,32 @@
 
     private void drawJoin(double pdx, double pdy,
                           double x0, double y0,
                           double dx, double dy,
                           double omx, double omy,
-                          double mx, double my)
+                          double mx, double my,
+                          final int outcode)
     {
         if (prev != DRAWING_OP_TO) {
             emitMoveTo(x0 + mx, y0 + my);
-            this.sdx = dx;
-            this.sdy = dy;
-            this.smx = mx;
-            this.smy = my;
+            if (!opened) {
+                this.sdx = dx;
+                this.sdy = dy;
+                this.smx = mx;
+                this.smy = my;
+            }
         } else {
-            boolean cw = isCW(pdx, pdy, dx, dy);
-            if (joinStyle == JOIN_MITER) {
-                drawMiter(pdx, pdy, x0, y0, dx, dy, omx, omy, mx, my, cw);
-            } else if (joinStyle == JOIN_ROUND) {
-                drawRoundJoin(x0, y0,
-                              omx, omy,
-                              mx, my, cw,
-                              ROUND_JOIN_THRESHOLD);
+            final boolean cw = isCW(pdx, pdy, dx, dy);
+            if (outcode == 0) {
+                if (joinStyle == JOIN_MITER) {
+                    drawMiter(pdx, pdy, x0, y0, dx, dy, omx, omy, mx, my, cw);
+                } else if (joinStyle == JOIN_ROUND) {
+                    drawRoundJoin(x0, y0,
+                                  omx, omy,
+                                  mx, my, cw,
+                                  ROUND_JOIN_THRESHOLD);
+                }
             }
             emitLineTo(x0, y0, !cw);
         }
         prev = DRAWING_OP_TO;
     }

@@ -939,23 +1084,42 @@
         ret = DHelpers.filterOutNotInAB(ts, 0, ret, 0.0001d, 0.9999d);
         DHelpers.isort(ts, 0, ret);
         return ret;
     }
 
-    @Override public void curveTo(double x1, double y1,
-                                  double x2, double y2,
-                                  double x3, double y3)
-    {
+    @Override
+    public void curveTo(final double x1, final double y1,
+                        final double x2, final double y2,
+                        final double x3, final double y3)
+    {
+        final int outcode0 = this.cOutCode;
+        if (clipRect != null) {
+            final int outcode3 = DHelpers.outcode(x3, y3, clipRect);
+            this.cOutCode = outcode3;
+
+            if (outcode3 != 0) {
+                final int outcode1 = DHelpers.outcode(x1, y1, clipRect);
+                final int outcode2 = DHelpers.outcode(x2, y2, clipRect);
+
+                // basic rejection criteria
+                if ((outcode0 & outcode1 & outcode2 & outcode3) != 0) {
+                    moveTo(x3, y3, outcode0);
+                    opened = true;
+                    return;
+                }
+            }
+        }
+
         final double[] mid = middle;
 
         mid[0] = cx0; mid[1] = cy0;
         mid[2] = x1;  mid[3] = y1;
         mid[4] = x2;  mid[5] = y2;
         mid[6] = x3;  mid[7] = y3;
 
         // need these so we can update the state at the end of this method
-        final double xf = mid[6], yf = mid[7];
+        final double xf = x3, yf = y3;
         double dxs = mid[2] - mid[0];
         double dys = mid[3] - mid[1];
         double dxf = mid[6] - mid[4];
         double dyf = mid[7] - mid[5];
 

@@ -977,10 +1141,14 @@
                 dyf = mid[7] - mid[1];
             }
         }
         if (dxs == 0.0d && dys == 0.0d) {
             // this happens if the "curve" is just a point
+            // fix outcode0 for lineTo() call:
+            if (clipRect != null) {
+                this.cOutCode = outcode0;
+            }
             lineTo(mid[0], mid[1]);
             return;
         }
 
         // if these vectors are too small, normalize them, to avoid future

@@ -995,11 +1163,11 @@
             dxf /= len;
             dyf /= len;
         }
 
         computeOffset(dxs, dys, lineWidth2, offset0);
-        drawJoin(cdx, cdy, cx0, cy0, dxs, dys, cmx, cmy, offset0[0], offset0[1]);
+        drawJoin(cdx, cdy, cx0, cy0, dxs, dys, cmx, cmy, offset0[0], offset0[1], outcode0);
 
         final int nSplits = findSubdivPoints(curve, mid, subdivTs, 8, lineWidth2);
 
         double prevT = 0.0d;
         for (int i = 0, off = 0; i < nSplits; i++, off += 6) {

@@ -1030,38 +1198,62 @@
             default:
             }
             emitLineToRev(r[kind - 2], r[kind - 1]);
         }
 
-        this.cmx = (l[kind - 2] - r[kind - 2]) / 2.0d;
-        this.cmy = (l[kind - 1] - r[kind - 1]) / 2.0d;
-        this.cdx = dxf;
-        this.cdy = dyf;
+        this.prev = DRAWING_OP_TO;
         this.cx0 = xf;
         this.cy0 = yf;
-        this.prev = DRAWING_OP_TO;
+        this.cdx = dxf;
+        this.cdy = dyf;
+        this.cmx = (l[kind - 2] - r[kind - 2]) / 2.0d;
+        this.cmy = (l[kind - 1] - r[kind - 1]) / 2.0d;
     }
 
-    @Override public void quadTo(double x1, double y1, double x2, double y2) {
+    @Override
+    public void quadTo(final double x1, final double y1,
+                       final double x2, final double y2)
+    {
+        final int outcode0 = this.cOutCode;
+        if (clipRect != null) {
+            final int outcode2 = DHelpers.outcode(x2, y2, clipRect);
+            this.cOutCode = outcode2;
+
+            if (outcode2 != 0) {
+                final int outcode1 = DHelpers.outcode(x1, y1, clipRect);
+
+                // basic rejection criteria
+                if ((outcode0 & outcode1 & outcode2) != 0) {
+                    moveTo(x2, y2, outcode0);
+                    opened = true;
+                    return;
+                }
+            }
+        }
+
         final double[] mid = middle;
 
         mid[0] = cx0; mid[1] = cy0;
         mid[2] = x1;  mid[3] = y1;
         mid[4] = x2;  mid[5] = y2;
 
         // need these so we can update the state at the end of this method
-        final double xf = mid[4], yf = mid[5];
+        final double xf = x2, yf = y2;
         double dxs = mid[2] - mid[0];
         double dys = mid[3] - mid[1];
         double dxf = mid[4] - mid[2];
         double dyf = mid[5] - mid[3];
         if ((dxs == 0.0d && dys == 0.0d) || (dxf == 0.0d && dyf == 0.0d)) {
             dxs = dxf = mid[4] - mid[0];
             dys = dyf = mid[5] - mid[1];
         }
         if (dxs == 0.0d && dys == 0.0d) {
             // this happens if the "curve" is just a point
+            // fix outcode0 for lineTo() call:
+            if (clipRect != null) {
+                this.cOutCode = outcode0;
+            }
             lineTo(mid[0], mid[1]);
             return;
         }
         // if these vectors are too small, normalize them, to avoid future
         // precision problems.

@@ -1075,11 +1267,11 @@
             dxf /= len;
             dyf /= len;
         }
 
         computeOffset(dxs, dys, lineWidth2, offset0);
-        drawJoin(cdx, cdy, cx0, cy0, dxs, dys, cmx, cmy, offset0[0], offset0[1]);
+        drawJoin(cdx, cdy, cx0, cy0, dxs, dys, cmx, cmy, offset0[0], offset0[1], outcode0);
 
         int nSplits = findSubdivPoints(curve, mid, subdivTs, 6, lineWidth2);
 
         double prevt = 0.0d;
         for (int i = 0, off = 0; i < nSplits; i++, off += 4) {

@@ -1110,216 +1302,18 @@
             default:
             }
             emitLineToRev(r[kind - 2], r[kind - 1]);
         }
 
-        this.cmx = (l[kind - 2] - r[kind - 2]) / 2.0d;
-        this.cmy = (l[kind - 1] - r[kind - 1]) / 2.0d;
-        this.cdx = dxf;
-        this.cdy = dyf;
+        this.prev = DRAWING_OP_TO;
         this.cx0 = xf;
         this.cy0 = yf;
-        this.prev = DRAWING_OP_TO;
+        this.cdx = dxf;
+        this.cdy = dyf;
+        this.cmx = (l[kind - 2] - r[kind - 2]) / 2.0d;
+        this.cmy = (l[kind - 1] - r[kind - 1]) / 2.0d;
     }
 
     @Override public long getNativeConsumer() {
         throw new InternalError("Stroker doesn't use a native consumer");
     }
-
-    // a stack of polynomial curves where each curve shares endpoints with
-    // adjacent ones.
-    static final class PolyStack {
-        private static final byte TYPE_LINETO  = (byte) 0;
-        private static final byte TYPE_QUADTO  = (byte) 1;
-        private static final byte TYPE_CUBICTO = (byte) 2;
-
-        // curves capacity = edges count (8192) = edges x 2 (coords)
-        private static final int INITIAL_CURVES_COUNT = INITIAL_EDGES_COUNT << 1;
-
-        // types capacity = edges count (4096)
-        private static final int INITIAL_TYPES_COUNT = INITIAL_EDGES_COUNT;
-
-        double[] curves;
-        int end;
-        byte[] curveTypes;
-        int numCurves;
-
-        // per-thread renderer context
-        final DRendererContext rdrCtx;
-
-        // curves ref (dirty)
-        final DoubleArrayCache.Reference curves_ref;
-        // curveTypes ref (dirty)
-        final ByteArrayCache.Reference curveTypes_ref;
-
-        // used marks (stats only)
-        int curveTypesUseMark;
-        int curvesUseMark;
-
-        /**
-         * Constructor
-         * @param rdrCtx per-thread renderer context
-         */
-        PolyStack(final DRendererContext rdrCtx) {
-            this.rdrCtx = rdrCtx;
-
-            curves_ref = rdrCtx.newDirtyDoubleArrayRef(INITIAL_CURVES_COUNT); // 32K
-            curves     = curves_ref.initial;
-
-            curveTypes_ref = rdrCtx.newDirtyByteArrayRef(INITIAL_TYPES_COUNT); // 4K
-            curveTypes     = curveTypes_ref.initial;
-            numCurves = 0;
-            end = 0;
-
-            if (DO_STATS) {
-                curveTypesUseMark = 0;
-                curvesUseMark = 0;
-            }
-        }
-
-        /**
-         * Disposes this PolyStack:
-         * clean up before reusing this instance
-         */
-        void dispose() {
-            end = 0;
-            numCurves = 0;
-
-            if (DO_STATS) {
-                rdrCtx.stats.stat_rdr_poly_stack_types.add(curveTypesUseMark);
-                rdrCtx.stats.stat_rdr_poly_stack_curves.add(curvesUseMark);
-                rdrCtx.stats.hist_rdr_poly_stack_curves.add(curvesUseMark);
-
-                // reset marks
-                curveTypesUseMark = 0;
-                curvesUseMark = 0;
-            }
-
-            // Return arrays:
-            // curves and curveTypes are kept dirty
-            curves     = curves_ref.putArray(curves);
-            curveTypes = curveTypes_ref.putArray(curveTypes);
-        }
-
-        private void ensureSpace(final int n) {
-            // use substraction to avoid integer overflow:
-            if (curves.length - end < n) {
-                if (DO_STATS) {
-                    rdrCtx.stats.stat_array_stroker_polystack_curves
-                        .add(end + n);
-                }
-                curves = curves_ref.widenArray(curves, end, end + n);
-            }
-            if (curveTypes.length <= numCurves) {
-                if (DO_STATS) {
-                    rdrCtx.stats.stat_array_stroker_polystack_curveTypes
-                        .add(numCurves + 1);
-                }
-                curveTypes = curveTypes_ref.widenArray(curveTypes,
-                                                       numCurves,
-                                                       numCurves + 1);
-            }
-        }
-
-        void pushCubic(double x0, double y0,
-                       double x1, double y1,
-                       double x2, double y2)
-        {
-            ensureSpace(6);
-            curveTypes[numCurves++] = TYPE_CUBICTO;
-            // we reverse the coordinate order to make popping easier
-            final double[] _curves = curves;
-            int e = end;
-            _curves[e++] = x2;    _curves[e++] = y2;
-            _curves[e++] = x1;    _curves[e++] = y1;
-            _curves[e++] = x0;    _curves[e++] = y0;
-            end = e;
-        }
-
-        void pushQuad(double x0, double y0,
-                      double x1, double y1)
-        {
-            ensureSpace(4);
-            curveTypes[numCurves++] = TYPE_QUADTO;
-            final double[] _curves = curves;
-            int e = end;
-            _curves[e++] = x1;    _curves[e++] = y1;
-            _curves[e++] = x0;    _curves[e++] = y0;
-            end = e;
-        }
-
-        void pushLine(double x, double y) {
-            ensureSpace(2);
-            curveTypes[numCurves++] = TYPE_LINETO;
-            curves[end++] = x;    curves[end++] = y;
-        }
-
-        void popAll(DPathConsumer2D io) {
-            if (DO_STATS) {
-                // update used marks:
-                if (numCurves > curveTypesUseMark) {
-                    curveTypesUseMark = numCurves;
-                }
-                if (end > curvesUseMark) {
-                    curvesUseMark = end;
-                }
-            }
-            final byte[]  _curveTypes = curveTypes;
-            final double[] _curves = curves;
-            int nc = numCurves;
-            int e  = end;
-
-            while (nc != 0) {
-                switch(_curveTypes[--nc]) {
-                case TYPE_LINETO:
-                    e -= 2;
-                    io.lineTo(_curves[e], _curves[e+1]);
-                    continue;
-                case TYPE_QUADTO:
-                    e -= 4;
-                    io.quadTo(_curves[e+0], _curves[e+1],
-                              _curves[e+2], _curves[e+3]);
-                    continue;
-                case TYPE_CUBICTO:
-                    e -= 6;
-                    io.curveTo(_curves[e+0], _curves[e+1],
-                               _curves[e+2], _curves[e+3],
-                               _curves[e+4], _curves[e+5]);
-                    continue;
-                default:
-                }
-            }
-            numCurves = 0;
-            end = 0;
-        }
-
-        @Override
-        public String toString() {
-            String ret = "";
-            int nc = numCurves;
-            int last = end;
-            int len;
-            while (nc != 0) {
-                switch(curveTypes[--nc]) {
-                case TYPE_LINETO:
-                    len = 2;
-                    ret += "line: ";
-                    break;
-                case TYPE_QUADTO:
-                    len = 4;
-                    ret += "quad: ";
-                    break;
-                case TYPE_CUBICTO:
-                    len = 6;
-                    ret += "cubic: ";
-                    break;
-                default:
-                    len = 0;
-                }
-                last -= len;
-                ret += Arrays.toString(Arrays.copyOfRange(curves, last, last+len))
-                                       + "\n";
-            }
-            return ret;
-        }
-    }
 }
< prev index next >