LCOV - code coverage report
Current view: top level - core/vg - SPVectorPathData.cc (source / functions) Hit Total Coverage
Test: coverage.info Lines: 554 560 98.9 %
Date: 2024-05-12 00:16:13 Functions: 87 87 100.0 %

          Line data    Source code
       1             : /**
       2             :  Copyright (c) 2024 Stappler LLC <admin@stappler.dev>
       3             : 
       4             :  Permission is hereby granted, free of charge, to any person obtaining a copy
       5             :  of this software and associated documentation files (the "Software"), to deal
       6             :  in the Software without restriction, including without limitation the rights
       7             :  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
       8             :  copies of the Software, and to permit persons to whom the Software is
       9             :  furnished to do so, subject to the following conditions:
      10             : 
      11             :  The above copyright notice and this permission notice shall be included in
      12             :  all copies or substantial portions of the Software.
      13             : 
      14             :  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
      15             :  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
      16             :  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
      17             :  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
      18             :  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
      19             :  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
      20             :  THE SOFTWARE.
      21             :  **/
      22             : 
      23             : #include "SPVectorPathData.h"
      24             : #include "SPFilesystem.h"
      25             : 
      26             : namespace STAPPLER_VERSIONIZED stappler::vg {
      27             : 
      28             : #define SP_PATH_LOG(...)
      29             : //#define SP_PATH_LOG(...) stappler::log::format(log::Debug, "Path Debug", __VA_ARGS__)
      30             : 
      31             : #define SP_PATH_LOG_TEXT(...)
      32             : //#define SP_PATH_LOG_TEXT(...) stappler::log::text(log::Debug, "Path Debug", __VA_ARGS__)
      33             : 
      34             : 
      35             : // to prevent math errors on relative values we use double for SVG reader
      36             : // Path itself uses single-word float for performance
      37             : class SVGPathReader {
      38             : public:
      39         175 :         static bool readFileContent(PathWriter *p, StringView content) {
      40         175 :                 StringView r(content);
      41         175 :                 r.skipUntilString("<path ");
      42         175 :                 if (!r.is("<path ")) {
      43             :                         return false;
      44             :                 }
      45             : 
      46         150 :                 r.skipString("<path ");
      47         150 :                 StringView pathContent = r.readUntil<StringView::Chars<'>'>>();
      48         150 :                 pathContent.skipUntilString("d=\"");
      49         150 :                 if (pathContent.is("d=\"")) {
      50         150 :                         pathContent.skipString("d=\"");
      51         150 :                         return readPath(p, pathContent.readUntil<StringView::Chars<'"'>>());
      52             :                 }
      53             :                 return false;
      54             :         }
      55             : 
      56          75 :         static bool readFile(PathWriter *p, const StringView &str) {
      57          75 :                 if (!str.empty()) {
      58          75 :                         auto content = filesystem::readTextFile<memory::StandartInterface>(str);
      59          75 :                         return readFileContent(p, content);
      60          75 :                 }
      61             :                 return false;
      62             :         }
      63             : 
      64        1450 :         static bool readPath(PathWriter *p, const StringView &r) {
      65        1450 :                 if (r.size() > 0) {
      66        1450 :                         SVGPathReader reader(p, r);
      67        1450 :                         return reader.parse();
      68             :                 }
      69             :                 return false;
      70             :         }
      71             : 
      72             : protected:
      73        1450 :         bool parse() {
      74        2900 :                 while (!reader.empty()) {
      75        1450 :                         if (!readCmdGroup()) {
      76             :                                 return false;
      77             :                         }
      78             :                 }
      79             : 
      80             :                 return true;
      81             :         }
      82             : 
      83        1450 :         bool readCmdGroup() {
      84        1450 :                 readWhitespace();
      85        2900 :                 while (!reader.empty()) {
      86        1450 :                         if (!readCmd()) {
      87             :                                 return false;
      88             :                         }
      89             :                 }
      90             :                 return true;
      91             :         }
      92             : 
      93        1450 :         bool readCmd() {
      94        1450 :                 if (!readMoveTo()) {
      95             :                         return false;
      96             :                 }
      97        1450 :                 readWhitespace();
      98             : 
      99             :                 bool readNext = true;
     100        8850 :                 while(readNext) {
     101        5950 :                         readNext = readDrawTo();
     102        5950 :                         if (readNext) {
     103        4500 :                                 readWhitespace();
     104             :                         }
     105             :                 }
     106             : 
     107             :                 return true;
     108             :         }
     109             : 
     110        1450 :         bool readMoveTo() {
     111        1450 :                 if (reader >= 1) {
     112        1450 :                         readWhitespace();
     113             :                         bool relative = true;
     114        1450 :                         if (reader.is('M')) {
     115        1400 :                                 relative = false; ++ reader;
     116          50 :                         } else if (reader.is('m')) {
     117          50 :                                 relative = true; ++ reader;
     118             :                         } else {
     119             :                                 return false;
     120             :                         }
     121             : 
     122        1450 :                         readWhitespace();
     123        1450 :                         return readMoveToArgs(relative);
     124             :                 }
     125             :                 return false;
     126             :         }
     127             : 
     128        5950 :         bool readDrawTo() {
     129        5950 :                 if (reader >= 1) {
     130        4500 :                         auto c = reader[0];
     131        4500 :                         ++ reader;
     132             : 
     133        4500 :                         readWhitespace();
     134        4500 :                         if (c == 'M' || c == 'm') {
     135         225 :                                 return readMoveToArgs(c == 'm');
     136        4275 :                         } else if (c == 'Z' || c == 'z') {
     137             :                                 SP_PATH_LOG("Z");
     138         600 :                                 if (_pathStarted) {
     139         600 :                                         _x = _sx;
     140         600 :                                         _y = _sy;
     141         600 :                                         _pathStarted = false;
     142             :                                 }
     143         600 :                                 path->closePath();
     144         600 :                                 return true;
     145        3675 :                         } else if (c == 'L' || c == 'l') {
     146         750 :                                 return readLineToArgs(c == 'l');
     147        2925 :                         } else if (c == 'H' || c == 'h') {
     148         425 :                                 return readHorizontalLineTo(c == 'h');
     149        2500 :                         } else if (c == 'V' || c == 'v') {
     150         425 :                                 return readVerticalLineTo(c == 'v');
     151        2075 :                         } else if (c == 'C' || c == 'c') {
     152        1175 :                                 return readCubicBezier(c == 'c');
     153         900 :                         } else if (c == 'S' || c == 's') {
     154          50 :                                 return readCubicBezierShort(c == 's');
     155         850 :                         } else if (c == 'Q' || c == 'q') {
     156         250 :                                 return readQuadraticBezier(c == 'q');
     157         600 :                         } else if (c == 'T' || c == 't') {
     158          50 :                                 return readQuadraticBezierShort(c == 't');
     159         550 :                         } else if (c == 'A' || c == 'a') {
     160         550 :                                 return readEllipticalArc(c == 'a');
     161             :                         }
     162             :                 }
     163             :                 return false;
     164             :         }
     165             : 
     166        2425 :         bool readLineToArgs(bool relative) {
     167             :                 double x, y;
     168             :                 bool readNext = true, first = true, ret = false;
     169        6450 :                 while (readNext) {
     170        3250 :                         readCommaWhitespace();
     171        3250 :                         readNext = readCoordPair(x, y);
     172        3250 :                         if (first && !readNext) {
     173             :                                 return false;
     174        1600 :                         } else if (readNext) {
     175         825 :                                 if (first) { ret = true; first = false; }
     176         825 :                                 if (relative) { x = _x + x; y = _y + y; }
     177             : 
     178             :                                 SP_PATH_LOG("L %f %f (%f %f)", x, y, x - _x, y - _y);
     179         825 :                                 _x = x; _y = y; _b = false;
     180         825 :                                 path->lineTo(x, y);
     181             :                         }
     182             :                 }
     183             :                 return ret;
     184             :         }
     185             : 
     186         425 :         bool readHorizontalLineTo(bool relative) {
     187             :                 double x;
     188             :                 bool readNext = true, first = true, ret = false;
     189        1700 :                 while (readNext) {
     190         850 :                         readCommaWhitespace();
     191         850 :                         readNext = readNumber(x);
     192         850 :                         if (first && !readNext) {
     193             :                                 return false;
     194         850 :                         } else if (readNext) {
     195         425 :                                 if (first) { ret = true; first = false; }
     196         425 :                                 if (relative) { x = _x + x; }
     197             : 
     198             :                                 SP_PATH_LOG("H %f (%f)", x, x - _x);
     199         425 :                                 _x = x; _b = false;
     200         425 :                                 path->lineTo(x, _y);
     201             :                         }
     202             :                 }
     203             :                 return ret;
     204             :         }
     205             : 
     206         425 :         bool readVerticalLineTo(bool relative) {
     207             :                 double y;
     208             :                 bool readNext = true, first = true, ret = false;
     209        1700 :                 while (readNext) {
     210         850 :                         readCommaWhitespace();
     211         850 :                         readNext = readNumber(y);
     212         850 :                         if (first && !readNext) {
     213             :                                 return false;
     214         850 :                         } else if (readNext) {
     215         425 :                                 if (first) { ret = true; first = false; }
     216         425 :                                 if (relative) { y = _y + y; }
     217             : 
     218             :                                 SP_PATH_LOG("V %f (%f)", y, y - _y);
     219         425 :                                 _y = y; _b = false;
     220         425 :                                 path->lineTo(_x, y);
     221             :                         }
     222             :                 }
     223             :                 return ret;
     224             :         }
     225             : 
     226        1175 :         bool readCubicBezier(bool relative) {
     227             :                 double x1, y1, x2, y2, x, y;
     228             :                 bool readNext = true, first = true, ret = false;
     229        4700 :                 while (readNext) {
     230        2350 :                         readCommaWhitespace();
     231        2350 :                         readNext = readCurveToArg(x1, y1, x2, y2, x, y);
     232        2350 :                         if (first && !readNext) {
     233             :                                 return false;
     234        2350 :                         } else if (readNext) {
     235        1175 :                                 if (first) { ret = true; first = false; }
     236        1175 :                                 if (relative) {
     237         500 :                                         x1 = _x + x1; y1 = _y + y1;
     238         500 :                                         x2 = _x + x2; y2 = _y + y2;
     239         500 :                                         x = _x + x; y = _y + y;
     240             :                                 }
     241        1175 :                                 _x = x; _y = y; _bx = x2, _by = y2; _b = true;
     242             :                                 SP_PATH_LOG("C %f %f %f %f %f %f", x1, y1, x2, y2, x, y);
     243        1175 :                                 path->cubicTo(x1, y1, x2, y2, x, y);
     244             :                         }
     245             :                 }
     246             :                 return ret;
     247             :         }
     248             : 
     249          50 :         bool readCubicBezierShort(bool relative) {
     250             :                 double x1, y1, x2, y2, x, y;
     251             :                 bool readNext = true, first = true, ret = false;
     252         200 :                 while (readNext) {
     253         100 :                         readCommaWhitespace();
     254         100 :                         readNext = readSmoothCurveToArg(x2, y2, x, y);
     255         100 :                         if (first && !readNext) {
     256             :                                 return false;
     257         100 :                         } else if (readNext) {
     258          50 :                                 if (first) { ret = true; first = false; }
     259             : 
     260          50 :                                 getNewBezierParams(x1, y1);
     261          50 :                                 if (relative) {
     262          25 :                                         x2 = _x + x2; y2 = _y + y2;
     263          25 :                                         x = _x + x; y = _y + y;
     264             :                                 }
     265          50 :                                 _x = x; _y = y; _bx = x2, _by = y2; _b = true;
     266             :                                 SP_PATH_LOG("S (%f %f) %f %f %f %f", x1, y1, x2, y2, x, y);
     267          50 :                                 path->cubicTo(x1, y1, x2, y2, x, y);
     268             :                         }
     269             :                 }
     270             :                 return ret;
     271             :         }
     272             : 
     273         250 :         bool readQuadraticBezier(bool relative) {
     274             :                 double x1, y1, x, y;
     275             :                 bool readNext = true, first = true, ret = false;
     276        1000 :                 while (readNext) {
     277         500 :                         readCommaWhitespace();
     278         500 :                         readNext = readQuadraticCurveToArg(x1, y1, x, y);
     279         500 :                         if (first && !readNext) {
     280             :                                 return false;
     281         500 :                         } else if (readNext) {
     282         250 :                                 if (first) { ret = true; first = false; }
     283         250 :                                 if (relative) {
     284          50 :                                         x1 = _x + x1; y1 = _y + y1;
     285          50 :                                         x = _x + x; y = _y + y;
     286             :                                 }
     287         250 :                                 _x = x; _y = y; _bx = x1, _by = y1; _b = true;
     288         250 :                                 path->quadTo(x1, y1, x, y);
     289             :                         }
     290             :                 }
     291             :                 return ret;
     292             :         }
     293             : 
     294          50 :         bool readQuadraticBezierShort(bool relative) {
     295             :                 double x1, y1, x, y;
     296             :                 bool readNext = true, first = true, ret = false;
     297         200 :                 while (readNext) {
     298         100 :                         readCommaWhitespace();
     299         100 :                         readNext = readSmoothQuadraticCurveToArg(x, y);
     300         100 :                         if (first && !readNext) {
     301             :                                 return false;
     302         100 :                         } else if (readNext) {
     303          50 :                                 if (first) { ret = true; first = false; }
     304          50 :                                 getNewBezierParams(x1, y1);
     305          50 :                                 if (relative) {
     306          25 :                                         x = _x + x; y = _y + y;
     307             :                                 }
     308          50 :                                 _x = x; _y = y; _bx = x1, _by = y1; _b = true;
     309          50 :                                 path->quadTo(x1, y1, x, y);
     310             :                         }
     311             :                 }
     312             :                 return ret;
     313             :         }
     314             : 
     315         550 :         bool readEllipticalArc(bool relative) {
     316             :                 double rx, ry, xAxisRotation, x, y;
     317             :                 bool largeArc, sweep;
     318             : 
     319             :                 bool readNext = true, first = true, ret = false;
     320        2275 :                 while (readNext) {
     321        1175 :                         readCommaWhitespace();
     322        1175 :                         readNext = readEllipticalArcArg(rx, ry, xAxisRotation, largeArc, sweep, x, y);
     323        1175 :                         if (first && !readNext) {
     324             :                                 return false;
     325        1175 :                         } else if (readNext) {
     326         625 :                                 if (first) { ret = true; first = false; }
     327         625 :                                 if (relative) {
     328         100 :                                         x = _x + x; y = _y + y;
     329             :                                 }
     330             : 
     331         625 :                                 if (rx == 0 || ry == 0) {
     332          25 :                                         _x = x; _y = y; _b = false;
     333          25 :                                         path->lineTo(x, y);
     334             :                                 } else {
     335         600 :                                         _x = x; _y = y; _b = false;
     336         600 :                                         path->arcTo(rx, ry, xAxisRotation, largeArc, sweep, x, y);
     337             :                                 }
     338             :                         }
     339             :                 }
     340             :                 return ret;
     341             :         }
     342             : 
     343        1675 :         bool readMoveToArgs(bool relative) {
     344        1675 :                 double x = 0.0f, y = 0.0f;
     345        1675 :                 if (!readCoordPair(x, y)) {
     346             :                         return false;
     347             :                 }
     348             : 
     349        1675 :                 if (relative) {
     350         200 :                         x = _x + x;
     351         200 :                         y = _y + y;
     352             :                 }
     353             : 
     354        1675 :                 _b = false;
     355        1675 :                 _x = x;
     356        1675 :                 _y = y;
     357             :                 //if (!_pathStarted) {
     358        1675 :                         _sx = _x;
     359        1675 :                         _sy = _y;
     360        1675 :                         _pathStarted = true;
     361             :                 //}
     362             : 
     363             :                 SP_PATH_LOG("M %f %f", _x, _y);
     364        1675 :                 path->moveTo(x, y);
     365        1675 :                 readCommaWhitespace();
     366        1675 :                 readLineToArgs(relative);
     367             : 
     368             :                 return true;
     369             :         }
     370             : 
     371        2350 :         bool readCurveToArg(double &x1, double &y1, double &x2, double &y2, double &x, double &y) {
     372        2350 :                 if (!readCoordPair(x1, y1)) {
     373             :                         return false;
     374             :                 }
     375        1175 :                 readCommaWhitespace();
     376        1175 :                 if (!readCoordPair(x2, y2)) {
     377             :                         return false;
     378             :                 }
     379        1175 :                 readCommaWhitespace();
     380        1175 :                 if (!readCoordPair(x, y)) {
     381           0 :                         return false;
     382             :                 }
     383             :                 return true;
     384             :         }
     385             : 
     386         100 :         bool readSmoothCurveToArg(double &x2, double &y2, double &x, double &y) {
     387         100 :                 return readQuadraticCurveToArg(x2, y2, x, y);
     388             :         }
     389             : 
     390         600 :         bool readQuadraticCurveToArg(double &x1, double &y1, double &x, double &y) {
     391         600 :                 if (!readCoordPair(x1, y1)) {
     392             :                         return false;
     393             :                 }
     394         300 :                 readCommaWhitespace();
     395         300 :                 if (!readCoordPair(x, y)) {
     396           0 :                         return false;
     397             :                 }
     398             :                 return true;
     399             :         }
     400             : 
     401        1175 :         bool readEllipticalArcArg(double &_rx, double &_ry, double &_xAxisRotation,
     402             :                         bool &_largeArc, bool &_sweep, double &_dx, double &_dy) {
     403             :                 double rx, ry, xAxisRotation, x, y;
     404             :                 bool largeArc, sweep;
     405             : 
     406        1175 :                 if (!readCoordPair(rx, ry)) {
     407             :                         return false;
     408             :                 }
     409         625 :                 readCommaWhitespace();
     410         625 :                 if (!readNumber(xAxisRotation)) {
     411             :                         return false;
     412             :                 }
     413             : 
     414         625 :                 if (!readCommaWhitespace()) {
     415             :                         return false;
     416             :                 }
     417             : 
     418         625 :                 if (!readFlag(largeArc)) {
     419             :                         return false;
     420             :                 }
     421             : 
     422         625 :                 readCommaWhitespace();
     423         625 :                 if (!readFlag(sweep)) {
     424             :                         return false;
     425             :                 }
     426             : 
     427         625 :                 readCommaWhitespace();
     428         625 :                 if (!readCoordPair(x, y)) {
     429             :                         return false;
     430             :                 }
     431             : 
     432         625 :                 _rx = rx;
     433         625 :                 _ry = ry;
     434         625 :                 _xAxisRotation = xAxisRotation;
     435         625 :                 _largeArc = largeArc;
     436         625 :                 _sweep = sweep;
     437         625 :                 _dx = x;
     438         625 :                 _dy = y;
     439             : 
     440         625 :                 return true;
     441             :         }
     442             : 
     443         100 :         bool readSmoothQuadraticCurveToArg(double &x, double &y) {
     444         100 :                 return readCoordPair(x, y);
     445             :         }
     446             : 
     447       12425 :         bool readCoordPair(double &x, double &y) {
     448       12425 :                 double value1 = 0.0f, value2 = 0.0f;
     449       12425 :                 if (!readNumber(value1)) {
     450             :                         return false;
     451             :                 }
     452        7925 :                 readCommaWhitespace();
     453        7925 :                 if (!readNumber(value2)) {
     454             :                         return false;
     455             :                 }
     456             : 
     457        7925 :                 x = value1;
     458        7925 :                 y = value2;
     459        7925 :                 return true;
     460             :         }
     461             : 
     462       43375 :         bool readWhitespace() { return reader.readChars<StringView::WhiteSpace>().size() != 0; }
     463             : 
     464       23925 :         bool readCommaWhitespace() {
     465       23925 :                 if (reader >= 1) {
     466       23375 :                         bool ws = readWhitespace();
     467       23375 :                         if (reader.is(',')) {
     468        5200 :                                 ++ reader;
     469             :                         } else {
     470             :                                 return ws;
     471             :                         }
     472        5200 :                         readWhitespace();
     473        5200 :                         return true;
     474             :                 }
     475             :                 return false;
     476             :         }
     477             : 
     478       22675 :         bool readNumber(double &val) {
     479       22675 :                 if (!reader.empty()) {
     480       21600 :                         if (!reader.readDouble().grab(val)) {
     481             :                                 return false;
     482             :                         }
     483       17325 :                         return true;
     484             :                 }
     485             :                 return false;
     486             :         }
     487             : 
     488        1250 :         bool readFlag(bool &flag) {
     489        1250 :                 if (reader >= 1) {
     490        1250 :                         if (reader.is('0') || reader.is('1')) {
     491        1250 :                                 flag = (reader.is('1'));
     492        1250 :                                 ++ reader;
     493        1250 :                                 return true;
     494             :                         }
     495             :                 }
     496             :                 return false;
     497             :         }
     498             : 
     499         100 :         void getNewBezierParams(double &bx, double &by) {
     500         100 :                 if (_b) {
     501         100 :                         bx = _x * 2 - _bx; by = _y * 2 - _by;
     502             :                 } else {
     503           0 :                         bx = _x; by = _y;
     504             :                 }
     505         100 :         }
     506             : 
     507        1450 :         SVGPathReader(PathWriter *d, const StringView &r)
     508        1450 :         : path(d), reader(r) { }
     509             : 
     510             :         double _x = 0.0f, _y = 0.0f;
     511             : 
     512             :         bool _b = false;
     513             :         double _bx = 0.0f, _by = 0.0f;
     514             : 
     515             :         double _sx = 0.0f, _sy = 0.0f;
     516             :         bool _pathStarted = false;
     517             :         PathWriter *path = nullptr;
     518             :         StringView reader;
     519             : };
     520             : 
     521             : template <typename Interface>
     522             : class PathBinaryEncoder {
     523             : public: // utility
     524       72550 :         PathBinaryEncoder(typename Interface::BytesType *b) : buffer(b) { }
     525             : 
     526     9151375 :         void emplace(uint8_t c) {
     527     9151375 :                 buffer->emplace_back(c);
     528     9151375 :         }
     529             : 
     530     2858250 :         void emplace(const uint8_t *buf, size_t size) {
     531     2858250 :                 size_t tmpSize = buffer->size();
     532     2858250 :                 buffer->resize(tmpSize + size);
     533     2858250 :                 memcpy(buffer->data() + tmpSize, buf, size);
     534     2858250 :         }
     535             : 
     536             : private:
     537             :         typename Interface::BytesType *buffer;
     538             : };
     539             : 
     540             : template <typename Interface, typename Source>
     541       72550 : auto encodePath(const Source &source) -> typename Interface::BytesType {
     542       72550 :         typename Interface::BytesType ret;
     543       72550 :         ret.reserve(source.commands.size() * sizeof(Command) + source.points.size() * sizeof(CommandData) + 2 * (sizeof(size_t) + 1));
     544       72550 :         PathBinaryEncoder<Interface> enc(&ret);
     545             : 
     546       72550 :         data::cbor::_writeInt(enc, 1); // version
     547       72550 :         data::cbor::_writeInt(enc, source.commands.size());
     548       72550 :         data::cbor::_writeInt(enc, source.points.size());
     549       72550 :         auto d = source.points.data();
     550     2389425 :         for (auto &it : source.commands) {
     551     2316875 :                 data::cbor::_writeInt(enc, toInt(it));
     552     2316875 :                 switch (it) {
     553     1461400 :                 case Command::MoveTo:
     554             :                 case Command::LineTo:
     555     1461400 :                         data::cbor::_writeNumber(enc, d[0].p.x);
     556     1461400 :                         data::cbor::_writeNumber(enc, d[0].p.y);
     557             :                         SP_PATH_LOG_TEXT("L ", d[0].p.x, " ", d[0].p.y);
     558     1461400 :                         ++ d;
     559     1461400 :                         break;
     560         150 :                 case Command::QuadTo:
     561         150 :                         data::cbor::_writeNumber(enc, d[0].p.x);
     562         150 :                         data::cbor::_writeNumber(enc, d[0].p.y);
     563         150 :                         data::cbor::_writeNumber(enc, d[1].p.x);
     564         150 :                         data::cbor::_writeNumber(enc, d[1].p.y);
     565             :                         SP_PATH_LOG_TEXT("Q ", d[0].p.x, " ", d[0].p.y, " ", d[1].p.x, " ", d[1].p.y);
     566         150 :                         d += 2;
     567         150 :                         break;
     568      572425 :                 case Command::CubicTo:
     569      572425 :                         data::cbor::_writeNumber(enc, d[0].p.x);
     570      572425 :                         data::cbor::_writeNumber(enc, d[0].p.y);
     571      572425 :                         data::cbor::_writeNumber(enc, d[1].p.x);
     572      572425 :                         data::cbor::_writeNumber(enc, d[1].p.y);
     573      572425 :                         data::cbor::_writeNumber(enc, d[2].p.x);
     574      572425 :                         data::cbor::_writeNumber(enc, d[2].p.y);
     575             :                         SP_PATH_LOG_TEXT("C ", d[0].p.x, " ", d[0].p.y, " ", d[1].p.x, " ", d[1].p.y, " ", d[2].p.x, " ", d[2].p.y);
     576      572425 :                         d += 3;
     577      572425 :                         break;
     578       22650 :                 case Command::ArcTo:
     579       22650 :                         data::cbor::_writeNumber(enc, d[0].p.x);
     580       22650 :                         data::cbor::_writeNumber(enc, d[0].p.y);
     581       22650 :                         data::cbor::_writeNumber(enc, d[1].p.x);
     582       22650 :                         data::cbor::_writeNumber(enc, d[1].p.y);
     583       22650 :                         data::cbor::_writeNumber(enc, d[2].f.v);
     584       66075 :                         data::cbor::_writeInt(enc, (uint32_t(d[2].f.a ? 1 : 0) << 1) | uint32_t(d[2].f.b ? 1 : 0));
     585             :                         SP_PATH_LOG_TEXT("A ", d[0].p.x, " ", d[0].p.y, " ", d[1].p.x, " ", d[1].p.y, " " << d[2].f.v, " ",
     586             :                                         ((uint32_t(d[2].f.a ? 1 : 0) << 1) | uint32_t(d[2].f.b ? 1 : 0)), " ", d[2].f.a, " ", d[2].f.b);
     587       22650 :                         d += 3;
     588       22650 :                         break;
     589             :                 case Command::ClosePath:
     590             :                         SP_PATH_LOG_TEXT("Z");
     591             :                         break;
     592             :                 default: break;
     593             :                 }
     594             :         }
     595             : 
     596       72550 :         return ret;
     597           0 : }
     598             : 
     599             : template <typename Interface, typename Source>
     600       71875 : auto pathToString(const Source &source, bool newline) -> typename Interface::StringType {
     601       71875 :         BufferTemplate<Interface> buffer;
     602             : 
     603             :         //stream << std::setprecision(std::numeric_limits<double>::max_digits10);
     604       71875 :         auto d = source.points.data();
     605     2388750 :         for (auto &it : source.commands) {
     606     2316875 :                 switch (it) {
     607      266250 :                 case Command::MoveTo:
     608      266250 :                         if (newline && d != source.points.data()) {
     609         350 :                                 buffer.putc('\n');
     610             :                         }
     611      266250 :                         buffer.putStrings("M ", d[0].p.x, ",", d[0].p.y, " ");
     612      266250 :                         ++ d;
     613      266250 :                         break;
     614     1195150 :                 case Command::LineTo:
     615     1195150 :                         buffer.putStrings("L ", d[0].p.x, ",", d[0].p.y, " ");
     616     1195150 :                         ++ d;
     617     1195150 :                         break;
     618         150 :                 case Command::QuadTo:
     619         150 :                         buffer.putStrings("Q ", d[0].p.x, ",", d[0].p.y, " ", d[1].p.x, ",", d[1].p.y, " ");
     620         150 :                         d += 2;
     621         150 :                         break;
     622      572425 :                 case Command::CubicTo:
     623      572425 :                         buffer.putStrings("C ", d[0].p.x, ",", d[0].p.y, " ",
     624      572425 :                                         d[1].p.x, ",", d[1].p.y, " ",
     625      572425 :                                         d[2].p.x, ",", d[2].p.y, " ");
     626      572425 :                         d += 3;
     627      572425 :                         break;
     628       22650 :                 case Command::ArcTo:
     629       22650 :                         buffer.putStrings("A ", d[0].p.x, ",", d[0].p.y, " ",
     630       45300 :                                         d[2].f.v, " ", int32_t(d[2].f.a), " ", int32_t(d[2].f.b), " ",
     631       22650 :                                         d[1].p.x, ",", d[1].p.y, " ");
     632       22650 :                         d += 3;
     633       22650 :                         break;
     634      260250 :                 case Command::ClosePath:
     635      260250 :                         buffer.put("Z ", 2);
     636             :                         break;
     637             :                 default: break;
     638             :                 }
     639             :         }
     640             : 
     641       71875 :         if (newline) {
     642         200 :                 buffer.putc('\n');
     643             :         }
     644             : 
     645      143750 :         return buffer.str();
     646       71875 : }
     647             : 
     648             : template <>
     649         675 : void PathData<memory::PoolInterface>::clear() {
     650         675 :         points.clear();
     651         675 :         commands.clear();
     652         675 : }
     653             : 
     654             : template <>
     655      541806 : void PathData<memory::StandartInterface>::clear() {
     656      541806 :         points.clear();
     657      541806 :         commands.clear();
     658      541806 : }
     659             : 
     660             : template <>
     661         900 : PathWriter PathData<memory::PoolInterface>::getWriter() {
     662         900 :         return PathWriter(*this);
     663             : }
     664             : 
     665             : template <>
     666      548178 : PathWriter PathData<memory::StandartInterface>::getWriter() {
     667      548178 :         return PathWriter(*this);
     668             : }
     669             : 
     670             : template <>
     671             : template <>
     672         725 : auto PathData<memory::PoolInterface>::encode<memory::PoolInterface>() const -> memory::PoolInterface::BytesType {
     673         725 :         return encodePath<memory::PoolInterface>(*this);
     674             : }
     675             : 
     676             : template <>
     677             : template <>
     678          50 : auto PathData<memory::PoolInterface>::encode<memory::StandartInterface>() const -> memory::StandartInterface::BytesType {
     679          50 :         return encodePath<memory::StandartInterface>(*this);
     680             : }
     681             : 
     682             : template <>
     683             : template <>
     684          50 : auto PathData<memory::StandartInterface>::encode<memory::PoolInterface>() const -> memory::PoolInterface::BytesType {
     685          50 :         return encodePath<memory::PoolInterface>(*this);
     686             : }
     687             : 
     688             : template <>
     689             : template <>
     690       71725 : auto PathData<memory::StandartInterface>::encode<memory::StandartInterface>() const -> memory::StandartInterface::BytesType {
     691       71725 :         return encodePath<memory::StandartInterface>(*this);
     692             : }
     693             : 
     694             : template <>
     695             : template <>
     696          50 : auto PathData<memory::PoolInterface>::toString<memory::PoolInterface>(bool newline) const -> memory::PoolInterface::StringType {
     697          50 :         return pathToString<memory::PoolInterface>(*this, newline);
     698             : }
     699             : 
     700             : template <>
     701             : template <>
     702          50 : auto PathData<memory::PoolInterface>::toString<memory::StandartInterface>(bool newline) const -> memory::StandartInterface::StringType {
     703          50 :         return pathToString<memory::StandartInterface>(*this, newline);
     704             : }
     705             : 
     706             : template <>
     707             : template <>
     708          50 : auto PathData<memory::StandartInterface>::toString<memory::PoolInterface>(bool newline) const -> memory::PoolInterface::StringType {
     709          50 :         return pathToString<memory::PoolInterface>(*this, newline);
     710             : }
     711             : 
     712             : template <>
     713             : template <>
     714       71725 : auto PathData<memory::StandartInterface>::toString<memory::StandartInterface>(bool newline) const -> memory::StandartInterface::StringType {
     715       71725 :         return pathToString<memory::StandartInterface>(*this, newline);
     716             : }
     717             : 
     718      548353 : PathWriter::PathWriter(PathData<mem_std::Interface> &d)
     719      548353 : : points(d.points), commands(d.commands) { }
     720             : 
     721         900 : PathWriter::PathWriter(PathData<mem_pool::Interface> &d)
     722         900 : : points(d.points), commands(d.commands) { }
     723             : 
     724         250 : PathWriter::operator bool () const {
     725         250 :         return points && commands;
     726             : }
     727             : 
     728         275 : bool PathWriter::empty() const {
     729         275 :         return commands.empty();
     730             : }
     731             : 
     732         125 : void PathWriter::reserve(size_t size) {
     733         125 :         commands.reserve(size);
     734         125 :         points.reserve(size * 3);
     735         125 : }
     736             : 
     737         600 : bool PathWriter::readFromPathString(StringView str) {
     738         600 :         commands.clear();
     739         600 :         points.clear();
     740             : 
     741         600 :         if (!SVGPathReader::readPath(this, str)) {
     742           0 :                 return false;
     743             :         }
     744             :         return true;
     745             : }
     746             : 
     747         100 : bool PathWriter::readFromFileContent(StringView str) {
     748         100 :         commands.clear();
     749         100 :         points.clear();
     750             : 
     751         100 :         if (!SVGPathReader::readFileContent(this, str)) {
     752           0 :                 return false;
     753             :         }
     754             :         return true;
     755             : }
     756             : 
     757          75 : bool PathWriter::readFromFile(StringView str) {
     758          75 :         commands.clear();
     759          75 :         points.clear();
     760             : 
     761          75 :         if (!SVGPathReader::readFile(this, str)) {
     762          25 :                 return false;
     763             :         }
     764             :         return true;
     765             : }
     766             : 
     767      541106 : bool PathWriter::readFromBytes(BytesView bytes) {
     768      541106 :         commands.clear();
     769      541106 :         points.clear();
     770             : 
     771      541106 :         return addPath(bytes);
     772             : }
     773             : 
     774     2031782 : PathWriter &PathWriter::moveTo(float x, float y) {
     775     2031782 :         commands.emplace_back(Command::MoveTo);
     776     2031782 :         points.emplace_back(CommandData(x, y));
     777     2031782 :         return *this;
     778             : }
     779             : 
     780          25 : PathWriter &PathWriter::moveTo(const Vec2 &point) {
     781          25 :         return this->moveTo(point.x, point.y);
     782             :         return *this;
     783             : }
     784             : 
     785     9124294 : PathWriter &PathWriter::lineTo(float x, float y) {
     786     9124294 :         commands.emplace_back((commands.empty() || commands.back() == Command::ClosePath) ? Command::MoveTo : Command::LineTo);
     787     9124294 :         points.emplace_back(CommandData(x, y));
     788     9124294 :         return *this;
     789             : }
     790          25 : PathWriter &PathWriter::lineTo(const Vec2 &point) {
     791          25 :         this->lineTo(point.x, point.y);
     792          25 :         return *this;
     793             : }
     794             : 
     795         475 : PathWriter &PathWriter::quadTo(float x1, float y1, float x2, float y2) {
     796         475 :         commands.emplace_back(Command::QuadTo);
     797         475 :         points.emplace_back(CommandData(x1, y1));
     798         475 :         points.emplace_back(CommandData(x2, y2));
     799         475 :         return *this;
     800             : }
     801          25 : PathWriter &PathWriter::quadTo(const Vec2& p1, const Vec2& p2) {
     802          25 :         this->quadTo(p1.x, p1.y, p2.x, p2.y);
     803          25 :         return *this;
     804             : }
     805             : 
     806     4376366 : PathWriter &PathWriter::cubicTo(float x1, float y1, float x2, float y2, float x3, float y3) {
     807     4376366 :         commands.emplace_back(Command::CubicTo);
     808     4376366 :         points.emplace_back(CommandData(x1, y1));
     809     4376366 :         points.emplace_back(CommandData(x2, y2));
     810     4376366 :         points.emplace_back(CommandData(x3, y3));
     811     4376366 :         return *this;
     812             : }
     813          25 : PathWriter &PathWriter::cubicTo(const Vec2& p1, const Vec2& p2, const Vec2& p3) {
     814          25 :         this->cubicTo(p1.x, p1.y, p2.x, p2.y, p3.x, p3.y);
     815          25 :         return *this;
     816             : }
     817             : 
     818             : // use _to_rad user suffix to convert from degrees to radians
     819      172810 : PathWriter &PathWriter::arcTo(float rx, float ry, float rotation, bool largeFlag, bool sweepFlag, float x, float y) {
     820      172810 :         commands.emplace_back(Command::ArcTo);
     821      172810 :         points.emplace_back(CommandData(rx, ry));
     822      172810 :         points.emplace_back(CommandData(x, y));
     823      172810 :         points.emplace_back(CommandData(rotation, largeFlag, sweepFlag));
     824      172810 :         return *this;
     825             : }
     826          25 : PathWriter &PathWriter::arcTo(const Vec2 & r, float rotation, bool largeFlag, bool sweepFlag, const Vec2 &target) {
     827          25 :         this->arcTo(r.x, r.y, rotation, largeFlag, sweepFlag, target.x, target.y);
     828          25 :         return *this;
     829             : }
     830             : 
     831     1988066 : PathWriter &PathWriter::closePath() {
     832     1988066 :         commands.emplace_back(Command::ClosePath);
     833     1988066 :         return *this;
     834             : }
     835             : 
     836         225 : PathWriter &PathWriter::addRect(const Rect& rect) {
     837         225 :         addRect(rect.origin.x, rect.origin.y, rect.size.width, rect.size.height);
     838         225 :         return *this;
     839             : }
     840             : 
     841          75 : PathWriter &PathWriter::addRect(const Rect& rect, float rx, float ry) {
     842          75 :         addRect(rect.origin.x, rect.origin.y, rect.size.width, rect.size.height, rx, ry);
     843          75 :         return *this;
     844             : }
     845             : 
     846         250 : PathWriter &PathWriter::addRect(float x, float y, float width, float height) {
     847         250 :         moveTo(x, y);
     848         250 :         lineTo(x + width, y);
     849         250 :         lineTo(x + width, y + height);
     850         250 :         lineTo(x, y + height);
     851         250 :         closePath();
     852         250 :         return *this;
     853             : }
     854             : 
     855          50 : PathWriter &PathWriter::addOval(const Rect& oval) {
     856          50 :         addEllipse(oval.getMidX(), oval.getMidY(), oval.size.width / 2.0f, oval.size.height / 2.0f);
     857          50 :         return *this;
     858             : }
     859             : 
     860         175 : PathWriter &PathWriter::addCircle(float x, float y, float radius) {
     861         175 :         moveTo(x + radius, y);
     862         175 :         arcTo(radius, radius, 0, false, false, x, y - radius);
     863         175 :         arcTo(radius, radius, 0, false, false, x - radius, y);
     864         175 :         arcTo(radius, radius, 0, false, false, x, y + radius);
     865         175 :         arcTo(radius, radius, 0, false, false, x + radius, y);
     866         175 :         closePath();
     867         175 :         return *this;
     868             : }
     869             : 
     870          75 : PathWriter &PathWriter::addEllipse(float x, float y, float rx, float ry) {
     871          75 :         moveTo(x + rx, y);
     872          75 :         arcTo(rx, ry, 0, false, false, x, y - ry);
     873          75 :         arcTo(rx, ry, 0, false, false, x - rx, y);
     874          75 :         arcTo(rx, ry, 0, false, false, x, y + ry);
     875          75 :         arcTo(rx, ry, 0, false, false, x + rx, y);
     876          75 :         closePath();
     877          75 :         return *this;
     878             : }
     879             : 
     880         375 : PathWriter &PathWriter::addArc(const Rect& oval, float startAngleInRadians, float sweepAngleInRadians) {
     881         375 :         const auto rx = oval.size.width / 2;
     882         375 :         const auto ry = oval.size.height / 2;
     883             : 
     884         375 :         const auto x = rx * cosf(startAngleInRadians);
     885         375 :         const auto y = ry * sinf(startAngleInRadians);
     886             : 
     887         375 :         const auto sx = rx * cosf(startAngleInRadians + sweepAngleInRadians);
     888         375 :         const auto sy = ry * sinf(startAngleInRadians + sweepAngleInRadians);
     889             : 
     890         375 :         moveTo(oval.origin.x + rx + x, oval.origin.y + ry + y);
     891         375 :         arcTo(rx, ry, 0.0f, (sweepAngleInRadians > numbers::pi)?true:false, true, oval.origin.x + rx + sx, oval.origin.y + ry + sy);
     892         375 :         return *this;
     893             : }
     894             : 
     895         175 : PathWriter &PathWriter::addRect(float x, float y, float width, float height, float rx, float ry) {
     896         175 :         if (isnan(rx)) {
     897          25 :                 rx = 0.0f;
     898             :         }
     899         175 :         if (isnan(ry)) {
     900          25 :                 ry = 0.0f;
     901             :         }
     902             : 
     903         175 :         if (rx == 0.0f && ry == 0.0f) {
     904          25 :                 return addRect(x, y, width, height);
     905         150 :         } else if (rx == 0.0f) {
     906          25 :                 rx = ry;
     907         125 :         } else if (ry == 0.0f) {
     908          25 :                 ry = rx;
     909             :         }
     910             : 
     911         150 :         rx = std::min(width / 2.0f, rx);
     912         150 :         ry = std::min(height / 2.0f, ry);
     913             : 
     914         150 :         moveTo(x + width - rx, y);
     915         150 :         arcTo(rx, ry, 0, false, true, x + width, y + ry);
     916         150 :         lineTo(x + width, y + height - ry);
     917         150 :         arcTo(rx, ry, 0, false, true, x + width - rx, y + height);
     918         150 :         lineTo(x + rx, y + height);
     919         150 :         arcTo(rx, ry, 0, false, true, x, y + height - ry);
     920         150 :         lineTo(x, y + ry);
     921         150 :         arcTo(rx, ry, 0, false, true, x + rx, y);
     922         150 :         closePath();
     923         150 :         return *this;
     924             : }
     925             : 
     926          50 : bool PathWriter::addPath(const PathData<memory::StandartInterface> &d) {
     927          50 :         commands.reserve(commands.size() + d.commands.size());
     928         600 :         for (auto &it : d.commands) { commands.emplace_back(Command(it)); }
     929             : 
     930          50 :         points.reserve(points.size() + d.points.size());
     931         900 :         for (auto &it : d.points) { points.emplace_back(CommandData(it)); }
     932             : 
     933          50 :         return true;
     934             : }
     935             : 
     936          25 : bool PathWriter::addPath(const PathData<memory::PoolInterface> &d) {
     937          25 :         commands.reserve(commands.size() + d.commands.size());
     938          25 :         for (auto &it : d.commands) { commands.emplace_back(Command(it)); }
     939             : 
     940          25 :         points.reserve(points.size() + d.points.size());
     941          25 :         for (auto &it : d.points) { points.emplace_back(CommandData(it)); }
     942             : 
     943          25 :         return true;
     944             : }
     945             : 
     946      541806 : bool PathWriter::addPath(BytesView data) {
     947             :         float x1, y1, x2, y2, x3, y3;
     948             :         uint32_t tmp;
     949             : 
     950      541806 :         BytesViewNetwork reader(data);
     951             : 
     952      541806 :         auto v = data::cbor::_readInt(reader);
     953      541806 :         if (v != 1) {
     954             :                 return false; // unsupported version
     955             :         }
     956             : 
     957      541806 :         auto ncommands = data::cbor::_readInt(reader);
     958      541806 :         auto npoints = data::cbor::_readInt(reader);
     959      541806 :         commands.reserve(ncommands);
     960      541806 :         points.reserve(npoints);
     961    18190085 :         for (; ncommands != 0; --ncommands) {
     962    17648279 :                 auto cmd = data::cbor::_readInt(reader);
     963    17648279 :                 switch (uint8_t(cmd)) {
     964     2024335 :                 case toInt(Command::MoveTo):
     965     2024335 :                         x1 = data::cbor::_readNumber(reader);
     966     2024335 :                         y1 = data::cbor::_readNumber(reader);
     967             :                         SP_PATH_LOG_TEXT("L ", x1, " ", y1);
     968     2024335 :                         moveTo(x1, y1);
     969             :                         break;
     970     9104299 :                 case toInt(Command::LineTo):
     971     9104299 :                         x1 = data::cbor::_readNumber(reader);
     972     9104299 :                         y1 = data::cbor::_readNumber(reader);
     973             :                         SP_PATH_LOG_TEXT("L ", x1, " ", y1);
     974     9104299 :                         lineTo(x1, y1);
     975             :                         break;
     976         150 :                 case toInt(Command::QuadTo):
     977         150 :                         x1 = data::cbor::_readNumber(reader);
     978         150 :                         y1 = data::cbor::_readNumber(reader);
     979         150 :                         x2 = data::cbor::_readNumber(reader);
     980         150 :                         y2 = data::cbor::_readNumber(reader);
     981             :                         SP_PATH_LOG_TEXT("Q ", x1, " ", y1, " ", x2, " ", y2);
     982         150 :                         quadTo(x1, y1, x2, y2);
     983             :                         break;
     984     4375116 :                 case toInt(Command::CubicTo):
     985     4375116 :                         x1 = data::cbor::_readNumber(reader);
     986     4375116 :                         y1 = data::cbor::_readNumber(reader);
     987     4375116 :                         x2 = data::cbor::_readNumber(reader);
     988     4375116 :                         y2 = data::cbor::_readNumber(reader);
     989     4375116 :                         x3 = data::cbor::_readNumber(reader);
     990     4375116 :                         y3 = data::cbor::_readNumber(reader);
     991             :                         SP_PATH_LOG_TEXT("C ", x1, " ", y1, " ", x2, " ", y2, " ", x3, " ", y3);
     992     4375116 :                         cubicTo(x1, y1, x2, y2, x3, y3);
     993             :                         break;
     994      162235 :                 case toInt(Command::ArcTo):
     995      162235 :                         x1 = data::cbor::_readNumber(reader);
     996      162235 :                         y1 = data::cbor::_readNumber(reader);
     997      162235 :                         x2 = data::cbor::_readNumber(reader);
     998      162235 :                         y2 = data::cbor::_readNumber(reader);
     999      162235 :                         x3 = data::cbor::_readNumber(reader);
    1000      162235 :                         tmp = data::cbor::_readInt(reader);
    1001             :                         SP_PATH_LOG_TEXT("A ", x1, " ", y1, " ", x2, " ", y2, " ", x3, " ", tmp, " ", ((tmp & 2) != 0), " ", ((tmp & 1) != 0));
    1002      162235 :                         arcTo(x1, y1, x3, (tmp & 2) != 0, (tmp & 1) != 0, x2, y2);
    1003             :                         break;
    1004     1982144 :                 case toInt(Command::ClosePath):
    1005     1982144 :                         closePath();
    1006             :                         SP_PATH_LOG_TEXT("Z");
    1007             :                         break;
    1008             :                 default: break;
    1009             :                 }
    1010             :         }
    1011             :         return true;
    1012             : }
    1013             : 
    1014         700 : bool PathWriter::addPath(StringView str) {
    1015         700 :         return SVGPathReader::readPath(this, str);
    1016             : }
    1017             : 
    1018             : }

Generated by: LCOV version 1.14