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     SkASSERT(str);
     44     while (is_sep(*str))
     45         str++;
     46     return str;
     47 }
     48 
     49 static const char* find_points(const char str[], SkPoint value[], int count,
     50                                bool isRelative, SkPoint* relative) {
     51     str = SkParse::FindScalars(str, &value[0].fX, count * 2);
     52     if (isRelative) {
     53         for (int index = 0; index < count; index++) {
     54             value[index].fX += relative->fX;
     55             value[index].fY += relative->fY;
     56         }
     57     }
     58     return str;
     59 }
     60 
     61 static const char* find_scalar(const char str[], SkScalar* value,
     62                                bool isRelative, SkScalar relative) {
     63     str = SkParse::FindScalar(str, value);
     64     if (isRelative) {
     65         *value += relative;
     66     }
     67     return str;
     68 }
     69 
     70 bool SkParsePath::FromSVGString(const char data[], SkPath* result) {
     71     SkPath path;
     72     SkPoint f = {0, 0};
     73     SkPoint c = {0, 0};
     74     SkPoint lastc = {0, 0};
     75     SkPoint points[3];
     76     char op = '\0';
     77     char previousOp = '\0';
     78     bool relative = false;
     79     for (;;) {
     80         data = skip_ws(data);
     81         if (data[0] == '\0') {
     82             break;
     83         }
     84         char ch = data[0];
     85         if (is_digit(ch) || ch == '-' || ch == '+') {
     86             if (op == '\0') {
     87                 return false;
     88             }
     89         } else {
     90             op = ch;
     91             relative = false;
     92             if (is_lower(op)) {
     93                 op = (char) to_upper(op);
     94                 relative = true;
     95             }
     96             data++;
     97             data = skip_sep(data);
     98         }
     99         switch (op) {
    100             case 'M':
    101                 data = find_points(data, points, 1, relative, &c);
    102                 path.moveTo(points[0]);
    103                 op = 'L';
    104                 c = points[0];
    105                 break;
    106             case 'L':
    107                 data = find_points(data, points, 1, relative, &c);
    108                 path.lineTo(points[0]);
    109                 c = points[0];
    110                 break;
    111             case 'H': {
    112                 SkScalar x;
    113                 data = find_scalar(data, &x, relative, c.fX);
    114                 path.lineTo(x, c.fY);
    115                 c.fX = x;
    116             } break;
    117             case 'V': {
    118                 SkScalar y;
    119                 data = find_scalar(data, &y, relative, c.fY);
    120                 path.lineTo(c.fX, y);
    121                 c.fY = y;
    122             } break;
    123             case 'C':
    124                 data = find_points(data, points, 3, relative, &c);
    125                 goto cubicCommon;
    126             case 'S':
    127                 data = find_points(data, &points[1], 2, relative, &c);
    128                 points[0] = c;
    129                 if (previousOp == 'C' || previousOp == 'S') {
    130                     points[0].fX -= lastc.fX - c.fX;
    131                     points[0].fY -= lastc.fY - c.fY;
    132                 }
    133             cubicCommon:
    134                 path.cubicTo(points[0], points[1], points[2]);
    135                 lastc = points[1];
    136                 c = points[2];
    137                 break;
    138             case 'Q':  // Quadratic Bezier Curve
    139                 data = find_points(data, points, 2, relative, &c);
    140                 goto quadraticCommon;
    141             case 'T':
    142                 data = find_points(data, &points[1], 1, relative, &c);
    143                 points[0] = points[1];
    144                 if (previousOp == 'Q' || previousOp == 'T') {
    145                     points[0].fX = c.fX * 2 - lastc.fX;
    146                     points[0].fY = c.fY * 2 - lastc.fY;
    147                 }
    148             quadraticCommon:
    149                 path.quadTo(points[0], points[1]);
    150                 lastc = points[0];
    151                 c = points[1];
    152                 break;
    153             case 'Z':
    154                 path.close();
    155 #if 0   // !!! still a bug?
    156                 if (fPath.isEmpty() && (f.fX != 0 || f.fY != 0)) {
    157                     c.fX -= SkScalar.Epsilon;   // !!! enough?
    158                     fPath.moveTo(c);
    159                     fPath.lineTo(f);
    160                     fPath.close();
    161                 }
    162 #endif
    163                 c = f;
    164                 op = '\0';
    165                 break;
    166             case '~': {
    167                 SkPoint args[2];
    168                 data = find_points(data, args, 2, false, NULL);
    169                 path.moveTo(args[0].fX, args[0].fY);
    170                 path.lineTo(args[1].fX, args[1].fY);
    171             } break;
    172             default:
    173                 return false;
    174         }
    175         if (previousOp == 0) {
    176             f = c;
    177         }
    178         previousOp = op;
    179     }
    180     // we're good, go ahead and swap in the result
    181     result->swap(path);
    182     return true;
    183 }
    184 
    185 ///////////////////////////////////////////////////////////////////////////////
    186 
    187 #include "SkString.h"
    188 #include "SkStream.h"
    189 
    190 static void write_scalar(SkWStream* stream, SkScalar value) {
    191 #ifdef SK_SCALAR_IS_FLOAT
    192     char buffer[64];
    193 #ifdef SK_BUILD_FOR_WIN32
    194     int len = _snprintf(buffer, sizeof(buffer), "%g", value);
    195 #else
    196     int len = snprintf(buffer, sizeof(buffer), "%g", value);
    197 #endif
    198     char* stop = buffer + len;
    199 #else
    200     char    buffer[SkStrAppendScalar_MaxSize];
    201     char*   stop = SkStrAppendScalar(buffer, value);
    202 #endif
    203     stream->write(buffer, stop - buffer);
    204 }
    205 
    206 static void append_scalars(SkWStream* stream, char verb, const SkScalar data[],
    207                            int count) {
    208     stream->write(&verb, 1);
    209     write_scalar(stream, data[0]);
    210     for (int i = 1; i < count; i++) {
    211         stream->write(" ", 1);
    212         write_scalar(stream, data[i]);
    213     }
    214 }
    215 
    216 void SkParsePath::ToSVGString(const SkPath& path, SkString* str) {
    217     SkDynamicMemoryWStream  stream;
    218 
    219     SkPath::Iter    iter(path, false);
    220     SkPoint         pts[4];
    221 
    222     for (;;) {
    223         switch (iter.next(pts, false)) {
    224              case SkPath::kConic_Verb:
    225                 SkASSERT(0);
    226                 break;
    227            case SkPath::kMove_Verb:
    228                 append_scalars(&stream, 'M', &pts[0].fX, 2);
    229                 break;
    230             case SkPath::kLine_Verb:
    231                 append_scalars(&stream, 'L', &pts[1].fX, 2);
    232                 break;
    233             case SkPath::kQuad_Verb:
    234                 append_scalars(&stream, 'Q', &pts[1].fX, 4);
    235                 break;
    236             case SkPath::kCubic_Verb:
    237                 append_scalars(&stream, 'C', &pts[1].fX, 6);
    238                 break;
    239             case SkPath::kClose_Verb:
    240                 stream.write("Z", 1);
    241                 break;
    242             case SkPath::kDone_Verb:
    243                 str->resize(stream.getOffset());
    244                 stream.copyTo(str->writable_str());
    245             return;
    246         }
    247     }
    248 }
    249