library/src/main/java/com/github/siyamed/shapeimageview/path/parser/PathParser.java
public static Path doPath(String s) {
        int n = s.length();
        ParserHelper ph = new ParserHelper(s);
        ph.skipWhitespace();
        Path p = new Path();
        float lastX = 0;
        float lastY = 0;
        float lastX1 = 0;
        float lastY1 = 0;
        float contourInitialX = 0;
        float contourInitialY = 0;
        RectF r = new RectF();
        char cmd = 'x';
        while (ph.pos < n) {
            char next = s.charAt(ph.pos);
            if (!Character.isDigit(next) && !(next == '.') && !(next == '-')) {
                cmd = next;
                ph.advance();
            } else if (cmd == 'M') { // implied command
                cmd = 'L';
            } else if (cmd == 'm') { // implied command
                cmd = 'l';
            } else { // implied command
                //ignore
            }
            p.computeBounds(r, true);
            // Log.d(TAG, "  " + cmd + " " + r);
            // Util.debug("* Commands remaining: '" + path + "'.");
            boolean wasCurve = false;
            switch (cmd) {
                case 'M':
                case 'm': {
                    float x = ph.nextFloat();
                    float y = ph.nextFloat();
                    if (cmd == 'm') {
                        p.rMoveTo(x, y);
                        lastX += x;
                        lastY += y;
                    } else {
                        p.moveTo(x, y);
                        lastX = x;
                        lastY = y;
                    }
                    contourInitialX = lastX;
                    contourInitialY = lastY;
                    break;
                }
                case 'Z':
                case 'z': {
                    /// p.lineTo(contourInitialX, contourInitialY);
                    p.close();
                    lastX = contourInitialX;
                    lastY = contourInitialY;
                    break;
                }
                case 'L':
                case 'l': {
                    float x = ph.nextFloat();
                    float y = ph.nextFloat();
                    if (cmd == 'l') {
                        p.rLineTo(x, y);
                        lastX += x;
                        lastY += y;
                    } else {
                        p.lineTo(x, y);
                        lastX = x;
                        lastY = y;
                    }
                    break;
                }
                case 'H':
                case 'h': {
                    float x = ph.nextFloat();
                    if (cmd == 'h') {
                        p.rLineTo(x, 0);
                        lastX += x;
                    } else {
                        p.lineTo(x, lastY);
                        lastX = x;
                    }
                    break;
                }
                case 'V':
                case 'v': {
                    float y = ph.nextFloat();
                    if (cmd == 'v') {
                        p.rLineTo(0, y);
                        lastY += y;
                    } else {
                        p.lineTo(lastX, y);
                        lastY = y;
                    }
                    break;
                }
                case 'C':
                case 'c': {
                    wasCurve = true;
                    float x1 = ph.nextFloat();
                    float y1 = ph.nextFloat();
                    float x2 = ph.nextFloat();
                    float y2 = ph.nextFloat();
                    float x = ph.nextFloat();
                    float y = ph.nextFloat();
                    if (cmd == 'c') {
                        x1 += lastX;
                        x2 += lastX;
                        x += lastX;
                        y1 += lastY;
                        y2 += lastY;
                        y += lastY;
                    }
                    p.cubicTo(x1, y1, x2, y2, x, y);
                    lastX1 = x2;
                    lastY1 = y2;
                    lastX = x;
                    lastY = y;
                    break;
                }
                case 'S':
                case 's': {
                    wasCurve = true;
                    float x2 = ph.nextFloat();
                    float y2 = ph.nextFloat();
                    float x = ph.nextFloat();
                    float y = ph.nextFloat();
                    if (cmd == 's') {
                        x2 += lastX;
                        x += lastX;
                        y2 += lastY;
                        y += lastY;
                    }
                    float x1 = 2 * lastX - lastX1;
                    float y1 = 2 * lastY - lastY1;
                    p.cubicTo(x1, y1, x2, y2, x, y);
                    lastX1 = x2;
                    lastY1 = y2;
                    lastX = x;
                    lastY = y;
                    break;
                }
                case 'A':
                case 'a': {
                    float rx = ph.nextFloat();
                    float ry = ph.nextFloat();
                    float theta = ph.nextFloat();
                    int largeArc = (int) ph.nextFloat();
                    int sweepArc = (int) ph.nextFloat();
                    float x = ph.nextFloat();
                    float y = ph.nextFloat();
                    if (cmd == 'a') {
                        x += lastX;
                        y += lastY;
                    }
                    drawArc(p, lastX, lastY, x, y, rx, ry, theta, largeArc == 1, sweepArc == 1);
                    lastX = x;
                    lastY = y;
                    break;
                }
                case 'T':
                case 't': {
                    wasCurve = true;
                    float x = ph.nextFloat();
                    float y = ph.nextFloat();
                    if (cmd == 't') {
                        x += lastX;
                        y += lastY;
                    }
                    float x1 = 2 * lastX - lastX1;
                    float y1 = 2 * lastY - lastY1;
                    p.cubicTo( lastX, lastY, x1, y1, x, y );
                    lastX = x;
                    lastY = y;
                    lastX1 = x1;
                    lastY1 = y1;
                    break;
                }
                case 'Q':
                case 'q': {
                    wasCurve = true;
                    float x1 = ph.nextFloat();
                    float y1 = ph.nextFloat();
                    float x = ph.nextFloat();
                    float y = ph.nextFloat();
                    if (cmd == 'q') {
                        x += lastX;
                        y += lastY;
                        x1 += lastX;
                        y1 += lastY;
                    }
                    p.cubicTo( lastX, lastY, x1, y1, x, y );
                    lastX1 = x1;
                    lastY1 = y1;
                    lastX = x;
                    lastY = y;
                    break;
                }
                default:
                    Log.w(TAG, "Invalid path command: " + cmd);
                    ph.advance();
            }
            if (!wasCurve) {
                lastX1 = lastX;
                lastY1 = lastY;
            }
            ph.skipWhitespace();
        }
        return p;
    }
