gfx/skia/trunk/src/animator/SkParseSVGPath.cpp

changeset 0
6474c204b198
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/gfx/skia/trunk/src/animator/SkParseSVGPath.cpp	Wed Dec 31 06:09:35 2014 +0100
     1.3 @@ -0,0 +1,234 @@
     1.4 +
     1.5 +/*
     1.6 + * Copyright 2006 The Android Open Source Project
     1.7 + *
     1.8 + * Use of this source code is governed by a BSD-style license that can be
     1.9 + * found in the LICENSE file.
    1.10 + */
    1.11 +
    1.12 +
    1.13 +#include <ctype.h>
    1.14 +#include "SkDrawPath.h"
    1.15 +#include "SkParse.h"
    1.16 +#include "SkPoint.h"
    1.17 +#include "SkUtils.h"
    1.18 +#define QUADRATIC_APPROXIMATION 1
    1.19 +
    1.20 +#if QUADRATIC_APPROXIMATION
    1.21 +////////////////////////////////////////////////////////////////////////////////////
    1.22 +//functions to approximate a cubic using two quadratics
    1.23 +
    1.24 +//      midPt sets the first argument to be the midpoint of the other two
    1.25 +//      it is used by quadApprox
    1.26 +static inline void midPt(SkPoint& dest,const SkPoint& a,const SkPoint& b)
    1.27 +{
    1.28 +    dest.set(SkScalarAve(a.fX, b.fX),SkScalarAve(a.fY, b.fY));
    1.29 +}
    1.30 +//      quadApprox - makes an approximation, which we hope is faster
    1.31 +static void quadApprox(SkPath &fPath, const SkPoint &p0, const SkPoint &p1, const SkPoint &p2)
    1.32 +{
    1.33 +    //divide the cubic up into two cubics, then convert them into quadratics
    1.34 +    //define our points
    1.35 +    SkPoint c,j,k,l,m,n,o,p,q, mid;
    1.36 +    fPath.getLastPt(&c);
    1.37 +    midPt(j, p0, c);
    1.38 +    midPt(k, p0, p1);
    1.39 +    midPt(l, p1, p2);
    1.40 +    midPt(o, j, k);
    1.41 +    midPt(p, k, l);
    1.42 +    midPt(q, o, p);
    1.43 +    //compute the first half
    1.44 +    m.set(SkScalarHalf(3*j.fX - c.fX), SkScalarHalf(3*j.fY - c.fY));
    1.45 +    n.set(SkScalarHalf(3*o.fX -q.fX), SkScalarHalf(3*o.fY - q.fY));
    1.46 +    midPt(mid,m,n);
    1.47 +    fPath.quadTo(mid,q);
    1.48 +    c = q;
    1.49 +    //compute the second half
    1.50 +    m.set(SkScalarHalf(3*p.fX - c.fX), SkScalarHalf(3*p.fY - c.fY));
    1.51 +    n.set(SkScalarHalf(3*l.fX -p2.fX),SkScalarHalf(3*l.fY -p2.fY));
    1.52 +    midPt(mid,m,n);
    1.53 +    fPath.quadTo(mid,p2);
    1.54 +}
    1.55 +#endif
    1.56 +
    1.57 +
    1.58 +static inline bool is_between(int c, int min, int max)
    1.59 +{
    1.60 +    return (unsigned)(c - min) <= (unsigned)(max - min);
    1.61 +}
    1.62 +
    1.63 +static inline bool is_ws(int c)
    1.64 +{
    1.65 +    return is_between(c, 1, 32);
    1.66 +}
    1.67 +
    1.68 +static inline bool is_digit(int c)
    1.69 +{
    1.70 +    return is_between(c, '0', '9');
    1.71 +}
    1.72 +
    1.73 +static inline bool is_sep(int c)
    1.74 +{
    1.75 +    return is_ws(c) || c == ',';
    1.76 +}
    1.77 +
    1.78 +static const char* skip_ws(const char str[])
    1.79 +{
    1.80 +    SkASSERT(str);
    1.81 +    while (is_ws(*str))
    1.82 +        str++;
    1.83 +    return str;
    1.84 +}
    1.85 +
    1.86 +static const char* skip_sep(const char str[])
    1.87 +{
    1.88 +    SkASSERT(str);
    1.89 +    while (is_sep(*str))
    1.90 +        str++;
    1.91 +    return str;
    1.92 +}
    1.93 +
    1.94 +static const char* find_points(const char str[], SkPoint value[], int count,
    1.95 +     bool isRelative, SkPoint* relative)
    1.96 +{
    1.97 +    str = SkParse::FindScalars(str, &value[0].fX, count * 2);
    1.98 +    if (isRelative) {
    1.99 +        for (int index = 0; index < count; index++) {
   1.100 +            value[index].fX += relative->fX;
   1.101 +            value[index].fY += relative->fY;
   1.102 +        }
   1.103 +    }
   1.104 +    return str;
   1.105 +}
   1.106 +
   1.107 +static const char* find_scalar(const char str[], SkScalar* value,
   1.108 +    bool isRelative, SkScalar relative)
   1.109 +{
   1.110 +    str = SkParse::FindScalar(str, value);
   1.111 +    if (isRelative)
   1.112 +        *value += relative;
   1.113 +    return str;
   1.114 +}
   1.115 +
   1.116 +void SkDrawPath::parseSVG() {
   1.117 +    fPath.reset();
   1.118 +    const char* data = d.c_str();
   1.119 +    SkPoint f = {0, 0};
   1.120 +    SkPoint c = {0, 0};
   1.121 +    SkPoint lastc = {0, 0};
   1.122 +    SkPoint points[3];
   1.123 +    char op = '\0';
   1.124 +    char previousOp = '\0';
   1.125 +    bool relative = false;
   1.126 +    do {
   1.127 +        data = skip_ws(data);
   1.128 +        if (data[0] == '\0')
   1.129 +            break;
   1.130 +        char ch = data[0];
   1.131 +        if (is_digit(ch) || ch == '-' || ch == '+') {
   1.132 +            if (op == '\0')
   1.133 +                return;
   1.134 +        }
   1.135 +        else {
   1.136 +            op = ch;
   1.137 +            relative = false;
   1.138 +            if (islower(op)) {
   1.139 +                op = (char) toupper(op);
   1.140 +                relative = true;
   1.141 +            }
   1.142 +            data++;
   1.143 +            data = skip_sep(data);
   1.144 +        }
   1.145 +        switch (op) {
   1.146 +            case 'M':
   1.147 +                data = find_points(data, points, 1, relative, &c);
   1.148 +                fPath.moveTo(points[0]);
   1.149 +                op = 'L';
   1.150 +                c = points[0];
   1.151 +                break;
   1.152 +            case 'L':
   1.153 +                data = find_points(data, points, 1, relative, &c);
   1.154 +                fPath.lineTo(points[0]);
   1.155 +                c = points[0];
   1.156 +                break;
   1.157 +            case 'H': {
   1.158 +                SkScalar x;
   1.159 +                data = find_scalar(data, &x, relative, c.fX);
   1.160 +                fPath.lineTo(x, c.fY);
   1.161 +                c.fX = x;
   1.162 +            }
   1.163 +                break;
   1.164 +            case 'V': {
   1.165 +                SkScalar y;
   1.166 +                data = find_scalar(data, &y, relative, c.fY);
   1.167 +                fPath.lineTo(c.fX, y);
   1.168 +                c.fY = y;
   1.169 +            }
   1.170 +                break;
   1.171 +            case 'C':
   1.172 +                data = find_points(data, points, 3, relative, &c);
   1.173 +                goto cubicCommon;
   1.174 +            case 'S':
   1.175 +                data = find_points(data, &points[1], 2, relative, &c);
   1.176 +                points[0] = c;
   1.177 +                if (previousOp == 'C' || previousOp == 'S') {
   1.178 +                    points[0].fX -= lastc.fX - c.fX;
   1.179 +                    points[0].fY -= lastc.fY - c.fY;
   1.180 +                }
   1.181 +            cubicCommon:
   1.182 +    //          if (data[0] == '\0')
   1.183 +    //              return;
   1.184 +#if QUADRATIC_APPROXIMATION
   1.185 +                    quadApprox(fPath, points[0], points[1], points[2]);
   1.186 +#else   //this way just does a boring, slow old cubic
   1.187 +                    fPath.cubicTo(points[0], points[1], points[2]);
   1.188 +#endif
   1.189 +        //if we are using the quadApprox, lastc is what it would have been if we had used
   1.190 +        //cubicTo
   1.191 +                    lastc = points[1];
   1.192 +                    c = points[2];
   1.193 +                break;
   1.194 +            case 'Q':  // Quadratic Bezier Curve
   1.195 +                data = find_points(data, points, 2, relative, &c);
   1.196 +                goto quadraticCommon;
   1.197 +            case 'T':
   1.198 +                data = find_points(data, &points[1], 1, relative, &c);
   1.199 +                points[0] = points[1];
   1.200 +                if (previousOp == 'Q' || previousOp == 'T') {
   1.201 +                    points[0].fX = c.fX * 2 - lastc.fX;
   1.202 +                    points[0].fY = c.fY * 2 - lastc.fY;
   1.203 +                }
   1.204 +            quadraticCommon:
   1.205 +                fPath.quadTo(points[0], points[1]);
   1.206 +                lastc = points[0];
   1.207 +                c = points[1];
   1.208 +                break;
   1.209 +            case 'Z':
   1.210 +                fPath.close();
   1.211 +#if 0   // !!! still a bug?
   1.212 +                if (fPath.isEmpty() && (f.fX != 0 || f.fY != 0)) {
   1.213 +                    c.fX -= SkScalar.Epsilon;   // !!! enough?
   1.214 +                    fPath.moveTo(c);
   1.215 +                    fPath.lineTo(f);
   1.216 +                    fPath.close();
   1.217 +                }
   1.218 +#endif
   1.219 +                c = f;
   1.220 +                op = '\0';
   1.221 +                break;
   1.222 +            case '~': {
   1.223 +                SkPoint args[2];
   1.224 +                data = find_points(data, args, 2, false, NULL);
   1.225 +                fPath.moveTo(args[0].fX, args[0].fY);
   1.226 +                fPath.lineTo(args[1].fX, args[1].fY);
   1.227 +            }
   1.228 +                break;
   1.229 +            default:
   1.230 +                SkASSERT(0);
   1.231 +                return;
   1.232 +        }
   1.233 +        if (previousOp == 0)
   1.234 +            f = c;
   1.235 +        previousOp = op;
   1.236 +    } while (data[0] > 0);
   1.237 +}

mercurial