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 +}