public static Path doPath(String s) {
        int n = s.length();
        ParserHelper ph = new ParserHelper(s);
        ph.skipWhitespace();
        Path p = new Path();
        float lastX = 0;
        float lastY = 0;
        float lastX1 = 0;
        float lastY1 = 0;
        float contourInitialX = 0;
        float contourInitialY = 0;
        RectF r = new RectF();
        char cmd = 'x';
        while (ph.pos < n) {
            char next = s.charAt(ph.pos);
            if (!Character.isDigit(next) && !(next == '.') && !(next == '-')) {
                cmd = next;
                ph.advance();
            } else if (cmd == 'M') { // implied command
                cmd = 'L';
            } else if (cmd == 'm') { // implied command
                cmd = 'l';
            } else { // implied command
                //ignore
            }
            p.computeBounds(r, true);
            // Log.d(TAG, "  " + cmd + " " + r);
            // Util.debug("* Commands remaining: '" + path + "'.");
            boolean wasCurve = false;
            switch (cmd) {
                case 'M':
                case 'm': {
                    float x = ph.nextFloat();
                    float y = ph.nextFloat();
                    if (cmd == 'm') {
                        p.rMoveTo(x, y);
                        lastX += x;
                        lastY += y;
                    } else {
                        p.moveTo(x, y);
                        lastX = x;
                        lastY = y;
                    }
                    contourInitialX = lastX;
                    contourInitialY = lastY;
                    break;
                }
                case 'Z':
                case 'z': {
                    /// p.lineTo(contourInitialX, contourInitialY);
                    p.close();
                    lastX = contourInitialX;
                    lastY = contourInitialY;
                    break;
                }
                case 'L':
                case 'l': {
                    float x = ph.nextFloat();
                    float y = ph.nextFloat();
                    if (cmd == 'l') {
                        p.rLineTo(x, y);
                        lastX += x;
                        lastY += y;
                    } else {
                        p.lineTo(x, y);
                        lastX = x;
                        lastY = y;
                    }
                    break;
                }
                case 'H':
                case 'h': {
                    float x = ph.nextFloat();
                    if (cmd == 'h') {
                        p.rLineTo(x, 0);
                        lastX += x;
                    } else {
                        p.lineTo(x, lastY);
                        lastX = x;
                    }
                    break;
                }
                case 'V':
                case 'v': {
                    float y = ph.nextFloat();
                    if (cmd == 'v') {
                        p.rLineTo(0, y);
                        lastY += y;
                    } else {
                        p.lineTo(lastX, y);
                        lastY = y;
                    }
                    break;
                }
                case 'C':
                case 'c': {
                    wasCurve = true;
                    float x1 = ph.nextFloat();
                    float y1 = ph.nextFloat();
                    float x2 = ph.nextFloat();
                    float y2 = ph.nextFloat();
                    float x = ph.nextFloat();
                    float y = ph.nextFloat();
                    if (cmd == 'c') {
                        x1 += lastX;
                        x2 += lastX;
                        x += lastX;
                        y1 += lastY;
                        y2 += lastY;
                        y += lastY;
                    }
                    p.cubicTo(x1, y1, x2, y2, x, y);
                    lastX1 = x2;
                    lastY1 = y2;
                    lastX = x;
                    lastY = y;
                    break;
                }
                case 'S':
                case 's': {
                    wasCurve = true;
                    float x2 = ph.nextFloat();
                    float y2 = ph.nextFloat();
                    float x = ph.nextFloat();
                    float y = ph.nextFloat();
                    if (cmd == 's') {
                        x2 += lastX;
                        x += lastX;
                        y2 += lastY;
                        y += lastY;
                    }
                    float x1 = 2 * lastX - lastX1;
                    float y1 = 2 * lastY - lastY1;
                    p.cubicTo(x1, y1, x2, y2, x, y);
                    lastX1 = x2;
                    lastY1 = y2;
                    lastX = x;
                    lastY = y;
                    break;
                }
                case 'A':
                case 'a': {
                    float rx = ph.nextFloat();
                    float ry = ph.nextFloat();
                    float theta = ph.nextFloat();
                    int largeArc = (int) ph.nextFloat();
                    int sweepArc = (int) ph.nextFloat();
                    float x = ph.nextFloat();
                    float y = ph.nextFloat();
                    if (cmd == 'a') {
                        x += lastX;
                        y += lastY;
                    }
                    drawArc(p, lastX, lastY, x, y, rx, ry, theta, largeArc == 1, sweepArc == 1);
                    lastX = x;
                    lastY = y;
                    break;
                }
                case 'T':
                case 't': {
                    wasCurve = true;
                    float x = ph.nextFloat();
                    float y = ph.nextFloat();
                    if (cmd == 't') {
                        x += lastX;
                        y += lastY;
                    }
                    float x1 = 2 * lastX - lastX1;
                    float y1 = 2 * lastY - lastY1;
                    p.cubicTo( lastX, lastY, x1, y1, x, y );
                    lastX = x;
                    lastY = y;
                    lastX1 = x1;
                    lastY1 = y1;
                    break;
                }
                case 'Q':
                case 'q': {
                    wasCurve = true;
                    float x1 = ph.nextFloat();
                    float y1 = ph.nextFloat();
                    float x = ph.nextFloat();
                    float y = ph.nextFloat();
                    if (cmd == 'q') {
                        x += lastX;
                        y += lastY;
                        x1 += lastX;
                        y1 += lastY;
                    }
                    p.cubicTo( lastX, lastY, x1, y1, x, y );
                    lastX1 = x1;
                    lastY1 = y1;
                    lastX = x;
                    lastY = y;
                    break;
                }
                default:
                    Log.w(TAG, "Invalid path command: " + cmd);
                    ph.advance();
            }
            if (!wasCurve) {
                lastX1 = lastX;
                lastY1 = lastY;
            }
            ph.skipWhitespace();
        }
        return p;
    }
