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