1cb93a386Sopenharmony_ci/* 2cb93a386Sopenharmony_ci * Copyright 2011 Google Inc. 3cb93a386Sopenharmony_ci * 4cb93a386Sopenharmony_ci * Use of this source code is governed by a BSD-style license that can be 5cb93a386Sopenharmony_ci * found in the LICENSE file. 6cb93a386Sopenharmony_ci */ 7cb93a386Sopenharmony_ci#include "include/utils/SkParse.h" 8cb93a386Sopenharmony_ci#include "include/utils/SkParsePath.h" 9cb93a386Sopenharmony_ci 10cb93a386Sopenharmony_cistatic inline bool is_between(int c, int min, int max) { 11cb93a386Sopenharmony_ci return (unsigned)(c - min) <= (unsigned)(max - min); 12cb93a386Sopenharmony_ci} 13cb93a386Sopenharmony_ci 14cb93a386Sopenharmony_cistatic inline bool is_ws(int c) { 15cb93a386Sopenharmony_ci return is_between(c, 1, 32); 16cb93a386Sopenharmony_ci} 17cb93a386Sopenharmony_ci 18cb93a386Sopenharmony_cistatic inline bool is_digit(int c) { 19cb93a386Sopenharmony_ci return is_between(c, '0', '9'); 20cb93a386Sopenharmony_ci} 21cb93a386Sopenharmony_ci 22cb93a386Sopenharmony_cistatic inline bool is_sep(int c) { 23cb93a386Sopenharmony_ci return is_ws(c) || c == ','; 24cb93a386Sopenharmony_ci} 25cb93a386Sopenharmony_ci 26cb93a386Sopenharmony_cistatic inline bool is_lower(int c) { 27cb93a386Sopenharmony_ci return is_between(c, 'a', 'z'); 28cb93a386Sopenharmony_ci} 29cb93a386Sopenharmony_ci 30cb93a386Sopenharmony_cistatic inline int to_upper(int c) { 31cb93a386Sopenharmony_ci return c - 'a' + 'A'; 32cb93a386Sopenharmony_ci} 33cb93a386Sopenharmony_ci 34cb93a386Sopenharmony_cistatic const char* skip_ws(const char str[]) { 35cb93a386Sopenharmony_ci SkASSERT(str); 36cb93a386Sopenharmony_ci while (is_ws(*str)) 37cb93a386Sopenharmony_ci str++; 38cb93a386Sopenharmony_ci return str; 39cb93a386Sopenharmony_ci} 40cb93a386Sopenharmony_ci 41cb93a386Sopenharmony_cistatic const char* skip_sep(const char str[]) { 42cb93a386Sopenharmony_ci if (!str) { 43cb93a386Sopenharmony_ci return nullptr; 44cb93a386Sopenharmony_ci } 45cb93a386Sopenharmony_ci while (is_sep(*str)) 46cb93a386Sopenharmony_ci str++; 47cb93a386Sopenharmony_ci return str; 48cb93a386Sopenharmony_ci} 49cb93a386Sopenharmony_ci 50cb93a386Sopenharmony_cistatic const char* find_points(const char str[], SkPoint value[], int count, 51cb93a386Sopenharmony_ci bool isRelative, SkPoint* relative) { 52cb93a386Sopenharmony_ci str = SkParse::FindScalars(str, &value[0].fX, count * 2); 53cb93a386Sopenharmony_ci if (isRelative) { 54cb93a386Sopenharmony_ci for (int index = 0; index < count; index++) { 55cb93a386Sopenharmony_ci value[index].fX += relative->fX; 56cb93a386Sopenharmony_ci value[index].fY += relative->fY; 57cb93a386Sopenharmony_ci } 58cb93a386Sopenharmony_ci } 59cb93a386Sopenharmony_ci return str; 60cb93a386Sopenharmony_ci} 61cb93a386Sopenharmony_ci 62cb93a386Sopenharmony_cistatic const char* find_scalar(const char str[], SkScalar* value, 63cb93a386Sopenharmony_ci bool isRelative, SkScalar relative) { 64cb93a386Sopenharmony_ci str = SkParse::FindScalar(str, value); 65cb93a386Sopenharmony_ci if (!str) { 66cb93a386Sopenharmony_ci return nullptr; 67cb93a386Sopenharmony_ci } 68cb93a386Sopenharmony_ci if (isRelative) { 69cb93a386Sopenharmony_ci *value += relative; 70cb93a386Sopenharmony_ci } 71cb93a386Sopenharmony_ci str = skip_sep(str); 72cb93a386Sopenharmony_ci return str; 73cb93a386Sopenharmony_ci} 74cb93a386Sopenharmony_ci 75cb93a386Sopenharmony_ci// https://www.w3.org/TR/SVG11/paths.html#PathDataBNF 76cb93a386Sopenharmony_ci// 77cb93a386Sopenharmony_ci// flag: 78cb93a386Sopenharmony_ci// "0" | "1" 79cb93a386Sopenharmony_cistatic const char* find_flag(const char str[], bool* value) { 80cb93a386Sopenharmony_ci if (!str) { 81cb93a386Sopenharmony_ci return nullptr; 82cb93a386Sopenharmony_ci } 83cb93a386Sopenharmony_ci if (str[0] != '1' && str[0] != '0') { 84cb93a386Sopenharmony_ci return nullptr; 85cb93a386Sopenharmony_ci } 86cb93a386Sopenharmony_ci *value = str[0] != '0'; 87cb93a386Sopenharmony_ci str = skip_sep(str + 1); 88cb93a386Sopenharmony_ci return str; 89cb93a386Sopenharmony_ci} 90cb93a386Sopenharmony_ci 91cb93a386Sopenharmony_cibool SkParsePath::FromSVGString(const char data[], SkPath* result) { 92cb93a386Sopenharmony_ci SkPath path; 93cb93a386Sopenharmony_ci SkPoint first = {0, 0}; 94cb93a386Sopenharmony_ci SkPoint c = {0, 0}; 95cb93a386Sopenharmony_ci SkPoint lastc = {0, 0}; 96cb93a386Sopenharmony_ci SkPoint points[3]; 97cb93a386Sopenharmony_ci char op = '\0'; 98cb93a386Sopenharmony_ci char previousOp = '\0'; 99cb93a386Sopenharmony_ci bool relative = false; 100cb93a386Sopenharmony_ci for (;;) { 101cb93a386Sopenharmony_ci if (!data) { 102cb93a386Sopenharmony_ci // Truncated data 103cb93a386Sopenharmony_ci return false; 104cb93a386Sopenharmony_ci } 105cb93a386Sopenharmony_ci data = skip_ws(data); 106cb93a386Sopenharmony_ci if (data[0] == '\0') { 107cb93a386Sopenharmony_ci break; 108cb93a386Sopenharmony_ci } 109cb93a386Sopenharmony_ci char ch = data[0]; 110cb93a386Sopenharmony_ci if (is_digit(ch) || ch == '-' || ch == '+' || ch == '.') { 111cb93a386Sopenharmony_ci if (op == '\0' || op == 'Z') { 112cb93a386Sopenharmony_ci return false; 113cb93a386Sopenharmony_ci } 114cb93a386Sopenharmony_ci } else if (is_sep(ch)) { 115cb93a386Sopenharmony_ci data = skip_sep(data); 116cb93a386Sopenharmony_ci } else { 117cb93a386Sopenharmony_ci op = ch; 118cb93a386Sopenharmony_ci relative = false; 119cb93a386Sopenharmony_ci if (is_lower(op)) { 120cb93a386Sopenharmony_ci op = (char) to_upper(op); 121cb93a386Sopenharmony_ci relative = true; 122cb93a386Sopenharmony_ci } 123cb93a386Sopenharmony_ci data++; 124cb93a386Sopenharmony_ci data = skip_sep(data); 125cb93a386Sopenharmony_ci } 126cb93a386Sopenharmony_ci switch (op) { 127cb93a386Sopenharmony_ci case 'M': 128cb93a386Sopenharmony_ci data = find_points(data, points, 1, relative, &c); 129cb93a386Sopenharmony_ci path.moveTo(points[0]); 130cb93a386Sopenharmony_ci previousOp = '\0'; 131cb93a386Sopenharmony_ci op = 'L'; 132cb93a386Sopenharmony_ci c = points[0]; 133cb93a386Sopenharmony_ci break; 134cb93a386Sopenharmony_ci case 'L': 135cb93a386Sopenharmony_ci data = find_points(data, points, 1, relative, &c); 136cb93a386Sopenharmony_ci path.lineTo(points[0]); 137cb93a386Sopenharmony_ci c = points[0]; 138cb93a386Sopenharmony_ci break; 139cb93a386Sopenharmony_ci case 'H': { 140cb93a386Sopenharmony_ci SkScalar x; 141cb93a386Sopenharmony_ci data = find_scalar(data, &x, relative, c.fX); 142cb93a386Sopenharmony_ci path.lineTo(x, c.fY); 143cb93a386Sopenharmony_ci c.fX = x; 144cb93a386Sopenharmony_ci } break; 145cb93a386Sopenharmony_ci case 'V': { 146cb93a386Sopenharmony_ci SkScalar y; 147cb93a386Sopenharmony_ci data = find_scalar(data, &y, relative, c.fY); 148cb93a386Sopenharmony_ci path.lineTo(c.fX, y); 149cb93a386Sopenharmony_ci c.fY = y; 150cb93a386Sopenharmony_ci } break; 151cb93a386Sopenharmony_ci case 'C': 152cb93a386Sopenharmony_ci data = find_points(data, points, 3, relative, &c); 153cb93a386Sopenharmony_ci goto cubicCommon; 154cb93a386Sopenharmony_ci case 'S': 155cb93a386Sopenharmony_ci data = find_points(data, &points[1], 2, relative, &c); 156cb93a386Sopenharmony_ci points[0] = c; 157cb93a386Sopenharmony_ci if (previousOp == 'C' || previousOp == 'S') { 158cb93a386Sopenharmony_ci points[0].fX -= lastc.fX - c.fX; 159cb93a386Sopenharmony_ci points[0].fY -= lastc.fY - c.fY; 160cb93a386Sopenharmony_ci } 161cb93a386Sopenharmony_ci cubicCommon: 162cb93a386Sopenharmony_ci path.cubicTo(points[0], points[1], points[2]); 163cb93a386Sopenharmony_ci lastc = points[1]; 164cb93a386Sopenharmony_ci c = points[2]; 165cb93a386Sopenharmony_ci break; 166cb93a386Sopenharmony_ci case 'Q': // Quadratic Bezier Curve 167cb93a386Sopenharmony_ci data = find_points(data, points, 2, relative, &c); 168cb93a386Sopenharmony_ci goto quadraticCommon; 169cb93a386Sopenharmony_ci case 'T': 170cb93a386Sopenharmony_ci data = find_points(data, &points[1], 1, relative, &c); 171cb93a386Sopenharmony_ci points[0] = c; 172cb93a386Sopenharmony_ci if (previousOp == 'Q' || previousOp == 'T') { 173cb93a386Sopenharmony_ci points[0].fX -= lastc.fX - c.fX; 174cb93a386Sopenharmony_ci points[0].fY -= lastc.fY - c.fY; 175cb93a386Sopenharmony_ci } 176cb93a386Sopenharmony_ci quadraticCommon: 177cb93a386Sopenharmony_ci path.quadTo(points[0], points[1]); 178cb93a386Sopenharmony_ci lastc = points[0]; 179cb93a386Sopenharmony_ci c = points[1]; 180cb93a386Sopenharmony_ci break; 181cb93a386Sopenharmony_ci case 'A': { 182cb93a386Sopenharmony_ci SkPoint radii; 183cb93a386Sopenharmony_ci SkScalar angle; 184cb93a386Sopenharmony_ci bool largeArc, sweep; 185cb93a386Sopenharmony_ci if ((data = find_points(data, &radii, 1, false, nullptr)) 186cb93a386Sopenharmony_ci && (data = skip_sep(data)) 187cb93a386Sopenharmony_ci && (data = find_scalar(data, &angle, false, 0)) 188cb93a386Sopenharmony_ci && (data = skip_sep(data)) 189cb93a386Sopenharmony_ci && (data = find_flag(data, &largeArc)) 190cb93a386Sopenharmony_ci && (data = skip_sep(data)) 191cb93a386Sopenharmony_ci && (data = find_flag(data, &sweep)) 192cb93a386Sopenharmony_ci && (data = skip_sep(data)) 193cb93a386Sopenharmony_ci && (data = find_points(data, &points[0], 1, relative, &c))) { 194cb93a386Sopenharmony_ci path.arcTo(radii, angle, (SkPath::ArcSize) largeArc, 195cb93a386Sopenharmony_ci (SkPathDirection) !sweep, points[0]); 196cb93a386Sopenharmony_ci path.getLastPt(&c); 197cb93a386Sopenharmony_ci } 198cb93a386Sopenharmony_ci } break; 199cb93a386Sopenharmony_ci case 'Z': 200cb93a386Sopenharmony_ci path.close(); 201cb93a386Sopenharmony_ci c = first; 202cb93a386Sopenharmony_ci break; 203cb93a386Sopenharmony_ci case '~': { 204cb93a386Sopenharmony_ci SkPoint args[2]; 205cb93a386Sopenharmony_ci data = find_points(data, args, 2, false, nullptr); 206cb93a386Sopenharmony_ci path.moveTo(args[0].fX, args[0].fY); 207cb93a386Sopenharmony_ci path.lineTo(args[1].fX, args[1].fY); 208cb93a386Sopenharmony_ci } break; 209cb93a386Sopenharmony_ci default: 210cb93a386Sopenharmony_ci return false; 211cb93a386Sopenharmony_ci } 212cb93a386Sopenharmony_ci if (previousOp == 0) { 213cb93a386Sopenharmony_ci first = c; 214cb93a386Sopenharmony_ci } 215cb93a386Sopenharmony_ci previousOp = op; 216cb93a386Sopenharmony_ci } 217cb93a386Sopenharmony_ci // we're good, go ahead and swap in the result 218cb93a386Sopenharmony_ci result->swap(path); 219cb93a386Sopenharmony_ci return true; 220cb93a386Sopenharmony_ci} 221cb93a386Sopenharmony_ci 222cb93a386Sopenharmony_ci/////////////////////////////////////////////////////////////////////////////// 223cb93a386Sopenharmony_ci 224cb93a386Sopenharmony_ci#include "include/core/SkStream.h" 225cb93a386Sopenharmony_ci#include "include/core/SkString.h" 226cb93a386Sopenharmony_ci#include "src/core/SkGeometry.h" 227cb93a386Sopenharmony_ci 228cb93a386Sopenharmony_cistatic void write_scalar(SkWStream* stream, SkScalar value) { 229cb93a386Sopenharmony_ci char buffer[64]; 230cb93a386Sopenharmony_ci#ifdef SK_BUILD_FOR_WIN 231cb93a386Sopenharmony_ci int len = _snprintf(buffer, sizeof(buffer), "%g", value); 232cb93a386Sopenharmony_ci#else 233cb93a386Sopenharmony_ci int len = snprintf(buffer, sizeof(buffer), "%g", value); 234cb93a386Sopenharmony_ci#endif 235cb93a386Sopenharmony_ci char* stop = buffer + len; 236cb93a386Sopenharmony_ci stream->write(buffer, stop - buffer); 237cb93a386Sopenharmony_ci} 238cb93a386Sopenharmony_ci 239cb93a386Sopenharmony_civoid SkParsePath::ToSVGString(const SkPath& path, SkString* str, PathEncoding encoding) { 240cb93a386Sopenharmony_ci SkDynamicMemoryWStream stream; 241cb93a386Sopenharmony_ci 242cb93a386Sopenharmony_ci SkPoint current_point{0,0}; 243cb93a386Sopenharmony_ci const auto rel_selector = encoding == PathEncoding::Relative; 244cb93a386Sopenharmony_ci 245cb93a386Sopenharmony_ci const auto append_command = [&](char cmd, const SkPoint pts[], size_t count) { 246cb93a386Sopenharmony_ci // Use lower case cmds for relative encoding. 247cb93a386Sopenharmony_ci cmd += 32 * rel_selector; 248cb93a386Sopenharmony_ci stream.write(&cmd, 1); 249cb93a386Sopenharmony_ci 250cb93a386Sopenharmony_ci for (size_t i = 0; i < count; ++i) { 251cb93a386Sopenharmony_ci const auto pt = pts[i] - current_point; 252cb93a386Sopenharmony_ci if (i > 0) { 253cb93a386Sopenharmony_ci stream.write(" ", 1); 254cb93a386Sopenharmony_ci } 255cb93a386Sopenharmony_ci write_scalar(&stream, pt.fX); 256cb93a386Sopenharmony_ci stream.write(" ", 1); 257cb93a386Sopenharmony_ci write_scalar(&stream, pt.fY); 258cb93a386Sopenharmony_ci } 259cb93a386Sopenharmony_ci 260cb93a386Sopenharmony_ci SkASSERT(count > 0); 261cb93a386Sopenharmony_ci // For relative encoding, track the current point (otherwise == origin). 262cb93a386Sopenharmony_ci current_point = pts[count - 1] * rel_selector; 263cb93a386Sopenharmony_ci }; 264cb93a386Sopenharmony_ci 265cb93a386Sopenharmony_ci SkPath::Iter iter(path, false); 266cb93a386Sopenharmony_ci SkPoint pts[4]; 267cb93a386Sopenharmony_ci 268cb93a386Sopenharmony_ci for (;;) { 269cb93a386Sopenharmony_ci switch (iter.next(pts)) { 270cb93a386Sopenharmony_ci case SkPath::kConic_Verb: { 271cb93a386Sopenharmony_ci const SkScalar tol = SK_Scalar1 / 1024; // how close to a quad 272cb93a386Sopenharmony_ci SkAutoConicToQuads quadder; 273cb93a386Sopenharmony_ci const SkPoint* quadPts = quadder.computeQuads(pts, iter.conicWeight(), tol); 274cb93a386Sopenharmony_ci for (int i = 0; i < quadder.countQuads(); ++i) { 275cb93a386Sopenharmony_ci append_command('Q', &quadPts[i*2 + 1], 2); 276cb93a386Sopenharmony_ci } 277cb93a386Sopenharmony_ci } break; 278cb93a386Sopenharmony_ci case SkPath::kMove_Verb: 279cb93a386Sopenharmony_ci append_command('M', &pts[0], 1); 280cb93a386Sopenharmony_ci break; 281cb93a386Sopenharmony_ci case SkPath::kLine_Verb: 282cb93a386Sopenharmony_ci append_command('L', &pts[1], 1); 283cb93a386Sopenharmony_ci break; 284cb93a386Sopenharmony_ci case SkPath::kQuad_Verb: 285cb93a386Sopenharmony_ci append_command('Q', &pts[1], 2); 286cb93a386Sopenharmony_ci break; 287cb93a386Sopenharmony_ci case SkPath::kCubic_Verb: 288cb93a386Sopenharmony_ci append_command('C', &pts[1], 3); 289cb93a386Sopenharmony_ci break; 290cb93a386Sopenharmony_ci case SkPath::kClose_Verb: 291cb93a386Sopenharmony_ci stream.write("Z", 1); 292cb93a386Sopenharmony_ci break; 293cb93a386Sopenharmony_ci case SkPath::kDone_Verb: 294cb93a386Sopenharmony_ci str->resize(stream.bytesWritten()); 295cb93a386Sopenharmony_ci stream.copyTo(str->writable_str()); 296cb93a386Sopenharmony_ci return; 297cb93a386Sopenharmony_ci } 298cb93a386Sopenharmony_ci } 299cb93a386Sopenharmony_ci} 300