public static Path doPath ( String s ) { int n = s . length ( ) ; ParserHelper ph = new ParserHelper ( s ) ; ph . skipWhitespace ( ) ; Path p = new Path ( ) ; float lastX = 0 ; float lastY = 0 ; float lastX1 = 0 ; float lastY1 = 0 ; float contourInitialX = 0 ; float contourInitialY = 0 ; RectF r = new RectF ( ) ; char cmd = ' ' ; while ( ph . pos < n ) { char next = s . charAt ( ph . pos ) ; if ( ! Character . isDigit ( next ) && ! ( next == ' ' ) && ! ( next == ' ' ) ) { cmd = next ; ph . advance ( ) ; } else if ( cmd == ' ' ) { // implied command cmd = ' ' ; } else if ( cmd == ' ' ) { // implied command cmd = ' ' ; } else { // implied command //ignore } p . computeBounds ( r , true ) ; // Log.d(TAG, "  " + cmd + " " + r); // Util.debug("* Commands remaining: '" + path + "'."); boolean wasCurve = false ; switch ( cmd ) { case ' ' : case ' ' : { float x = ph . nextFloat ( ) ; float y = ph . nextFloat ( ) ; if ( cmd == ' ' ) { p . rMoveTo ( x , y ) ; lastX += x ; lastY += y ; } else { p . moveTo ( x , y ) ; lastX = x ; lastY = y ; } contourInitialX = lastX ; contourInitialY = lastY ; break ; } case ' ' : case ' ' : { /// p.lineTo(contourInitialX, contourInitialY); p . close ( ) ; lastX = contourInitialX ; lastY = contourInitialY ; break ; } case ' ' : case ' ' : { float x = ph . nextFloat ( ) ; float y = ph . nextFloat ( ) ; if ( cmd == ' ' ) { p . rLineTo ( x , y ) ; lastX += x ; lastY += y ; } else { p . lineTo ( x , y ) ; lastX = x ; lastY = y ; } break ; } case ' ' : case ' ' : { float x = ph . nextFloat ( ) ; if ( cmd == ' ' ) { p . rLineTo ( x , 0 ) ; lastX += x ; } else { p . lineTo ( x , lastY ) ; lastX = x ; } break ; } case ' ' : case ' ' : { float y = ph . nextFloat ( ) ; if ( cmd == ' ' ) { p . rLineTo ( 0 , y ) ; lastY += y ; } else { p . lineTo ( lastX , y ) ; lastY = y ; } break ; } case ' ' : case ' ' : { wasCurve = true ; float x1 = ph . nextFloat ( ) ; float y1 = ph . nextFloat ( ) ; float x2 = ph . nextFloat ( ) ; float y2 = ph . nextFloat ( ) ; float x = ph . nextFloat ( ) ; float y = ph . nextFloat ( ) ; if ( cmd == ' ' ) { x1 += lastX ; x2 += lastX ; x += lastX ; y1 += lastY ; y2 += lastY ; y += lastY ; } p . cubicTo ( x1 , y1 , x2 , y2 , x , y ) ; lastX1 = x2 ; lastY1 = y2 ; lastX = x ; lastY = y ; break ; } case ' ' : case ' ' : { wasCurve = true ; float x2 = ph . nextFloat ( ) ; float y2 = ph . nextFloat ( ) ; float x = ph . nextFloat ( ) ; float y = ph . nextFloat ( ) ; if ( cmd == ' ' ) { x2 += lastX ; x += lastX ; y2 += lastY ; y += lastY ; } float x1 = 2 * lastX - lastX1 ; float y1 = 2 * lastY - lastY1 ; p . cubicTo ( x1 , y1 , x2 , y2 , x , y ) ; lastX1 = x2 ; lastY1 = y2 ; lastX = x ; lastY = y ; break ; } case ' ' : case ' ' : { float rx = ph . nextFloat ( ) ; float ry = ph . nextFloat ( ) ; float theta = ph . nextFloat ( ) ; int largeArc = ( int ) ph . nextFloat ( ) ; int sweepArc = ( int ) ph . nextFloat ( ) ; float x = ph . nextFloat ( ) ; float y = ph . nextFloat ( ) ; if ( cmd == ' ' ) { x += lastX ; y += lastY ; } drawArc ( p , lastX , lastY , x , y , rx , ry , theta , largeArc == 1 , sweepArc == 1 ) ; lastX = x ; lastY = y ; break ; } case ' ' : case ' ' : { wasCurve = true ; float x = ph . nextFloat ( ) ; float y = ph . nextFloat ( ) ; if ( cmd == ' ' ) { x += lastX ; y += lastY ; } float x1 = 2 * lastX - lastX1 ; float y1 = 2 * lastY - lastY1 ; p . cubicTo ( lastX , lastY , x1 , y1 , x , y ) ; lastX = x ; lastY = y ; lastX1 = x1 ; lastY1 = y1 ; break ; } case ' ' : case ' ' : { wasCurve = true ; float x1 = ph . nextFloat ( ) ; float y1 = ph . nextFloat ( ) ; float x = ph . nextFloat ( ) ; float y = ph . nextFloat ( ) ; if ( cmd == ' ' ) { x += lastX ; y += lastY ; x1 += lastX ; y1 += lastY ; } p . cubicTo ( lastX , lastY , x1 , y1 , x , y ) ; lastX1 = x1 ; lastY1 = y1 ; lastX = x ; lastY = y ; break ; } default : Log . w ( TAG , "Invalid path command: " + cmd ) ; ph . advance ( ) ; } if ( ! wasCurve ) { lastX1 = lastX ; lastY1 = lastY ; } ph . skipWhitespace ( ) ; } return p ; }
/*
This is where the hard-to-parse paths are handled.
Uppercase rules are absolute positions, lowercase are relative.
Types of path rules:
<p/>
<ol>
<li>M/m - (x y)+ - Move to (without drawing)
<li>Z/z - (no params) - Close path (back to starting point)
<li>L/l - (x y)+ - Line to
<li>H/h - x+ - Horizontal ine to
<li>V/v - y+ - Vertical line to
<li>C/c - (x1 y1 x2 y2 x y)+ - Cubic bezier to
<li>S/s - (x2 y2 x y)+ - Smooth cubic bezier to (shorthand that assumes the x2, y2 from previous C/S is the x1, y1 of this bezier)
<li>Q/q - (x1 y1 x y)+ - Quadratic bezier to
<li>T/t - (x y)+ - Smooth quadratic bezier to (assumes previous control point is "reflection" of last one w.r.t. to current point)
</ol>
<p/>
Numbers are separate by whitespace, comma or nothing at all (!) if they are self-delimiting, (ie. begin with a - sign)
/ * This is where the hard - to - parse paths are handled . Uppercase rules are absolute positions lowercase are relative . Types of path rules : <p / > <ol > <li > M / m - ( x y ) + - Move to ( without drawing ) <li > Z / z - ( no params ) - Close path ( back to starting point ) <li > L / l - ( x y ) + - Line to <li > H / h - x + - Horizontal ine to <li > V / v - y + - Vertical line to <li > C / c - ( x1 y1 x2 y2 x y ) + - Cubic bezier to <li > S / s - ( x2 y2 x y ) + - Smooth cubic bezier to ( shorthand that assumes the x2 y2 from previous C / S is the x1 y1 of this bezier ) <li > Q / q - ( x1 y1 x y ) + - Quadratic bezier to <li > T / t - ( x y ) + - Smooth quadratic bezier to ( assumes previous control point is reflection of last one w . r . t . to current point ) < / ol > <p / > Numbers are separate by whitespace comma or nothing at all ( ! ) if they are self - delimiting ( ie . begin with a - sign )