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 : }
|