src/java.desktop/share/classes/sun/java2d/marlin/DStroker.java
changeset 49496 1ea202af7a97
parent 48284 fd7fbc929001
child 51933 4ec74929fbfe
equal deleted inserted replaced
49495:f46bfa7a2956 49496:1ea202af7a97
     1 /*
     1 /*
     2  * Copyright (c) 2007, 2017, Oracle and/or its affiliates. All rights reserved.
     2  * Copyright (c) 2007, 2018, Oracle and/or its affiliates. All rights reserved.
     3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
     3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
     4  *
     4  *
     5  * This code is free software; you can redistribute it and/or modify it
     5  * This code is free software; you can redistribute it and/or modify it
     6  * under the terms of the GNU General Public License version 2 only, as
     6  * under the terms of the GNU General Public License version 2 only, as
     7  * published by the Free Software Foundation.  Oracle designates this
     7  * published by the Free Software Foundation.  Oracle designates this
    25 
    25 
    26 package sun.java2d.marlin;
    26 package sun.java2d.marlin;
    27 
    27 
    28 import java.util.Arrays;
    28 import java.util.Arrays;
    29 import sun.java2d.marlin.DHelpers.PolyStack;
    29 import sun.java2d.marlin.DHelpers.PolyStack;
       
    30 import sun.java2d.marlin.DTransformingPathConsumer2D.CurveBasicMonotonizer;
       
    31 import sun.java2d.marlin.DTransformingPathConsumer2D.CurveClipSplitter;
    30 
    32 
    31 // TODO: some of the arithmetic here is too verbose and prone to hard to
    33 // TODO: some of the arithmetic here is too verbose and prone to hard to
    32 // debug typos. We should consider making a small Point/Vector class that
    34 // debug typos. We should consider making a small Point/Vector class that
    33 // has methods like plus(Point), minus(Point), dot(Point), cross(Point)and such
    35 // has methods like plus(Point), minus(Point), dot(Point), cross(Point)and such
    34 final class DStroker implements DPathConsumer2D, MarlinConst {
    36 final class DStroker implements DPathConsumer2D, MarlinConst {
    35 
    37 
    36     private static final int MOVE_TO = 0;
    38     private static final int MOVE_TO = 0;
    37     private static final int DRAWING_OP_TO = 1; // ie. curve, line, or quad
    39     private static final int DRAWING_OP_TO = 1; // ie. curve, line, or quad
    38     private static final int CLOSE = 2;
    40     private static final int CLOSE = 2;
    39 
    41 
    40     // pisces used to use fixed point arithmetic with 16 decimal digits. I
    42     // round join threshold = 1 subpixel
    41     // didn't want to change the values of the constant below when I converted
    43     private static final double ERR_JOIN = (1.0f / MIN_SUBPIXELS);
    42     // it to floating point, so that's why the divisions by 2^16 are there.
    44     private static final double ROUND_JOIN_THRESHOLD = ERR_JOIN * ERR_JOIN;
    43     private static final double ROUND_JOIN_THRESHOLD = 1000.0d/65536.0d;
       
    44 
    45 
    45     // kappa = (4/3) * (SQRT(2) - 1)
    46     // kappa = (4/3) * (SQRT(2) - 1)
    46     private static final double C = (4.0d * (Math.sqrt(2.0d) - 1.0d) / 3.0d);
    47     private static final double C = (4.0d * (Math.sqrt(2.0d) - 1.0d) / 3.0d);
    47 
    48 
    48     // SQRT(2)
    49     // SQRT(2)
    49     private static final double SQRT_2 = Math.sqrt(2.0d);
    50     private static final double SQRT_2 = Math.sqrt(2.0d);
    50 
       
    51     private static final int MAX_N_CURVES = 11;
       
    52 
    51 
    53     private DPathConsumer2D out;
    52     private DPathConsumer2D out;
    54 
    53 
    55     private int capStyle;
    54     private int capStyle;
    56     private int joinStyle;
    55     private int joinStyle;
    78     // would be error prone and hard to read, so we keep these anyway.
    77     // would be error prone and hard to read, so we keep these anyway.
    79     private double smx, smy, cmx, cmy;
    78     private double smx, smy, cmx, cmy;
    80 
    79 
    81     private final PolyStack reverse;
    80     private final PolyStack reverse;
    82 
    81 
    83     // This is where the curve to be processed is put. We give it
       
    84     // enough room to store all curves.
       
    85     private final double[] middle = new double[MAX_N_CURVES * 6 + 2];
       
    86     private final double[] lp = new double[8];
    82     private final double[] lp = new double[8];
    87     private final double[] rp = new double[8];
    83     private final double[] rp = new double[8];
    88     private final double[] subdivTs = new double[MAX_N_CURVES - 1];
       
    89 
    84 
    90     // per-thread renderer context
    85     // per-thread renderer context
    91     final DRendererContext rdrCtx;
    86     final DRendererContext rdrCtx;
    92 
    87 
    93     // dirty curve
    88     // dirty curve
   104 
    99 
   105     // flag indicating if the path is opened (clipped)
   100     // flag indicating if the path is opened (clipped)
   106     private boolean opened = false;
   101     private boolean opened = false;
   107     // flag indicating if the starting point's cap is done
   102     // flag indicating if the starting point's cap is done
   108     private boolean capStart = false;
   103     private boolean capStart = false;
       
   104     // flag indicating to monotonize curves
       
   105     private boolean monotonize;
       
   106 
       
   107     private boolean subdivide = false;
       
   108     private final CurveClipSplitter curveSplitter;
   109 
   109 
   110     /**
   110     /**
   111      * Constructs a <code>DStroker</code>.
   111      * Constructs a <code>DStroker</code>.
   112      * @param rdrCtx per-thread renderer context
   112      * @param rdrCtx per-thread renderer context
   113      */
   113      */
   122                     rdrCtx.stats.stat_array_str_polystack_curves,
   122                     rdrCtx.stats.stat_array_str_polystack_curves,
   123                     rdrCtx.stats.stat_array_str_polystack_types)
   123                     rdrCtx.stats.stat_array_str_polystack_types)
   124             : new PolyStack(rdrCtx);
   124             : new PolyStack(rdrCtx);
   125 
   125 
   126         this.curve = rdrCtx.curve;
   126         this.curve = rdrCtx.curve;
       
   127         this.curveSplitter = rdrCtx.curveClipSplitter;
   127     }
   128     }
   128 
   129 
   129     /**
   130     /**
   130      * Inits the <code>DStroker</code>.
   131      * Inits the <code>DStroker</code>.
   131      *
   132      *
   137      * @param joinStyle the desired line join style, one of
   138      * @param joinStyle the desired line join style, one of
   138      * <code>JOIN_MITER</code>, <code>JOIN_ROUND</code> or
   139      * <code>JOIN_MITER</code>, <code>JOIN_ROUND</code> or
   139      * <code>JOIN_BEVEL</code>.
   140      * <code>JOIN_BEVEL</code>.
   140      * @param miterLimit the desired miter limit
   141      * @param miterLimit the desired miter limit
   141      * @param scale scaling factor applied to clip boundaries
   142      * @param scale scaling factor applied to clip boundaries
       
   143      * @param subdivideCurves true to indicate to subdivide curves, false if dasher does
   142      * @return this instance
   144      * @return this instance
   143      */
   145      */
   144     DStroker init(final DPathConsumer2D pc2d,
   146     DStroker init(final DPathConsumer2D pc2d,
   145                   final double lineWidth,
   147                   final double lineWidth,
   146                   final int capStyle,
   148                   final int capStyle,
   147                   final int joinStyle,
   149                   final int joinStyle,
   148                   final double miterLimit,
   150                   final double miterLimit,
   149                   final double scale)
   151                   final double scale,
       
   152                   final boolean subdivideCurves)
   150     {
   153     {
   151         this.out = pc2d;
   154         this.out = pc2d;
   152 
   155 
   153         this.lineWidth2 = lineWidth / 2.0d;
   156         this.lineWidth2 = lineWidth / 2.0d;
   154         this.invHalfLineWidth2Sq = 1.0d / (2.0d * lineWidth2 * lineWidth2);
   157         this.invHalfLineWidth2Sq = 1.0d / (2.0d * lineWidth2 * lineWidth2);
       
   158         this.monotonize = subdivideCurves;
       
   159 
   155         this.capStyle = capStyle;
   160         this.capStyle = capStyle;
   156         this.joinStyle = joinStyle;
   161         this.joinStyle = joinStyle;
   157 
   162 
   158         final double limit = miterLimit * lineWidth2;
   163         final double limit = miterLimit * lineWidth2;
   159         this.miterLimitSq = limit * limit;
   164         this.miterLimitSq = limit * limit;
   187             _clipRect[0] -= margin - rdrOffY;
   192             _clipRect[0] -= margin - rdrOffY;
   188             _clipRect[1] += margin + rdrOffY;
   193             _clipRect[1] += margin + rdrOffY;
   189             _clipRect[2] -= margin - rdrOffX;
   194             _clipRect[2] -= margin - rdrOffX;
   190             _clipRect[3] += margin + rdrOffX;
   195             _clipRect[3] += margin + rdrOffX;
   191             this.clipRect = _clipRect;
   196             this.clipRect = _clipRect;
       
   197 
       
   198             // initialize curve splitter here for stroker & dasher:
       
   199             if (DO_CLIP_SUBDIVIDER) {
       
   200                 subdivide = subdivideCurves;
       
   201                 // adjust padded clip rectangle:
       
   202                 curveSplitter.init();
       
   203             } else {
       
   204                 subdivide = false;
       
   205             }
   192         } else {
   206         } else {
   193             this.clipRect = null;
   207             this.clipRect = null;
   194             this.cOutCode = 0;
   208             this.cOutCode = 0;
   195             this.sOutCode = 0;
   209             this.sOutCode = 0;
   196         }
   210         }
   197         return this; // fluent API
   211         return this; // fluent API
       
   212     }
       
   213 
       
   214     void disableClipping() {
       
   215         this.clipRect = null;
       
   216         this.cOutCode = 0;
       
   217         this.sOutCode = 0;
   198     }
   218     }
   199 
   219 
   200     /**
   220     /**
   201      * Disposes this stroker:
   221      * Disposes this stroker:
   202      * clean up before reusing this instance
   222      * clean up before reusing this instance
   211             // Force zero-fill dirty arrays:
   231             // Force zero-fill dirty arrays:
   212             Arrays.fill(offset0, 0.0d);
   232             Arrays.fill(offset0, 0.0d);
   213             Arrays.fill(offset1, 0.0d);
   233             Arrays.fill(offset1, 0.0d);
   214             Arrays.fill(offset2, 0.0d);
   234             Arrays.fill(offset2, 0.0d);
   215             Arrays.fill(miter, 0.0d);
   235             Arrays.fill(miter, 0.0d);
   216             Arrays.fill(middle, 0.0d);
       
   217             Arrays.fill(lp, 0.0d);
   236             Arrays.fill(lp, 0.0d);
   218             Arrays.fill(rp, 0.0d);
   237             Arrays.fill(rp, 0.0d);
   219             Arrays.fill(subdivTs, 0.0d);
       
   220         }
   238         }
   221     }
   239     }
   222 
   240 
   223     private static void computeOffset(final double lx, final double ly,
   241     private static void computeOffset(final double lx, final double ly,
   224                                       final double w, final double[] m)
   242                                       final double w, final double[] m)
   246                                 final double dx2, final double dy2)
   264                                 final double dx2, final double dy2)
   247     {
   265     {
   248         return dx1 * dy2 <= dy1 * dx2;
   266         return dx1 * dy2 <= dy1 * dx2;
   249     }
   267     }
   250 
   268 
   251     private void drawRoundJoin(double x, double y,
   269     private void mayDrawRoundJoin(double cx, double cy,
   252                                double omx, double omy, double mx, double my,
   270                                   double omx, double omy,
   253                                boolean rev,
   271                                   double mx, double my,
   254                                double threshold)
   272                                   boolean rev)
   255     {
   273     {
   256         if ((omx == 0.0d && omy == 0.0d) || (mx == 0.0d && my == 0.0d)) {
   274         if ((omx == 0.0d && omy == 0.0d) || (mx == 0.0d && my == 0.0d)) {
   257             return;
   275             return;
   258         }
   276         }
   259 
   277 
   260         double domx = omx - mx;
   278         final double domx = omx - mx;
   261         double domy = omy - my;
   279         final double domy = omy - my;
   262         double len = domx*domx + domy*domy;
   280         final double lenSq = domx*domx + domy*domy;
   263         if (len < threshold) {
   281 
       
   282         if (lenSq < ROUND_JOIN_THRESHOLD) {
   264             return;
   283             return;
   265         }
   284         }
   266 
   285 
   267         if (rev) {
   286         if (rev) {
   268             omx = -omx;
   287             omx = -omx;
   269             omy = -omy;
   288             omy = -omy;
   270             mx  = -mx;
   289             mx  = -mx;
   271             my  = -my;
   290             my  = -my;
   272         }
   291         }
   273         drawRoundJoin(x, y, omx, omy, mx, my, rev);
   292         drawRoundJoin(cx, cy, omx, omy, mx, my, rev);
   274     }
   293     }
   275 
   294 
   276     private void drawRoundJoin(double cx, double cy,
   295     private void drawRoundJoin(double cx, double cy,
   277                                double omx, double omy,
   296                                double omx, double omy,
   278                                double mx, double my,
   297                                double mx, double my,
   379     // and (x0p, y0p) -> (x1p, y1p) in m[off] and m[off+1]
   398     // and (x0p, y0p) -> (x1p, y1p) in m[off] and m[off+1]
   380     private static void computeMiter(final double x0, final double y0,
   399     private static void computeMiter(final double x0, final double y0,
   381                                      final double x1, final double y1,
   400                                      final double x1, final double y1,
   382                                      final double x0p, final double y0p,
   401                                      final double x0p, final double y0p,
   383                                      final double x1p, final double y1p,
   402                                      final double x1p, final double y1p,
   384                                      final double[] m, int off)
   403                                      final double[] m)
   385     {
   404     {
   386         double x10 = x1 - x0;
   405         double x10 = x1 - x0;
   387         double y10 = y1 - y0;
   406         double y10 = y1 - y0;
   388         double x10p = x1p - x0p;
   407         double x10p = x1p - x0p;
   389         double y10p = y1p - y0p;
   408         double y10p = y1p - y0p;
   398         // (mx == omx && my == omy) will be true, and drawMiter will return
   417         // (mx == omx && my == omy) will be true, and drawMiter will return
   399         // immediately).
   418         // immediately).
   400         double den = x10*y10p - x10p*y10;
   419         double den = x10*y10p - x10p*y10;
   401         double t = x10p*(y0-y0p) - y10p*(x0-x0p);
   420         double t = x10p*(y0-y0p) - y10p*(x0-x0p);
   402         t /= den;
   421         t /= den;
   403         m[off++] = x0 + t*x10;
   422         m[0] = x0 + t*x10;
   404         m[off]   = y0 + t*y10;
   423         m[1] = y0 + t*y10;
   405     }
   424     }
   406 
   425 
   407     // Return the intersection point of the lines (x0, y0) -> (x1, y1)
   426     // Return the intersection point of the lines (x0, y0) -> (x1, y1)
   408     // and (x0p, y0p) -> (x1p, y1p) in m[off] and m[off+1]
   427     // and (x0p, y0p) -> (x1p, y1p) in m[off] and m[off+1]
   409     private static void safeComputeMiter(final double x0, final double y0,
   428     private static void safeComputeMiter(final double x0, final double y0,
   410                                          final double x1, final double y1,
   429                                          final double x1, final double y1,
   411                                          final double x0p, final double y0p,
   430                                          final double x0p, final double y0p,
   412                                          final double x1p, final double y1p,
   431                                          final double x1p, final double y1p,
   413                                          final double[] m, int off)
   432                                          final double[] m)
   414     {
   433     {
   415         double x10 = x1 - x0;
   434         double x10 = x1 - x0;
   416         double y10 = y1 - y0;
   435         double y10 = y1 - y0;
   417         double x10p = x1p - x0p;
   436         double x10p = x1p - x0p;
   418         double y10p = y1p - y0p;
   437         double y10p = y1p - y0p;
   426         // miter drawing because it won't be called by drawMiter (because
   445         // miter drawing because it won't be called by drawMiter (because
   427         // (mx == omx && my == omy) will be true, and drawMiter will return
   446         // (mx == omx && my == omy) will be true, and drawMiter will return
   428         // immediately).
   447         // immediately).
   429         double den = x10*y10p - x10p*y10;
   448         double den = x10*y10p - x10p*y10;
   430         if (den == 0.0d) {
   449         if (den == 0.0d) {
   431             m[off++] = (x0 + x0p) / 2.0d;
   450             m[2] = (x0 + x0p) / 2.0d;
   432             m[off]   = (y0 + y0p) / 2.0d;
   451             m[3] = (y0 + y0p) / 2.0d;
   433             return;
   452         } else {
   434         }
   453             double t = x10p*(y0-y0p) - y10p*(x0-x0p);
   435         double t = x10p*(y0-y0p) - y10p*(x0-x0p);
   454             t /= den;
   436         t /= den;
   455             m[2] = x0 + t*x10;
   437         m[off++] = x0 + t*x10;
   456             m[3] = y0 + t*y10;
   438         m[off] = y0 + t*y10;
   457         }
   439     }
   458     }
   440 
   459 
   441     private void drawMiter(final double pdx, final double pdy,
   460     private void drawMiter(final double pdx, final double pdy,
   442                            final double x0, final double y0,
   461                            final double x0, final double y0,
   443                            final double dx, final double dy,
   462                            final double dx, final double dy,
   444                            double omx, double omy, double mx, double my,
   463                            double omx, double omy,
       
   464                            double mx, double my,
   445                            boolean rev)
   465                            boolean rev)
   446     {
   466     {
   447         if ((mx == omx && my == omy) ||
   467         if ((mx == omx && my == omy) ||
   448             (pdx == 0.0d && pdy == 0.0d) ||
   468             (pdx == 0.0d && pdy == 0.0d) ||
   449             (dx == 0.0d && dy == 0.0d))
   469             (dx == 0.0d && dy == 0.0d))
   457             mx  = -mx;
   477             mx  = -mx;
   458             my  = -my;
   478             my  = -my;
   459         }
   479         }
   460 
   480 
   461         computeMiter((x0 - pdx) + omx, (y0 - pdy) + omy, x0 + omx, y0 + omy,
   481         computeMiter((x0 - pdx) + omx, (y0 - pdy) + omy, x0 + omx, y0 + omy,
   462                      (dx + x0) + mx, (dy + y0) + my, x0 + mx, y0 + my,
   482                      (dx + x0) + mx, (dy + y0) + my, x0 + mx, y0 + my, miter);
   463                      miter, 0);
       
   464 
   483 
   465         final double miterX = miter[0];
   484         final double miterX = miter[0];
   466         final double miterY = miter[1];
   485         final double miterY = miter[1];
   467         double lenSq = (miterX-x0)*(miterX-x0) + (miterY-y0)*(miterY-y0);
   486         double lenSq = (miterX-x0)*(miterX-x0) + (miterY-y0)*(miterY-y0);
   468 
   487 
   476         }
   495         }
   477     }
   496     }
   478 
   497 
   479     @Override
   498     @Override
   480     public void moveTo(final double x0, final double y0) {
   499     public void moveTo(final double x0, final double y0) {
   481         moveTo(x0, y0, cOutCode);
   500         _moveTo(x0, y0, cOutCode);
   482         // update starting point:
   501         // update starting point:
   483         this.sx0 = x0;
   502         this.sx0 = x0;
   484         this.sy0 = y0;
   503         this.sy0 = y0;
   485         this.sdx = 1.0d;
   504         this.sdx = 1.0d;
   486         this.sdy = 0.0d;
   505         this.sdy = 0.0d;
   492             this.cOutCode = outcode;
   511             this.cOutCode = outcode;
   493             this.sOutCode = outcode;
   512             this.sOutCode = outcode;
   494         }
   513         }
   495     }
   514     }
   496 
   515 
   497     private void moveTo(final double x0, final double y0,
   516     private void _moveTo(final double x0, final double y0,
   498                         final int outcode)
   517                         final int outcode)
   499     {
   518     {
   500         if (prev == MOVE_TO) {
   519         if (prev == MOVE_TO) {
   501             this.cx0 = x0;
   520             this.cx0 = x0;
   502             this.cy0 = y0;
   521             this.cy0 = y0;
   519 
   538 
   520     private void lineTo(final double x1, final double y1,
   539     private void lineTo(final double x1, final double y1,
   521                         final boolean force)
   540                         final boolean force)
   522     {
   541     {
   523         final int outcode0 = this.cOutCode;
   542         final int outcode0 = this.cOutCode;
       
   543 
   524         if (!force && clipRect != null) {
   544         if (!force && clipRect != null) {
   525             final int outcode1 = DHelpers.outcode(x1, y1, clipRect);
   545             final int outcode1 = DHelpers.outcode(x1, y1, clipRect);
       
   546 
       
   547             // Should clip
       
   548             final int orCode = (outcode0 | outcode1);
       
   549             if (orCode != 0) {
       
   550                 final int sideCode = outcode0 & outcode1;
       
   551 
       
   552                 // basic rejection criteria:
       
   553                 if (sideCode == 0) {
       
   554                     // ovelap clip:
       
   555                     if (subdivide) {
       
   556                         // avoid reentrance
       
   557                         subdivide = false;
       
   558                         // subdivide curve => callback with subdivided parts:
       
   559                         boolean ret = curveSplitter.splitLine(cx0, cy0, x1, y1,
       
   560                                                               orCode, this);
       
   561                         // reentrance is done:
       
   562                         subdivide = true;
       
   563                         if (ret) {
       
   564                             return;
       
   565                         }
       
   566                     }
       
   567                     // already subdivided so render it
       
   568                 } else {
       
   569                     this.cOutCode = outcode1;
       
   570                     _moveTo(x1, y1, outcode0);
       
   571                     opened = true;
       
   572                     return;
       
   573                 }
       
   574             }
       
   575 
   526             this.cOutCode = outcode1;
   576             this.cOutCode = outcode1;
   527 
       
   528             // basic rejection criteria
       
   529             if ((outcode0 & outcode1) != 0) {
       
   530                 moveTo(x1, y1, outcode0);
       
   531                 opened = true;
       
   532                 return;
       
   533             }
       
   534         }
   577         }
   535 
   578 
   536         double dx = x1 - cx0;
   579         double dx = x1 - cx0;
   537         double dy = y1 - cy0;
   580         double dy = y1 - cy0;
   538         if (dx == 0.0d && dy == 0.0d) {
   581         if (dx == 0.0d && dy == 0.0d) {
   750             final boolean cw = isCW(pdx, pdy, dx, dy);
   793             final boolean cw = isCW(pdx, pdy, dx, dy);
   751             if (outcode == 0) {
   794             if (outcode == 0) {
   752                 if (joinStyle == JOIN_MITER) {
   795                 if (joinStyle == JOIN_MITER) {
   753                     drawMiter(pdx, pdy, x0, y0, dx, dy, omx, omy, mx, my, cw);
   796                     drawMiter(pdx, pdy, x0, y0, dx, dy, omx, omy, mx, my, cw);
   754                 } else if (joinStyle == JOIN_ROUND) {
   797                 } else if (joinStyle == JOIN_ROUND) {
   755                     drawRoundJoin(x0, y0,
   798                     mayDrawRoundJoin(x0, y0, omx, omy, mx, my, cw);
   756                                   omx, omy,
       
   757                                   mx, my, cw,
       
   758                                   ROUND_JOIN_THRESHOLD);
       
   759                 }
   799                 }
   760             }
   800             }
   761             emitLineTo(x0, y0, !cw);
   801             emitLineTo(x0, y0, !cw);
   762         }
   802         }
   763         prev = DRAWING_OP_TO;
   803         prev = DRAWING_OP_TO;
   764     }
   804     }
   765 
   805 
   766     private static boolean within(final double x1, final double y1,
   806     private static boolean within(final double x1, final double y1,
   767                                   final double x2, final double y2,
   807                                   final double x2, final double y2,
   768                                   final double ERR)
   808                                   final double err)
   769     {
   809     {
   770         assert ERR > 0 : "";
   810         assert err > 0 : "";
   771         // compare taxicab distance. ERR will always be small, so using
   811         // compare taxicab distance. ERR will always be small, so using
   772         // true distance won't give much benefit
   812         // true distance won't give much benefit
   773         return (DHelpers.within(x1, x2, ERR) &&  // we want to avoid calling Math.abs
   813         return (DHelpers.within(x1, x2, err) && // we want to avoid calling Math.abs
   774                 DHelpers.within(y1, y2, ERR)); // this is just as good.
   814                 DHelpers.within(y1, y2, err));  // this is just as good.
   775     }
   815     }
   776 
   816 
   777     private void getLineOffsets(double x1, double y1,
   817     private void getLineOffsets(final double x1, final double y1,
   778                                 double x2, double y2,
   818                                 final double x2, final double y2,
   779                                 double[] left, double[] right) {
   819                                 final double[] left, final double[] right)
       
   820     {
   780         computeOffset(x2 - x1, y2 - y1, lineWidth2, offset0);
   821         computeOffset(x2 - x1, y2 - y1, lineWidth2, offset0);
   781         final double mx = offset0[0];
   822         final double mx = offset0[0];
   782         final double my = offset0[1];
   823         final double my = offset0[1];
   783         left[0] = x1 + mx;
   824         left[0] = x1 + mx;
   784         left[1] = y1 + my;
   825         left[1] = y1 + my;
   785         left[2] = x2 + mx;
   826         left[2] = x2 + mx;
   786         left[3] = y2 + my;
   827         left[3] = y2 + my;
       
   828 
   787         right[0] = x1 - mx;
   829         right[0] = x1 - mx;
   788         right[1] = y1 - my;
   830         right[1] = y1 - my;
   789         right[2] = x2 - mx;
   831         right[2] = x2 - mx;
   790         right[3] = y2 - my;
   832         right[3] = y2 - my;
   791     }
   833     }
   792 
   834 
   793     private int computeOffsetCubic(double[] pts, final int off,
   835     private int computeOffsetCubic(final double[] pts, final int off,
   794                                    double[] leftOff, double[] rightOff)
   836                                    final double[] leftOff,
       
   837                                    final double[] rightOff)
   795     {
   838     {
   796         // if p1=p2 or p3=p4 it means that the derivative at the endpoint
   839         // if p1=p2 or p3=p4 it means that the derivative at the endpoint
   797         // vanishes, which creates problems with computeOffset. Usually
   840         // vanishes, which creates problems with computeOffset. Usually
   798         // this happens when this stroker object is trying to widen
   841         // this happens when this stroker object is trying to widen
   799         // a curve with a cusp. What happens is that curveTo splits
   842         // a curve with a cusp. What happens is that curveTo splits
   800         // the input curve at the cusp, and passes it to this function.
   843         // the input curve at the cusp, and passes it to this function.
   801         // because of inaccuracies in the splitting, we consider points
   844         // because of inaccuracies in the splitting, we consider points
   802         // equal if they're very close to each other.
   845         // equal if they're very close to each other.
   803         final double x1 = pts[off + 0], y1 = pts[off + 1];
   846         final double x1 = pts[off    ], y1 = pts[off + 1];
   804         final double x2 = pts[off + 2], y2 = pts[off + 3];
   847         final double x2 = pts[off + 2], y2 = pts[off + 3];
   805         final double x3 = pts[off + 4], y3 = pts[off + 5];
   848         final double x3 = pts[off + 4], y3 = pts[off + 5];
   806         final double x4 = pts[off + 6], y4 = pts[off + 7];
   849         final double x4 = pts[off + 6], y4 = pts[off + 7];
   807 
   850 
   808         double dx4 = x4 - x3;
   851         double dx4 = x4 - x3;
   812 
   855 
   813         // if p1 == p2 && p3 == p4: draw line from p1->p4, unless p1 == p4,
   856         // if p1 == p2 && p3 == p4: draw line from p1->p4, unless p1 == p4,
   814         // in which case ignore if p1 == p2
   857         // in which case ignore if p1 == p2
   815         final boolean p1eqp2 = within(x1, y1, x2, y2, 6.0d * Math.ulp(y2));
   858         final boolean p1eqp2 = within(x1, y1, x2, y2, 6.0d * Math.ulp(y2));
   816         final boolean p3eqp4 = within(x3, y3, x4, y4, 6.0d * Math.ulp(y4));
   859         final boolean p3eqp4 = within(x3, y3, x4, y4, 6.0d * Math.ulp(y4));
       
   860 
   817         if (p1eqp2 && p3eqp4) {
   861         if (p1eqp2 && p3eqp4) {
   818             getLineOffsets(x1, y1, x4, y4, leftOff, rightOff);
   862             getLineOffsets(x1, y1, x4, y4, leftOff, rightOff);
   819             return 4;
   863             return 4;
   820         } else if (p1eqp2) {
   864         } else if (p1eqp2) {
   821             dx1 = x3 - x1;
   865             dx1 = x3 - x1;
   827 
   871 
   828         // if p2-p1 and p4-p3 are parallel, that must mean this curve is a line
   872         // if p2-p1 and p4-p3 are parallel, that must mean this curve is a line
   829         double dotsq = (dx1 * dx4 + dy1 * dy4);
   873         double dotsq = (dx1 * dx4 + dy1 * dy4);
   830         dotsq *= dotsq;
   874         dotsq *= dotsq;
   831         double l1sq = dx1 * dx1 + dy1 * dy1, l4sq = dx4 * dx4 + dy4 * dy4;
   875         double l1sq = dx1 * dx1 + dy1 * dy1, l4sq = dx4 * dx4 + dy4 * dy4;
       
   876 
   832         if (DHelpers.within(dotsq, l1sq * l4sq, 4.0d * Math.ulp(dotsq))) {
   877         if (DHelpers.within(dotsq, l1sq * l4sq, 4.0d * Math.ulp(dotsq))) {
   833             getLineOffsets(x1, y1, x4, y4, leftOff, rightOff);
   878             getLineOffsets(x1, y1, x4, y4, leftOff, rightOff);
   834             return 4;
   879             return 4;
   835         }
   880         }
   836 
   881 
   940     }
   985     }
   941 
   986 
   942     // compute offset curves using bezier spline through t=0.5 (i.e.
   987     // compute offset curves using bezier spline through t=0.5 (i.e.
   943     // ComputedCurve(0.5) == IdealParallelCurve(0.5))
   988     // ComputedCurve(0.5) == IdealParallelCurve(0.5))
   944     // return the kind of curve in the right and left arrays.
   989     // return the kind of curve in the right and left arrays.
   945     private int computeOffsetQuad(double[] pts, final int off,
   990     private int computeOffsetQuad(final double[] pts, final int off,
   946                                   double[] leftOff, double[] rightOff)
   991                                   final double[] leftOff,
   947     {
   992                                   final double[] rightOff)
   948         final double x1 = pts[off + 0], y1 = pts[off + 1];
   993     {
       
   994         final double x1 = pts[off    ], y1 = pts[off + 1];
   949         final double x2 = pts[off + 2], y2 = pts[off + 3];
   995         final double x2 = pts[off + 2], y2 = pts[off + 3];
   950         final double x3 = pts[off + 4], y3 = pts[off + 5];
   996         final double x3 = pts[off + 4], y3 = pts[off + 5];
   951 
   997 
   952         final double dx3 = x3 - x2;
   998         final double dx3 = x3 - x2;
   953         final double dy3 = y3 - y2;
   999         final double dy3 = y3 - y2;
   964 
  1010 
   965         // if p1 == p2 && p3 == p4: draw line from p1->p4, unless p1 == p4,
  1011         // if p1 == p2 && p3 == p4: draw line from p1->p4, unless p1 == p4,
   966         // in which case ignore.
  1012         // in which case ignore.
   967         final boolean p1eqp2 = within(x1, y1, x2, y2, 6.0d * Math.ulp(y2));
  1013         final boolean p1eqp2 = within(x1, y1, x2, y2, 6.0d * Math.ulp(y2));
   968         final boolean p2eqp3 = within(x2, y2, x3, y3, 6.0d * Math.ulp(y3));
  1014         final boolean p2eqp3 = within(x2, y2, x3, y3, 6.0d * Math.ulp(y3));
       
  1015 
   969         if (p1eqp2 || p2eqp3) {
  1016         if (p1eqp2 || p2eqp3) {
   970             getLineOffsets(x1, y1, x3, y3, leftOff, rightOff);
  1017             getLineOffsets(x1, y1, x3, y3, leftOff, rightOff);
   971             return 4;
  1018             return 4;
   972         }
  1019         }
   973 
  1020 
   974         // if p2-p1 and p4-p3 are parallel, that must mean this curve is a line
  1021         // if p2-p1 and p4-p3 are parallel, that must mean this curve is a line
   975         double dotsq = (dx1 * dx3 + dy1 * dy3);
  1022         double dotsq = (dx1 * dx3 + dy1 * dy3);
   976         dotsq *= dotsq;
  1023         dotsq *= dotsq;
   977         double l1sq = dx1 * dx1 + dy1 * dy1, l3sq = dx3 * dx3 + dy3 * dy3;
  1024         double l1sq = dx1 * dx1 + dy1 * dy1, l3sq = dx3 * dx3 + dy3 * dy3;
       
  1025 
   978         if (DHelpers.within(dotsq, l1sq * l3sq, 4.0d * Math.ulp(dotsq))) {
  1026         if (DHelpers.within(dotsq, l1sq * l3sq, 4.0d * Math.ulp(dotsq))) {
   979             getLineOffsets(x1, y1, x3, y3, leftOff, rightOff);
  1027             getLineOffsets(x1, y1, x3, y3, leftOff, rightOff);
   980             return 4;
  1028             return 4;
   981         }
  1029         }
   982 
  1030 
   988 
  1036 
   989         double x1p = x1 + offset0[0]; // start
  1037         double x1p = x1 + offset0[0]; // start
   990         double y1p = y1 + offset0[1]; // point
  1038         double y1p = y1 + offset0[1]; // point
   991         double x3p = x3 + offset1[0]; // end
  1039         double x3p = x3 + offset1[0]; // end
   992         double y3p = y3 + offset1[1]; // point
  1040         double y3p = y3 + offset1[1]; // point
   993         safeComputeMiter(x1p, y1p, x1p+dx1, y1p+dy1, x3p, y3p, x3p-dx3, y3p-dy3, leftOff, 2);
  1041         safeComputeMiter(x1p, y1p, x1p+dx1, y1p+dy1, x3p, y3p, x3p-dx3, y3p-dy3, leftOff);
   994         leftOff[0] = x1p; leftOff[1] = y1p;
  1042         leftOff[0] = x1p; leftOff[1] = y1p;
   995         leftOff[4] = x3p; leftOff[5] = y3p;
  1043         leftOff[4] = x3p; leftOff[5] = y3p;
   996 
  1044 
   997         x1p = x1 - offset0[0]; y1p = y1 - offset0[1];
  1045         x1p = x1 - offset0[0]; y1p = y1 - offset0[1];
   998         x3p = x3 - offset1[0]; y3p = y3 - offset1[1];
  1046         x3p = x3 - offset1[0]; y3p = y3 - offset1[1];
   999         safeComputeMiter(x1p, y1p, x1p+dx1, y1p+dy1, x3p, y3p, x3p-dx3, y3p-dy3, rightOff, 2);
  1047         safeComputeMiter(x1p, y1p, x1p+dx1, y1p+dy1, x3p, y3p, x3p-dx3, y3p-dy3, rightOff);
  1000         rightOff[0] = x1p; rightOff[1] = y1p;
  1048         rightOff[0] = x1p; rightOff[1] = y1p;
  1001         rightOff[4] = x3p; rightOff[5] = y3p;
  1049         rightOff[4] = x3p; rightOff[5] = y3p;
  1002         return 6;
  1050         return 6;
  1003     }
       
  1004 
       
  1005     // finds values of t where the curve in pts should be subdivided in order
       
  1006     // to get good offset curves a distance of w away from the middle curve.
       
  1007     // Stores the points in ts, and returns how many of them there were.
       
  1008     private static int findSubdivPoints(final DCurve c, double[] pts, double[] ts,
       
  1009                                         final int type, final double w)
       
  1010     {
       
  1011         final double x12 = pts[2] - pts[0];
       
  1012         final double y12 = pts[3] - pts[1];
       
  1013         // if the curve is already parallel to either axis we gain nothing
       
  1014         // from rotating it.
       
  1015         if (y12 != 0.0d && x12 != 0.0d) {
       
  1016             // we rotate it so that the first vector in the control polygon is
       
  1017             // parallel to the x-axis. This will ensure that rotated quarter
       
  1018             // circles won't be subdivided.
       
  1019             final double hypot = Math.sqrt(x12 * x12 + y12 * y12);
       
  1020             final double cos = x12 / hypot;
       
  1021             final double sin = y12 / hypot;
       
  1022             final double x1 = cos * pts[0] + sin * pts[1];
       
  1023             final double y1 = cos * pts[1] - sin * pts[0];
       
  1024             final double x2 = cos * pts[2] + sin * pts[3];
       
  1025             final double y2 = cos * pts[3] - sin * pts[2];
       
  1026             final double x3 = cos * pts[4] + sin * pts[5];
       
  1027             final double y3 = cos * pts[5] - sin * pts[4];
       
  1028 
       
  1029             switch(type) {
       
  1030             case 8:
       
  1031                 final double x4 = cos * pts[6] + sin * pts[7];
       
  1032                 final double y4 = cos * pts[7] - sin * pts[6];
       
  1033                 c.set(x1, y1, x2, y2, x3, y3, x4, y4);
       
  1034                 break;
       
  1035             case 6:
       
  1036                 c.set(x1, y1, x2, y2, x3, y3);
       
  1037                 break;
       
  1038             default:
       
  1039             }
       
  1040         } else {
       
  1041             c.set(pts, type);
       
  1042         }
       
  1043 
       
  1044         int ret = 0;
       
  1045         // we subdivide at values of t such that the remaining rotated
       
  1046         // curves are monotonic in x and y.
       
  1047         ret += c.dxRoots(ts, ret);
       
  1048         ret += c.dyRoots(ts, ret);
       
  1049         // subdivide at inflection points.
       
  1050         if (type == 8) {
       
  1051             // quadratic curves can't have inflection points
       
  1052             ret += c.infPoints(ts, ret);
       
  1053         }
       
  1054 
       
  1055         // now we must subdivide at points where one of the offset curves will have
       
  1056         // a cusp. This happens at ts where the radius of curvature is equal to w.
       
  1057         ret += c.rootsOfROCMinusW(ts, ret, w, 0.0001d);
       
  1058 
       
  1059         ret = DHelpers.filterOutNotInAB(ts, 0, ret, 0.0001d, 0.9999d);
       
  1060         DHelpers.isort(ts, 0, ret);
       
  1061         return ret;
       
  1062     }
  1051     }
  1063 
  1052 
  1064     @Override
  1053     @Override
  1065     public void curveTo(final double x1, final double y1,
  1054     public void curveTo(final double x1, final double y1,
  1066                         final double x2, final double y2,
  1055                         final double x2, final double y2,
  1067                         final double x3, final double y3)
  1056                         final double x3, final double y3)
  1068     {
  1057     {
  1069         final int outcode0 = this.cOutCode;
  1058         final int outcode0 = this.cOutCode;
       
  1059 
  1070         if (clipRect != null) {
  1060         if (clipRect != null) {
       
  1061             final int outcode1 = DHelpers.outcode(x1, y1, clipRect);
       
  1062             final int outcode2 = DHelpers.outcode(x2, y2, clipRect);
  1071             final int outcode3 = DHelpers.outcode(x3, y3, clipRect);
  1063             final int outcode3 = DHelpers.outcode(x3, y3, clipRect);
  1072             this.cOutCode = outcode3;
  1064 
  1073 
  1065             // Should clip
  1074             if ((outcode0 & outcode3) != 0) {
  1066             final int orCode = (outcode0 | outcode1 | outcode2 | outcode3);
  1075                 final int outcode1 = DHelpers.outcode(x1, y1, clipRect);
  1067             if (orCode != 0) {
  1076                 final int outcode2 = DHelpers.outcode(x2, y2, clipRect);
  1068                 final int sideCode = outcode0 & outcode1 & outcode2 & outcode3;
  1077 
  1069 
  1078                 // basic rejection criteria
  1070                 // basic rejection criteria:
  1079                 if ((outcode0 & outcode1 & outcode2 & outcode3) != 0) {
  1071                 if (sideCode == 0) {
  1080                     moveTo(x3, y3, outcode0);
  1072                     // ovelap clip:
       
  1073                     if (subdivide) {
       
  1074                         // avoid reentrance
       
  1075                         subdivide = false;
       
  1076                         // subdivide curve => callback with subdivided parts:
       
  1077                         boolean ret = curveSplitter.splitCurve(cx0, cy0, x1, y1,
       
  1078                                                                x2, y2, x3, y3,
       
  1079                                                                orCode, this);
       
  1080                         // reentrance is done:
       
  1081                         subdivide = true;
       
  1082                         if (ret) {
       
  1083                             return;
       
  1084                         }
       
  1085                     }
       
  1086                     // already subdivided so render it
       
  1087                 } else {
       
  1088                     this.cOutCode = outcode3;
       
  1089                     _moveTo(x3, y3, outcode0);
  1081                     opened = true;
  1090                     opened = true;
  1082                     return;
  1091                     return;
  1083                 }
  1092                 }
  1084             }
  1093             }
  1085         }
  1094 
  1086 
  1095             this.cOutCode = outcode3;
  1087         final double[] mid = middle;
  1096         }
  1088 
  1097         _curveTo(x1, y1, x2, y2, x3, y3, outcode0);
  1089         mid[0] = cx0; mid[1] = cy0;
  1098     }
  1090         mid[2] = x1;  mid[3] = y1;
  1099 
  1091         mid[4] = x2;  mid[5] = y2;
  1100     private void _curveTo(final double x1, final double y1,
  1092         mid[6] = x3;  mid[7] = y3;
  1101                           final double x2, final double y2,
  1093 
  1102                           final double x3, final double y3,
       
  1103                           final int outcode0)
       
  1104     {
  1094         // need these so we can update the state at the end of this method
  1105         // need these so we can update the state at the end of this method
  1095         final double xf = x3, yf = y3;
  1106         double dxs = x1 - cx0;
  1096         double dxs = mid[2] - mid[0];
  1107         double dys = y1 - cy0;
  1097         double dys = mid[3] - mid[1];
  1108         double dxf = x3 - x2;
  1098         double dxf = mid[6] - mid[4];
  1109         double dyf = y3 - y2;
  1099         double dyf = mid[7] - mid[5];
  1110 
  1100 
  1111         if ((dxs == 0.0d) && (dys == 0.0d)) {
  1101         boolean p1eqp2 = (dxs == 0.0d && dys == 0.0d);
  1112             dxs = x2 - cx0;
  1102         boolean p3eqp4 = (dxf == 0.0d && dyf == 0.0d);
  1113             dys = y2 - cy0;
  1103         if (p1eqp2) {
  1114             if ((dxs == 0.0d) && (dys == 0.0d)) {
  1104             dxs = mid[4] - mid[0];
  1115                 dxs = x3 - cx0;
  1105             dys = mid[5] - mid[1];
  1116                 dys = y3 - cy0;
  1106             if (dxs == 0.0d && dys == 0.0d) {
  1117             }
  1107                 dxs = mid[6] - mid[0];
  1118         }
  1108                 dys = mid[7] - mid[1];
  1119         if ((dxf == 0.0d) && (dyf == 0.0d)) {
  1109             }
  1120             dxf = x3 - x1;
  1110         }
  1121             dyf = y3 - y1;
  1111         if (p3eqp4) {
  1122             if ((dxf == 0.0d) && (dyf == 0.0d)) {
  1112             dxf = mid[6] - mid[2];
  1123                 dxf = x3 - cx0;
  1113             dyf = mid[7] - mid[3];
  1124                 dyf = y3 - cy0;
  1114             if (dxf == 0.0d && dyf == 0.0d) {
  1125             }
  1115                 dxf = mid[6] - mid[0];
  1126         }
  1116                 dyf = mid[7] - mid[1];
  1127         if ((dxs == 0.0d) && (dys == 0.0d)) {
  1117             }
       
  1118         }
       
  1119         if (dxs == 0.0d && dys == 0.0d) {
       
  1120             // this happens if the "curve" is just a point
  1128             // this happens if the "curve" is just a point
  1121             // fix outcode0 for lineTo() call:
  1129             // fix outcode0 for lineTo() call:
  1122             if (clipRect != null) {
  1130             if (clipRect != null) {
  1123                 this.cOutCode = outcode0;
  1131                 this.cOutCode = outcode0;
  1124             }
  1132             }
  1125             lineTo(mid[0], mid[1]);
  1133             lineTo(cx0, cy0);
  1126             return;
  1134             return;
  1127         }
  1135         }
  1128 
  1136 
  1129         // if these vectors are too small, normalize them, to avoid future
  1137         // if these vectors are too small, normalize them, to avoid future
  1130         // precision problems.
  1138         // precision problems.
  1131         if (Math.abs(dxs) < 0.1d && Math.abs(dys) < 0.1d) {
  1139         if (Math.abs(dxs) < 0.1d && Math.abs(dys) < 0.1d) {
  1132             double len = Math.sqrt(dxs*dxs + dys*dys);
  1140             final double len = Math.sqrt(dxs * dxs + dys * dys);
  1133             dxs /= len;
  1141             dxs /= len;
  1134             dys /= len;
  1142             dys /= len;
  1135         }
  1143         }
  1136         if (Math.abs(dxf) < 0.1d && Math.abs(dyf) < 0.1d) {
  1144         if (Math.abs(dxf) < 0.1d && Math.abs(dyf) < 0.1d) {
  1137             double len = Math.sqrt(dxf*dxf + dyf*dyf);
  1145             final double len = Math.sqrt(dxf * dxf + dyf * dyf);
  1138             dxf /= len;
  1146             dxf /= len;
  1139             dyf /= len;
  1147             dyf /= len;
  1140         }
  1148         }
  1141 
  1149 
  1142         computeOffset(dxs, dys, lineWidth2, offset0);
  1150         computeOffset(dxs, dys, lineWidth2, offset0);
  1143         drawJoin(cdx, cdy, cx0, cy0, dxs, dys, cmx, cmy, offset0[0], offset0[1], outcode0);
  1151         drawJoin(cdx, cdy, cx0, cy0, dxs, dys, cmx, cmy, offset0[0], offset0[1], outcode0);
  1144 
  1152 
  1145         final int nSplits = findSubdivPoints(curve, mid, subdivTs, 8, lineWidth2);
  1153         int nSplits = 0;
  1146 
  1154         final double[] mid;
  1147         double prevT = 0.0d;
       
  1148         for (int i = 0, off = 0; i < nSplits; i++, off += 6) {
       
  1149             final double t = subdivTs[i];
       
  1150             DHelpers.subdivideCubicAt((t - prevT) / (1.0d - prevT),
       
  1151                                      mid, off, mid, off, mid, off + 6);
       
  1152             prevT = t;
       
  1153         }
       
  1154 
       
  1155         final double[] l = lp;
  1155         final double[] l = lp;
       
  1156 
       
  1157         if (monotonize) {
       
  1158             // monotonize curve:
       
  1159             final CurveBasicMonotonizer monotonizer
       
  1160                 = rdrCtx.monotonizer.curve(cx0, cy0, x1, y1, x2, y2, x3, y3);
       
  1161 
       
  1162             nSplits = monotonizer.nbSplits;
       
  1163             mid = monotonizer.middle;
       
  1164         } else {
       
  1165             // use left instead:
       
  1166             mid = l;
       
  1167             mid[0] = cx0; mid[1] = cy0;
       
  1168             mid[2] = x1;  mid[3] = y1;
       
  1169             mid[4] = x2;  mid[5] = y2;
       
  1170             mid[6] = x3;  mid[7] = y3;
       
  1171         }
  1156         final double[] r = rp;
  1172         final double[] r = rp;
  1157 
  1173 
  1158         int kind = 0;
  1174         int kind = 0;
  1159         for (int i = 0, off = 0; i <= nSplits; i++, off += 6) {
  1175         for (int i = 0, off = 0; i <= nSplits; i++, off += 6) {
  1160             kind = computeOffsetCubic(mid, off, l, r);
  1176             kind = computeOffsetCubic(mid, off, l, r);
  1174             }
  1190             }
  1175             emitLineToRev(r[kind - 2], r[kind - 1]);
  1191             emitLineToRev(r[kind - 2], r[kind - 1]);
  1176         }
  1192         }
  1177 
  1193 
  1178         this.prev = DRAWING_OP_TO;
  1194         this.prev = DRAWING_OP_TO;
  1179         this.cx0 = xf;
  1195         this.cx0 = x3;
  1180         this.cy0 = yf;
  1196         this.cy0 = y3;
  1181         this.cdx = dxf;
  1197         this.cdx = dxf;
  1182         this.cdy = dyf;
  1198         this.cdy = dyf;
  1183         this.cmx = (l[kind - 2] - r[kind - 2]) / 2.0d;
  1199         this.cmx = (l[kind - 2] - r[kind - 2]) / 2.0d;
  1184         this.cmy = (l[kind - 1] - r[kind - 1]) / 2.0d;
  1200         this.cmy = (l[kind - 1] - r[kind - 1]) / 2.0d;
  1185     }
  1201     }
  1187     @Override
  1203     @Override
  1188     public void quadTo(final double x1, final double y1,
  1204     public void quadTo(final double x1, final double y1,
  1189                        final double x2, final double y2)
  1205                        final double x2, final double y2)
  1190     {
  1206     {
  1191         final int outcode0 = this.cOutCode;
  1207         final int outcode0 = this.cOutCode;
       
  1208 
  1192         if (clipRect != null) {
  1209         if (clipRect != null) {
       
  1210             final int outcode1 = DHelpers.outcode(x1, y1, clipRect);
  1193             final int outcode2 = DHelpers.outcode(x2, y2, clipRect);
  1211             final int outcode2 = DHelpers.outcode(x2, y2, clipRect);
  1194             this.cOutCode = outcode2;
  1212 
  1195 
  1213             // Should clip
  1196             if ((outcode0 & outcode2) != 0) {
  1214             final int orCode = (outcode0 | outcode1 | outcode2);
  1197                 final int outcode1 = DHelpers.outcode(x1, y1, clipRect);
  1215             if (orCode != 0) {
  1198 
  1216                 final int sideCode = outcode0 & outcode1 & outcode2;
  1199                 // basic rejection criteria
  1217 
  1200                 if ((outcode0 & outcode1 & outcode2) != 0) {
  1218                 // basic rejection criteria:
  1201                     moveTo(x2, y2, outcode0);
  1219                 if (sideCode == 0) {
       
  1220                     // ovelap clip:
       
  1221                     if (subdivide) {
       
  1222                         // avoid reentrance
       
  1223                         subdivide = false;
       
  1224                         // subdivide curve => call lineTo() with subdivided curves:
       
  1225                         boolean ret = curveSplitter.splitQuad(cx0, cy0, x1, y1,
       
  1226                                                               x2, y2, orCode, this);
       
  1227                         // reentrance is done:
       
  1228                         subdivide = true;
       
  1229                         if (ret) {
       
  1230                             return;
       
  1231                         }
       
  1232                     }
       
  1233                     // already subdivided so render it
       
  1234                 } else {
       
  1235                     this.cOutCode = outcode2;
       
  1236                     _moveTo(x2, y2, outcode0);
  1202                     opened = true;
  1237                     opened = true;
  1203                     return;
  1238                     return;
  1204                 }
  1239                 }
  1205             }
  1240             }
  1206         }
  1241 
  1207 
  1242             this.cOutCode = outcode2;
  1208         final double[] mid = middle;
  1243         }
  1209 
  1244         _quadTo(x1, y1, x2, y2, outcode0);
  1210         mid[0] = cx0; mid[1] = cy0;
  1245     }
  1211         mid[2] = x1;  mid[3] = y1;
  1246 
  1212         mid[4] = x2;  mid[5] = y2;
  1247     private void _quadTo(final double x1, final double y1,
  1213 
  1248                          final double x2, final double y2,
       
  1249                          final int outcode0)
       
  1250     {
  1214         // need these so we can update the state at the end of this method
  1251         // need these so we can update the state at the end of this method
  1215         final double xf = x2, yf = y2;
  1252         double dxs = x1 - cx0;
  1216         double dxs = mid[2] - mid[0];
  1253         double dys = y1 - cy0;
  1217         double dys = mid[3] - mid[1];
  1254         double dxf = x2 - x1;
  1218         double dxf = mid[4] - mid[2];
  1255         double dyf = y2 - y1;
  1219         double dyf = mid[5] - mid[3];
  1256 
  1220         if ((dxs == 0.0d && dys == 0.0d) || (dxf == 0.0d && dyf == 0.0d)) {
  1257         if (((dxs == 0.0d) && (dys == 0.0d)) || ((dxf == 0.0d) && (dyf == 0.0d))) {
  1221             dxs = dxf = mid[4] - mid[0];
  1258             dxs = dxf = x2 - cx0;
  1222             dys = dyf = mid[5] - mid[1];
  1259             dys = dyf = y2 - cy0;
  1223         }
  1260         }
  1224         if (dxs == 0.0d && dys == 0.0d) {
  1261         if ((dxs == 0.0d) && (dys == 0.0d)) {
  1225             // this happens if the "curve" is just a point
  1262             // this happens if the "curve" is just a point
  1226             // fix outcode0 for lineTo() call:
  1263             // fix outcode0 for lineTo() call:
  1227             if (clipRect != null) {
  1264             if (clipRect != null) {
  1228                 this.cOutCode = outcode0;
  1265                 this.cOutCode = outcode0;
  1229             }
  1266             }
  1230             lineTo(mid[0], mid[1]);
  1267             lineTo(cx0, cy0);
  1231             return;
  1268             return;
  1232         }
  1269         }
  1233         // if these vectors are too small, normalize them, to avoid future
  1270         // if these vectors are too small, normalize them, to avoid future
  1234         // precision problems.
  1271         // precision problems.
  1235         if (Math.abs(dxs) < 0.1d && Math.abs(dys) < 0.1d) {
  1272         if (Math.abs(dxs) < 0.1d && Math.abs(dys) < 0.1d) {
  1236             double len = Math.sqrt(dxs*dxs + dys*dys);
  1273             final double len = Math.sqrt(dxs * dxs + dys * dys);
  1237             dxs /= len;
  1274             dxs /= len;
  1238             dys /= len;
  1275             dys /= len;
  1239         }
  1276         }
  1240         if (Math.abs(dxf) < 0.1d && Math.abs(dyf) < 0.1d) {
  1277         if (Math.abs(dxf) < 0.1d && Math.abs(dyf) < 0.1d) {
  1241             double len = Math.sqrt(dxf*dxf + dyf*dyf);
  1278             final double len = Math.sqrt(dxf * dxf + dyf * dyf);
  1242             dxf /= len;
  1279             dxf /= len;
  1243             dyf /= len;
  1280             dyf /= len;
  1244         }
  1281         }
  1245 
       
  1246         computeOffset(dxs, dys, lineWidth2, offset0);
  1282         computeOffset(dxs, dys, lineWidth2, offset0);
  1247         drawJoin(cdx, cdy, cx0, cy0, dxs, dys, cmx, cmy, offset0[0], offset0[1], outcode0);
  1283         drawJoin(cdx, cdy, cx0, cy0, dxs, dys, cmx, cmy, offset0[0], offset0[1], outcode0);
  1248 
  1284 
  1249         int nSplits = findSubdivPoints(curve, mid, subdivTs, 6, lineWidth2);
  1285         int nSplits = 0;
  1250 
  1286         final double[] mid;
  1251         double prevt = 0.0d;
       
  1252         for (int i = 0, off = 0; i < nSplits; i++, off += 4) {
       
  1253             final double t = subdivTs[i];
       
  1254             DHelpers.subdivideQuadAt((t - prevt) / (1.0d - prevt),
       
  1255                                     mid, off, mid, off, mid, off + 4);
       
  1256             prevt = t;
       
  1257         }
       
  1258 
       
  1259         final double[] l = lp;
  1287         final double[] l = lp;
       
  1288 
       
  1289         if (monotonize) {
       
  1290             // monotonize quad:
       
  1291             final CurveBasicMonotonizer monotonizer
       
  1292                 = rdrCtx.monotonizer.quad(cx0, cy0, x1, y1, x2, y2);
       
  1293 
       
  1294             nSplits = monotonizer.nbSplits;
       
  1295             mid = monotonizer.middle;
       
  1296         } else {
       
  1297             // use left instead:
       
  1298             mid = l;
       
  1299             mid[0] = cx0; mid[1] = cy0;
       
  1300             mid[2] = x1;  mid[3] = y1;
       
  1301             mid[4] = x2;  mid[5] = y2;
       
  1302         }
  1260         final double[] r = rp;
  1303         final double[] r = rp;
  1261 
  1304 
  1262         int kind = 0;
  1305         int kind = 0;
  1263         for (int i = 0, off = 0; i <= nSplits; i++, off += 4) {
  1306         for (int i = 0, off = 0; i <= nSplits; i++, off += 4) {
  1264             kind = computeOffsetQuad(mid, off, l, r);
  1307             kind = computeOffsetQuad(mid, off, l, r);
  1278             }
  1321             }
  1279             emitLineToRev(r[kind - 2], r[kind - 1]);
  1322             emitLineToRev(r[kind - 2], r[kind - 1]);
  1280         }
  1323         }
  1281 
  1324 
  1282         this.prev = DRAWING_OP_TO;
  1325         this.prev = DRAWING_OP_TO;
  1283         this.cx0 = xf;
  1326         this.cx0 = x2;
  1284         this.cy0 = yf;
  1327         this.cy0 = y2;
  1285         this.cdx = dxf;
  1328         this.cdx = dxf;
  1286         this.cdy = dyf;
  1329         this.cdy = dyf;
  1287         this.cmx = (l[kind - 2] - r[kind - 2]) / 2.0d;
  1330         this.cmx = (l[kind - 2] - r[kind - 2]) / 2.0d;
  1288         this.cmy = (l[kind - 1] - r[kind - 1]) / 2.0d;
  1331         this.cmy = (l[kind - 1] - r[kind - 1]) / 2.0d;
  1289     }
  1332     }