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