Line data Source code
1 : /**
2 : Copyright (c) 2022 Roman Katuntsev <sbkarr@stappler.org>
3 : Copyright (c) 2023 Stappler LLC <admin@stappler.dev>
4 :
5 : Permission is hereby granted, free of charge, to any person obtaining a copy
6 : of this software and associated documentation files (the "Software"), to deal
7 : in the Software without restriction, including without limitation the rights
8 : to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 : copies of the Software, and to permit persons to whom the Software is
10 : furnished to do so, subject to the following conditions:
11 :
12 : The above copyright notice and this permission notice shall be included in
13 : all copies or substantial portions of the Software.
14 :
15 : THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 : IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 : FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 : AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 : LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 : OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21 : THE SOFTWARE.
22 : **/
23 :
24 : #include "SPVectorImage.h"
25 : #include "SPSvgReader.h"
26 :
27 : #if MODULE_STAPPLER_BITMAP
28 : #include "SPBitmap.h"
29 : #endif
30 :
31 : namespace STAPPLER_VERSIONIZED stappler::vg {
32 :
33 75 : bool VectorPathRef::init(VectorImage *image, const String &id, const Rc<VectorPath> &path) {
34 75 : _image = image;
35 75 : _id = id;
36 75 : _path = path;
37 75 : return true;
38 : }
39 :
40 546628 : bool VectorPathRef::init(VectorImage *image, const String &id, Rc<VectorPath> &&path) {
41 546628 : _image = image;
42 546628 : _id = id;
43 546628 : _path = move(path);
44 546628 : return true;
45 : }
46 :
47 25 : size_t VectorPathRef::count() const {
48 25 : return _path ? _path->count() : 0;
49 : }
50 :
51 50 : VectorPathRef & VectorPathRef::setPath(BytesView bytes) {
52 50 : if (_path) {
53 50 : _path->init(bytes);
54 : }
55 50 : return *this;
56 : }
57 :
58 25 : VectorPathRef & VectorPathRef::setPath(StringView str) {
59 25 : if (_path) {
60 25 : _path->init(str);
61 : }
62 25 : return *this;
63 : }
64 :
65 5447 : VectorPathRef & VectorPathRef::openForWriting(const Callback<void(PathWriter &)> &cb) {
66 5447 : if (_copyOnWrite) {
67 0 : copy();
68 : }
69 :
70 5447 : if (_path) {
71 5447 : _path->openForWriting(cb);
72 5447 : if (_image) { _image->setDirty(); }
73 : }
74 5447 : return *this;
75 : }
76 :
77 3997 : VectorPathRef & VectorPathRef::setFillColor(const Color4B &color) {
78 3997 : if (_path && _path->getFillColor() == color) {
79 : return *this;
80 : }
81 :
82 50 : if (_copyOnWrite) {
83 25 : copy();
84 : }
85 :
86 50 : if (_path) {
87 50 : _path->setFillColor(color);
88 50 : if (_image) { _image->setDirty(); }
89 : }
90 : return *this;
91 : }
92 :
93 25 : const Color4B &VectorPathRef::getFillColor() const {
94 25 : return _path ? _path->getFillColor() : Color4B::BLACK;
95 : }
96 :
97 25 : VectorPathRef & VectorPathRef::setStrokeColor(const Color4B &color) {
98 25 : if (_path && _path->getStrokeColor() == color) {
99 : return *this;
100 : }
101 :
102 25 : if (_copyOnWrite) {
103 25 : copy();
104 : }
105 :
106 25 : if (_path) {
107 25 : _path->setStrokeColor(color);
108 25 : if (_image) { _image->setDirty(); }
109 : }
110 : return *this;
111 : }
112 :
113 25 : const Color4B &VectorPathRef::getStrokeColor() const {
114 25 : return _path ? _path->getStrokeColor() : Color4B::BLACK;
115 : }
116 :
117 2823 : VectorPathRef & VectorPathRef::setFillOpacity(uint8_t value) {
118 2823 : if (_path && _path->getFillOpacity() == value) {
119 : return *this;
120 : }
121 :
122 279 : if (_copyOnWrite) {
123 25 : copy();
124 : }
125 :
126 279 : if (_path) {
127 279 : _path->setFillOpacity(value);
128 279 : if (_image) { _image->setDirty(); }
129 : }
130 : return *this;
131 : }
132 54511 : uint8_t VectorPathRef::getFillOpacity() const {
133 54511 : return _path ? _path->getFillOpacity() : 0;
134 : }
135 :
136 25 : VectorPathRef & VectorPathRef::setStrokeOpacity(uint8_t value) {
137 25 : if (_path && _path->getStrokeOpacity() == value) {
138 : return *this;
139 : }
140 :
141 25 : if (_copyOnWrite) {
142 25 : copy();
143 : }
144 :
145 25 : if (_path) {
146 25 : _path->setStrokeOpacity(value);
147 25 : if (_image) { _image->setDirty(); }
148 : }
149 : return *this;
150 : }
151 :
152 25 : uint8_t VectorPathRef::getStrokeOpacity() const {
153 25 : return _path ? _path->getStrokeOpacity() : 0;
154 : }
155 :
156 425650 : VectorPathRef & VectorPathRef::setStrokeWidth(float width) {
157 425650 : if (_path && _path->getStrokeWidth() == width) {
158 : return *this;
159 : }
160 :
161 425650 : if (_copyOnWrite) {
162 25 : copy();
163 : }
164 :
165 425650 : if (_path) {
166 425650 : _path->setStrokeWidth(width);
167 425650 : if (_image) { _image->setDirty(); }
168 : }
169 : return *this;
170 : }
171 :
172 25 : float VectorPathRef::getStrokeWidth() const {
173 25 : return _path ? _path->getStrokeWidth() : 0.0f;
174 : }
175 :
176 44896 : VectorPathRef &VectorPathRef::setWindingRule(vg::Winding value) {
177 44896 : if (_path && _path->getWindingRule() == value) {
178 : return *this;
179 : }
180 :
181 44896 : if (_copyOnWrite) {
182 25 : copy();
183 : }
184 :
185 44896 : if (_path) {
186 44896 : _path->setWindingRule(value);
187 44896 : if (_image) { _image->setDirty(); }
188 : }
189 : return *this;
190 : }
191 :
192 25 : vg::Winding VectorPathRef::getWindingRule() const {
193 25 : return _path ? _path->getWindingRule() : vg::Winding::NonZero;
194 : }
195 :
196 432370 : VectorPathRef & VectorPathRef::setStyle(vg::DrawStyle s) {
197 432370 : if (_path && _path->getStyle() == s) {
198 : return *this;
199 : }
200 :
201 77046 : if (_copyOnWrite) {
202 0 : copy();
203 : }
204 :
205 77046 : if (_path) {
206 77046 : _path->setStyle(s);
207 77046 : if (_image) { _image->setDirty(); }
208 : }
209 : return *this;
210 : }
211 :
212 57284 : vg::DrawStyle VectorPathRef::getStyle() const {
213 57284 : return _path ? _path->getStyle() : vg::DrawStyle::FillAndStroke;
214 : }
215 :
216 45396 : VectorPathRef & VectorPathRef::setTransform(const Mat4 &t) {
217 45396 : if (_path && _path->getTransform() == t) {
218 : return *this;
219 : }
220 :
221 44871 : if (_copyOnWrite) {
222 25 : copy();
223 : }
224 :
225 44871 : if (_path) {
226 44871 : _path->setTransform(t);
227 44871 : if (_image) { _image->setDirty(); }
228 : }
229 : return *this;
230 : }
231 :
232 25 : VectorPathRef & VectorPathRef::applyTransform(const Mat4 &t) {
233 25 : if (_copyOnWrite) {
234 25 : copy();
235 : }
236 :
237 25 : if (_path) {
238 25 : _path->applyTransform(t);
239 25 : if (_image) { _image->setDirty(); }
240 : }
241 25 : return *this;
242 : }
243 :
244 25 : const Mat4 &VectorPathRef::getTransform() const {
245 25 : return _path ? _path->getTransform() : Mat4::IDENTITY;
246 : }
247 :
248 474143 : VectorPathRef & VectorPathRef::setAntialiased(bool value) {
249 474143 : if (_path && _path->isAntialiased() == value) {
250 : return *this;
251 : }
252 :
253 190643 : if (_copyOnWrite) {
254 0 : copy();
255 : }
256 :
257 190643 : if (_path) {
258 190643 : _path->setAntialiased(value);
259 190643 : if (_image) { _image->setDirty(); }
260 : }
261 : return *this;
262 : }
263 :
264 60015 : bool VectorPathRef::isAntialiased() const {
265 60015 : return _path ? _path->isAntialiased() : false;
266 : }
267 :
268 25 : VectorPathRef & VectorPathRef::clear() {
269 25 : if (_copyOnWrite) {
270 25 : copy();
271 : }
272 :
273 25 : if (_path) {
274 25 : _path->clear();
275 25 : if (_image) { _image->setDirty(); }
276 : }
277 25 : return *this;
278 : }
279 :
280 25 : StringView VectorPathRef::getId() const {
281 25 : return _id;
282 : }
283 :
284 50 : bool VectorPathRef::empty() const {
285 50 : return _path ? _path->empty() : true;
286 : }
287 :
288 50 : bool VectorPathRef::valid() const {
289 50 : return _path && _image;
290 : }
291 :
292 25 : VectorPathRef::operator bool() const {
293 25 : return valid() && !empty();
294 : }
295 :
296 50 : void VectorPathRef::setPath(Rc<VectorPath> &&path) {
297 50 : _path = move(path);
298 50 : _copyOnWrite = false;
299 50 : }
300 :
301 611981 : VectorPath *VectorPathRef::getPath() const {
302 611981 : return _path;
303 : }
304 :
305 484243 : void VectorPathRef::markCopyOnWrite() {
306 484243 : _copyOnWrite = true;
307 484243 : }
308 :
309 546703 : void VectorPathRef::setImage(VectorImage *image) {
310 546703 : _image = image;
311 546703 : }
312 :
313 225 : void VectorPathRef::copy() {
314 225 : if (_copyOnWrite) {
315 225 : if (_image) {
316 225 : _path = _image->copyPath(_id);
317 : }
318 225 : _copyOnWrite = false;
319 : }
320 225 : }
321 :
322 75 : bool VectorImageData::init(VectorImage *image, Size2 size, Rect viewBox, Interface::VectorType<vg::PathXRef> &&order,
323 : Interface::MapType<String, VectorPath> &&paths, uint16_t ids) {
324 75 : _imageSize = size;
325 75 : _image = image;
326 :
327 75 : if (!viewBox.equals(Rect::ZERO)) {
328 50 : const float scaleX = _imageSize.width / viewBox.size.width;
329 50 : const float scaleY = _imageSize.height / viewBox.size.height;
330 50 : _viewBoxTransform = Mat4::IDENTITY;
331 50 : _viewBoxTransform.scale(scaleX, scaleY, 1.0f);
332 50 : _viewBoxTransform.translate(-viewBox.origin.x, -viewBox.origin.y, 0.0f);
333 50 : _viewBox = Rect(viewBox.origin.x * scaleX, viewBox.origin.y * scaleY,
334 : viewBox.size.width * scaleX, viewBox.size.height * scaleY);
335 : } else {
336 25 : _viewBox = Rect(0, 0, _imageSize.width, _imageSize.height);
337 : }
338 :
339 75 : _nextId = ids;
340 75 : _order = move(order);
341 :
342 150 : for (auto &it : paths) {
343 75 : _paths.emplace(it.first, Rc<VectorPath>::alloc(move(it.second)));
344 : }
345 :
346 75 : return true;
347 : }
348 :
349 548431 : bool VectorImageData::init(VectorImage *image, Size2 size, Rect viewBox) {
350 548431 : _imageSize = size;
351 548431 : _image = image;
352 548431 : _viewBox = viewBox;
353 548431 : return true;
354 : }
355 :
356 250 : bool VectorImageData::init(VectorImageData &data) {
357 250 : _allowBatchDrawing = data._allowBatchDrawing;
358 250 : _imageSize = data._imageSize;
359 250 : _viewBox = data._viewBox;
360 250 : _viewBoxTransform = data._viewBoxTransform;
361 250 : _order = data._order;
362 250 : _paths = data._paths;
363 250 : _nextId = data._nextId;
364 250 : _image = data._image;
365 250 : return true;
366 : }
367 :
368 25 : void VectorImageData::setImageSize(const Size2 &size) {
369 25 : _imageSize = size;
370 25 : }
371 :
372 75 : const Interface::MapType<Interface::StringType, Rc<VectorPath>> &VectorImageData::getPaths() const {
373 75 : return _paths;
374 : }
375 :
376 225 : Rc<VectorPath> VectorImageData::copyPath(StringView str) {
377 225 : auto it = _paths.find(str);
378 225 : if (it != _paths.end()) {
379 225 : it->second = Rc<VectorPath>::alloc(*it->second);
380 225 : return it->second;
381 : }
382 0 : return nullptr;
383 : }
384 :
385 546553 : uint16_t VectorImageData::getNextId() {
386 546553 : auto ret = _nextId;
387 546553 : ++ _nextId;
388 546553 : return ret;
389 : }
390 :
391 546678 : Rc<VectorPath> VectorImageData::addPath(StringView id, StringView cache, VectorPath &&path, Mat4 mat) {
392 546678 : String idStr;
393 546678 : if (id.empty()) {
394 0 : idStr = mem_std::toString("auto-", getNextId());
395 0 : id = idStr;
396 : }
397 :
398 546678 : Rc<VectorPath> ret;
399 546678 : auto it = _paths.find(id);
400 546678 : if (it == _paths.end()) {
401 546628 : ret = _paths.emplace(id.str<Interface>(), Rc<VectorPath>::alloc(move(path))).first->second;
402 546628 : _order.emplace_back(vg::PathXRef{id.str<Interface>(), cache.str<Interface>(), mat});
403 : } else {
404 50 : ret = it->second = Rc<VectorPath>::alloc(move(path));
405 : bool found = false;
406 175 : for (auto &iit : _order) {
407 125 : if (iit.id == id) {
408 50 : iit.mat = mat;
409 : found = true;
410 : }
411 : }
412 50 : if (!found) {
413 0 : _order.emplace_back(vg::PathXRef{id.str<Interface>(), cache.str<Interface>(), mat});
414 : }
415 : }
416 :
417 546678 : return ret;
418 546678 : }
419 :
420 50 : void VectorImageData::removePath(StringView id) {
421 50 : auto it = _paths.find(id);
422 50 : if (it != _paths.end()) {
423 50 : _paths.erase(it);
424 : }
425 :
426 50 : auto iit = _order.begin();
427 225 : while (iit != _order.end()) {
428 175 : if (iit->id == id) {
429 50 : iit = _order.erase(iit);
430 : } else {
431 125 : ++ iit;
432 : }
433 : }
434 50 : }
435 :
436 710 : void VectorImageData::clear() {
437 710 : _paths.clear();
438 710 : _order.clear();
439 710 : }
440 :
441 25 : void VectorImageData::resetDrawOrder() {
442 25 : _order.clear();
443 100 : for (auto &it : _paths) {
444 75 : _order.emplace_back(vg::PathXRef{it.first});
445 : }
446 25 : }
447 :
448 : #if MODULE_STAPPLER_BITMAP
449 25 : bool VectorImage::isSvg(StringView str) {
450 25 : return bitmap::check(bitmap::FileFormat::Svg, (const uint8_t *)str.data(), str.size());
451 : }
452 :
453 25 : bool VectorImage::isSvg(BytesView data) {
454 25 : return bitmap::check(bitmap::FileFormat::Svg, data.data(), data.size());
455 : }
456 :
457 : #if MODULE_STAPPLER_FILESYSTEM
458 25 : bool VectorImage::isSvg(FilePath file) {
459 25 : auto d = filesystem::readIntoMemory<Interface>(file.get(), 0, 512);
460 50 : return bitmap::check(bitmap::FileFormat::Svg, d.data(), d.size());
461 25 : }
462 : #endif
463 : #endif // MODULE_STAPPLER_BITMAP
464 :
465 600887 : VectorImage::~VectorImage() {
466 1095109 : for (auto &it : _paths) {
467 546603 : it.second->setImage(nullptr);
468 : }
469 600887 : }
470 :
471 25 : bool VectorImage::init(Size2 size, StringView data) {
472 25 : VectorPath path;
473 25 : if (!path.init(data)) {
474 : return false;
475 : }
476 25 : return init(size, std::move(path));
477 25 : }
478 :
479 50 : bool VectorImage::init(Size2 size, VectorPath && path) {
480 50 : _data = Rc<VectorImageData>::create(this, size, Rect(0, 0, size.width, size.height));
481 50 : addPath(move(path));
482 50 : return true;
483 : }
484 :
485 548381 : bool VectorImage::init(Size2 size) {
486 548381 : _data = Rc<VectorImageData>::create(this, size, Rect(0, 0, size.width, size.height));
487 548381 : return true;
488 : }
489 :
490 50 : bool VectorImage::init(StringView data) {
491 50 : String tmp = data.str<Interface>();
492 50 : vg::SvgReader reader;
493 50 : html::parse<vg::SvgReader, StringView, vg::SvgTag>(reader, StringView(tmp));
494 :
495 50 : if (!reader._paths.empty()) {
496 100 : _data = Rc<VectorImageData>::create(this, Size2(reader._width, reader._height), reader._viewBox,
497 100 : move(reader._drawOrder), move(reader._paths), reader._nextId);
498 100 : for (auto &it : _data->getPaths()) {
499 50 : _paths.emplace(it.first, Rc<VectorPathRef>::create(this, it.first, it.second));
500 : }
501 :
502 50 : auto t = Mat4::IDENTITY;
503 50 : t.scale(1, -1, 1);
504 50 : t.translate(0, -reader._height, 0);
505 :
506 50 : _data->setViewBoxTransform(t);
507 :
508 : return true;
509 : } else {
510 0 : log::error("layout::Image", "No paths found in input string");
511 : }
512 :
513 0 : return false;
514 50 : }
515 :
516 25 : bool VectorImage::init(BytesView data) {
517 25 : vg::SvgReader reader;
518 25 : html::parse<vg::SvgReader, StringView, vg::SvgTag>(reader, StringView((const char *)data.data(), data.size()));
519 :
520 25 : if (!reader._paths.empty()) {
521 50 : _data = Rc<VectorImageData>::create(this, Size2(reader._width, reader._height), reader._viewBox,
522 50 : move(reader._drawOrder), move(reader._paths), reader._nextId);
523 50 : for (auto &it : _data->getPaths()) {
524 25 : _paths.emplace(it.first, Rc<VectorPathRef>::create(this, it.first, it.second));
525 : }
526 :
527 25 : auto t = Mat4::IDENTITY;
528 25 : t.scale(1, -1, 1);
529 25 : t.translate(0, -reader._height, 0);
530 :
531 25 : _data->setViewBoxTransform(t);
532 :
533 : return true;
534 : } else {
535 0 : log::error("layout::Image", "No paths found in input data");
536 : }
537 :
538 0 : return false;
539 25 : }
540 :
541 : #if MODULE_STAPPLER_FILESYSTEM
542 25 : bool VectorImage::init(FilePath path) {
543 25 : return init(filesystem::readTextFile<Interface>(path.get()));
544 : }
545 : #endif
546 :
547 25 : void VectorImage::setImageSize(const Size2 &size) {
548 25 : if (size == _data->getImageSize()) {
549 : return;
550 : }
551 :
552 25 : if (_copyOnWrite) {
553 25 : copy();
554 : }
555 :
556 25 : _data->setImageSize(size);
557 : }
558 :
559 128884 : Size2 VectorImage::getImageSize() const {
560 128884 : return _data->getImageSize();
561 : }
562 :
563 25 : Rect VectorImage::getViewBox() const {
564 25 : return _data->getViewBox();
565 : }
566 :
567 50 : Rc<VectorPathRef> VectorImage::addPath(const VectorPath &path, StringView tag, StringView cache, Mat4 vec) {
568 50 : return addPath(VectorPath(path), tag, cache, vec);
569 : }
570 :
571 546678 : Rc<VectorPathRef> VectorImage::addPath(VectorPath &&path, StringView tag, StringView cache, Mat4 vec) {
572 546678 : if (_copyOnWrite) {
573 25 : copy();
574 : }
575 :
576 546678 : String idStr;
577 546678 : if (tag.empty()) {
578 546553 : idStr = mem_std::toString("auto-", _data->getNextId());
579 546553 : tag = idStr;
580 : }
581 :
582 546678 : auto pathObj = _data->addPath(tag, cache, move(path), vec);
583 :
584 546678 : setDirty();
585 :
586 546678 : auto it = _paths.find(tag);
587 546678 : if (it == _paths.end()) {
588 546628 : auto obj = Rc<VectorPathRef>::create(this, tag.str<Interface>(), move(pathObj));
589 546628 : return _paths.emplace(idStr.empty() ? tag.str<Interface>() : move(idStr), move(obj)).first->second;
590 546628 : } else {
591 50 : it->second->setPath(move(pathObj));
592 50 : return it->second;
593 : }
594 546678 : }
595 :
596 546578 : Rc<VectorPathRef> VectorImage::addPath(StringView tag, StringView cache, Mat4 vec) {
597 546578 : return addPath(VectorPath(), tag, cache, vec);
598 : }
599 :
600 50 : Rc<VectorPathRef> VectorImage::getPath(StringView tag) const {
601 50 : auto it = _paths.find(tag);
602 50 : if (it != _paths.end()) {
603 50 : return it->second;
604 : }
605 0 : return nullptr;
606 : }
607 :
608 25 : void VectorImage::removePath(const Rc<VectorPathRef> &path) {
609 25 : removePath(path->getId());
610 25 : }
611 :
612 50 : void VectorImage::removePath(StringView tag) {
613 50 : if (_copyOnWrite) {
614 50 : copy();
615 : }
616 :
617 50 : _data->removePath(tag);
618 50 : auto it = _paths.find(tag);
619 50 : if (it != _paths.end()) {
620 50 : it->second->setImage(nullptr);
621 50 : _paths.erase(it);
622 : }
623 50 : setDirty();
624 50 : }
625 :
626 710 : void VectorImage::clear() {
627 710 : if (_copyOnWrite) {
628 25 : copy();
629 : }
630 :
631 710 : _data->clear();
632 :
633 760 : for (auto &it : _paths) {
634 50 : it.second->setImage(nullptr);
635 : }
636 710 : _paths.clear();
637 710 : setDirty();
638 710 : }
639 :
640 25 : const Interface::VectorType<PathXRef> &VectorImage::getDrawOrder() const {
641 25 : return _data->getDrawOrder();
642 : }
643 :
644 25 : void VectorImage::setDrawOrder(const Interface::VectorType<PathXRef> &vec) {
645 25 : if (_copyOnWrite) {
646 25 : copy();
647 : }
648 :
649 25 : _data->setDrawOrder(Interface::VectorType<PathXRef>(vec));
650 25 : setDirty();
651 25 : }
652 :
653 25 : void VectorImage::setDrawOrder(Interface::VectorType<PathXRef> &&vec) {
654 25 : if (_copyOnWrite) {
655 25 : copy();
656 : }
657 :
658 25 : _data->setDrawOrder(move(vec));
659 25 : setDirty();
660 25 : }
661 :
662 25 : void VectorImage::resetDrawOrder() {
663 25 : if (_copyOnWrite) {
664 25 : copy();
665 : }
666 :
667 25 : _data->resetDrawOrder();
668 25 : setDirty();
669 25 : }
670 :
671 25 : void VectorImage::setViewBoxTransform(const Mat4 &m) {
672 25 : if (_data->getViewBoxTransform() == m) {
673 : return;
674 : }
675 :
676 25 : if (_copyOnWrite) {
677 25 : copy();
678 : }
679 :
680 25 : _data->setViewBoxTransform(m);
681 25 : setDirty();
682 : }
683 :
684 25 : const Mat4 &VectorImage::getViewBoxTransform() const {
685 25 : return _data->getViewBoxTransform();
686 : }
687 :
688 50 : void VectorImage::setBatchDrawing(bool value) {
689 50 : if (_data->isBatchDrawing() == value) {
690 : return;
691 : }
692 :
693 25 : if (_copyOnWrite) {
694 25 : copy();
695 : }
696 :
697 25 : _data->setBatchDrawing(value);
698 : }
699 :
700 25 : bool VectorImage::isBatchDrawing() const {
701 25 : return _data->isBatchDrawing();
702 : }
703 :
704 483351 : Rc<VectorImageData> VectorImage::popData() {
705 483351 : markCopyOnWrite();
706 483351 : return _data;
707 : }
708 :
709 153940 : bool VectorImage::isDirty() const {
710 153940 : return _dirty;
711 : }
712 :
713 1342083 : void VectorImage::setDirty() {
714 1342083 : _dirty = true;
715 1342083 : }
716 :
717 57851 : void VectorImage::clearDirty() {
718 57851 : _dirty = false;
719 57851 : }
720 :
721 475 : void VectorImage::copy() {
722 475 : if (_copyOnWrite) {
723 250 : _data = Rc<VectorImageData>::create(*_data.get());
724 250 : _copyOnWrite = false;
725 : }
726 475 : }
727 :
728 483351 : void VectorImage::markCopyOnWrite() {
729 483351 : _copyOnWrite = true;
730 967369 : for (auto &it : _paths) {
731 484018 : it.second->markCopyOnWrite();
732 : }
733 483351 : }
734 :
735 225 : Rc<VectorPath> VectorImage::copyPath(StringView str) {
736 225 : copy();
737 225 : return _data->copyPath(str);
738 : }
739 :
740 : }
|