caryclark@google.com | 45a8fc6 | 2013-02-14 15:29:11 +0000 | [diff] [blame] | 1 | #include <ctype.h> |
| 2 | #include "SkPath.h" |
| 3 | #include "SkParse.h" |
| 4 | #include "SkPoint.h" |
| 5 | #include "SkUtils.h" |
| 6 | #define QUADRATIC_APPROXIMATION 0 |
| 7 | |
| 8 | const char logoStr[] = |
| 9 | "<path fill=\"#0081C6\"" |
| 10 | "d=\"M440.51,289.479c1.623,1.342,5.01,4.164,5.01,9.531c0,5.223-2.965,7.697-5.93,10.024" |
| 11 | "c-0.918,0.916-1.977,1.907-1.977,3.462c0,1.551,1.059,2.397,1.834,3.035l2.545,1.973c3.105,2.613,5.928,5.016,5.928,9.889" |
| 12 | "c0,6.635-6.426,13.341-18.566,13.341c-10.238,0-15.178-4.87-15.178-10.097c0-2.543,1.268-6.139,5.438-8.613" |
| 13 | "c4.373-2.682,10.307-3.033,13.482-3.249c-0.99-1.271-2.119-2.61-2.119-4.798c0-1.199,0.355-1.907,0.707-2.754" |
| 14 | "c-0.779,0.07-1.553,0.141-2.26,0.141c-7.482,0-11.719-5.579-11.719-11.082c0-3.247,1.484-6.851,4.518-9.461" |
| 15 | "c4.025-3.318,8.824-3.883,12.639-3.883h14.541l-4.518,2.541H440.51z" |
| 16 | "M435.494,320.826c-0.562-0.072-0.916-0.072-1.619-0.072" |
| 17 | "c-0.637,0-4.451,0.143-7.416,1.132c-1.553,0.564-6.07,2.257-6.07,7.271c0,5.013,4.873,8.615,12.426,8.615" |
| 18 | "c6.775,0,10.379-3.253,10.379-7.624C443.193,326.54,440.863,324.64,435.494,320.826z" |
| 19 | "M437.543,307.412" |
| 20 | "c1.623-1.627,1.764-3.883,1.764-5.154c0-5.083-3.035-12.99-8.893-12.99c-1.838,0-3.812,0.918-4.945,2.331" |
| 21 | "c-1.199,1.483-1.551,3.387-1.551,5.225c0,4.729,2.754,12.565,8.826,12.565C434.508,309.389,436.41,308.543,437.543,307.412z\"/>" |
| 22 | "<path fill=\"#FFD200\"" |
| 23 | "d=\"M396.064,319.696c-11.206,0-17.198-8.739-17.198-16.636c0-9.233,7.542-17.126,18.258-17.126" |
| 24 | "c10.357,0,16.844,8.104,16.844,16.635C413.969,310.884,407.557,319.696,396.064,319.696z" |
| 25 | "M404.873,313.987" |
| 26 | "c1.695-2.257,2.119-5.074,2.119-7.826c0-6.202-2.961-18.042-11.701-18.042c-2.326,0-4.652,0.918-6.342,2.399" |
| 27 | "c-2.749,2.465-3.245,5.566-3.245,8.599c0,6.977,3.454,18.463,11.984,18.463C400.436,317.58,403.256,316.242,404.873,313.987z\"/>" |
| 28 | "<path fill=\"#ED174F\"" |
| 29 | "d=\"M357.861,319.696c-11.207,0-17.199-8.739-17.199-16.636c0-9.233,7.544-17.126,18.258-17.126" |
| 30 | "c10.359,0,16.845,8.104,16.845,16.635C375.764,310.884,369.351,319.696,357.861,319.696z" |
| 31 | "M366.671,313.987" |
| 32 | "c1.693-2.257,2.116-5.074,2.116-7.826c0-6.202-2.961-18.042-11.701-18.042c-2.325,0-4.652,0.918-6.344,2.399" |
| 33 | "c-2.749,2.465-3.241,5.566-3.241,8.599c0,6.977,3.452,18.463,11.983,18.463C362.234,317.58,365.053,316.242,366.671,313.987z\"/>" |
| 34 | "<path fill=\"#0081C6\"" |
| 35 | "d=\"M335.278,318.591l-10.135,2.339c-4.111,0.638-7.795,1.204-11.69,1.204" |
| 36 | "c-19.56,0-26.998-14.386-26.998-25.654c0-13.746,10.558-26.498,28.629-26.498c3.827,0,7.51,0.564,10.839,1.486" |
| 37 | "c5.316,1.488,7.796,3.331,9.355,4.394l-5.883,5.599l-2.479,0.565l1.771-2.837c-2.408-2.336-6.805-6.658-15.164-6.658" |
| 38 | "c-11.196,0-19.63,8.507-19.63,20.906c0,13.319,9.638,25.861,25.084,25.861c4.539,0,6.874-0.918,9-1.771v-11.407l-10.698,0.566" |
| 39 | "l5.667-3.047h15.023l-1.841,1.77c-0.5,0.424-0.567,0.57-0.71,1.133c-0.073,0.64-0.141,2.695-0.141,3.403V318.591z\"/>" |
| 40 | "<path fill=\"#49A942\"" |
| 41 | "d=\"M462.908,316.552c-2.342-0.214-2.832-0.638-2.832-3.401v-0.782v-39.327c0.014-0.153,0.025-0.31,0.041-0.457" |
| 42 | "c0.283-2.479,0.992-2.903,3.189-4.182h-10.135l-5.316,2.552h5.418v0.032l-0.004-0.024v41.406v2.341" |
| 43 | "c0,1.416-0.281,1.629-1.912,3.753H463.9l2.623-1.557C465.318,316.763,464.113,316.692,462.908,316.552z\"/>" |
| 44 | "<path fill=\"#ED174F\"" |
| 45 | "d=\"M491.742,317.203c-0.771,0.422-1.547,0.916-2.318,1.268c-2.326,1.055-4.719,1.336-6.83,1.336" |
| 46 | "c-2.25,0-5.77-0.143-9.361-2.744c-4.992-3.521-7.176-9.572-7.176-14.851c0-10.906,8.869-16.255,16.115-16.255" |
| 47 | "c2.533,0,5.141,0.633,7.252,1.972c3.516,2.318,4.43,5.344,4.922,6.963l-16.535,6.688l-5.422,0.422" |
| 48 | "c1.758,8.938,7.812,14.145,14.498,14.145c3.59,0,6.193-1.266,8.586-2.461L491.742,317.203z" |
| 49 | "M485.129,296.229" |
| 50 | "c1.336-0.493,2.039-0.914,2.039-1.899c0-2.812-3.166-6.053-6.967-6.053c-2.818,0-8.094,2.183-8.094,9.783" |
| 51 | "c0,1.197,0.141,2.464,0.213,3.73L485.129,296.229z\"/>" |
| 52 | "<path fill=\"#77787B\"" |
| 53 | "d=\"M498.535,286.439v4.643h-0.564v-4.643h-1.537v-0.482h3.637v0.482H498.535z\"/>" |
| 54 | "<path fill=\"#77787B\"" |
| 55 | "d=\"M504.863,291.082v-4.687h-0.023l-1.432,4.687h-0.439l-1.443-4.687h-0.02v4.687h-0.512v-5.125h0.877" |
| 56 | "l1.307,4.143h0.018l1.285-4.143h0.891v5.125H504.863z\"/>" |
| 57 | ; |
| 58 | |
| 59 | size_t logoStrLen = sizeof(logoStr); |
| 60 | |
| 61 | #if QUADRATIC_APPROXIMATION |
| 62 | //////////////////////////////////////////////////////////////////////////////////// |
| 63 | //functions to approximate a cubic using two quadratics |
| 64 | |
| 65 | // midPt sets the first argument to be the midpoint of the other two |
| 66 | // it is used by quadApprox |
| 67 | static inline void midPt(SkPoint& dest,const SkPoint& a,const SkPoint& b) |
| 68 | { |
| 69 | dest.set(SkScalarAve(a.fX, b.fX),SkScalarAve(a.fY, b.fY)); |
| 70 | } |
| 71 | // quadApprox - makes an approximation, which we hope is faster |
| 72 | static void quadApprox(SkPath &fPath, const SkPoint &p0, const SkPoint &p1, const SkPoint &p2) |
| 73 | { |
| 74 | //divide the cubic up into two cubics, then convert them into quadratics |
| 75 | //define our points |
| 76 | SkPoint c,j,k,l,m,n,o,p,q, mid; |
| 77 | fPath.getLastPt(&c); |
| 78 | midPt(j, p0, c); |
| 79 | midPt(k, p0, p1); |
| 80 | midPt(l, p1, p2); |
| 81 | midPt(o, j, k); |
| 82 | midPt(p, k, l); |
| 83 | midPt(q, o, p); |
| 84 | //compute the first half |
| 85 | m.set(SkScalarHalf(3*j.fX - c.fX), SkScalarHalf(3*j.fY - c.fY)); |
| 86 | n.set(SkScalarHalf(3*o.fX -q.fX), SkScalarHalf(3*o.fY - q.fY)); |
| 87 | midPt(mid,m,n); |
| 88 | fPath.quadTo(mid,q); |
| 89 | c = q; |
| 90 | //compute the second half |
| 91 | m.set(SkScalarHalf(3*p.fX - c.fX), SkScalarHalf(3*p.fY - c.fY)); |
| 92 | n.set(SkScalarHalf(3*l.fX -p2.fX),SkScalarHalf(3*l.fY -p2.fY)); |
| 93 | midPt(mid,m,n); |
| 94 | fPath.quadTo(mid,p2); |
| 95 | } |
| 96 | #endif |
| 97 | |
| 98 | |
| 99 | static inline bool is_between(int c, int min, int max) |
| 100 | { |
| 101 | return (unsigned)(c - min) <= (unsigned)(max - min); |
| 102 | } |
| 103 | |
| 104 | static inline bool is_ws(int c) |
| 105 | { |
| 106 | return is_between(c, 1, 32); |
| 107 | } |
| 108 | |
| 109 | static inline bool is_digit(int c) |
| 110 | { |
| 111 | return is_between(c, '0', '9'); |
| 112 | } |
| 113 | |
| 114 | static inline bool is_sep(int c) |
| 115 | { |
| 116 | return is_ws(c) || c == ','; |
| 117 | } |
| 118 | |
| 119 | static const char* skip_ws(const char str[]) |
| 120 | { |
| 121 | SkASSERT(str); |
| 122 | while (is_ws(*str)) |
| 123 | str++; |
| 124 | return str; |
| 125 | } |
| 126 | |
| 127 | static const char* skip_sep(const char str[]) |
| 128 | { |
| 129 | SkASSERT(str); |
| 130 | while (is_sep(*str)) |
| 131 | str++; |
| 132 | return str; |
| 133 | } |
| 134 | |
| 135 | static const char* find_points(const char str[], SkPoint value[], int count, |
| 136 | bool isRelative, SkPoint* relative) |
| 137 | { |
| 138 | str = SkParse::FindScalars(str, &value[0].fX, count * 2); |
| 139 | if (isRelative) { |
| 140 | for (int index = 0; index < count; index++) { |
| 141 | value[index].fX += relative->fX; |
| 142 | value[index].fY += relative->fY; |
| 143 | } |
| 144 | } |
| 145 | return str; |
| 146 | } |
| 147 | |
| 148 | static const char* find_scalar(const char str[], SkScalar* value, |
| 149 | bool isRelative, SkScalar relative) |
| 150 | { |
| 151 | str = SkParse::FindScalar(str, value); |
| 152 | if (isRelative) |
| 153 | *value += relative; |
| 154 | return str; |
| 155 | } |
| 156 | |
| 157 | static void showPathContour(SkPath::Iter& iter) { |
| 158 | uint8_t verb; |
| 159 | SkPoint pts[4]; |
| 160 | while ((verb = iter.next(pts)) != SkPath::kDone_Verb) { |
| 161 | switch (verb) { |
| 162 | case SkPath::kMove_Verb: |
| 163 | SkDebugf("path.moveTo(%1.9gf,%1.9gf);\n", pts[0].fX, pts[0].fY); |
| 164 | continue; |
| 165 | case SkPath::kLine_Verb: |
| 166 | SkDebugf("path.lineTo(%1.9gf,%1.9gf);\n", pts[1].fX, pts[1].fY); |
| 167 | break; |
| 168 | case SkPath::kQuad_Verb: |
| 169 | SkDebugf("path.quadTo(%1.9gf,%1.9gf, %1.9gf,%1.9gf);\n", |
| 170 | pts[1].fX, pts[1].fY, pts[2].fX, pts[2].fY); |
| 171 | break; |
| 172 | case SkPath::kCubic_Verb: |
| 173 | SkDebugf("path.cubicTo(%1.9gf,%1.9gf, %1.9gf,%1.9gf, %1.9gf,%1.9gf);\n", |
| 174 | pts[1].fX, pts[1].fY, pts[2].fX, pts[2].fY, pts[3].fX, pts[3].fY); |
| 175 | break; |
| 176 | case SkPath::kClose_Verb: |
| 177 | SkDebugf("path.close();\n"); |
| 178 | break; |
| 179 | default: |
| 180 | SkDEBUGFAIL("bad verb"); |
| 181 | return; |
| 182 | } |
| 183 | } |
| 184 | } |
| 185 | |
| 186 | static void showPath(const SkPath& path) { |
| 187 | SkPath::Iter iter(path, true); |
| 188 | int rectCount = path.isRectContours() ? path.rectContours(NULL, NULL) : 0; |
| 189 | if (rectCount > 0) { |
| 190 | SkTDArray<SkRect> rects; |
| 191 | SkTDArray<SkPath::Direction> directions; |
| 192 | rects.setCount(rectCount); |
| 193 | directions.setCount(rectCount); |
| 194 | path.rectContours(rects.begin(), directions.begin()); |
| 195 | for (int contour = 0; contour < rectCount; ++contour) { |
| 196 | const SkRect& rect = rects[contour]; |
| 197 | SkDebugf("path.addRect(%1.9g, %1.9g, %1.9g, %1.9g, %s);\n", rect.fLeft, rect.fTop, |
| 198 | rect.fRight, rect.fBottom, directions[contour] == SkPath::kCCW_Direction |
| 199 | ? "SkPath::kCCW_Direction" : "SkPath::kCW_Direction"); |
| 200 | } |
| 201 | return; |
| 202 | } |
| 203 | iter.setPath(path, true); |
| 204 | showPathContour(iter); |
| 205 | } |
| 206 | |
| 207 | static const char* parsePath(const char* data) { |
| 208 | SkPath fPath; |
| 209 | SkPoint f = {0, 0}; |
| 210 | SkPoint c = {0, 0}; |
| 211 | SkPoint lastc = {0, 0}; |
| 212 | SkPoint points[3]; |
| 213 | char op = '\0'; |
| 214 | char previousOp = '\0'; |
| 215 | bool relative = false; |
| 216 | do { |
| 217 | data = skip_ws(data); |
| 218 | if (data[0] == '\0') |
| 219 | break; |
| 220 | char ch = data[0]; |
| 221 | if (is_digit(ch) || ch == '-' || ch == '+') { |
| 222 | if (op == '\0') { |
| 223 | SkASSERT(0); |
| 224 | return 0; |
| 225 | } |
| 226 | } |
| 227 | else { |
| 228 | op = ch; |
| 229 | relative = false; |
| 230 | if (islower(op)) { |
| 231 | op = (char) toupper(op); |
| 232 | relative = true; |
| 233 | } |
| 234 | data++; |
| 235 | data = skip_sep(data); |
| 236 | } |
| 237 | switch (op) { |
| 238 | case 'M': |
| 239 | data = find_points(data, points, 1, relative, &c); |
| 240 | fPath.moveTo(points[0]); |
| 241 | op = 'L'; |
| 242 | c = points[0]; |
| 243 | break; |
| 244 | case 'L': |
| 245 | data = find_points(data, points, 1, relative, &c); |
| 246 | fPath.lineTo(points[0]); |
| 247 | c = points[0]; |
| 248 | break; |
| 249 | case 'H': { |
| 250 | SkScalar x; |
| 251 | data = find_scalar(data, &x, relative, c.fX); |
| 252 | fPath.lineTo(x, c.fY); |
| 253 | c.fX = x; |
| 254 | } |
| 255 | break; |
| 256 | case 'V': { |
| 257 | SkScalar y; |
| 258 | data = find_scalar(data, &y, relative, c.fY); |
| 259 | fPath.lineTo(c.fX, y); |
| 260 | c.fY = y; |
| 261 | } |
| 262 | break; |
| 263 | case 'C': |
| 264 | data = find_points(data, points, 3, relative, &c); |
| 265 | goto cubicCommon; |
| 266 | case 'S': |
| 267 | data = find_points(data, &points[1], 2, relative, &c); |
| 268 | points[0] = c; |
| 269 | if (previousOp == 'C' || previousOp == 'S') { |
| 270 | points[0].fX -= lastc.fX - c.fX; |
| 271 | points[0].fY -= lastc.fY - c.fY; |
| 272 | } |
| 273 | cubicCommon: |
| 274 | // if (data[0] == '\0') |
| 275 | // return; |
| 276 | #if QUADRATIC_APPROXIMATION |
| 277 | quadApprox(fPath, points[0], points[1], points[2]); |
| 278 | #else //this way just does a boring, slow old cubic |
| 279 | fPath.cubicTo(points[0], points[1], points[2]); |
| 280 | #endif |
| 281 | //if we are using the quadApprox, lastc is what it would have been if we had used |
| 282 | //cubicTo |
| 283 | lastc = points[1]; |
| 284 | c = points[2]; |
| 285 | break; |
| 286 | case 'Q': // Quadratic Bezier Curve |
| 287 | data = find_points(data, points, 2, relative, &c); |
| 288 | goto quadraticCommon; |
| 289 | case 'T': |
| 290 | data = find_points(data, &points[1], 1, relative, &c); |
| 291 | points[0] = points[1]; |
| 292 | if (previousOp == 'Q' || previousOp == 'T') { |
| 293 | points[0].fX = c.fX * 2 - lastc.fX; |
| 294 | points[0].fY = c.fY * 2 - lastc.fY; |
| 295 | } |
| 296 | quadraticCommon: |
| 297 | fPath.quadTo(points[0], points[1]); |
| 298 | lastc = points[0]; |
| 299 | c = points[1]; |
| 300 | break; |
| 301 | case 'Z': |
| 302 | fPath.close(); |
| 303 | #if 0 // !!! still a bug? |
| 304 | if (fPath.isEmpty() && (f.fX != 0 || f.fY != 0)) { |
| 305 | c.fX -= SkScalar.Epsilon; // !!! enough? |
| 306 | fPath.moveTo(c); |
| 307 | fPath.lineTo(f); |
| 308 | fPath.close(); |
| 309 | } |
| 310 | #endif |
| 311 | c = f; |
| 312 | op = '\0'; |
| 313 | break; |
| 314 | case '~': { |
| 315 | SkPoint args[2]; |
| 316 | data = find_points(data, args, 2, false, NULL); |
| 317 | fPath.moveTo(args[0].fX, args[0].fY); |
| 318 | fPath.lineTo(args[1].fX, args[1].fY); |
| 319 | } |
| 320 | break; |
| 321 | default: |
| 322 | SkASSERT(0); |
| 323 | return 0; |
| 324 | } |
| 325 | if (previousOp == 0) |
| 326 | f = c; |
| 327 | previousOp = op; |
| 328 | } while (data[0] != '"'); |
| 329 | showPath(fPath); |
| 330 | return data; |
| 331 | } |
| 332 | |
| 333 | const char pathPrefix[] = "<path fill=\""; |
| 334 | |
| 335 | void parseSVG(); |
| 336 | void parseSVG() { |
| 337 | const char* data = logoStr; |
| 338 | const char* dataEnd = logoStr + logoStrLen - 1; |
| 339 | while (data < dataEnd) { |
| 340 | SkASSERT(strncmp(data, pathPrefix, sizeof(pathPrefix) - 1) == 0); |
| 341 | data += sizeof(pathPrefix) - 1; |
| 342 | SkDebugf("paint.setColor(0xFF%c%c%c%c%c%c);\n", data[1], data[2], data[3], data[4], |
| 343 | data[5], data[6]); |
| 344 | data += 8; |
| 345 | SkASSERT(strncmp(data, "d=\"", 3) == 0); |
| 346 | data += 3; |
| 347 | SkDebugf("path.reset();\n"); |
| 348 | data = parsePath(data); |
| 349 | SkDebugf("canvas->drawPath(path, paint);\n"); |
| 350 | SkASSERT(strncmp(data, "\"/>", 3) == 0); |
| 351 | data += 3; |
| 352 | } |
| 353 | } |