michael@0: michael@0: /* michael@0: * Copyright 2006 The Android Open Source Project michael@0: * michael@0: * Use of this source code is governed by a BSD-style license that can be michael@0: * found in the LICENSE file. michael@0: */ michael@0: michael@0: michael@0: #include michael@0: #include "SkDrawPath.h" michael@0: #include "SkParse.h" michael@0: #include "SkPoint.h" michael@0: #include "SkUtils.h" michael@0: #define QUADRATIC_APPROXIMATION 1 michael@0: michael@0: #if QUADRATIC_APPROXIMATION michael@0: //////////////////////////////////////////////////////////////////////////////////// michael@0: //functions to approximate a cubic using two quadratics michael@0: michael@0: // midPt sets the first argument to be the midpoint of the other two michael@0: // it is used by quadApprox michael@0: static inline void midPt(SkPoint& dest,const SkPoint& a,const SkPoint& b) michael@0: { michael@0: dest.set(SkScalarAve(a.fX, b.fX),SkScalarAve(a.fY, b.fY)); michael@0: } michael@0: // quadApprox - makes an approximation, which we hope is faster michael@0: static void quadApprox(SkPath &fPath, const SkPoint &p0, const SkPoint &p1, const SkPoint &p2) michael@0: { michael@0: //divide the cubic up into two cubics, then convert them into quadratics michael@0: //define our points michael@0: SkPoint c,j,k,l,m,n,o,p,q, mid; michael@0: fPath.getLastPt(&c); michael@0: midPt(j, p0, c); michael@0: midPt(k, p0, p1); michael@0: midPt(l, p1, p2); michael@0: midPt(o, j, k); michael@0: midPt(p, k, l); michael@0: midPt(q, o, p); michael@0: //compute the first half michael@0: m.set(SkScalarHalf(3*j.fX - c.fX), SkScalarHalf(3*j.fY - c.fY)); michael@0: n.set(SkScalarHalf(3*o.fX -q.fX), SkScalarHalf(3*o.fY - q.fY)); michael@0: midPt(mid,m,n); michael@0: fPath.quadTo(mid,q); michael@0: c = q; michael@0: //compute the second half michael@0: m.set(SkScalarHalf(3*p.fX - c.fX), SkScalarHalf(3*p.fY - c.fY)); michael@0: n.set(SkScalarHalf(3*l.fX -p2.fX),SkScalarHalf(3*l.fY -p2.fY)); michael@0: midPt(mid,m,n); michael@0: fPath.quadTo(mid,p2); michael@0: } michael@0: #endif michael@0: michael@0: michael@0: static inline bool is_between(int c, int min, int max) michael@0: { michael@0: return (unsigned)(c - min) <= (unsigned)(max - min); michael@0: } michael@0: michael@0: static inline bool is_ws(int c) michael@0: { michael@0: return is_between(c, 1, 32); michael@0: } michael@0: michael@0: static inline bool is_digit(int c) michael@0: { michael@0: return is_between(c, '0', '9'); michael@0: } michael@0: michael@0: static inline bool is_sep(int c) michael@0: { michael@0: return is_ws(c) || c == ','; michael@0: } michael@0: michael@0: static const char* skip_ws(const char str[]) michael@0: { michael@0: SkASSERT(str); michael@0: while (is_ws(*str)) michael@0: str++; michael@0: return str; michael@0: } michael@0: michael@0: static const char* skip_sep(const char str[]) michael@0: { michael@0: SkASSERT(str); michael@0: while (is_sep(*str)) michael@0: str++; michael@0: return str; michael@0: } michael@0: michael@0: static const char* find_points(const char str[], SkPoint value[], int count, michael@0: bool isRelative, SkPoint* relative) michael@0: { michael@0: str = SkParse::FindScalars(str, &value[0].fX, count * 2); michael@0: if (isRelative) { michael@0: for (int index = 0; index < count; index++) { michael@0: value[index].fX += relative->fX; michael@0: value[index].fY += relative->fY; michael@0: } michael@0: } michael@0: return str; michael@0: } michael@0: michael@0: static const char* find_scalar(const char str[], SkScalar* value, michael@0: bool isRelative, SkScalar relative) michael@0: { michael@0: str = SkParse::FindScalar(str, value); michael@0: if (isRelative) michael@0: *value += relative; michael@0: return str; michael@0: } michael@0: michael@0: void SkDrawPath::parseSVG() { michael@0: fPath.reset(); michael@0: const char* data = d.c_str(); michael@0: SkPoint f = {0, 0}; michael@0: SkPoint c = {0, 0}; michael@0: SkPoint lastc = {0, 0}; michael@0: SkPoint points[3]; michael@0: char op = '\0'; michael@0: char previousOp = '\0'; michael@0: bool relative = false; michael@0: do { michael@0: data = skip_ws(data); michael@0: if (data[0] == '\0') michael@0: break; michael@0: char ch = data[0]; michael@0: if (is_digit(ch) || ch == '-' || ch == '+') { michael@0: if (op == '\0') michael@0: return; michael@0: } michael@0: else { michael@0: op = ch; michael@0: relative = false; michael@0: if (islower(op)) { michael@0: op = (char) toupper(op); michael@0: relative = true; michael@0: } michael@0: data++; michael@0: data = skip_sep(data); michael@0: } michael@0: switch (op) { michael@0: case 'M': michael@0: data = find_points(data, points, 1, relative, &c); michael@0: fPath.moveTo(points[0]); michael@0: op = 'L'; michael@0: c = points[0]; michael@0: break; michael@0: case 'L': michael@0: data = find_points(data, points, 1, relative, &c); michael@0: fPath.lineTo(points[0]); michael@0: c = points[0]; michael@0: break; michael@0: case 'H': { michael@0: SkScalar x; michael@0: data = find_scalar(data, &x, relative, c.fX); michael@0: fPath.lineTo(x, c.fY); michael@0: c.fX = x; michael@0: } michael@0: break; michael@0: case 'V': { michael@0: SkScalar y; michael@0: data = find_scalar(data, &y, relative, c.fY); michael@0: fPath.lineTo(c.fX, y); michael@0: c.fY = y; michael@0: } michael@0: break; michael@0: case 'C': michael@0: data = find_points(data, points, 3, relative, &c); michael@0: goto cubicCommon; michael@0: case 'S': michael@0: data = find_points(data, &points[1], 2, relative, &c); michael@0: points[0] = c; michael@0: if (previousOp == 'C' || previousOp == 'S') { michael@0: points[0].fX -= lastc.fX - c.fX; michael@0: points[0].fY -= lastc.fY - c.fY; michael@0: } michael@0: cubicCommon: michael@0: // if (data[0] == '\0') michael@0: // return; michael@0: #if QUADRATIC_APPROXIMATION michael@0: quadApprox(fPath, points[0], points[1], points[2]); michael@0: #else //this way just does a boring, slow old cubic michael@0: fPath.cubicTo(points[0], points[1], points[2]); michael@0: #endif michael@0: //if we are using the quadApprox, lastc is what it would have been if we had used michael@0: //cubicTo michael@0: lastc = points[1]; michael@0: c = points[2]; michael@0: break; michael@0: case 'Q': // Quadratic Bezier Curve michael@0: data = find_points(data, points, 2, relative, &c); michael@0: goto quadraticCommon; michael@0: case 'T': michael@0: data = find_points(data, &points[1], 1, relative, &c); michael@0: points[0] = points[1]; michael@0: if (previousOp == 'Q' || previousOp == 'T') { michael@0: points[0].fX = c.fX * 2 - lastc.fX; michael@0: points[0].fY = c.fY * 2 - lastc.fY; michael@0: } michael@0: quadraticCommon: michael@0: fPath.quadTo(points[0], points[1]); michael@0: lastc = points[0]; michael@0: c = points[1]; michael@0: break; michael@0: case 'Z': michael@0: fPath.close(); michael@0: #if 0 // !!! still a bug? michael@0: if (fPath.isEmpty() && (f.fX != 0 || f.fY != 0)) { michael@0: c.fX -= SkScalar.Epsilon; // !!! enough? michael@0: fPath.moveTo(c); michael@0: fPath.lineTo(f); michael@0: fPath.close(); michael@0: } michael@0: #endif michael@0: c = f; michael@0: op = '\0'; michael@0: break; michael@0: case '~': { michael@0: SkPoint args[2]; michael@0: data = find_points(data, args, 2, false, NULL); michael@0: fPath.moveTo(args[0].fX, args[0].fY); michael@0: fPath.lineTo(args[1].fX, args[1].fY); michael@0: } michael@0: break; michael@0: default: michael@0: SkASSERT(0); michael@0: return; michael@0: } michael@0: if (previousOp == 0) michael@0: f = c; michael@0: previousOp = op; michael@0: } while (data[0] > 0); michael@0: }