Home | History | Annotate | Download | only in awt
      1 /*
      2  *  Licensed to the Apache Software Foundation (ASF) under one or more
      3  *  contributor license agreements.  See the NOTICE file distributed with
      4  *  this work for additional information regarding copyright ownership.
      5  *  The ASF licenses this file to You under the Apache License, Version 2.0
      6  *  (the "License"); you may not use this file except in compliance with
      7  *  the License.  You may obtain a copy of the License at
      8  *
      9  *     http://www.apache.org/licenses/LICENSE-2.0
     10  *
     11  *  Unless required by applicable law or agreed to in writing, software
     12  *  distributed under the License is distributed on an "AS IS" BASIS,
     13  *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     14  *  See the License for the specific language governing permissions and
     15  *  limitations under the License.
     16  */
     17 /**
     18  * @author Denis M. Kishenko
     19  * @version $Revision$
     20  */
     21 
     22 package java.awt;
     23 
     24 import java.awt.geom.GeneralPath;
     25 import java.awt.geom.PathIterator;
     26 
     27 import org.apache.harmony.awt.internal.nls.Messages;
     28 import org.apache.harmony.misc.HashCode;
     29 
     30 /**
     31  * The BasicStroke class specifies a set of rendering attributes for the
     32  * outlines of graphics primitives. The BasicStroke attributes describe the
     33  * shape of the pen which draws the outline of a Shape and the decorations
     34  * applied at the ends and joins of path segments of the Shape. The BasicStroke
     35  * has the following rendering attributes:
     36  * <p>
     37  * <ul>
     38  * <li>line width -the pen width which draws the outlines.</li>
     39  * <li>end caps - indicates the decoration applied to the ends of unclosed
     40  * subpaths and dash segments. The BasicStroke defines three different
     41  * decorations: CAP_BUTT, CAP_ROUND, and CAP_SQUARE.</li>
     42  * <li>line joins - indicates the decoration applied at the intersection of two
     43  * path segments and at the intersection of the endpoints of a subpath. The
     44  * BasicStroke defines three decorations: JOIN_BEVEL, JOIN_MITER, and
     45  * JOIN_ROUND.</li>
     46  * <li>miter limit - the limit to trim a line join that has a JOIN_MITER
     47  * decoration.</li>
     48  * <li>dash attributes - the definition of how to make a dash pattern by
     49  * alternating between opaque and transparent sections</li>
     50  * </ul>
     51  * </p>
     52  *
     53  * @since Android 1.0
     54  */
     55 public class BasicStroke implements Stroke {
     56 
     57     /**
     58      * The Constant CAP_BUTT indicates the ends of unclosed subpaths and dash
     59      * segments have no added decoration.
     60      */
     61     public static final int CAP_BUTT = 0;
     62 
     63     /**
     64      * The Constant CAP_ROUND indicates the ends of unclosed subpaths and dash
     65      * segments have a round decoration.
     66      */
     67     public static final int CAP_ROUND = 1;
     68 
     69     /**
     70      * The Constant CAP_SQUARE indicates the ends of unclosed subpaths and dash
     71      * segments have a square projection.
     72      */
     73     public static final int CAP_SQUARE = 2;
     74 
     75     /**
     76      * The Constant JOIN_MITER indicates that path segments are joined by
     77      * extending their outside edges until they meet.
     78      */
     79     public static final int JOIN_MITER = 0;
     80 
     81     /**
     82      * The Constant JOIN_ROUND indicates that path segments are joined by
     83      * rounding off the corner at a radius of half the line width.
     84      */
     85     public static final int JOIN_ROUND = 1;
     86 
     87     /**
     88      * The Constant JOIN_BEVEL indicates that path segments are joined by
     89      * connecting the outer corners of their wide outlines with a straight
     90      * segment.
     91      */
     92     public static final int JOIN_BEVEL = 2;
     93 
     94     /**
     95      * Constants for calculating.
     96      */
     97     static final int MAX_LEVEL = 20; // Maximal deepness of curve subdivision
     98 
     99     /**
    100      * The Constant CURVE_DELTA.
    101      */
    102     static final double CURVE_DELTA = 2.0; // Width tolerance
    103 
    104     /**
    105      * The Constant CORNER_ANGLE.
    106      */
    107     static final double CORNER_ANGLE = 4.0; // Minimum corner angle
    108 
    109     /**
    110      * The Constant CORNER_ZERO.
    111      */
    112     static final double CORNER_ZERO = 0.01; // Zero angle
    113 
    114     /**
    115      * The Constant CUBIC_ARC.
    116      */
    117     static final double CUBIC_ARC = 4.0 / 3.0 * (Math.sqrt(2.0) - 1);
    118 
    119     /**
    120      * Stroke width.
    121      */
    122     float width;
    123 
    124     /**
    125      * Stroke cap type.
    126      */
    127     int cap;
    128 
    129     /**
    130      * Stroke join type.
    131      */
    132     int join;
    133 
    134     /**
    135      * Stroke miter limit.
    136      */
    137     float miterLimit;
    138 
    139     /**
    140      * Stroke dashes array.
    141      */
    142     float dash[];
    143 
    144     /**
    145      * Stroke dash phase.
    146      */
    147     float dashPhase;
    148 
    149     /**
    150      * The temporary pre-calculated values.
    151      */
    152     double curveDelta;
    153 
    154     /**
    155      * The corner delta.
    156      */
    157     double cornerDelta;
    158 
    159     /**
    160      * The zero delta.
    161      */
    162     double zeroDelta;
    163 
    164     /**
    165      * The w2.
    166      */
    167     double w2;
    168 
    169     /**
    170      * The fmy.
    171      */
    172     double fmx, fmy;
    173 
    174     /**
    175      * The smy.
    176      */
    177     double scx, scy, smx, smy;
    178 
    179     /**
    180      * The cy.
    181      */
    182     double mx, my, cx, cy;
    183 
    184     /**
    185      * The temporary indicators.
    186      */
    187     boolean isMove;
    188 
    189     /**
    190      * The is first.
    191      */
    192     boolean isFirst;
    193 
    194     /**
    195      * The check move.
    196      */
    197     boolean checkMove;
    198 
    199     /**
    200      * The temporary and destination work paths.
    201      */
    202     BufferedPath dst, lp, rp, sp;
    203 
    204     /**
    205      * Stroke dasher class.
    206      */
    207     Dasher dasher;
    208 
    209     /**
    210      * Instantiates a new BasicStroke with default width, cap, join, limit, dash
    211      * attributes parameters. The default parameters are a solid line of width
    212      * 1.0, CAP_SQUARE, JOIN_MITER, a miter limit of 10.0, null dash attributes,
    213      * and a dash phase of 0.0f.
    214      */
    215     public BasicStroke() {
    216         this(1.0f, CAP_SQUARE, JOIN_MITER, 10.0f, null, 0.0f);
    217     }
    218 
    219     /**
    220      * Instantiates a new BasicStroke with the specified width, caps, joins,
    221      * limit, dash attributes, dash phase parameters.
    222      *
    223      * @param width
    224      *            the width of BasikStroke.
    225      * @param cap
    226      *            the end decoration of BasikStroke.
    227      * @param join
    228      *            the join segments decoration.
    229      * @param miterLimit
    230      *            the limit to trim the miter join.
    231      * @param dash
    232      *            the array with the dashing pattern.
    233      * @param dashPhase
    234      *            the offset to start the dashing pattern.
    235      */
    236     public BasicStroke(float width, int cap, int join, float miterLimit, float[] dash,
    237             float dashPhase) {
    238         if (width < 0.0f) {
    239             // awt.133=Negative width
    240             throw new IllegalArgumentException(Messages.getString("awt.133")); //$NON-NLS-1$
    241         }
    242         if (cap != CAP_BUTT && cap != CAP_ROUND && cap != CAP_SQUARE) {
    243             // awt.134=Illegal cap
    244             throw new IllegalArgumentException(Messages.getString("awt.134")); //$NON-NLS-1$
    245         }
    246         if (join != JOIN_MITER && join != JOIN_ROUND && join != JOIN_BEVEL) {
    247             // awt.135=Illegal join
    248             throw new IllegalArgumentException(Messages.getString("awt.135")); //$NON-NLS-1$
    249         }
    250         if (join == JOIN_MITER && miterLimit < 1.0f) {
    251             // awt.136=miterLimit less than 1.0f
    252             throw new IllegalArgumentException(Messages.getString("awt.136")); //$NON-NLS-1$
    253         }
    254         if (dash != null) {
    255             if (dashPhase < 0.0f) {
    256                 // awt.137=Negative dashPhase
    257                 throw new IllegalArgumentException(Messages.getString("awt.137")); //$NON-NLS-1$
    258             }
    259             if (dash.length == 0) {
    260                 // awt.138=Zero dash length
    261                 throw new IllegalArgumentException(Messages.getString("awt.138")); //$NON-NLS-1$
    262             }
    263             ZERO: {
    264                 for (int i = 0; i < dash.length; i++) {
    265                     if (dash[i] < 0.0) {
    266                         // awt.139=Negative dash[{0}]
    267                         throw new IllegalArgumentException(Messages.getString("awt.139", i)); //$NON-NLS-1$
    268                     }
    269                     if (dash[i] > 0.0) {
    270                         break ZERO;
    271                     }
    272                 }
    273                 // awt.13A=All dash lengths zero
    274                 throw new IllegalArgumentException(Messages.getString("awt.13A")); //$NON-NLS-1$
    275             }
    276         }
    277         this.width = width;
    278         this.cap = cap;
    279         this.join = join;
    280         this.miterLimit = miterLimit;
    281         this.dash = dash;
    282         this.dashPhase = dashPhase;
    283     }
    284 
    285     /**
    286      * Instantiates a new BasicStroke with specified width, cap, join, limit and
    287      * default dash attributes parameters.
    288      *
    289      * @param width
    290      *            the width of BasikStroke.
    291      * @param cap
    292      *            the end decoration of BasikStroke.
    293      * @param join
    294      *            the join segments decoration.
    295      * @param miterLimit
    296      *            the limit to trim the miter join.
    297      */
    298     public BasicStroke(float width, int cap, int join, float miterLimit) {
    299         this(width, cap, join, miterLimit, null, 0.0f);
    300     }
    301 
    302     /**
    303      * Instantiates a new BasicStroke with specified width, cap, join and
    304      * default limit and dash attributes parameters.
    305      *
    306      * @param width
    307      *            the width of BasikStroke.
    308      * @param cap
    309      *            the end decoration of BasikStroke.
    310      * @param join
    311      *            the join segments decoration.
    312      */
    313     public BasicStroke(float width, int cap, int join) {
    314         this(width, cap, join, 10.0f, null, 0.0f);
    315     }
    316 
    317     /**
    318      * Instantiates a new BasicStroke with specified width and default cap,
    319      * join, limit, dash attributes parameters.
    320      *
    321      * @param width
    322      *            the width of BasicStroke.
    323      */
    324     public BasicStroke(float width) {
    325         this(width, CAP_SQUARE, JOIN_MITER, 10.0f, null, 0.0f);
    326     }
    327 
    328     /**
    329      * Gets the line width of the BasicStroke.
    330      *
    331      * @return the line width of the BasicStroke.
    332      */
    333     public float getLineWidth() {
    334         return width;
    335     }
    336 
    337     /**
    338      * Gets the end cap style of the BasicStroke.
    339      *
    340      * @return the end cap style of the BasicStroke.
    341      */
    342     public int getEndCap() {
    343         return cap;
    344     }
    345 
    346     /**
    347      * Gets the line join style of the BasicStroke.
    348      *
    349      * @return the line join style of the BasicStroke.
    350      */
    351     public int getLineJoin() {
    352         return join;
    353     }
    354 
    355     /**
    356      * Gets the miter limit of the BasicStroke (the limit to trim the miter
    357      * join).
    358      *
    359      * @return the miter limit of the BasicStroke.
    360      */
    361     public float getMiterLimit() {
    362         return miterLimit;
    363     }
    364 
    365     /**
    366      * Gets the dash attributes array of the BasicStroke.
    367      *
    368      * @return the dash attributes array of the BasicStroke.
    369      */
    370     public float[] getDashArray() {
    371         return dash;
    372     }
    373 
    374     /**
    375      * Gets the dash phase of the BasicStroke.
    376      *
    377      * @return the dash phase of the BasicStroke.
    378      */
    379     public float getDashPhase() {
    380         return dashPhase;
    381     }
    382 
    383     /**
    384      * Returns hash code of this BasicStroke.
    385      *
    386      * @return the hash code of this BasicStroke.
    387      */
    388     @Override
    389     public int hashCode() {
    390         HashCode hash = new HashCode();
    391         hash.append(width);
    392         hash.append(cap);
    393         hash.append(join);
    394         hash.append(miterLimit);
    395         if (dash != null) {
    396             hash.append(dashPhase);
    397             for (float element : dash) {
    398                 hash.append(element);
    399             }
    400         }
    401         return hash.hashCode();
    402     }
    403 
    404     /**
    405      * Compares this BasicStroke object with the specified Object.
    406      *
    407      * @param obj
    408      *            the Object to be compared.
    409      * @return true, if the Object is a BasicStroke with the same data values as
    410      *         this BasicStroke; false otherwise.
    411      */
    412     @Override
    413     public boolean equals(Object obj) {
    414         if (obj == this) {
    415             return true;
    416         }
    417         if (obj instanceof BasicStroke) {
    418             BasicStroke bs = (BasicStroke)obj;
    419             return bs.width == width && bs.cap == cap && bs.join == join
    420                     && bs.miterLimit == miterLimit && bs.dashPhase == dashPhase
    421                     && java.util.Arrays.equals(bs.dash, dash);
    422         }
    423         return false;
    424     }
    425 
    426     /**
    427      * Calculates allowable curve derivation.
    428      *
    429      * @param width
    430      *            the width.
    431      * @return the curve delta.
    432      */
    433     double getCurveDelta(double width) {
    434         double a = width + CURVE_DELTA;
    435         double cos = 1.0 - 2.0 * width * width / (a * a);
    436         double sin = Math.sqrt(1.0 - cos * cos);
    437         return Math.abs(sin / cos);
    438     }
    439 
    440     /**
    441      * Calculates the value to detect a small angle.
    442      *
    443      * @param width
    444      *            the width.
    445      * @return the corner delta.
    446      */
    447     double getCornerDelta(double width) {
    448         return width * width * Math.sin(Math.PI * CORNER_ANGLE / 180.0);
    449     }
    450 
    451     /**
    452      * Calculates value to detect a zero angle.
    453      *
    454      * @param width
    455      *            the width.
    456      * @return the zero delta.
    457      */
    458     double getZeroDelta(double width) {
    459         return width * width * Math.sin(Math.PI * CORNER_ZERO / 180.0);
    460     }
    461 
    462     /**
    463      * Creates a Shape from the outline of the specified shape drawn with this
    464      * BasicStroke.
    465      *
    466      * @param s
    467      *            the specified Shape to be stroked.
    468      * @return the Shape of the stroked outline.
    469      * @see java.awt.Stroke#createStrokedShape(java.awt.Shape)
    470      */
    471     public Shape createStrokedShape(Shape s) {
    472         w2 = width / 2.0;
    473         curveDelta = getCurveDelta(w2);
    474         cornerDelta = getCornerDelta(w2);
    475         zeroDelta = getZeroDelta(w2);
    476 
    477         dst = new BufferedPath();
    478         lp = new BufferedPath();
    479         rp = new BufferedPath();
    480 
    481         if (dash == null) {
    482             createSolidShape(s.getPathIterator(null));
    483         } else {
    484             createDashedShape(s.getPathIterator(null));
    485         }
    486 
    487         return dst.createGeneralPath();
    488     }
    489 
    490     /**
    491      * Generates a shape with a solid (not dashed) outline.
    492      *
    493      * @param p
    494      *            the PathIterator of source shape.
    495      */
    496     void createSolidShape(PathIterator p) {
    497         double coords[] = new double[6];
    498         mx = my = cx = cy = 0.0;
    499         isMove = false;
    500         isFirst = false;
    501         checkMove = true;
    502         boolean isClosed = true;
    503 
    504         while (!p.isDone()) {
    505             switch (p.currentSegment(coords)) {
    506                 case PathIterator.SEG_MOVETO:
    507                     if (!isClosed) {
    508                         closeSolidShape();
    509                     }
    510                     rp.clean();
    511                     mx = cx = coords[0];
    512                     my = cy = coords[1];
    513                     isMove = true;
    514                     isClosed = false;
    515                     break;
    516                 case PathIterator.SEG_LINETO:
    517                     addLine(cx, cy, cx = coords[0], cy = coords[1], true);
    518                     break;
    519                 case PathIterator.SEG_QUADTO:
    520                     addQuad(cx, cy, coords[0], coords[1], cx = coords[2], cy = coords[3]);
    521                     break;
    522                 case PathIterator.SEG_CUBICTO:
    523                     addCubic(cx, cy, coords[0], coords[1], coords[2], coords[3], cx = coords[4],
    524                             cy = coords[5]);
    525                     break;
    526                 case PathIterator.SEG_CLOSE:
    527                     addLine(cx, cy, mx, my, false);
    528                     addJoin(lp, mx, my, lp.xMove, lp.yMove, true);
    529                     addJoin(rp, mx, my, rp.xMove, rp.yMove, false);
    530                     lp.closePath();
    531                     rp.closePath();
    532                     lp.appendReverse(rp);
    533                     isClosed = true;
    534                     break;
    535             }
    536             p.next();
    537         }
    538         if (!isClosed) {
    539             closeSolidShape();
    540         }
    541 
    542         dst = lp;
    543     }
    544 
    545     /**
    546      * Closes solid shape path.
    547      */
    548     void closeSolidShape() {
    549         addCap(lp, cx, cy, rp.xLast, rp.yLast);
    550         lp.combine(rp);
    551         addCap(lp, mx, my, lp.xMove, lp.yMove);
    552         lp.closePath();
    553     }
    554 
    555     /**
    556      * Generates dashed stroked shape.
    557      *
    558      * @param p
    559      *            the PathIterator of source shape.
    560      */
    561     void createDashedShape(PathIterator p) {
    562         double coords[] = new double[6];
    563         mx = my = cx = cy = 0.0;
    564         smx = smy = scx = scy = 0.0;
    565         isMove = false;
    566         checkMove = false;
    567         boolean isClosed = true;
    568 
    569         while (!p.isDone()) {
    570             switch (p.currentSegment(coords)) {
    571                 case PathIterator.SEG_MOVETO:
    572 
    573                     if (!isClosed) {
    574                         closeDashedShape();
    575                     }
    576 
    577                     dasher = new Dasher(dash, dashPhase);
    578                     lp.clean();
    579                     rp.clean();
    580                     sp = null;
    581                     isFirst = true;
    582                     isMove = true;
    583                     isClosed = false;
    584                     mx = cx = coords[0];
    585                     my = cy = coords[1];
    586                     break;
    587                 case PathIterator.SEG_LINETO:
    588                     addDashLine(cx, cy, cx = coords[0], cy = coords[1]);
    589                     break;
    590                 case PathIterator.SEG_QUADTO:
    591                     addDashQuad(cx, cy, coords[0], coords[1], cx = coords[2], cy = coords[3]);
    592                     break;
    593                 case PathIterator.SEG_CUBICTO:
    594                     addDashCubic(cx, cy, coords[0], coords[1], coords[2], coords[3],
    595                             cx = coords[4], cy = coords[5]);
    596                     break;
    597                 case PathIterator.SEG_CLOSE:
    598                     addDashLine(cx, cy, cx = mx, cy = my);
    599 
    600                     if (dasher.isConnected()) {
    601                         // Connect current and head segments
    602                         addJoin(lp, fmx, fmy, sp.xMove, sp.yMove, true);
    603                         lp.join(sp);
    604                         addJoin(lp, fmx, fmy, rp.xLast, rp.yLast, true);
    605                         lp.combine(rp);
    606                         addCap(lp, smx, smy, lp.xMove, lp.yMove);
    607                         lp.closePath();
    608                         dst.append(lp);
    609                         sp = null;
    610                     } else {
    611                         closeDashedShape();
    612                     }
    613 
    614                     isClosed = true;
    615                     break;
    616             }
    617             p.next();
    618         }
    619 
    620         if (!isClosed) {
    621             closeDashedShape();
    622         }
    623 
    624     }
    625 
    626     /**
    627      * Closes dashed shape path.
    628      */
    629     void closeDashedShape() {
    630         // Add head segment
    631         if (sp != null) {
    632             addCap(sp, fmx, fmy, sp.xMove, sp.yMove);
    633             sp.closePath();
    634             dst.append(sp);
    635         }
    636         if (lp.typeSize > 0) {
    637             // Close current segment
    638             if (!dasher.isClosed()) {
    639                 addCap(lp, scx, scy, rp.xLast, rp.yLast);
    640                 lp.combine(rp);
    641                 addCap(lp, smx, smy, lp.xMove, lp.yMove);
    642                 lp.closePath();
    643             }
    644             dst.append(lp);
    645         }
    646     }
    647 
    648     /**
    649      * Adds cap to the work path.
    650      *
    651      * @param p
    652      *            the BufferedPath object of work path.
    653      * @param x0
    654      *            the x coordinate of the source path.
    655      * @param y0
    656      *            the y coordinate on the source path.
    657      * @param x2
    658      *            the x coordinate of the next point on the work path.
    659      * @param y2
    660      *            the y coordinate of the next point on the work path.
    661      */
    662     void addCap(BufferedPath p, double x0, double y0, double x2, double y2) {
    663         double x1 = p.xLast;
    664         double y1 = p.yLast;
    665         double x10 = x1 - x0;
    666         double y10 = y1 - y0;
    667         double x20 = x2 - x0;
    668         double y20 = y2 - y0;
    669 
    670         switch (cap) {
    671             case CAP_BUTT:
    672                 p.lineTo(x2, y2);
    673                 break;
    674             case CAP_ROUND:
    675                 double mx = x10 * CUBIC_ARC;
    676                 double my = y10 * CUBIC_ARC;
    677 
    678                 double x3 = x0 + y10;
    679                 double y3 = y0 - x10;
    680 
    681                 x10 *= CUBIC_ARC;
    682                 y10 *= CUBIC_ARC;
    683                 x20 *= CUBIC_ARC;
    684                 y20 *= CUBIC_ARC;
    685 
    686                 p.cubicTo(x1 + y10, y1 - x10, x3 + mx, y3 + my, x3, y3);
    687                 p.cubicTo(x3 - mx, y3 - my, x2 - y20, y2 + x20, x2, y2);
    688                 break;
    689             case CAP_SQUARE:
    690                 p.lineTo(x1 + y10, y1 - x10);
    691                 p.lineTo(x2 - y20, y2 + x20);
    692                 p.lineTo(x2, y2);
    693                 break;
    694         }
    695     }
    696 
    697     /**
    698      * Adds bevel and miter join to the work path.
    699      *
    700      * @param p
    701      *            the BufferedPath object of work path.
    702      * @param x0
    703      *            the x coordinate of the source path.
    704      * @param y0
    705      *            the y coordinate on the source path.
    706      * @param x2
    707      *            the x coordinate of the next point on the work path.
    708      * @param y2
    709      *            the y coordinate of the next point on the work path.
    710      * @param isLeft
    711      *            the orientation of work path, true if work path lies to the
    712      *            left from source path, false otherwise.
    713      */
    714     void addJoin(BufferedPath p, double x0, double y0, double x2, double y2, boolean isLeft) {
    715         double x1 = p.xLast;
    716         double y1 = p.yLast;
    717         double x10 = x1 - x0;
    718         double y10 = y1 - y0;
    719         double x20 = x2 - x0;
    720         double y20 = y2 - y0;
    721         double sin0 = x10 * y20 - y10 * x20;
    722 
    723         // Small corner
    724         if (-cornerDelta < sin0 && sin0 < cornerDelta) {
    725             double cos0 = x10 * x20 + y10 * y20;
    726             if (cos0 > 0.0) {
    727                 // if zero corner do nothing
    728                 if (-zeroDelta > sin0 || sin0 > zeroDelta) {
    729                     double x3 = x0 + w2 * w2 * (y20 - y10) / sin0;
    730                     double y3 = y0 + w2 * w2 * (x10 - x20) / sin0;
    731                     p.setLast(x3, y3);
    732                 }
    733                 return;
    734             }
    735             // Zero corner
    736             if (-zeroDelta < sin0 && sin0 < zeroDelta) {
    737                 p.lineTo(x2, y2);
    738             }
    739             return;
    740         }
    741 
    742         if (isLeft ^ (sin0 < 0.0)) {
    743             // Twisted corner
    744             p.lineTo(x0, y0);
    745             p.lineTo(x2, y2);
    746         } else {
    747             switch (join) {
    748                 case JOIN_BEVEL:
    749                     p.lineTo(x2, y2);
    750                     break;
    751                 case JOIN_MITER:
    752                     double s1 = x1 * x10 + y1 * y10;
    753                     double s2 = x2 * x20 + y2 * y20;
    754                     double x3 = (s1 * y20 - s2 * y10) / sin0;
    755                     double y3 = (s2 * x10 - s1 * x20) / sin0;
    756                     double x30 = x3 - x0;
    757                     double y30 = y3 - y0;
    758                     double miterLength = Math.sqrt(x30 * x30 + y30 * y30);
    759                     if (miterLength < miterLimit * w2) {
    760                         p.lineTo(x3, y3);
    761                     }
    762                     p.lineTo(x2, y2);
    763                     break;
    764                 case JOIN_ROUND:
    765                     addRoundJoin(p, x0, y0, x2, y2, isLeft);
    766                     break;
    767             }
    768         }
    769     }
    770 
    771     /**
    772      * Adds round join to the work path.
    773      *
    774      * @param p
    775      *            the BufferedPath object of work path.
    776      * @param x0
    777      *            the x coordinate of the source path.
    778      * @param y0
    779      *            the y coordinate on the source path.
    780      * @param x2
    781      *            the x coordinate of the next point on the work path.
    782      * @param y2
    783      *            the y coordinate of the next point on the work path.
    784      * @param isLeft
    785      *            the orientation of work path, true if work path lies to the
    786      *            left from source path, false otherwise.
    787      */
    788     void addRoundJoin(BufferedPath p, double x0, double y0, double x2, double y2, boolean isLeft) {
    789         double x1 = p.xLast;
    790         double y1 = p.yLast;
    791         double x10 = x1 - x0;
    792         double y10 = y1 - y0;
    793         double x20 = x2 - x0;
    794         double y20 = y2 - y0;
    795 
    796         double x30 = x10 + x20;
    797         double y30 = y10 + y20;
    798 
    799         double l30 = Math.sqrt(x30 * x30 + y30 * y30);
    800 
    801         if (l30 < 1E-5) {
    802             p.lineTo(x2, y2);
    803             return;
    804         }
    805 
    806         double w = w2 / l30;
    807 
    808         x30 *= w;
    809         y30 *= w;
    810 
    811         double x3 = x0 + x30;
    812         double y3 = y0 + y30;
    813 
    814         double cos = x10 * x20 + y10 * y20;
    815         double a = Math.acos(cos / (w2 * w2));
    816         if (cos >= 0.0) {
    817             double k = 4.0 / 3.0 * Math.tan(a / 4.0);
    818             if (isLeft) {
    819                 k = -k;
    820             }
    821 
    822             x10 *= k;
    823             y10 *= k;
    824             x20 *= k;
    825             y20 *= k;
    826 
    827             p.cubicTo(x1 - y10, y1 + x10, x2 + y20, y2 - x20, x2, y2);
    828         } else {
    829             double k = 4.0 / 3.0 * Math.tan(a / 8.0);
    830             if (isLeft) {
    831                 k = -k;
    832             }
    833 
    834             x10 *= k;
    835             y10 *= k;
    836             x20 *= k;
    837             y20 *= k;
    838             x30 *= k;
    839             y30 *= k;
    840 
    841             p.cubicTo(x1 - y10, y1 + x10, x3 + y30, y3 - x30, x3, y3);
    842             p.cubicTo(x3 - y30, y3 + x30, x2 + y20, y2 - x20, x2, y2);
    843         }
    844 
    845     }
    846 
    847     /**
    848      * Adds solid line segment to the work path.
    849      *
    850      * @param x1
    851      *            the x coordinate of the start line point.
    852      * @param y1
    853      *            the y coordinate of the start line point.
    854      * @param x2
    855      *            the x coordinate of the end line point.
    856      * @param y2
    857      *            the y coordinate of the end line point.
    858      * @param zero
    859      *            if true it's allowable to add zero length line segment.
    860      */
    861     void addLine(double x1, double y1, double x2, double y2, boolean zero) {
    862         double dx = x2 - x1;
    863         double dy = y2 - y1;
    864 
    865         if (dx == 0.0 && dy == 0.0) {
    866             if (!zero) {
    867                 return;
    868             }
    869             dx = w2;
    870             dy = 0;
    871         } else {
    872             double w = w2 / Math.sqrt(dx * dx + dy * dy);
    873             dx *= w;
    874             dy *= w;
    875         }
    876 
    877         double lx1 = x1 - dy;
    878         double ly1 = y1 + dx;
    879         double rx1 = x1 + dy;
    880         double ry1 = y1 - dx;
    881 
    882         if (checkMove) {
    883             if (isMove) {
    884                 isMove = false;
    885                 lp.moveTo(lx1, ly1);
    886                 rp.moveTo(rx1, ry1);
    887             } else {
    888                 addJoin(lp, x1, y1, lx1, ly1, true);
    889                 addJoin(rp, x1, y1, rx1, ry1, false);
    890             }
    891         }
    892 
    893         lp.lineTo(x2 - dy, y2 + dx);
    894         rp.lineTo(x2 + dy, y2 - dx);
    895     }
    896 
    897     /**
    898      * Adds solid quad segment to the work path.
    899      *
    900      * @param x1
    901      *            the x coordinate of the first control point.
    902      * @param y1
    903      *            the y coordinate of the first control point.
    904      * @param x2
    905      *            the x coordinate of the second control point.
    906      * @param y2
    907      *            the y coordinate of the second control point.
    908      * @param x3
    909      *            the x coordinate of the third control point.
    910      * @param y3
    911      *            the y coordinate of the third control point.
    912      */
    913     void addQuad(double x1, double y1, double x2, double y2, double x3, double y3) {
    914         double x21 = x2 - x1;
    915         double y21 = y2 - y1;
    916         double x23 = x2 - x3;
    917         double y23 = y2 - y3;
    918 
    919         double l21 = Math.sqrt(x21 * x21 + y21 * y21);
    920         double l23 = Math.sqrt(x23 * x23 + y23 * y23);
    921 
    922         if (l21 == 0.0 && l23 == 0.0) {
    923             addLine(x1, y1, x3, y3, false);
    924             return;
    925         }
    926 
    927         if (l21 == 0.0) {
    928             addLine(x2, y2, x3, y3, false);
    929             return;
    930         }
    931 
    932         if (l23 == 0.0) {
    933             addLine(x1, y1, x2, y2, false);
    934             return;
    935         }
    936 
    937         double w;
    938         w = w2 / l21;
    939         double mx1 = -y21 * w;
    940         double my1 = x21 * w;
    941         w = w2 / l23;
    942         double mx3 = y23 * w;
    943         double my3 = -x23 * w;
    944 
    945         double lx1 = x1 + mx1;
    946         double ly1 = y1 + my1;
    947         double rx1 = x1 - mx1;
    948         double ry1 = y1 - my1;
    949 
    950         if (checkMove) {
    951             if (isMove) {
    952                 isMove = false;
    953                 lp.moveTo(lx1, ly1);
    954                 rp.moveTo(rx1, ry1);
    955             } else {
    956                 addJoin(lp, x1, y1, lx1, ly1, true);
    957                 addJoin(rp, x1, y1, rx1, ry1, false);
    958             }
    959         }
    960 
    961         if (x21 * y23 - y21 * x23 == 0.0) {
    962             // On line curve
    963             if (x21 * x23 + y21 * y23 > 0.0) {
    964                 // Twisted curve
    965                 if (l21 == l23) {
    966                     double px = x1 + (x21 + x23) / 4.0;
    967                     double py = y1 + (y21 + y23) / 4.0;
    968                     lp.lineTo(px + mx1, py + my1);
    969                     rp.lineTo(px - mx1, py - my1);
    970                     lp.lineTo(px - mx1, py - my1);
    971                     rp.lineTo(px + mx1, py + my1);
    972                     lp.lineTo(x3 - mx1, y3 - my1);
    973                     rp.lineTo(x3 + mx1, y3 + my1);
    974                 } else {
    975                     double px1, py1;
    976                     double k = l21 / (l21 + l23);
    977                     double px = x1 + (x21 + x23) * k * k;
    978                     double py = y1 + (y21 + y23) * k * k;
    979                     px1 = (x1 + px) / 2.0;
    980                     py1 = (y1 + py) / 2.0;
    981                     lp.quadTo(px1 + mx1, py1 + my1, px + mx1, py + my1);
    982                     rp.quadTo(px1 - mx1, py1 - my1, px - mx1, py - my1);
    983                     lp.lineTo(px - mx1, py - my1);
    984                     rp.lineTo(px + mx1, py + my1);
    985                     px1 = (x3 + px) / 2.0;
    986                     py1 = (y3 + py) / 2.0;
    987                     lp.quadTo(px1 - mx1, py1 - my1, x3 - mx1, y3 - my1);
    988                     rp.quadTo(px1 + mx1, py1 + my1, x3 + mx1, y3 + my1);
    989                 }
    990             } else {
    991                 // Simple curve
    992                 lp.quadTo(x2 + mx1, y2 + my1, x3 + mx3, y3 + my3);
    993                 rp.quadTo(x2 - mx1, y2 - my1, x3 - mx3, y3 - my3);
    994             }
    995         } else {
    996             addSubQuad(x1, y1, x2, y2, x3, y3, 0);
    997         }
    998     }
    999 
   1000     /**
   1001      * Subdivides solid quad curve to make outline for source quad segment and
   1002      * adds it to work path.
   1003      *
   1004      * @param x1
   1005      *            the x coordinate of the first control point.
   1006      * @param y1
   1007      *            the y coordinate of the first control point.
   1008      * @param x2
   1009      *            the x coordinate of the second control point.
   1010      * @param y2
   1011      *            the y coordinate of the second control point.
   1012      * @param x3
   1013      *            the x coordinate of the third control point.
   1014      * @param y3
   1015      *            the y coordinate of the third control point.
   1016      * @param level
   1017      *            the maximum level of subdivision deepness.
   1018      */
   1019     void addSubQuad(double x1, double y1, double x2, double y2, double x3, double y3, int level) {
   1020         double x21 = x2 - x1;
   1021         double y21 = y2 - y1;
   1022         double x23 = x2 - x3;
   1023         double y23 = y2 - y3;
   1024 
   1025         double cos = x21 * x23 + y21 * y23;
   1026         double sin = x21 * y23 - y21 * x23;
   1027 
   1028         if (level < MAX_LEVEL && (cos >= 0.0 || (Math.abs(sin / cos) > curveDelta))) {
   1029             double c1x = (x2 + x1) / 2.0;
   1030             double c1y = (y2 + y1) / 2.0;
   1031             double c2x = (x2 + x3) / 2.0;
   1032             double c2y = (y2 + y3) / 2.0;
   1033             double c3x = (c1x + c2x) / 2.0;
   1034             double c3y = (c1y + c2y) / 2.0;
   1035             addSubQuad(x1, y1, c1x, c1y, c3x, c3y, level + 1);
   1036             addSubQuad(c3x, c3y, c2x, c2y, x3, y3, level + 1);
   1037         } else {
   1038             double w;
   1039             double l21 = Math.sqrt(x21 * x21 + y21 * y21);
   1040             double l23 = Math.sqrt(x23 * x23 + y23 * y23);
   1041             w = w2 / sin;
   1042             double mx2 = (x21 * l23 + x23 * l21) * w;
   1043             double my2 = (y21 * l23 + y23 * l21) * w;
   1044             w = w2 / l23;
   1045             double mx3 = y23 * w;
   1046             double my3 = -x23 * w;
   1047             lp.quadTo(x2 + mx2, y2 + my2, x3 + mx3, y3 + my3);
   1048             rp.quadTo(x2 - mx2, y2 - my2, x3 - mx3, y3 - my3);
   1049         }
   1050     }
   1051 
   1052     /**
   1053      * Adds solid cubic segment to the work path.
   1054      *
   1055      * @param x1
   1056      *            the x coordinate of the first control point.
   1057      * @param y1
   1058      *            the y coordinate of the first control point.
   1059      * @param x2
   1060      *            the x coordinate of the second control point.
   1061      * @param y2
   1062      *            the y coordinate of the second control point.
   1063      * @param x3
   1064      *            the x coordinate of the third control point.
   1065      * @param y3
   1066      *            the y coordinate of the third control point.
   1067      * @param x4
   1068      *            the x coordinate of the fours control point.
   1069      * @param y4
   1070      *            the y coordinate of the fours control point.
   1071      */
   1072     void addCubic(double x1, double y1, double x2, double y2, double x3, double y3, double x4,
   1073             double y4) {
   1074         double x12 = x1 - x2;
   1075         double y12 = y1 - y2;
   1076         double x23 = x2 - x3;
   1077         double y23 = y2 - y3;
   1078         double x34 = x3 - x4;
   1079         double y34 = y3 - y4;
   1080 
   1081         double l12 = Math.sqrt(x12 * x12 + y12 * y12);
   1082         double l23 = Math.sqrt(x23 * x23 + y23 * y23);
   1083         double l34 = Math.sqrt(x34 * x34 + y34 * y34);
   1084 
   1085         // All edges are zero
   1086         if (l12 == 0.0 && l23 == 0.0 && l34 == 0.0) {
   1087             addLine(x1, y1, x4, y4, false);
   1088             return;
   1089         }
   1090 
   1091         // One zero edge
   1092         if (l12 == 0.0 && l23 == 0.0) {
   1093             addLine(x3, y3, x4, y4, false);
   1094             return;
   1095         }
   1096 
   1097         if (l23 == 0.0 && l34 == 0.0) {
   1098             addLine(x1, y1, x2, y2, false);
   1099             return;
   1100         }
   1101 
   1102         if (l12 == 0.0 && l34 == 0.0) {
   1103             addLine(x2, y2, x3, y3, false);
   1104             return;
   1105         }
   1106 
   1107         double w, mx1, my1, mx4, my4;
   1108         boolean onLine;
   1109 
   1110         if (l12 == 0.0) {
   1111             w = w2 / l23;
   1112             mx1 = y23 * w;
   1113             my1 = -x23 * w;
   1114             w = w2 / l34;
   1115             mx4 = y34 * w;
   1116             my4 = -x34 * w;
   1117             onLine = -x23 * y34 + y23 * x34 == 0.0; // sin3
   1118         } else if (l34 == 0.0) {
   1119             w = w2 / l12;
   1120             mx1 = y12 * w;
   1121             my1 = -x12 * w;
   1122             w = w2 / l23;
   1123             mx4 = y23 * w;
   1124             my4 = -x23 * w;
   1125             onLine = -x12 * y23 + y12 * x23 == 0.0; // sin2
   1126         } else {
   1127             w = w2 / l12;
   1128             mx1 = y12 * w;
   1129             my1 = -x12 * w;
   1130             w = w2 / l34;
   1131             mx4 = y34 * w;
   1132             my4 = -x34 * w;
   1133             if (l23 == 0.0) {
   1134                 onLine = -x12 * y34 + y12 * x34 == 0.0;
   1135             } else {
   1136                 onLine = -x12 * y34 + y12 * x34 == 0.0 && -x12 * y23 + y12 * x23 == 0.0 && // sin2
   1137                         -x23 * y34 + y23 * x34 == 0.0; // sin3
   1138             }
   1139         }
   1140 
   1141         double lx1 = x1 + mx1;
   1142         double ly1 = y1 + my1;
   1143         double rx1 = x1 - mx1;
   1144         double ry1 = y1 - my1;
   1145 
   1146         if (checkMove) {
   1147             if (isMove) {
   1148                 isMove = false;
   1149                 lp.moveTo(lx1, ly1);
   1150                 rp.moveTo(rx1, ry1);
   1151             } else {
   1152                 addJoin(lp, x1, y1, lx1, ly1, true);
   1153                 addJoin(rp, x1, y1, rx1, ry1, false);
   1154             }
   1155         }
   1156 
   1157         if (onLine) {
   1158             if ((x1 == x2 && y1 < y2) || x1 < x2) {
   1159                 l12 = -l12;
   1160             }
   1161             if ((x2 == x3 && y2 < y3) || x2 < x3) {
   1162                 l23 = -l23;
   1163             }
   1164             if ((x3 == x4 && y3 < y4) || x3 < x4) {
   1165                 l34 = -l34;
   1166             }
   1167             double d = l23 * l23 - l12 * l34;
   1168             double roots[] = new double[3];
   1169             int rc = 0;
   1170             if (d == 0.0) {
   1171                 double t = (l12 - l23) / (l12 + l34 - l23 - l23);
   1172                 if (0.0 < t && t < 1.0) {
   1173                     roots[rc++] = t;
   1174                 }
   1175             } else if (d > 0.0) {
   1176                 d = Math.sqrt(d);
   1177                 double z = l12 + l34 - l23 - l23;
   1178                 double t;
   1179                 t = (l12 - l23 + d) / z;
   1180                 if (0.0 < t && t < 1.0) {
   1181                     roots[rc++] = t;
   1182                 }
   1183                 t = (l12 - l23 - d) / z;
   1184                 if (0.0 < t && t < 1.0) {
   1185                     roots[rc++] = t;
   1186                 }
   1187             }
   1188 
   1189             if (rc > 0) {
   1190                 // Sort roots
   1191                 if (rc == 2 && roots[0] > roots[1]) {
   1192                     double tmp = roots[0];
   1193                     roots[0] = roots[1];
   1194                     roots[1] = tmp;
   1195                 }
   1196                 roots[rc++] = 1.0;
   1197 
   1198                 double ax = -x34 - x12 + x23 + x23;
   1199                 double ay = -y34 - y12 + y23 + y23;
   1200                 double bx = 3.0 * (-x23 + x12);
   1201                 double by = 3.0 * (-y23 + y12);
   1202                 double cx = 3.0 * (-x12);
   1203                 double cy = 3.0 * (-y12);
   1204                 double xPrev = x1;
   1205                 double yPrev = y1;
   1206                 for (int i = 0; i < rc; i++) {
   1207                     double t = roots[i];
   1208                     double px = t * (t * (t * ax + bx) + cx) + x1;
   1209                     double py = t * (t * (t * ay + by) + cy) + y1;
   1210                     double px1 = (xPrev + px) / 2.0;
   1211                     double py1 = (yPrev + py) / 2.0;
   1212                     lp.cubicTo(px1 + mx1, py1 + my1, px1 + mx1, py1 + my1, px + mx1, py + my1);
   1213                     rp.cubicTo(px1 - mx1, py1 - my1, px1 - mx1, py1 - my1, px - mx1, py - my1);
   1214                     if (i < rc - 1) {
   1215                         lp.lineTo(px - mx1, py - my1);
   1216                         rp.lineTo(px + mx1, py + my1);
   1217                     }
   1218                     xPrev = px;
   1219                     yPrev = py;
   1220                     mx1 = -mx1;
   1221                     my1 = -my1;
   1222                 }
   1223             } else {
   1224                 lp.cubicTo(x2 + mx1, y2 + my1, x3 + mx4, y3 + my4, x4 + mx4, y4 + my4);
   1225                 rp.cubicTo(x2 - mx1, y2 - my1, x3 - mx4, y3 - my4, x4 - mx4, y4 - my4);
   1226             }
   1227         } else {
   1228             addSubCubic(x1, y1, x2, y2, x3, y3, x4, y4, 0);
   1229         }
   1230     }
   1231 
   1232     /**
   1233      * Subdivides solid cubic curve to make outline for source quad segment and
   1234      * adds it to work path.
   1235      *
   1236      * @param x1
   1237      *            the x coordinate of the first control point.
   1238      * @param y1
   1239      *            the y coordinate of the first control point.
   1240      * @param x2
   1241      *            the x coordinate of the second control point.
   1242      * @param y2
   1243      *            the y coordinate of the second control point.
   1244      * @param x3
   1245      *            the x coordinate of the third control point.
   1246      * @param y3
   1247      *            the y coordinate of the third control point.
   1248      * @param x4
   1249      *            the x coordinate of the fours control point.
   1250      * @param y4
   1251      *            the y coordinate of the fours control point.
   1252      * @param level
   1253      *            the maximum level of subdivision deepness.
   1254      */
   1255     void addSubCubic(double x1, double y1, double x2, double y2, double x3, double y3, double x4,
   1256             double y4, int level) {
   1257         double x12 = x1 - x2;
   1258         double y12 = y1 - y2;
   1259         double x23 = x2 - x3;
   1260         double y23 = y2 - y3;
   1261         double x34 = x3 - x4;
   1262         double y34 = y3 - y4;
   1263 
   1264         double cos2 = -x12 * x23 - y12 * y23;
   1265         double cos3 = -x23 * x34 - y23 * y34;
   1266         double sin2 = -x12 * y23 + y12 * x23;
   1267         double sin3 = -x23 * y34 + y23 * x34;
   1268         double sin0 = -x12 * y34 + y12 * x34;
   1269         double cos0 = -x12 * x34 - y12 * y34;
   1270 
   1271         if (level < MAX_LEVEL
   1272                 && (sin2 != 0.0 || sin3 != 0.0 || sin0 != 0.0)
   1273                 && (cos2 >= 0.0 || cos3 >= 0.0 || cos0 >= 0.0
   1274                         || (Math.abs(sin2 / cos2) > curveDelta)
   1275                         || (Math.abs(sin3 / cos3) > curveDelta) || (Math.abs(sin0 / cos0) > curveDelta))) {
   1276             double cx = (x2 + x3) / 2.0;
   1277             double cy = (y2 + y3) / 2.0;
   1278             double lx2 = (x2 + x1) / 2.0;
   1279             double ly2 = (y2 + y1) / 2.0;
   1280             double rx3 = (x3 + x4) / 2.0;
   1281             double ry3 = (y3 + y4) / 2.0;
   1282             double lx3 = (cx + lx2) / 2.0;
   1283             double ly3 = (cy + ly2) / 2.0;
   1284             double rx2 = (cx + rx3) / 2.0;
   1285             double ry2 = (cy + ry3) / 2.0;
   1286             cx = (lx3 + rx2) / 2.0;
   1287             cy = (ly3 + ry2) / 2.0;
   1288             addSubCubic(x1, y1, lx2, ly2, lx3, ly3, cx, cy, level + 1);
   1289             addSubCubic(cx, cy, rx2, ry2, rx3, ry3, x4, y4, level + 1);
   1290         } else {
   1291             double w, mx1, my1, mx2, my2, mx3, my3, mx4, my4;
   1292             double l12 = Math.sqrt(x12 * x12 + y12 * y12);
   1293             double l23 = Math.sqrt(x23 * x23 + y23 * y23);
   1294             double l34 = Math.sqrt(x34 * x34 + y34 * y34);
   1295 
   1296             if (l12 == 0.0) {
   1297                 w = w2 / l23;
   1298                 mx1 = y23 * w;
   1299                 my1 = -x23 * w;
   1300                 w = w2 / l34;
   1301                 mx4 = y34 * w;
   1302                 my4 = -x34 * w;
   1303             } else if (l34 == 0.0) {
   1304                 w = w2 / l12;
   1305                 mx1 = y12 * w;
   1306                 my1 = -x12 * w;
   1307                 w = w2 / l23;
   1308                 mx4 = y23 * w;
   1309                 my4 = -x23 * w;
   1310             } else {
   1311                 // Common case
   1312                 w = w2 / l12;
   1313                 mx1 = y12 * w;
   1314                 my1 = -x12 * w;
   1315                 w = w2 / l34;
   1316                 mx4 = y34 * w;
   1317                 my4 = -x34 * w;
   1318             }
   1319 
   1320             if (sin2 == 0.0) {
   1321                 mx2 = mx1;
   1322                 my2 = my1;
   1323             } else {
   1324                 w = w2 / sin2;
   1325                 mx2 = -(x12 * l23 - x23 * l12) * w;
   1326                 my2 = -(y12 * l23 - y23 * l12) * w;
   1327             }
   1328             if (sin3 == 0.0) {
   1329                 mx3 = mx4;
   1330                 my3 = my4;
   1331             } else {
   1332                 w = w2 / sin3;
   1333                 mx3 = -(x23 * l34 - x34 * l23) * w;
   1334                 my3 = -(y23 * l34 - y34 * l23) * w;
   1335             }
   1336 
   1337             lp.cubicTo(x2 + mx2, y2 + my2, x3 + mx3, y3 + my3, x4 + mx4, y4 + my4);
   1338             rp.cubicTo(x2 - mx2, y2 - my2, x3 - mx3, y3 - my3, x4 - mx4, y4 - my4);
   1339         }
   1340     }
   1341 
   1342     /**
   1343      * Adds dashed line segment to the work path.
   1344      *
   1345      * @param x1
   1346      *            the x coordinate of the start line point.
   1347      * @param y1
   1348      *            the y coordinate of the start line point.
   1349      * @param x2
   1350      *            the x coordinate of the end line point.
   1351      * @param y2
   1352      *            the y coordinate of the end line point.
   1353      */
   1354     void addDashLine(double x1, double y1, double x2, double y2) {
   1355         double x21 = x2 - x1;
   1356         double y21 = y2 - y1;
   1357 
   1358         double l21 = Math.sqrt(x21 * x21 + y21 * y21);
   1359 
   1360         if (l21 == 0.0) {
   1361             return;
   1362         }
   1363 
   1364         double px1, py1;
   1365         px1 = py1 = 0.0;
   1366         double w = w2 / l21;
   1367         double mx = -y21 * w;
   1368         double my = x21 * w;
   1369 
   1370         dasher.init(new DashIterator.Line(l21));
   1371 
   1372         while (!dasher.eof()) {
   1373             double t = dasher.getValue();
   1374             scx = x1 + t * x21;
   1375             scy = y1 + t * y21;
   1376 
   1377             if (dasher.isOpen()) {
   1378                 px1 = scx;
   1379                 py1 = scy;
   1380                 double lx1 = px1 + mx;
   1381                 double ly1 = py1 + my;
   1382                 double rx1 = px1 - mx;
   1383                 double ry1 = py1 - my;
   1384                 if (isMove) {
   1385                     isMove = false;
   1386                     smx = px1;
   1387                     smy = py1;
   1388                     rp.clean();
   1389                     lp.moveTo(lx1, ly1);
   1390                     rp.moveTo(rx1, ry1);
   1391                 } else {
   1392                     addJoin(lp, x1, y1, lx1, ly1, true);
   1393                     addJoin(rp, x1, y1, rx1, ry1, false);
   1394                 }
   1395             } else if (dasher.isContinue()) {
   1396                 double px2 = scx;
   1397                 double py2 = scy;
   1398                 lp.lineTo(px2 + mx, py2 + my);
   1399                 rp.lineTo(px2 - mx, py2 - my);
   1400                 if (dasher.close) {
   1401                     addCap(lp, px2, py2, rp.xLast, rp.yLast);
   1402                     lp.combine(rp);
   1403                     if (isFirst) {
   1404                         isFirst = false;
   1405                         fmx = smx;
   1406                         fmy = smy;
   1407                         sp = lp;
   1408                         lp = new BufferedPath();
   1409                     } else {
   1410                         addCap(lp, smx, smy, lp.xMove, lp.yMove);
   1411                         lp.closePath();
   1412                     }
   1413                     isMove = true;
   1414                 }
   1415             }
   1416 
   1417             dasher.next();
   1418         }
   1419     }
   1420 
   1421     /**
   1422      * Adds dashed quad segment to the work path.
   1423      *
   1424      * @param x1
   1425      *            the x coordinate of the first control point.
   1426      * @param y1
   1427      *            the y coordinate of the first control point.
   1428      * @param x2
   1429      *            the x coordinate of the second control point.
   1430      * @param y2
   1431      *            the y coordinate of the second control point.
   1432      * @param x3
   1433      *            the x coordinate of the third control point.
   1434      * @param y3
   1435      *            the y coordinate of the third control point.
   1436      */
   1437     void addDashQuad(double x1, double y1, double x2, double y2, double x3, double y3) {
   1438 
   1439         double x21 = x2 - x1;
   1440         double y21 = y2 - y1;
   1441         double x23 = x2 - x3;
   1442         double y23 = y2 - y3;
   1443 
   1444         double l21 = Math.sqrt(x21 * x21 + y21 * y21);
   1445         double l23 = Math.sqrt(x23 * x23 + y23 * y23);
   1446 
   1447         if (l21 == 0.0 && l23 == 0.0) {
   1448             return;
   1449         }
   1450 
   1451         if (l21 == 0.0) {
   1452             addDashLine(x2, y2, x3, y3);
   1453             return;
   1454         }
   1455 
   1456         if (l23 == 0.0) {
   1457             addDashLine(x1, y1, x2, y2);
   1458             return;
   1459         }
   1460 
   1461         double ax = x1 + x3 - x2 - x2;
   1462         double ay = y1 + y3 - y2 - y2;
   1463         double bx = x2 - x1;
   1464         double by = y2 - y1;
   1465         double cx = x1;
   1466         double cy = y1;
   1467 
   1468         double px1, py1, dx1, dy1;
   1469         px1 = py1 = dx1 = dy1 = 0.0;
   1470         double prev = 0.0;
   1471 
   1472         dasher.init(new DashIterator.Quad(x1, y1, x2, y2, x3, y3));
   1473 
   1474         while (!dasher.eof()) {
   1475             double t = dasher.getValue();
   1476             double dx = t * ax + bx;
   1477             double dy = t * ay + by;
   1478             scx = t * (dx + bx) + cx; // t^2 * ax + 2.0 * t * bx + cx
   1479             scy = t * (dy + by) + cy; // t^2 * ay + 2.0 * t * by + cy
   1480             if (dasher.isOpen()) {
   1481                 px1 = scx;
   1482                 py1 = scy;
   1483                 dx1 = dx;
   1484                 dy1 = dy;
   1485                 double w = w2 / Math.sqrt(dx1 * dx1 + dy1 * dy1);
   1486                 double mx1 = -dy1 * w;
   1487                 double my1 = dx1 * w;
   1488                 double lx1 = px1 + mx1;
   1489                 double ly1 = py1 + my1;
   1490                 double rx1 = px1 - mx1;
   1491                 double ry1 = py1 - my1;
   1492                 if (isMove) {
   1493                     isMove = false;
   1494                     smx = px1;
   1495                     smy = py1;
   1496                     rp.clean();
   1497                     lp.moveTo(lx1, ly1);
   1498                     rp.moveTo(rx1, ry1);
   1499                 } else {
   1500                     addJoin(lp, x1, y1, lx1, ly1, true);
   1501                     addJoin(rp, x1, y1, rx1, ry1, false);
   1502                 }
   1503             } else if (dasher.isContinue()) {
   1504                 double px3 = scx;
   1505                 double py3 = scy;
   1506                 double sx = x2 - x23 * prev;
   1507                 double sy = y2 - y23 * prev;
   1508                 double t2 = (t - prev) / (1 - prev);
   1509                 double px2 = px1 + (sx - px1) * t2;
   1510                 double py2 = py1 + (sy - py1) * t2;
   1511 
   1512                 addQuad(px1, py1, px2, py2, px3, py3);
   1513                 if (dasher.isClosed()) {
   1514                     addCap(lp, px3, py3, rp.xLast, rp.yLast);
   1515                     lp.combine(rp);
   1516                     if (isFirst) {
   1517                         isFirst = false;
   1518                         fmx = smx;
   1519                         fmy = smy;
   1520                         sp = lp;
   1521                         lp = new BufferedPath();
   1522                     } else {
   1523                         addCap(lp, smx, smy, lp.xMove, lp.yMove);
   1524                         lp.closePath();
   1525                     }
   1526                     isMove = true;
   1527                 }
   1528             }
   1529 
   1530             prev = t;
   1531             dasher.next();
   1532         }
   1533     }
   1534 
   1535     /**
   1536      * Adds dashed cubic segment to the work path.
   1537      *
   1538      * @param x1
   1539      *            the x coordinate of the first control point.
   1540      * @param y1
   1541      *            the y coordinate of the first control point.
   1542      * @param x2
   1543      *            the x coordinate of the second control point.
   1544      * @param y2
   1545      *            the y coordinate of the second control point.
   1546      * @param x3
   1547      *            the x coordinate of the third control point.
   1548      * @param y3
   1549      *            the y coordinate of the third control point.
   1550      * @param x4
   1551      *            the x coordinate of the fours control point.
   1552      * @param y4
   1553      *            the y coordinate of the fours control point.
   1554      */
   1555     void addDashCubic(double x1, double y1, double x2, double y2, double x3, double y3, double x4,
   1556             double y4) {
   1557 
   1558         double x12 = x1 - x2;
   1559         double y12 = y1 - y2;
   1560         double x23 = x2 - x3;
   1561         double y23 = y2 - y3;
   1562         double x34 = x3 - x4;
   1563         double y34 = y3 - y4;
   1564 
   1565         double l12 = Math.sqrt(x12 * x12 + y12 * y12);
   1566         double l23 = Math.sqrt(x23 * x23 + y23 * y23);
   1567         double l34 = Math.sqrt(x34 * x34 + y34 * y34);
   1568 
   1569         // All edges are zero
   1570         if (l12 == 0.0 && l23 == 0.0 && l34 == 0.0) {
   1571             // NOTHING
   1572             return;
   1573         }
   1574 
   1575         // One zero edge
   1576         if (l12 == 0.0 && l23 == 0.0) {
   1577             addDashLine(x3, y3, x4, y4);
   1578             return;
   1579         }
   1580 
   1581         if (l23 == 0.0 && l34 == 0.0) {
   1582             addDashLine(x1, y1, x2, y2);
   1583             return;
   1584         }
   1585 
   1586         if (l12 == 0.0 && l34 == 0.0) {
   1587             addDashLine(x2, y2, x3, y3);
   1588             return;
   1589         }
   1590 
   1591         double ax = x4 - x1 + 3.0 * (x2 - x3);
   1592         double ay = y4 - y1 + 3.0 * (y2 - y3);
   1593         double bx = 3.0 * (x1 + x3 - x2 - x2);
   1594         double by = 3.0 * (y1 + y3 - y2 - y2);
   1595         double cx = 3.0 * (x2 - x1);
   1596         double cy = 3.0 * (y2 - y1);
   1597         double dx = x1;
   1598         double dy = y1;
   1599 
   1600         double px1 = 0.0;
   1601         double py1 = 0.0;
   1602         double prev = 0.0;
   1603 
   1604         dasher.init(new DashIterator.Cubic(x1, y1, x2, y2, x3, y3, x4, y4));
   1605 
   1606         while (!dasher.eof()) {
   1607 
   1608             double t = dasher.getValue();
   1609             scx = t * (t * (t * ax + bx) + cx) + dx;
   1610             scy = t * (t * (t * ay + by) + cy) + dy;
   1611             if (dasher.isOpen()) {
   1612                 px1 = scx;
   1613                 py1 = scy;
   1614                 double dx1 = t * (t * (ax + ax + ax) + bx + bx) + cx;
   1615                 double dy1 = t * (t * (ay + ay + ay) + by + by) + cy;
   1616                 double w = w2 / Math.sqrt(dx1 * dx1 + dy1 * dy1);
   1617                 double mx1 = -dy1 * w;
   1618                 double my1 = dx1 * w;
   1619                 double lx1 = px1 + mx1;
   1620                 double ly1 = py1 + my1;
   1621                 double rx1 = px1 - mx1;
   1622                 double ry1 = py1 - my1;
   1623                 if (isMove) {
   1624                     isMove = false;
   1625                     smx = px1;
   1626                     smy = py1;
   1627                     rp.clean();
   1628                     lp.moveTo(lx1, ly1);
   1629                     rp.moveTo(rx1, ry1);
   1630                 } else {
   1631                     addJoin(lp, x1, y1, lx1, ly1, true);
   1632                     addJoin(rp, x1, y1, rx1, ry1, false);
   1633                 }
   1634             } else if (dasher.isContinue()) {
   1635                 double sx1 = x2 - x23 * prev;
   1636                 double sy1 = y2 - y23 * prev;
   1637                 double sx2 = x3 - x34 * prev;
   1638                 double sy2 = y3 - y34 * prev;
   1639                 double sx3 = sx1 + (sx2 - sx1) * prev;
   1640                 double sy3 = sy1 + (sy2 - sy1) * prev;
   1641                 double t2 = (t - prev) / (1 - prev);
   1642                 double sx4 = sx3 + (sx2 - sx3) * t2;
   1643                 double sy4 = sy3 + (sy2 - sy3) * t2;
   1644 
   1645                 double px4 = scx;
   1646                 double py4 = scy;
   1647                 double px2 = px1 + (sx3 - px1) * t2;
   1648                 double py2 = py1 + (sy3 - py1) * t2;
   1649                 double px3 = px2 + (sx4 - px2) * t2;
   1650                 double py3 = py2 + (sy4 - py2) * t2;
   1651 
   1652                 addCubic(px1, py1, px2, py2, px3, py3, px4, py4);
   1653                 if (dasher.isClosed()) {
   1654                     addCap(lp, px4, py4, rp.xLast, rp.yLast);
   1655                     lp.combine(rp);
   1656                     if (isFirst) {
   1657                         isFirst = false;
   1658                         fmx = smx;
   1659                         fmy = smy;
   1660                         sp = lp;
   1661                         lp = new BufferedPath();
   1662                     } else {
   1663                         addCap(lp, smx, smy, lp.xMove, lp.yMove);
   1664                         lp.closePath();
   1665                     }
   1666                     isMove = true;
   1667                 }
   1668             }
   1669 
   1670             prev = t;
   1671             dasher.next();
   1672         }
   1673     }
   1674 
   1675     /**
   1676      * Dasher class provides dashing for particular dash style.
   1677      */
   1678     class Dasher {
   1679 
   1680         /**
   1681          * The pos.
   1682          */
   1683         double pos;
   1684 
   1685         /**
   1686          * The first.
   1687          */
   1688         boolean close, visible, first;
   1689 
   1690         /**
   1691          * The dash.
   1692          */
   1693         float dash[];
   1694 
   1695         /**
   1696          * The phase.
   1697          */
   1698         float phase;
   1699 
   1700         /**
   1701          * The index.
   1702          */
   1703         int index;
   1704 
   1705         /**
   1706          * The iter.
   1707          */
   1708         DashIterator iter;
   1709 
   1710         /**
   1711          * Instantiates a new dasher.
   1712          *
   1713          * @param dash
   1714          *            the dash.
   1715          * @param phase
   1716          *            the phase.
   1717          */
   1718         Dasher(float dash[], float phase) {
   1719             this.dash = dash;
   1720             this.phase = phase;
   1721             index = 0;
   1722             pos = phase;
   1723             visible = true;
   1724             while (pos >= dash[index]) {
   1725                 visible = !visible;
   1726                 pos -= dash[index];
   1727                 index = (index + 1) % dash.length;
   1728             }
   1729             pos = -pos;
   1730             first = visible;
   1731         }
   1732 
   1733         /**
   1734          * Inits the.
   1735          *
   1736          * @param iter
   1737          *            the iter.
   1738          */
   1739         void init(DashIterator iter) {
   1740             this.iter = iter;
   1741             close = true;
   1742         }
   1743 
   1744         /**
   1745          * Checks if is open.
   1746          *
   1747          * @return true, if is open.
   1748          */
   1749         boolean isOpen() {
   1750             return visible && pos < iter.length;
   1751         }
   1752 
   1753         /**
   1754          * Checks if is continue.
   1755          *
   1756          * @return true, if is continue.
   1757          */
   1758         boolean isContinue() {
   1759             return !visible && pos > 0;
   1760         }
   1761 
   1762         /**
   1763          * Checks if is closed.
   1764          *
   1765          * @return true, if is closed.
   1766          */
   1767         boolean isClosed() {
   1768             return close;
   1769         }
   1770 
   1771         /**
   1772          * Checks if is connected.
   1773          *
   1774          * @return true, if is connected.
   1775          */
   1776         boolean isConnected() {
   1777             return first && !close;
   1778         }
   1779 
   1780         /**
   1781          * Eof.
   1782          *
   1783          * @return true, if successful.
   1784          */
   1785         boolean eof() {
   1786             if (!close) {
   1787                 pos -= iter.length;
   1788                 return true;
   1789             }
   1790             if (pos >= iter.length) {
   1791                 if (visible) {
   1792                     pos -= iter.length;
   1793                     return true;
   1794                 }
   1795                 close = pos == iter.length;
   1796             }
   1797             return false;
   1798         }
   1799 
   1800         /**
   1801          * Next.
   1802          */
   1803         void next() {
   1804             if (close) {
   1805                 pos += dash[index];
   1806                 index = (index + 1) % dash.length;
   1807             } else {
   1808                 // Go back
   1809                 index = (index + dash.length - 1) % dash.length;
   1810                 pos -= dash[index];
   1811             }
   1812             visible = !visible;
   1813         }
   1814 
   1815         /**
   1816          * Gets the value.
   1817          *
   1818          * @return the value.
   1819          */
   1820         double getValue() {
   1821             double t = iter.getNext(pos);
   1822             return t < 0 ? 0 : (t > 1 ? 1 : t);
   1823         }
   1824 
   1825     }
   1826 
   1827     /**
   1828      * DashIterator class provides dashing for particular segment type.
   1829      */
   1830     static abstract class DashIterator {
   1831 
   1832         /**
   1833          * The Constant FLATNESS.
   1834          */
   1835         static final double FLATNESS = 1.0;
   1836 
   1837         /**
   1838          * The Class Line.
   1839          */
   1840         static class Line extends DashIterator {
   1841 
   1842             /**
   1843              * Instantiates a new line.
   1844              *
   1845              * @param len
   1846              *            the len.
   1847              */
   1848             Line(double len) {
   1849                 length = len;
   1850             }
   1851 
   1852             @Override
   1853             double getNext(double dashPos) {
   1854                 return dashPos / length;
   1855             }
   1856 
   1857         }
   1858 
   1859         /**
   1860          * The Class Quad.
   1861          */
   1862         static class Quad extends DashIterator {
   1863 
   1864             /**
   1865              * The val size.
   1866              */
   1867             int valSize;
   1868 
   1869             /**
   1870              * The val pos.
   1871              */
   1872             int valPos;
   1873 
   1874             /**
   1875              * The cur len.
   1876              */
   1877             double curLen;
   1878 
   1879             /**
   1880              * The prev len.
   1881              */
   1882             double prevLen;
   1883 
   1884             /**
   1885              * The last len.
   1886              */
   1887             double lastLen;
   1888 
   1889             /**
   1890              * The values.
   1891              */
   1892             double[] values;
   1893 
   1894             /**
   1895              * The step.
   1896              */
   1897             double step;
   1898 
   1899             /**
   1900              * Instantiates a new quad.
   1901              *
   1902              * @param x1
   1903              *            the x1.
   1904              * @param y1
   1905              *            the y1.
   1906              * @param x2
   1907              *            the x2.
   1908              * @param y2
   1909              *            the y2.
   1910              * @param x3
   1911              *            the x3.
   1912              * @param y3
   1913              *            the y3.
   1914              */
   1915             Quad(double x1, double y1, double x2, double y2, double x3, double y3) {
   1916 
   1917                 double nx = x1 + x3 - x2 - x2;
   1918                 double ny = y1 + y3 - y2 - y2;
   1919 
   1920                 int n = (int)(1 + Math.sqrt(0.75 * (Math.abs(nx) + Math.abs(ny)) * FLATNESS));
   1921                 step = 1.0 / n;
   1922 
   1923                 double ax = x1 + x3 - x2 - x2;
   1924                 double ay = y1 + y3 - y2 - y2;
   1925                 double bx = 2.0 * (x2 - x1);
   1926                 double by = 2.0 * (y2 - y1);
   1927 
   1928                 double dx1 = step * (step * ax + bx);
   1929                 double dy1 = step * (step * ay + by);
   1930                 double dx2 = step * (step * ax * 2.0);
   1931                 double dy2 = step * (step * ay * 2.0);
   1932                 double vx = x1;
   1933                 double vy = y1;
   1934 
   1935                 valSize = n;
   1936                 values = new double[valSize];
   1937                 double pvx = vx;
   1938                 double pvy = vy;
   1939                 length = 0.0;
   1940                 for (int i = 0; i < n; i++) {
   1941                     vx += dx1;
   1942                     vy += dy1;
   1943                     dx1 += dx2;
   1944                     dy1 += dy2;
   1945                     double lx = vx - pvx;
   1946                     double ly = vy - pvy;
   1947                     values[i] = Math.sqrt(lx * lx + ly * ly);
   1948                     length += values[i];
   1949                     pvx = vx;
   1950                     pvy = vy;
   1951                 }
   1952 
   1953                 valPos = 0;
   1954                 curLen = 0.0;
   1955                 prevLen = 0.0;
   1956             }
   1957 
   1958             @Override
   1959             double getNext(double dashPos) {
   1960                 double t = 2.0;
   1961                 while (curLen <= dashPos && valPos < valSize) {
   1962                     prevLen = curLen;
   1963                     curLen += lastLen = values[valPos++];
   1964                 }
   1965                 if (curLen > dashPos) {
   1966                     t = (valPos - 1 + (dashPos - prevLen) / lastLen) * step;
   1967                 }
   1968                 return t;
   1969             }
   1970 
   1971         }
   1972 
   1973         /**
   1974          * The Class Cubic.
   1975          */
   1976         static class Cubic extends DashIterator {
   1977 
   1978             /**
   1979              * The val size.
   1980              */
   1981             int valSize;
   1982 
   1983             /**
   1984              * The val pos.
   1985              */
   1986             int valPos;
   1987 
   1988             /**
   1989              * The cur len.
   1990              */
   1991             double curLen;
   1992 
   1993             /**
   1994              * The prev len.
   1995              */
   1996             double prevLen;
   1997 
   1998             /**
   1999              * The last len.
   2000              */
   2001             double lastLen;
   2002 
   2003             /**
   2004              * The values.
   2005              */
   2006             double[] values;
   2007 
   2008             /**
   2009              * The step.
   2010              */
   2011             double step;
   2012 
   2013             /**
   2014              * Instantiates a new cubic.
   2015              *
   2016              * @param x1
   2017              *            the x1.
   2018              * @param y1
   2019              *            the y1.
   2020              * @param x2
   2021              *            the x2.
   2022              * @param y2
   2023              *            the y2.
   2024              * @param x3
   2025              *            the x3.
   2026              * @param y3
   2027              *            the y3.
   2028              * @param x4
   2029              *            the x4.
   2030              * @param y4
   2031              *            the y4.
   2032              */
   2033             Cubic(double x1, double y1, double x2, double y2, double x3, double y3, double x4,
   2034                     double y4) {
   2035 
   2036                 double nx1 = x1 + x3 - x2 - x2;
   2037                 double ny1 = y1 + y3 - y2 - y2;
   2038                 double nx2 = x2 + x4 - x3 - x3;
   2039                 double ny2 = y2 + y4 - y3 - y3;
   2040 
   2041                 double max = Math.max(Math.abs(nx1) + Math.abs(ny1), Math.abs(nx2) + Math.abs(ny2));
   2042                 int n = (int)(1 + Math.sqrt(0.75 * max) * FLATNESS);
   2043                 step = 1.0 / n;
   2044 
   2045                 double ax = x4 - x1 + 3.0 * (x2 - x3);
   2046                 double ay = y4 - y1 + 3.0 * (y2 - y3);
   2047                 double bx = 3.0 * (x1 + x3 - x2 - x2);
   2048                 double by = 3.0 * (y1 + y3 - y2 - y2);
   2049                 double cx = 3.0 * (x2 - x1);
   2050                 double cy = 3.0 * (y2 - y1);
   2051 
   2052                 double dx1 = step * (step * (step * ax + bx) + cx);
   2053                 double dy1 = step * (step * (step * ay + by) + cy);
   2054                 double dx2 = step * (step * (step * ax * 6.0 + bx * 2.0));
   2055                 double dy2 = step * (step * (step * ay * 6.0 + by * 2.0));
   2056                 double dx3 = step * (step * (step * ax * 6.0));
   2057                 double dy3 = step * (step * (step * ay * 6.0));
   2058                 double vx = x1;
   2059                 double vy = y1;
   2060 
   2061                 valSize = n;
   2062                 values = new double[valSize];
   2063                 double pvx = vx;
   2064                 double pvy = vy;
   2065                 length = 0.0;
   2066                 for (int i = 0; i < n; i++) {
   2067                     vx += dx1;
   2068                     vy += dy1;
   2069                     dx1 += dx2;
   2070                     dy1 += dy2;
   2071                     dx2 += dx3;
   2072                     dy2 += dy3;
   2073                     double lx = vx - pvx;
   2074                     double ly = vy - pvy;
   2075                     values[i] = Math.sqrt(lx * lx + ly * ly);
   2076                     length += values[i];
   2077                     pvx = vx;
   2078                     pvy = vy;
   2079                 }
   2080 
   2081                 valPos = 0;
   2082                 curLen = 0.0;
   2083                 prevLen = 0.0;
   2084             }
   2085 
   2086             @Override
   2087             double getNext(double dashPos) {
   2088                 double t = 2.0;
   2089                 while (curLen <= dashPos && valPos < valSize) {
   2090                     prevLen = curLen;
   2091                     curLen += lastLen = values[valPos++];
   2092                 }
   2093                 if (curLen > dashPos) {
   2094                     t = (valPos - 1 + (dashPos - prevLen) / lastLen) * step;
   2095                 }
   2096                 return t;
   2097             }
   2098 
   2099         }
   2100 
   2101         /**
   2102          * The length.
   2103          */
   2104         double length;
   2105 
   2106         /**
   2107          * Gets the next.
   2108          *
   2109          * @param dashPos
   2110          *            the dash pos.
   2111          * @return the next.
   2112          */
   2113         abstract double getNext(double dashPos);
   2114 
   2115     }
   2116 
   2117     /**
   2118      * BufferedPath class provides work path storing and processing.
   2119      */
   2120     static class BufferedPath {
   2121 
   2122         /**
   2123          * The Constant bufCapacity.
   2124          */
   2125         private static final int bufCapacity = 10;
   2126 
   2127         /**
   2128          * The point shift.
   2129          */
   2130         static int pointShift[] = {
   2131                 2, // MOVETO
   2132                 2, // LINETO
   2133                 4, // QUADTO
   2134                 6, // CUBICTO
   2135                 0
   2136         }; // CLOSE
   2137 
   2138         /**
   2139          * The types.
   2140          */
   2141         byte[] types;
   2142 
   2143         /**
   2144          * The points.
   2145          */
   2146         float[] points;
   2147 
   2148         /**
   2149          * The type size.
   2150          */
   2151         int typeSize;
   2152 
   2153         /**
   2154          * The point size.
   2155          */
   2156         int pointSize;
   2157 
   2158         /**
   2159          * The x last.
   2160          */
   2161         float xLast;
   2162 
   2163         /**
   2164          * The y last.
   2165          */
   2166         float yLast;
   2167 
   2168         /**
   2169          * The x move.
   2170          */
   2171         float xMove;
   2172 
   2173         /**
   2174          * The y move.
   2175          */
   2176         float yMove;
   2177 
   2178         /**
   2179          * Instantiates a new buffered path.
   2180          */
   2181         public BufferedPath() {
   2182             types = new byte[bufCapacity];
   2183             points = new float[bufCapacity * 2];
   2184         }
   2185 
   2186         /**
   2187          * Check buf.
   2188          *
   2189          * @param typeCount
   2190          *            the type count.
   2191          * @param pointCount
   2192          *            the point count.
   2193          */
   2194         void checkBuf(int typeCount, int pointCount) {
   2195             if (typeSize + typeCount > types.length) {
   2196                 byte tmp[] = new byte[typeSize + Math.max(bufCapacity, typeCount)];
   2197                 System.arraycopy(types, 0, tmp, 0, typeSize);
   2198                 types = tmp;
   2199             }
   2200             if (pointSize + pointCount > points.length) {
   2201                 float tmp[] = new float[pointSize + Math.max(bufCapacity * 2, pointCount)];
   2202                 System.arraycopy(points, 0, tmp, 0, pointSize);
   2203                 points = tmp;
   2204             }
   2205         }
   2206 
   2207         /**
   2208          * Checks if is empty.
   2209          *
   2210          * @return true, if is empty.
   2211          */
   2212         boolean isEmpty() {
   2213             return typeSize == 0;
   2214         }
   2215 
   2216         /**
   2217          * Clean.
   2218          */
   2219         void clean() {
   2220             typeSize = 0;
   2221             pointSize = 0;
   2222         }
   2223 
   2224         /**
   2225          * Move to.
   2226          *
   2227          * @param x
   2228          *            the x.
   2229          * @param y
   2230          *            the y.
   2231          */
   2232         void moveTo(double x, double y) {
   2233             checkBuf(1, 2);
   2234             types[typeSize++] = PathIterator.SEG_MOVETO;
   2235             points[pointSize++] = xMove = (float)x;
   2236             points[pointSize++] = yMove = (float)y;
   2237         }
   2238 
   2239         /**
   2240          * Line to.
   2241          *
   2242          * @param x
   2243          *            the x.
   2244          * @param y
   2245          *            the y.
   2246          */
   2247         void lineTo(double x, double y) {
   2248             checkBuf(1, 2);
   2249             types[typeSize++] = PathIterator.SEG_LINETO;
   2250             points[pointSize++] = xLast = (float)x;
   2251             points[pointSize++] = yLast = (float)y;
   2252         }
   2253 
   2254         /**
   2255          * Quad to.
   2256          *
   2257          * @param x1
   2258          *            the x1.
   2259          * @param y1
   2260          *            the y1.
   2261          * @param x2
   2262          *            the x2.
   2263          * @param y2
   2264          *            the y2.
   2265          */
   2266         void quadTo(double x1, double y1, double x2, double y2) {
   2267             checkBuf(1, 4);
   2268             types[typeSize++] = PathIterator.SEG_QUADTO;
   2269             points[pointSize++] = (float)x1;
   2270             points[pointSize++] = (float)y1;
   2271             points[pointSize++] = xLast = (float)x2;
   2272             points[pointSize++] = yLast = (float)y2;
   2273         }
   2274 
   2275         /**
   2276          * Cubic to.
   2277          *
   2278          * @param x1
   2279          *            the x1.
   2280          * @param y1
   2281          *            the y1.
   2282          * @param x2
   2283          *            the x2.
   2284          * @param y2
   2285          *            the y2.
   2286          * @param x3
   2287          *            the x3.
   2288          * @param y3
   2289          *            the y3.
   2290          */
   2291         void cubicTo(double x1, double y1, double x2, double y2, double x3, double y3) {
   2292             checkBuf(1, 6);
   2293             types[typeSize++] = PathIterator.SEG_CUBICTO;
   2294             points[pointSize++] = (float)x1;
   2295             points[pointSize++] = (float)y1;
   2296             points[pointSize++] = (float)x2;
   2297             points[pointSize++] = (float)y2;
   2298             points[pointSize++] = xLast = (float)x3;
   2299             points[pointSize++] = yLast = (float)y3;
   2300         }
   2301 
   2302         /**
   2303          * Close path.
   2304          */
   2305         void closePath() {
   2306             checkBuf(1, 0);
   2307             types[typeSize++] = PathIterator.SEG_CLOSE;
   2308         }
   2309 
   2310         /**
   2311          * Sets the last.
   2312          *
   2313          * @param x
   2314          *            the x.
   2315          * @param y
   2316          *            the y.
   2317          */
   2318         void setLast(double x, double y) {
   2319             points[pointSize - 2] = xLast = (float)x;
   2320             points[pointSize - 1] = yLast = (float)y;
   2321         }
   2322 
   2323         /**
   2324          * Append.
   2325          *
   2326          * @param p
   2327          *            the p.
   2328          */
   2329         void append(BufferedPath p) {
   2330             checkBuf(p.typeSize, p.pointSize);
   2331             System.arraycopy(p.points, 0, points, pointSize, p.pointSize);
   2332             System.arraycopy(p.types, 0, types, typeSize, p.typeSize);
   2333             pointSize += p.pointSize;
   2334             typeSize += p.typeSize;
   2335             xLast = points[pointSize - 2];
   2336             yLast = points[pointSize - 1];
   2337         }
   2338 
   2339         /**
   2340          * Append reverse.
   2341          *
   2342          * @param p
   2343          *            the p.
   2344          */
   2345         void appendReverse(BufferedPath p) {
   2346             checkBuf(p.typeSize, p.pointSize);
   2347             // Skip last point, beacause it's the first point of the second path
   2348             for (int i = p.pointSize - 2; i >= 0; i -= 2) {
   2349                 points[pointSize++] = p.points[i + 0];
   2350                 points[pointSize++] = p.points[i + 1];
   2351             }
   2352             // Skip first type, beacuse it's always MOVETO
   2353             int closeIndex = 0;
   2354             for (int i = p.typeSize - 1; i >= 0; i--) {
   2355                 byte type = p.types[i];
   2356                 if (type == PathIterator.SEG_MOVETO) {
   2357                     types[closeIndex] = PathIterator.SEG_MOVETO;
   2358                     types[typeSize++] = PathIterator.SEG_CLOSE;
   2359                 } else {
   2360                     if (type == PathIterator.SEG_CLOSE) {
   2361                         closeIndex = typeSize;
   2362                     }
   2363                     types[typeSize++] = type;
   2364                 }
   2365             }
   2366             xLast = points[pointSize - 2];
   2367             yLast = points[pointSize - 1];
   2368         }
   2369 
   2370         /**
   2371          * Join.
   2372          *
   2373          * @param p
   2374          *            the p.
   2375          */
   2376         void join(BufferedPath p) {
   2377             // Skip MOVETO
   2378             checkBuf(p.typeSize - 1, p.pointSize - 2);
   2379             System.arraycopy(p.points, 2, points, pointSize, p.pointSize - 2);
   2380             System.arraycopy(p.types, 1, types, typeSize, p.typeSize - 1);
   2381             pointSize += p.pointSize - 2;
   2382             typeSize += p.typeSize - 1;
   2383             xLast = points[pointSize - 2];
   2384             yLast = points[pointSize - 1];
   2385         }
   2386 
   2387         /**
   2388          * Combine.
   2389          *
   2390          * @param p
   2391          *            the p.
   2392          */
   2393         void combine(BufferedPath p) {
   2394             checkBuf(p.typeSize - 1, p.pointSize - 2);
   2395             // Skip last point, beacause it's the first point of the second path
   2396             for (int i = p.pointSize - 4; i >= 0; i -= 2) {
   2397                 points[pointSize++] = p.points[i + 0];
   2398                 points[pointSize++] = p.points[i + 1];
   2399             }
   2400             // Skip first type, beacuse it's always MOVETO
   2401             for (int i = p.typeSize - 1; i >= 1; i--) {
   2402                 types[typeSize++] = p.types[i];
   2403             }
   2404             xLast = points[pointSize - 2];
   2405             yLast = points[pointSize - 1];
   2406         }
   2407 
   2408         /**
   2409          * Creates the general path.
   2410          *
   2411          * @return the general path.
   2412          */
   2413         GeneralPath createGeneralPath() {
   2414             GeneralPath p = new GeneralPath();
   2415             int j = 0;
   2416             for (int i = 0; i < typeSize; i++) {
   2417                 int type = types[i];
   2418                 switch (type) {
   2419                     case PathIterator.SEG_MOVETO:
   2420                         p.moveTo(points[j], points[j + 1]);
   2421                         break;
   2422                     case PathIterator.SEG_LINETO:
   2423                         p.lineTo(points[j], points[j + 1]);
   2424                         break;
   2425                     case PathIterator.SEG_QUADTO:
   2426                         p.quadTo(points[j], points[j + 1], points[j + 2], points[j + 3]);
   2427                         break;
   2428                     case PathIterator.SEG_CUBICTO:
   2429                         p.curveTo(points[j], points[j + 1], points[j + 2], points[j + 3],
   2430                                 points[j + 4], points[j + 5]);
   2431                         break;
   2432                     case PathIterator.SEG_CLOSE:
   2433                         p.closePath();
   2434                         break;
   2435                 }
   2436                 j += pointShift[type];
   2437             }
   2438             return p;
   2439         }
   2440 
   2441     }
   2442 
   2443 }
   2444