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