sw_engine: implement linear gradient feature
[platform/core/graphics/tizenvg.git] / src / lib / sw_engine / tvgSwShape.cpp
1 /*
2  * Copyright (c) 2020 Samsung Electronics Co., Ltd All Rights Reserved
3  *
4  *  Licensed under the Apache License, Version 2.0 (the "License");
5  *  you may not use this file except in compliance with the License.
6  *  You may obtain a copy of the License at
7  *
8  *               http://www.apache.org/licenses/LICENSE-2.0
9  *
10  *  Unless required by applicable law or agreed to in writing, software
11  *  distributed under the License is distributed on an "AS IS" BASIS,
12  *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  *  See the License for the specific language governing permissions and
14  *  limitations under the License.
15  *
16  */
17 #ifndef _TVG_SW_SHAPE_H_
18 #define _TVG_SW_SHAPE_H_
19
20 #include "tvgSwCommon.h"
21
22 /************************************************************************/
23 /* Internal Class Implementation                                        */
24 /************************************************************************/
25
26 struct Line
27 {
28     Point pt1;
29     Point pt2;
30 };
31
32
33 struct Bezier
34 {
35     Point start;
36     Point ctrl1;
37     Point ctrl2;
38     Point end;
39 };
40
41
42 static float _lineLength(const Point& pt1, const Point& pt2)
43 {
44     /* approximate sqrt(x*x + y*y) using alpha max plus beta min algorithm.
45        With alpha = 1, beta = 3/8, giving results with the largest error less
46        than 7% compared to the exact value. */
47     Point diff = {pt2.x - pt1.x, pt2.y - pt1.y};
48     if (diff.x < 0) diff.x = -diff.x;
49     if (diff.y < 0) diff.y = -diff.y;
50     return (diff.x > diff.y) ? (diff.x + diff.y * 0.375f) : (diff.y + diff.x * 0.375f);
51 }
52
53
54 static void _lineSplitAt(const Line& cur, float at, Line& left, Line& right)
55 {
56     auto len = _lineLength(cur.pt1, cur.pt2);
57     auto dx = ((cur.pt2.x - cur.pt1.x) / len) * at;
58     auto dy = ((cur.pt2.y - cur.pt1.y) / len) * at;
59     left.pt1 = cur.pt1;
60     left.pt2.x = left.pt1.x + dx;
61     left.pt2.y = left.pt1.y + dy;
62     right.pt1 = left.pt2;
63     right.pt2 = cur.pt2;
64 }
65
66
67 static void _bezSplit(const Bezier&cur, Bezier& left, Bezier& right)
68 {
69     auto c = (cur.ctrl1.x + cur.ctrl2.x) * 0.5f;
70     left.ctrl1.x = (cur.start.x + cur.ctrl1.x) * 0.5f;
71     right.ctrl2.x = (cur.ctrl2.x + cur.end.x) * 0.5f;
72     left.start.x = cur.start.x;
73     right.end.x = cur.end.x;
74     left.ctrl2.x = (left.ctrl1.x + c) * 0.5f;
75     right.ctrl1.x = (right.ctrl2.x + c) * 0.5f;
76     left.end.x = right.start.x = (left.ctrl2.x + right.ctrl1.x) * 0.5f;
77
78     c = (cur.ctrl1.y + cur.ctrl2.y) * 0.5f;
79     left.ctrl1.y = (cur.start.y + cur.ctrl1.y) * 0.5f;
80     right.ctrl2.y = (cur.ctrl2.y + cur.end.y) * 0.5f;
81     left.start.y = cur.start.y;
82     right.end.y = cur.end.y;
83     left.ctrl2.y = (left.ctrl1.y + c) * 0.5f;
84     right.ctrl1.y = (right.ctrl2.y + c) * 0.5f;
85     left.end.y = right.start.y = (left.ctrl2.y + right.ctrl1.y) * 0.5f;
86 }
87
88
89 static float _bezLength(const Bezier& cur)
90 {
91     Bezier left, right;
92     auto len = _lineLength(cur.start, cur.ctrl1) + _lineLength(cur.ctrl1, cur.ctrl2) + _lineLength(cur.ctrl2, cur.end);
93     auto chord = _lineLength(cur.start, cur.end);
94
95     if (fabs(len - chord) > FLT_EPSILON) {
96         _bezSplit(cur, left, right);
97         return _bezLength(left) + _bezLength(right);
98     }
99     return len;
100 }
101
102
103 static void _bezSplitLeft(Bezier& cur, float at, Bezier& left)
104 {
105     left.start = cur.start;
106
107     left.ctrl1.x = cur.start.x + at * (cur.ctrl1.x - cur.start.x);
108     left.ctrl1.y = cur.start.y + at * (cur.ctrl1.y - cur.start.y);
109
110     left.ctrl2.x = cur.ctrl1.x + at * (cur.ctrl2.x - cur.ctrl1.x); // temporary holding spot
111     left.ctrl2.y = cur.ctrl1.y + at * (cur.ctrl2.y - cur.ctrl1.y); // temporary holding spot
112
113     cur.ctrl2.x = cur.ctrl2.x + at * (cur.end.x - cur.ctrl2.x);
114     cur.ctrl2.y = cur.ctrl2.y + at * (cur.end.y - cur.ctrl2.y);
115
116     cur.ctrl1.x = left.ctrl2.x + at * (cur.ctrl2.x - left.ctrl2.x);
117     cur.ctrl1.y = left.ctrl2.y + at * (cur.ctrl2.y - left.ctrl2.y);
118
119     left.ctrl2.x = left.ctrl1.x + at * (left.ctrl2.x - left.ctrl1.x);
120     left.ctrl2.y = left.ctrl1.y + at * (left.ctrl2.y - left.ctrl1.y);
121
122     left.end.x = cur.start.x = left.ctrl2.x + at * (cur.ctrl1.x - left.ctrl2.x);
123     left.end.y = cur.start.y = left.ctrl2.y + at * (cur.ctrl1.y - left.ctrl2.y);
124 }
125
126
127 static float _bezAt(const Bezier& bz, float at)
128 {
129     auto len = _bezLength(bz);
130     auto biggest = 1.0f;
131
132     if (at >= len) return 1.0f;
133
134     at *= 0.5f;
135
136     while (true) {
137         auto right = bz;
138         Bezier left;
139         _bezSplitLeft(right, at, left);
140         auto len2 = _bezLength(left);
141
142         if (fabs(len2 - len) < FLT_EPSILON) break;
143
144         if (len2 < len) {
145             at += (biggest - at) * 0.5f;
146         } else {
147             biggest = at;
148             at -= (at * 0.5f);
149         }
150     }
151     return at;
152 }
153
154
155 static void _bezSplitAt(const Bezier& cur, float at, Bezier& left, Bezier& right)
156 {
157     right = cur;
158     auto t = _bezAt(right, at);
159     _bezSplitLeft(right, t, left);
160 }
161
162
163 static void _growOutlineContour(SwOutline& outline, uint32_t n)
164 {
165     if (outline.reservedCntrsCnt >= outline.cntrsCnt + n) return;
166     outline.reservedCntrsCnt = outline.cntrsCnt + n;
167     outline.cntrs = static_cast<uint32_t*>(realloc(outline.cntrs, outline.reservedCntrsCnt * sizeof(uint32_t)));
168     assert(outline.cntrs);
169 }
170
171
172 static void _growOutlinePoint(SwOutline& outline, uint32_t n)
173 {
174     if (outline.reservedPtsCnt >= outline.ptsCnt + n) return;
175     outline.reservedPtsCnt = outline.ptsCnt + n;
176     outline.pts = static_cast<SwPoint*>(realloc(outline.pts, outline.reservedPtsCnt * sizeof(SwPoint)));
177     assert(outline.pts);
178     outline.types = static_cast<uint8_t*>(realloc(outline.types, outline.reservedPtsCnt * sizeof(uint8_t)));
179     assert(outline.types);
180 }
181
182
183 static void _delOutline(SwOutline* outline)
184 {
185     if (!outline) return;
186
187     if (outline->cntrs) free(outline->cntrs);
188     if (outline->pts) free(outline->pts);
189     if (outline->types) free(outline->types);
190     free(outline);
191 }
192
193
194 static void _outlineEnd(SwOutline& outline)
195 {
196     _growOutlineContour(outline, 1);
197     if (outline.ptsCnt > 0) {
198         outline.cntrs[outline.cntrsCnt] = outline.ptsCnt - 1;
199         ++outline.cntrsCnt;
200     }
201 }
202
203
204 static void _outlineMoveTo(SwOutline& outline, const Point* to)
205 {
206     assert(to);
207
208     _growOutlinePoint(outline, 1);
209
210     outline.pts[outline.ptsCnt] = TO_SWPOINT(to);
211     outline.types[outline.ptsCnt] = SW_CURVE_TYPE_POINT;
212
213     if (outline.ptsCnt > 0) {
214         _growOutlineContour(outline, 1);
215         outline.cntrs[outline.cntrsCnt] = outline.ptsCnt - 1;
216         ++outline.cntrsCnt;
217     }
218
219     ++outline.ptsCnt;
220 }
221
222
223 static void _outlineLineTo(SwOutline& outline, const Point* to)
224 {
225     assert(to);
226
227     _growOutlinePoint(outline, 1);
228
229     outline.pts[outline.ptsCnt] = TO_SWPOINT(to);
230     outline.types[outline.ptsCnt] = SW_CURVE_TYPE_POINT;
231     ++outline.ptsCnt;
232 }
233
234
235 static void _outlineCubicTo(SwOutline& outline, const Point* ctrl1, const Point* ctrl2, const Point* to)
236 {
237     assert(ctrl1 && ctrl2 && to);
238
239     _growOutlinePoint(outline, 3);
240
241     outline.pts[outline.ptsCnt] = TO_SWPOINT(ctrl1);
242     outline.types[outline.ptsCnt] = SW_CURVE_TYPE_CUBIC;
243     ++outline.ptsCnt;
244
245     outline.pts[outline.ptsCnt] = TO_SWPOINT(ctrl2);
246     outline.types[outline.ptsCnt] = SW_CURVE_TYPE_CUBIC;
247     ++outline.ptsCnt;
248
249     outline.pts[outline.ptsCnt] = TO_SWPOINT(to);
250     outline.types[outline.ptsCnt] = SW_CURVE_TYPE_POINT;
251     ++outline.ptsCnt;
252 }
253
254
255 static void _outlineClose(SwOutline& outline)
256 {
257     uint32_t i = 0;
258
259     if (outline.cntrsCnt > 0) {
260         i = outline.cntrs[outline.cntrsCnt - 1] + 1;
261     } else {
262         i = 0;   //First Path
263     }
264
265     //Make sure there is at least one point in the current path
266     if (outline.ptsCnt == i) {
267         outline.opened = true;
268         return;
269     }
270
271     //Close the path
272     _growOutlinePoint(outline, 1);
273
274     outline.pts[outline.ptsCnt] = outline.pts[i];
275     outline.types[outline.ptsCnt] = SW_CURVE_TYPE_POINT;
276     ++outline.ptsCnt;
277
278     outline.opened = false;
279 }
280
281
282 static void _initBBox(SwBBox& bbox)
283 {
284     bbox.min.x = bbox.min.y = 0;
285     bbox.max.x = bbox.max.y = 0;
286 }
287
288
289 static bool _updateBBox(SwOutline* outline, SwBBox& bbox)
290 {
291     if (!outline) return false;
292
293     auto pt = outline->pts;
294     assert(pt);
295
296     if (outline->ptsCnt <= 0) {
297         _initBBox(bbox);
298         return false;
299     }
300
301     auto xMin = pt->x;
302     auto xMax = pt->x;
303     auto yMin = pt->y;
304     auto yMax = pt->y;
305
306     ++pt;
307
308     for(uint32_t i = 1; i < outline->ptsCnt; ++i, ++pt) {
309         assert(pt);
310         if (xMin > pt->x) xMin = pt->x;
311         if (xMax < pt->x) xMax = pt->x;
312         if (yMin > pt->y) yMin = pt->y;
313         if (yMax < pt->y) yMax = pt->y;
314     }
315     bbox.min.x = xMin >> 6;
316     bbox.max.x = (xMax + 63) >> 6;
317     bbox.min.y = yMin >> 6;
318     bbox.max.y = (yMax + 63) >> 6;
319
320     if (xMax - xMin < 1 || yMax - yMin < 1) return false;
321
322     return true;
323 }
324
325
326 static bool _checkValid(const SwOutline* outline, const SwBBox& bbox, const SwSize& clip)
327 {
328     assert(outline);
329
330     if (outline->ptsCnt == 0 || outline->cntrsCnt <= 0) return false;
331
332     //Check boundary
333     if ((bbox.min.x > clip.w || bbox.min.y > clip.h) ||
334         (bbox.min.x + bbox.max.x < 0) ||
335         (bbox.min.y + bbox.max.y < 0)) return false;
336
337     return true;
338 }
339
340
341 static void _transformOutline(SwOutline* outline, const RenderTransform* transform)
342 {
343     assert(outline);
344
345     if (!transform) return;
346
347     for(uint32_t i = 0; i < outline->ptsCnt; ++i) {
348         auto dx = static_cast<float>(outline->pts[i].x >> 6);
349         auto dy = static_cast<float>(outline->pts[i].y >> 6);
350         auto tx = dx * transform->e11 + dy * transform->e12 + transform->e13;
351         auto ty = dx * transform->e21 + dy * transform->e22 + transform->e23;
352         auto pt = Point{tx + transform->e31, ty + transform->e32};
353         outline->pts[i] = TO_SWPOINT(&pt);
354     }
355 }
356
357
358 static void _dashLineTo(SwDashStroke& dash, const Point* to)
359 {
360     _growOutlinePoint(*dash.outline, dash.outline->ptsCnt >> 1);
361     _growOutlineContour(*dash.outline, dash.outline->cntrsCnt >> 1);
362
363     Line cur = {dash.ptCur, *to};
364     auto len = _lineLength(cur.pt1, cur.pt2);
365
366     if (len < dash.curLen) {
367         dash.curLen -= len;
368         if (!dash.curOpGap) {
369             _outlineMoveTo(*dash.outline, &dash.ptCur);
370             _outlineLineTo(*dash.outline, to);
371         }
372     } else {
373         while (len > dash.curLen) {
374             len -= dash.curLen;
375             Line left, right;
376             _lineSplitAt(cur, dash.curLen, left, right);;
377             dash.curIdx = (dash.curIdx + 1) % dash.cnt;
378             if (!dash.curOpGap) {
379                 _outlineMoveTo(*dash.outline, &left.pt1);
380                 _outlineLineTo(*dash.outline, &left.pt2);
381             }
382             dash.curLen = dash.pattern[dash.curIdx];
383             dash.curOpGap = !dash.curOpGap;
384             cur = right;
385             dash.ptCur = cur.pt1;
386         }
387         //leftovers
388         dash.curLen -= len;
389         if (!dash.curOpGap) {    
390             _outlineMoveTo(*dash.outline, &cur.pt1);
391             _outlineLineTo(*dash.outline, &cur.pt2);
392         }
393         if (dash.curLen < 1) {
394             //move to next dash
395             dash.curIdx = (dash.curIdx + 1) % dash.cnt;
396             dash.curLen = dash.pattern[dash.curIdx];
397             dash.curOpGap = !dash.curOpGap;
398         }
399     }
400     dash.ptCur = *to;
401 }
402
403
404 static void _dashCubicTo(SwDashStroke& dash, const Point* ctrl1, const Point* ctrl2, const Point* to)
405 {
406     _growOutlinePoint(*dash.outline, dash.outline->ptsCnt >> 1);
407     _growOutlineContour(*dash.outline, dash.outline->cntrsCnt >> 1);
408
409     Bezier cur = { dash.ptCur, *ctrl1, *ctrl2, *to};
410     auto len = _bezLength(cur);
411
412     if (len < dash.curLen) {
413         dash.curLen -= len;
414         if (!dash.curOpGap) {
415             _outlineMoveTo(*dash.outline, &dash.ptCur);
416             _outlineCubicTo(*dash.outline, ctrl1, ctrl2, to);
417         }
418     } else {
419         while (len > dash.curLen) {
420             Bezier left, right;
421             len -= dash.curLen;
422             _bezSplitAt(cur, dash.curLen, left, right);
423             dash.curIdx = (dash.curIdx + 1) % dash.cnt;
424             if (!dash.curOpGap) {
425                 _outlineMoveTo(*dash.outline, &left.start);
426                 _outlineCubicTo(*dash.outline, &left.ctrl1, &left.ctrl2, &left.end);
427             }
428             dash.curLen = dash.pattern[dash.curIdx];
429             dash.curOpGap = !dash.curOpGap;
430             cur = right;
431             dash.ptCur = right.start;
432         }
433         //leftovers
434         dash.curLen -= len;
435         if (!dash.curOpGap) {
436             _outlineMoveTo(*dash.outline, &cur.start);
437             _outlineCubicTo(*dash.outline, &cur.ctrl1, &cur.ctrl2, &cur.end);
438         }
439         if (dash.curLen < 1) {
440             //move to next dash
441             dash.curIdx = (dash.curIdx + 1) % dash.cnt;
442             dash.curLen = dash.pattern[dash.curIdx];
443             dash.curOpGap = !dash.curOpGap;
444         }
445     }
446     dash.ptCur = *to;
447 }
448
449
450 SwOutline* _genDashOutline(const Shape& shape)
451 {
452     const PathCommand* cmds = nullptr;
453     auto cmdCnt = shape.pathCommands(&cmds);
454
455     const Point* pts = nullptr;
456     auto ptsCnt = shape.pathCoords(&pts);
457
458     //No actual shape data
459     if (cmdCnt == 0 || ptsCnt == 0) return nullptr;
460
461     SwDashStroke dash;
462     dash.curIdx = 0;
463     dash.curLen = 0;
464     dash.ptStart = {0, 0};
465     dash.ptCur = {0, 0};
466     dash.curOpGap = false;
467
468     const float* pattern;
469     dash.cnt = shape.strokeDash(&pattern);
470     assert(dash.cnt > 0 && pattern);
471
472     //Is it safe to mutual exclusive?
473     dash.pattern = const_cast<float*>(pattern);
474     dash.outline = static_cast<SwOutline*>(calloc(1, sizeof(SwOutline)));
475     assert(dash.outline);
476     dash.outline->opened = true;
477
478     //smart reservation
479     auto outlinePtsCnt = 0;
480     auto outlineCntrsCnt = 0;
481
482     for (uint32_t i = 0; i < cmdCnt; ++i) {
483         switch(*(cmds + i)) {
484             case PathCommand::Close: {
485                 ++outlinePtsCnt;
486                 break;
487             }
488             case PathCommand::MoveTo: {
489                 ++outlineCntrsCnt;
490                 ++outlinePtsCnt;
491                 break;
492             }
493             case PathCommand::LineTo: {
494                 ++outlinePtsCnt;
495                 break;
496             }
497             case PathCommand::CubicTo: {
498                 outlinePtsCnt += 3;
499                 break;
500             }
501         }
502     }
503
504     ++outlinePtsCnt;    //for close
505     ++outlineCntrsCnt;  //for end
506
507     //Reserve Approximitely 20x...
508     _growOutlinePoint(*dash.outline, outlinePtsCnt * 20);
509     _growOutlineContour(*dash.outline, outlineCntrsCnt * 20);
510
511     while (cmdCnt-- > 0) {
512         switch(*cmds) {
513             case PathCommand::Close: {
514                 _dashLineTo(dash, &dash.ptStart);
515                 break;
516             }
517             case PathCommand::MoveTo: {
518                 //reset the dash
519                 dash.curIdx = 0;
520                 dash.curLen = *dash.pattern;
521                 dash.curOpGap = false;
522                 dash.ptStart = dash.ptCur = *pts;
523                 ++pts;
524                 break;
525             }
526             case PathCommand::LineTo: {
527                 _dashLineTo(dash, pts);
528                 ++pts;
529                 break;
530             }
531             case PathCommand::CubicTo: {
532                 _dashCubicTo(dash, pts, pts + 1, pts + 2);
533                 pts += 3;
534                 break;
535             }
536         }
537         ++cmds;
538     }
539
540     _outlineEnd(*dash.outline);
541
542     return dash.outline;
543 }
544
545
546 /************************************************************************/
547 /* External Class Implementation                                        */
548 /************************************************************************/
549
550 bool shapeGenRle(SwShape& shape, const Shape& sdata, const SwSize& clip, const RenderTransform* transform)
551 {
552     if (!shapeGenOutline(shape, sdata)) return false;
553
554     _transformOutline(shape.outline, transform);
555
556     if (!_updateBBox(shape.outline, shape.bbox)) goto end;
557
558     if (!_checkValid(shape.outline, shape.bbox, clip)) goto end;
559
560     shape.rle = rleRender(shape.outline, shape.bbox, clip);
561 end:
562     if (shape.rle) return true;
563     return false;
564 }
565
566
567 void shapeDelOutline(SwShape& shape)
568 {
569     auto outline = shape.outline;
570     _delOutline(outline);
571     shape.outline = nullptr;
572 }
573
574
575 void shapeReset(SwShape& shape)
576 {
577     shapeDelOutline(shape);
578     rleFree(shape.rle);
579     shape.rle = nullptr;
580     _initBBox(shape.bbox);
581 }
582
583
584 bool shapeGenOutline(SwShape& shape, const Shape& sdata)
585 {
586     const PathCommand* cmds = nullptr;
587     auto cmdCnt = sdata.pathCommands(&cmds);
588
589     const Point* pts = nullptr;
590     auto ptsCnt = sdata.pathCoords(&pts);
591
592     //No actual shape data
593     if (cmdCnt == 0 || ptsCnt == 0) return false;
594
595     //smart reservation
596     auto outlinePtsCnt = 0;
597     auto outlineCntrsCnt = 0;
598
599     for (uint32_t i = 0; i < cmdCnt; ++i) {
600         switch(*(cmds + i)) {
601             case PathCommand::Close: {
602                 ++outlinePtsCnt;
603                 break;
604             }
605             case PathCommand::MoveTo: {
606                 ++outlineCntrsCnt;
607                 ++outlinePtsCnt;
608                 break;
609             }
610             case PathCommand::LineTo: {
611                 ++outlinePtsCnt;
612                 break;
613             }
614             case PathCommand::CubicTo: {
615                 outlinePtsCnt += 3;
616                 break;
617             }
618         }
619     }
620
621     ++outlinePtsCnt;    //for close
622     ++outlineCntrsCnt;  //for end
623
624     auto outline = shape.outline;
625     if (!outline) outline = static_cast<SwOutline*>(calloc(1, sizeof(SwOutline)));
626     assert(outline);
627     outline->opened = true;
628
629     _growOutlinePoint(*outline, outlinePtsCnt);
630     _growOutlineContour(*outline, outlineCntrsCnt);
631
632     //Generate Outlines
633     while (cmdCnt-- > 0) {
634         switch(*cmds) {
635             case PathCommand::Close: {
636                 _outlineClose(*outline);
637                 break;
638             }
639             case PathCommand::MoveTo: {
640                 _outlineMoveTo(*outline, pts);
641                 ++pts;
642                 break;
643             }
644             case PathCommand::LineTo: {
645                 _outlineLineTo(*outline, pts);
646                 ++pts;
647                 break;
648             }
649             case PathCommand::CubicTo: {
650                 _outlineCubicTo(*outline, pts, pts + 1, pts + 2);
651                 pts += 3;
652                 break;
653             }
654         }
655         ++cmds;
656     }
657
658     _outlineEnd(*outline);
659
660     //FIXME:
661     //outline->flags = SwOutline::FillRule::Winding;
662
663     shape.outline = outline;
664
665     return true;
666 }
667
668
669 void shapeFree(SwShape* shape)
670 {
671     assert(shape);
672
673     shapeDelOutline(*shape);
674     rleFree(shape->rle);
675
676     if (shape->stroke) {
677         rleFree(shape->strokeRle);
678         strokeFree(shape->stroke);
679     }
680
681     free(shape);
682 }
683
684
685 void shapeDelStroke(SwShape& shape)
686 {
687     if (!shape.stroke) return;
688     rleFree(shape.strokeRle);
689     shape.strokeRle = nullptr;
690     strokeFree(shape.stroke);
691     shape.stroke = nullptr;
692 }
693
694
695 void shapeResetStroke(SwShape& shape, const Shape& sdata)
696 {
697     if (!shape.stroke) shape.stroke = static_cast<SwStroke*>(calloc(1, sizeof(SwStroke)));
698     auto stroke = shape.stroke;
699     assert(stroke);
700
701     strokeReset(*stroke, sdata);
702
703     rleFree(shape.strokeRle);
704     shape.strokeRle = nullptr;
705 }
706
707
708 bool shapeGenStrokeRle(SwShape& shape, const Shape& sdata, const SwSize& clip)
709 {
710     SwOutline* shapeOutline = nullptr;
711
712     //Dash Style Stroke
713     if (sdata.strokeDash(nullptr) > 0) {
714         shapeOutline = _genDashOutline(sdata);
715         if (!shapeOutline) return false;
716
717     //Normal Style stroke
718     } else {
719         if (!shape.outline) {
720             if (!shapeGenOutline(shape, sdata)) return false;
721         }
722         shapeOutline = shape.outline;
723     }
724
725     if (!strokeParseOutline(*shape.stroke, *shapeOutline)) return false;
726
727     auto strokeOutline = strokeExportOutline(*shape.stroke);
728     if (!strokeOutline) return false;
729
730     SwBBox bbox;
731     _updateBBox(strokeOutline, bbox);
732
733     if (!_checkValid(strokeOutline, bbox, clip)) return false;
734
735     shape.strokeRle = rleRender(strokeOutline, bbox, clip);
736
737     _delOutline(strokeOutline);
738
739     return true;
740 }
741
742
743 bool shapeGenFillColors(SwShape& shape, const Fill* fill)
744 {
745     assert(fill);
746
747     fillGenColorTable(shape.fill, fill);
748     return true;
749 }
750
751
752 void shapeResetFill(SwShape& shape, const Fill* fill)
753 {
754     assert(fill);
755
756     if (!shape.fill) shape.fill = static_cast<SwFill*>(calloc(1, sizeof(SwFill)));
757     assert(shape.fill);
758
759     fillReset(shape.fill, fill);
760 }
761
762
763 void shapeDelFill(SwShape& shape)
764 {
765     if (!shape.fill) return;
766     fillFree(shape.fill);
767     shape.fill = nullptr;
768 }
769
770
771 #endif /* _TVG_SW_SHAPE_H_ */