1 /*
2 * Copyright (c) 2020 - 2024 the ThorVG project. All rights reserved.
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 all
12 * 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 THE
20 * SOFTWARE.
21 */
22
23 #include "../../lv_conf_internal.h"
24 #if LV_USE_THORVG_INTERNAL
25
26 #include "tvgSwCommon.h"
27 #include "tvgMath.h"
28
29 /************************************************************************/
30 /* Internal Class Implementation */
31 /************************************************************************/
32
_outlineBegin(SwOutline & outline)33 static bool _outlineBegin(SwOutline& outline)
34 {
35 //Make a contour if lineTo/curveTo without calling close or moveTo beforehand.
36 if (outline.pts.empty()) return false;
37 outline.cntrs.push(outline.pts.count - 1);
38 outline.closed.push(false);
39 outline.pts.push(outline.pts[outline.cntrs.last()]);
40 outline.types.push(SW_CURVE_TYPE_POINT);
41 return false;
42 }
43
44
_outlineEnd(SwOutline & outline)45 static bool _outlineEnd(SwOutline& outline)
46 {
47 if (outline.pts.empty()) return false;
48 outline.cntrs.push(outline.pts.count - 1);
49 outline.closed.push(false);
50 return false;
51 }
52
53
_outlineMoveTo(SwOutline & outline,const Point * to,const Matrix & transform,bool closed=false)54 static bool _outlineMoveTo(SwOutline& outline, const Point* to, const Matrix& transform, bool closed = false)
55 {
56 //make it a contour, if the last contour is not closed yet.
57 if (!closed) _outlineEnd(outline);
58
59 outline.pts.push(mathTransform(to, transform));
60 outline.types.push(SW_CURVE_TYPE_POINT);
61 return false;
62 }
63
64
_outlineLineTo(SwOutline & outline,const Point * to,const Matrix & transform)65 static void _outlineLineTo(SwOutline& outline, const Point* to, const Matrix& transform)
66 {
67 outline.pts.push(mathTransform(to, transform));
68 outline.types.push(SW_CURVE_TYPE_POINT);
69 }
70
71
_outlineCubicTo(SwOutline & outline,const Point * ctrl1,const Point * ctrl2,const Point * to,const Matrix & transform)72 static void _outlineCubicTo(SwOutline& outline, const Point* ctrl1, const Point* ctrl2, const Point* to, const Matrix& transform)
73 {
74 outline.pts.push(mathTransform(ctrl1, transform));
75 outline.types.push(SW_CURVE_TYPE_CUBIC);
76
77 outline.pts.push(mathTransform(ctrl2, transform));
78 outline.types.push(SW_CURVE_TYPE_CUBIC);
79
80 outline.pts.push(mathTransform(to, transform));
81 outline.types.push(SW_CURVE_TYPE_POINT);
82 }
83
84
_outlineClose(SwOutline & outline)85 static bool _outlineClose(SwOutline& outline)
86 {
87 uint32_t i;
88 if (outline.cntrs.count > 0) i = outline.cntrs.last() + 1;
89 else i = 0;
90
91 //Make sure there is at least one point in the current path
92 if (outline.pts.count == i) return false;
93
94 //Close the path
95 outline.pts.push(outline.pts[i]);
96 outline.cntrs.push(outline.pts.count - 1);
97 outline.types.push(SW_CURVE_TYPE_POINT);
98 outline.closed.push(true);
99
100 return true;
101 }
102
103
_dashLineTo(SwDashStroke & dash,const Point * to,const Matrix & transform)104 static void _dashLineTo(SwDashStroke& dash, const Point* to, const Matrix& transform)
105 {
106 Line cur = {dash.ptCur, *to};
107 auto len = cur.length();
108
109 if (tvg::zero(len)) {
110 _outlineMoveTo(*dash.outline, &dash.ptCur, transform);
111 //draw the current line fully
112 } else if (len <= dash.curLen) {
113 dash.curLen -= len;
114 if (!dash.curOpGap) {
115 if (dash.move) {
116 _outlineMoveTo(*dash.outline, &dash.ptCur, transform);
117 dash.move = false;
118 }
119 _outlineLineTo(*dash.outline, to, transform);
120 }
121 //draw the current line partially
122 } else {
123 while (len - dash.curLen > 0.0001f) {
124 Line left, right;
125 if (dash.curLen > 0) {
126 len -= dash.curLen;
127 cur.split(dash.curLen, left, right);
128 if (!dash.curOpGap) {
129 if (dash.move || dash.pattern[dash.curIdx] - dash.curLen < FLOAT_EPSILON) {
130 _outlineMoveTo(*dash.outline, &left.pt1, transform);
131 dash.move = false;
132 }
133 _outlineLineTo(*dash.outline, &left.pt2, transform);
134 }
135 } else {
136 right = cur;
137 }
138 dash.curIdx = (dash.curIdx + 1) % dash.cnt;
139 dash.curLen = dash.pattern[dash.curIdx];
140 dash.curOpGap = !dash.curOpGap;
141 cur = right;
142 dash.ptCur = cur.pt1;
143 dash.move = true;
144 }
145 //leftovers
146 dash.curLen -= len;
147 if (!dash.curOpGap) {
148 if (dash.move) {
149 _outlineMoveTo(*dash.outline, &cur.pt1, transform);
150 dash.move = false;
151 }
152 _outlineLineTo(*dash.outline, &cur.pt2, transform);
153 }
154 if (dash.curLen < 1 && TO_SWCOORD(len) > 1) {
155 //move to next dash
156 dash.curIdx = (dash.curIdx + 1) % dash.cnt;
157 dash.curLen = dash.pattern[dash.curIdx];
158 dash.curOpGap = !dash.curOpGap;
159 }
160 }
161 dash.ptCur = *to;
162 }
163
164
_dashCubicTo(SwDashStroke & dash,const Point * ctrl1,const Point * ctrl2,const Point * to,const Matrix & transform)165 static void _dashCubicTo(SwDashStroke& dash, const Point* ctrl1, const Point* ctrl2, const Point* to, const Matrix& transform)
166 {
167 Bezier cur = {dash.ptCur, *ctrl1, *ctrl2, *to};
168 auto len = cur.length();
169
170 //draw the current line fully
171 if (tvg::zero(len)) {
172 _outlineMoveTo(*dash.outline, &dash.ptCur, transform);
173 } else if (len <= dash.curLen) {
174 dash.curLen -= len;
175 if (!dash.curOpGap) {
176 if (dash.move) {
177 _outlineMoveTo(*dash.outline, &dash.ptCur, transform);
178 dash.move = false;
179 }
180 _outlineCubicTo(*dash.outline, ctrl1, ctrl2, to, transform);
181 }
182 //draw the current line partially
183 } else {
184 while ((len - dash.curLen) > 0.0001f) {
185 Bezier left, right;
186 if (dash.curLen > 0) {
187 len -= dash.curLen;
188 cur.split(dash.curLen, left, right);
189 if (!dash.curOpGap) {
190 if (dash.move || dash.pattern[dash.curIdx] - dash.curLen < FLOAT_EPSILON) {
191 _outlineMoveTo(*dash.outline, &left.start, transform);
192 dash.move = false;
193 }
194 _outlineCubicTo(*dash.outline, &left.ctrl1, &left.ctrl2, &left.end, transform);
195 }
196 } else {
197 right = cur;
198 }
199 dash.curIdx = (dash.curIdx + 1) % dash.cnt;
200 dash.curLen = dash.pattern[dash.curIdx];
201 dash.curOpGap = !dash.curOpGap;
202 cur = right;
203 dash.ptCur = right.start;
204 dash.move = true;
205 }
206 //leftovers
207 dash.curLen -= len;
208 if (!dash.curOpGap) {
209 if (dash.move) {
210 _outlineMoveTo(*dash.outline, &cur.start, transform);
211 dash.move = false;
212 }
213 _outlineCubicTo(*dash.outline, &cur.ctrl1, &cur.ctrl2, &cur.end, transform);
214 }
215 if (dash.curLen < 0.1f && TO_SWCOORD(len) > 1) {
216 //move to next dash
217 dash.curIdx = (dash.curIdx + 1) % dash.cnt;
218 dash.curLen = dash.pattern[dash.curIdx];
219 dash.curOpGap = !dash.curOpGap;
220 }
221 }
222 dash.ptCur = *to;
223 }
224
225
_dashClose(SwDashStroke & dash,const Matrix & transform)226 static void _dashClose(SwDashStroke& dash, const Matrix& transform)
227 {
228 _dashLineTo(dash, &dash.ptStart, transform);
229 }
230
231
_dashMoveTo(SwDashStroke & dash,const Point * pts)232 static void _dashMoveTo(SwDashStroke& dash, const Point* pts)
233 {
234 dash.ptCur = *pts;
235 dash.ptStart = *pts;
236 dash.move = true;
237 }
238
239
_dashMoveTo(SwDashStroke & dash,uint32_t offIdx,float offset,const Point * pts)240 static void _dashMoveTo(SwDashStroke& dash, uint32_t offIdx, float offset, const Point* pts)
241 {
242 dash.curIdx = offIdx % dash.cnt;
243 dash.curLen = dash.pattern[dash.curIdx] - offset;
244 dash.curOpGap = offIdx % 2;
245 dash.ptStart = dash.ptCur = *pts;
246 dash.move = true;
247 }
248
249
_trimPattern(SwDashStroke * dash,const RenderShape * rshape,float length,float trimBegin,float trimEnd)250 static void _trimPattern(SwDashStroke* dash, const RenderShape* rshape, float length, float trimBegin, float trimEnd)
251 {
252 auto begin = length * trimBegin;
253 auto end = length * trimEnd;
254
255 //default
256 if (end > begin) {
257 if (begin > 0.0f) dash->cnt = 4;
258 else dash->cnt = 2;
259 //looping
260 } else dash->cnt = 3;
261
262 if (dash->cnt == 2) {
263 dash->pattern[0] = end - begin;
264 dash->pattern[1] = length - (end - begin);
265 } else if (dash->cnt == 3) {
266 dash->pattern[0] = end;
267 dash->pattern[1] = (begin - end);
268 dash->pattern[2] = length - begin;
269 } else {
270 dash->pattern[0] = 0; //zero dash to start with a space.
271 dash->pattern[1] = begin;
272 dash->pattern[2] = end - begin;
273 dash->pattern[3] = length - end;
274 }
275 }
276
277
_outlineLength(const RenderShape * rshape,uint32_t shiftPts,uint32_t shiftCmds,bool subpath)278 static float _outlineLength(const RenderShape* rshape, uint32_t shiftPts, uint32_t shiftCmds, bool subpath)
279 {
280 const PathCommand* cmds = rshape->path.cmds.data + shiftCmds;
281 auto cmdCnt = rshape->path.cmds.count - shiftCmds;
282 const Point* pts = rshape->path.pts.data + shiftPts;
283 auto ptsCnt = rshape->path.pts.count - shiftPts;
284
285 //No actual shape data
286 if (cmdCnt <= 0 || ptsCnt <= 0) return 0.0f;
287
288 const Point* close = nullptr;
289 auto len = 0.0f;
290
291 //must begin with moveTo
292 if (cmds[0] == PathCommand::MoveTo) {
293 close = pts;
294 cmds++;
295 pts++;
296 cmdCnt--;
297 }
298
299 while (cmdCnt-- > 0) {
300 switch (*cmds) {
301 case PathCommand::Close: {
302 len += length(pts - 1, close);
303 if (subpath) return len;
304 break;
305 }
306 case PathCommand::MoveTo: {
307 if (subpath) return len;
308 close = pts;
309 ++pts;
310 break;
311 }
312 case PathCommand::LineTo: {
313 len += length(pts - 1, pts);
314 ++pts;
315 break;
316 }
317 case PathCommand::CubicTo: {
318 len += Bezier{*(pts - 1), *pts, *(pts + 1), *(pts + 2)}.length();
319 pts += 3;
320 break;
321 }
322 }
323 ++cmds;
324 }
325 return len;
326 }
327
328
_genDashOutline(const RenderShape * rshape,const Matrix & transform,bool trimmed,SwMpool * mpool,unsigned tid)329 static SwOutline* _genDashOutline(const RenderShape* rshape, const Matrix& transform, bool trimmed, SwMpool* mpool, unsigned tid)
330 {
331 const PathCommand* cmds = rshape->path.cmds.data;
332 auto cmdCnt = rshape->path.cmds.count;
333 const Point* pts = rshape->path.pts.data;
334 auto ptsCnt = rshape->path.pts.count;
335
336 //No actual shape data
337 if (cmdCnt == 0 || ptsCnt == 0) return nullptr;
338
339 auto startPts = pts;
340 auto startCmds = cmds;
341
342 SwDashStroke dash;
343 auto offset = 0.0f;
344 dash.cnt = rshape->strokeDash((const float**)&dash.pattern, &offset);
345 auto simultaneous = rshape->stroke->trim.simultaneous;
346 float trimBegin = 0.0f, trimEnd = 1.0f;
347 if (trimmed) rshape->stroke->strokeTrim(trimBegin, trimEnd);
348
349 if (dash.cnt == 0) {
350 if (trimmed) dash.pattern = (float*)malloc(sizeof(float) * 4);
351 else return nullptr;
352 } else {
353 //TODO: handle dash + trim - for now trimming ignoring is forced
354 trimmed = false;
355 }
356
357 //offset
358 auto patternLength = 0.0f;
359 uint32_t offIdx = 0;
360 if (!tvg::zero(offset)) {
361 for (size_t i = 0; i < dash.cnt; ++i) patternLength += dash.pattern[i];
362 bool isOdd = dash.cnt % 2;
363 if (isOdd) patternLength *= 2;
364
365 offset = fmodf(offset, patternLength);
366 if (offset < 0) offset += patternLength;
367
368 for (size_t i = 0; i < dash.cnt * (1 + (size_t)isOdd); ++i, ++offIdx) {
369 auto curPattern = dash.pattern[i % dash.cnt];
370 if (offset < curPattern) break;
371 offset -= curPattern;
372 }
373 }
374
375 dash.outline = mpoolReqDashOutline(mpool, tid);
376
377 //must begin with moveTo
378 if (cmds[0] == PathCommand::MoveTo) {
379 if (trimmed) _trimPattern(&dash, rshape, _outlineLength(rshape, 0, 0, simultaneous), trimBegin, trimEnd);
380 _dashMoveTo(dash, offIdx, offset, pts);
381 cmds++;
382 pts++;
383 }
384
385 while (--cmdCnt > 0) {
386 switch (*cmds) {
387 case PathCommand::Close: {
388 _dashClose(dash, transform);
389 break;
390 }
391 case PathCommand::MoveTo: {
392 if (trimmed) {
393 if (simultaneous) {
394 _trimPattern(&dash, rshape, _outlineLength(rshape, pts - startPts, cmds - startCmds, true), trimBegin, trimEnd);
395 _dashMoveTo(dash, offIdx, offset, pts);
396 } else _dashMoveTo(dash, pts);
397 } else _dashMoveTo(dash, offIdx, offset, pts);
398 ++pts;
399 break;
400 }
401 case PathCommand::LineTo: {
402 _dashLineTo(dash, pts, transform);
403 ++pts;
404 break;
405 }
406 case PathCommand::CubicTo: {
407 _dashCubicTo(dash, pts, pts + 1, pts + 2, transform);
408 pts += 3;
409 break;
410 }
411 }
412 ++cmds;
413 }
414
415 _outlineEnd(*dash.outline);
416
417 if (trimmed) free(dash.pattern);
418
419 return dash.outline;
420 }
421
422
_axisAlignedRect(const SwOutline * outline)423 static bool _axisAlignedRect(const SwOutline* outline)
424 {
425 //Fast Track: axis-aligned rectangle?
426 if (outline->pts.count != 5) return false;
427 if (outline->types[2] == SW_CURVE_TYPE_CUBIC) return false;
428
429 auto pt1 = outline->pts.data + 0;
430 auto pt2 = outline->pts.data + 1;
431 auto pt3 = outline->pts.data + 2;
432 auto pt4 = outline->pts.data + 3;
433
434 auto a = SwPoint{pt1->x, pt3->y};
435 auto b = SwPoint{pt3->x, pt1->y};
436
437 if ((*pt2 == a && *pt4 == b) || (*pt2 == b && *pt4 == a)) return true;
438
439 return false;
440 }
441
442
_genOutline(SwShape * shape,const RenderShape * rshape,const Matrix & transform,SwMpool * mpool,unsigned tid,bool hasComposite)443 static bool _genOutline(SwShape* shape, const RenderShape* rshape, const Matrix& transform, SwMpool* mpool, unsigned tid, bool hasComposite)
444 {
445 const PathCommand* cmds = rshape->path.cmds.data;
446 auto cmdCnt = rshape->path.cmds.count;
447 const Point* pts = rshape->path.pts.data;
448 auto ptsCnt = rshape->path.pts.count;
449
450 //No actual shape data
451 if (cmdCnt == 0 || ptsCnt == 0) return false;
452
453 shape->outline = mpoolReqOutline(mpool, tid);
454 auto outline = shape->outline;
455 auto closed = false;
456
457 //Generate Outlines
458 while (cmdCnt-- > 0) {
459 switch (*cmds) {
460 case PathCommand::Close: {
461 if (!closed) closed = _outlineClose(*outline);
462 break;
463 }
464 case PathCommand::MoveTo: {
465 closed = _outlineMoveTo(*outline, pts, transform, closed);
466 ++pts;
467 break;
468 }
469 case PathCommand::LineTo: {
470 if (closed) closed = _outlineBegin(*outline);
471 _outlineLineTo(*outline, pts, transform);
472 ++pts;
473 break;
474 }
475 case PathCommand::CubicTo: {
476 if (closed) closed = _outlineBegin(*outline);
477 _outlineCubicTo(*outline, pts, pts + 1, pts + 2, transform);
478 pts += 3;
479 break;
480 }
481 }
482 ++cmds;
483 }
484
485 if (!closed) _outlineEnd(*outline);
486
487 outline->fillRule = rshape->rule;
488 shape->outline = outline;
489
490 shape->fastTrack = (!hasComposite && _axisAlignedRect(shape->outline));
491 return true;
492 }
493
494
495 /************************************************************************/
496 /* External Class Implementation */
497 /************************************************************************/
498
shapePrepare(SwShape * shape,const RenderShape * rshape,const Matrix & transform,const SwBBox & clipRegion,SwBBox & renderRegion,SwMpool * mpool,unsigned tid,bool hasComposite)499 bool shapePrepare(SwShape* shape, const RenderShape* rshape, const Matrix& transform, const SwBBox& clipRegion, SwBBox& renderRegion, SwMpool* mpool, unsigned tid, bool hasComposite)
500 {
501 if (!_genOutline(shape, rshape, transform, mpool, tid, hasComposite)) return false;
502 if (!mathUpdateOutlineBBox(shape->outline, clipRegion, renderRegion, shape->fastTrack)) return false;
503
504 shape->bbox = renderRegion;
505
506 //Check valid region
507 if (renderRegion.max.x - renderRegion.min.x < 1 && renderRegion.max.y - renderRegion.min.y < 1) return false;
508
509 //Check boundary
510 if (renderRegion.min.x >= clipRegion.max.x || renderRegion.min.y >= clipRegion.max.y ||
511 renderRegion.max.x <= clipRegion.min.x || renderRegion.max.y <= clipRegion.min.y) return false;
512
513 return true;
514 }
515
516
shapePrepared(const SwShape * shape)517 bool shapePrepared(const SwShape* shape)
518 {
519 return shape->rle ? true : false;
520 }
521
522
shapeGenRle(SwShape * shape,TVG_UNUSED const RenderShape * rshape,bool antiAlias)523 bool shapeGenRle(SwShape* shape, TVG_UNUSED const RenderShape* rshape, bool antiAlias)
524 {
525 //FIXME: Should we draw it?
526 //Case: Stroke Line
527 //if (shape.outline->opened) return true;
528
529 //Case A: Fast Track Rectangle Drawing
530 if (shape->fastTrack) return true;
531
532 //Case B: Normal Shape RLE Drawing
533 if ((shape->rle = rleRender(shape->rle, shape->outline, shape->bbox, antiAlias))) return true;
534
535 return false;
536 }
537
538
shapeDelOutline(SwShape * shape,SwMpool * mpool,uint32_t tid)539 void shapeDelOutline(SwShape* shape, SwMpool* mpool, uint32_t tid)
540 {
541 mpoolRetOutline(mpool, tid);
542 shape->outline = nullptr;
543 }
544
545
shapeReset(SwShape * shape)546 void shapeReset(SwShape* shape)
547 {
548 rleReset(shape->rle);
549 rleReset(shape->strokeRle);
550 shape->fastTrack = false;
551 shape->bbox.reset();
552 }
553
554
shapeFree(SwShape * shape)555 void shapeFree(SwShape* shape)
556 {
557 rleFree(shape->rle);
558 shape->rle = nullptr;
559
560 shapeDelFill(shape);
561
562 if (shape->stroke) {
563 rleFree(shape->strokeRle);
564 shape->strokeRle = nullptr;
565 strokeFree(shape->stroke);
566 shape->stroke = nullptr;
567 }
568 }
569
570
shapeDelStroke(SwShape * shape)571 void shapeDelStroke(SwShape* shape)
572 {
573 if (!shape->stroke) return;
574 rleFree(shape->strokeRle);
575 shape->strokeRle = nullptr;
576 strokeFree(shape->stroke);
577 shape->stroke = nullptr;
578 }
579
580
shapeResetStroke(SwShape * shape,const RenderShape * rshape,const Matrix & transform)581 void shapeResetStroke(SwShape* shape, const RenderShape* rshape, const Matrix& transform)
582 {
583 if (!shape->stroke) shape->stroke = static_cast<SwStroke*>(calloc(1, sizeof(SwStroke)));
584 auto stroke = shape->stroke;
585 if (!stroke) return;
586
587 strokeReset(stroke, rshape, transform);
588 rleReset(shape->strokeRle);
589 }
590
591
shapeGenStrokeRle(SwShape * shape,const RenderShape * rshape,const Matrix & transform,const SwBBox & clipRegion,SwBBox & renderRegion,SwMpool * mpool,unsigned tid)592 bool shapeGenStrokeRle(SwShape* shape, const RenderShape* rshape, const Matrix& transform, const SwBBox& clipRegion, SwBBox& renderRegion, SwMpool* mpool, unsigned tid)
593 {
594 SwOutline* shapeOutline = nullptr;
595 SwOutline* strokeOutline = nullptr;
596 auto dashStroking = false;
597 auto ret = true;
598
599 //Dash style (+trimming)
600 auto trimmed = rshape->strokeTrim();
601 if (rshape->stroke->dashCnt > 0 || trimmed) {
602 shapeOutline = _genDashOutline(rshape, transform, trimmed, mpool, tid);
603 if (!shapeOutline) return false;
604 dashStroking = true;
605 //Normal style
606 } else {
607 if (!shape->outline) {
608 if (!_genOutline(shape, rshape, transform, mpool, tid, false)) return false;
609 }
610 shapeOutline = shape->outline;
611 }
612
613 if (!strokeParseOutline(shape->stroke, *shapeOutline)) {
614 ret = false;
615 goto clear;
616 }
617
618 strokeOutline = strokeExportOutline(shape->stroke, mpool, tid);
619
620 if (!mathUpdateOutlineBBox(strokeOutline, clipRegion, renderRegion, false)) {
621 ret = false;
622 goto clear;
623 }
624
625 shape->strokeRle = rleRender(shape->strokeRle, strokeOutline, renderRegion, true);
626
627 clear:
628 if (dashStroking) mpoolRetDashOutline(mpool, tid);
629 mpoolRetStrokeOutline(mpool, tid);
630
631 return ret;
632 }
633
634
shapeGenFillColors(SwShape * shape,const Fill * fill,const Matrix & transform,SwSurface * surface,uint8_t opacity,bool ctable)635 bool shapeGenFillColors(SwShape* shape, const Fill* fill, const Matrix& transform, SwSurface* surface, uint8_t opacity, bool ctable)
636 {
637 return fillGenColorTable(shape->fill, fill, transform, surface, opacity, ctable);
638 }
639
640
shapeGenStrokeFillColors(SwShape * shape,const Fill * fill,const Matrix & transform,SwSurface * surface,uint8_t opacity,bool ctable)641 bool shapeGenStrokeFillColors(SwShape* shape, const Fill* fill, const Matrix& transform, SwSurface* surface, uint8_t opacity, bool ctable)
642 {
643 return fillGenColorTable(shape->stroke->fill, fill, transform, surface, opacity, ctable);
644 }
645
646
shapeResetFill(SwShape * shape)647 void shapeResetFill(SwShape* shape)
648 {
649 if (!shape->fill) {
650 shape->fill = static_cast<SwFill*>(calloc(1, sizeof(SwFill)));
651 if (!shape->fill) return;
652 }
653 fillReset(shape->fill);
654 }
655
656
shapeResetStrokeFill(SwShape * shape)657 void shapeResetStrokeFill(SwShape* shape)
658 {
659 if (!shape->stroke->fill) {
660 shape->stroke->fill = static_cast<SwFill*>(calloc(1, sizeof(SwFill)));
661 if (!shape->stroke->fill) return;
662 }
663 fillReset(shape->stroke->fill);
664 }
665
666
shapeDelFill(SwShape * shape)667 void shapeDelFill(SwShape* shape)
668 {
669 if (!shape->fill) return;
670 fillFree(shape->fill);
671 shape->fill = nullptr;
672 }
673
674
shapeDelStrokeFill(SwShape * shape)675 void shapeDelStrokeFill(SwShape* shape)
676 {
677 if (!shape->stroke->fill) return;
678 fillFree(shape->stroke->fill);
679 shape->stroke->fill = nullptr;
680 }
681
682 #endif /* LV_USE_THORVG_INTERNAL */
683
684