common bezier: code refactoring.
[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 static float _lineLength(const Point& pt1, const Point& pt2)
34 {
35     /* approximate sqrt(x*x + y*y) using alpha max plus beta min algorithm.
36        With alpha = 1, beta = 3/8, giving results with the largest error less
37        than 7% compared to the exact value. */
38     Point diff = {pt2.x - pt1.x, pt2.y - pt1.y};
39     if (diff.x < 0) diff.x = -diff.x;
40     if (diff.y < 0) diff.y = -diff.y;
41     return (diff.x > diff.y) ? (diff.x + diff.y * 0.375f) : (diff.y + diff.x * 0.375f);
42 }
43
44
45 static void _lineSplitAt(const Line& cur, float at, Line& left, Line& right)
46 {
47     auto len = _lineLength(cur.pt1, cur.pt2);
48     auto dx = ((cur.pt2.x - cur.pt1.x) / len) * at;
49     auto dy = ((cur.pt2.y - cur.pt1.y) / len) * at;
50     left.pt1 = cur.pt1;
51     left.pt2.x = left.pt1.x + dx;
52     left.pt2.y = left.pt1.y + dy;
53     right.pt1 = left.pt2;
54     right.pt2 = cur.pt2;
55 }
56
57
58 static void _growOutlineContour(SwOutline& outline, uint32_t n)
59 {
60     if (outline.reservedCntrsCnt >= outline.cntrsCnt + n) return;
61     outline.reservedCntrsCnt = outline.cntrsCnt + n;
62     outline.cntrs = static_cast<uint32_t*>(realloc(outline.cntrs, outline.reservedCntrsCnt * sizeof(uint32_t)));
63     assert(outline.cntrs);
64 }
65
66
67 static void _growOutlinePoint(SwOutline& outline, uint32_t n)
68 {
69     if (outline.reservedPtsCnt >= outline.ptsCnt + n) return;
70     outline.reservedPtsCnt = outline.ptsCnt + n;
71     outline.pts = static_cast<SwPoint*>(realloc(outline.pts, outline.reservedPtsCnt * sizeof(SwPoint)));
72     assert(outline.pts);
73     outline.types = static_cast<uint8_t*>(realloc(outline.types, outline.reservedPtsCnt * sizeof(uint8_t)));
74     assert(outline.types);
75 }
76
77
78 static void _delOutline(SwOutline* outline)
79 {
80     if (!outline) return;
81
82     if (outline->cntrs) free(outline->cntrs);
83     if (outline->pts) free(outline->pts);
84     if (outline->types) free(outline->types);
85     free(outline);
86 }
87
88
89 static void _outlineEnd(SwOutline& outline)
90 {
91     _growOutlineContour(outline, 1);
92     if (outline.ptsCnt > 0) {
93         outline.cntrs[outline.cntrsCnt] = outline.ptsCnt - 1;
94         ++outline.cntrsCnt;
95     }
96 }
97
98
99 static void _outlineMoveTo(SwOutline& outline, const Point* to)
100 {
101     assert(to);
102
103     _growOutlinePoint(outline, 1);
104
105     outline.pts[outline.ptsCnt] = TO_SWPOINT(to);
106     outline.types[outline.ptsCnt] = SW_CURVE_TYPE_POINT;
107
108     if (outline.ptsCnt > 0) {
109         _growOutlineContour(outline, 1);
110         outline.cntrs[outline.cntrsCnt] = outline.ptsCnt - 1;
111         ++outline.cntrsCnt;
112     }
113
114     ++outline.ptsCnt;
115 }
116
117
118 static void _outlineLineTo(SwOutline& outline, const Point* to)
119 {
120     assert(to);
121
122     _growOutlinePoint(outline, 1);
123
124     outline.pts[outline.ptsCnt] = TO_SWPOINT(to);
125     outline.types[outline.ptsCnt] = SW_CURVE_TYPE_POINT;
126     ++outline.ptsCnt;
127 }
128
129
130 static void _outlineCubicTo(SwOutline& outline, const Point* ctrl1, const Point* ctrl2, const Point* to)
131 {
132     assert(ctrl1 && ctrl2 && to);
133
134     _growOutlinePoint(outline, 3);
135
136     outline.pts[outline.ptsCnt] = TO_SWPOINT(ctrl1);
137     outline.types[outline.ptsCnt] = SW_CURVE_TYPE_CUBIC;
138     ++outline.ptsCnt;
139
140     outline.pts[outline.ptsCnt] = TO_SWPOINT(ctrl2);
141     outline.types[outline.ptsCnt] = SW_CURVE_TYPE_CUBIC;
142     ++outline.ptsCnt;
143
144     outline.pts[outline.ptsCnt] = TO_SWPOINT(to);
145     outline.types[outline.ptsCnt] = SW_CURVE_TYPE_POINT;
146     ++outline.ptsCnt;
147 }
148
149
150 static void _outlineClose(SwOutline& outline)
151 {
152     uint32_t i = 0;
153
154     if (outline.cntrsCnt > 0) {
155         i = outline.cntrs[outline.cntrsCnt - 1] + 1;
156     } else {
157         i = 0;   //First Path
158     }
159
160     //Make sure there is at least one point in the current path
161     if (outline.ptsCnt == i) {
162         outline.opened = true;
163         return;
164     }
165
166     //Close the path
167     _growOutlinePoint(outline, 1);
168
169     outline.pts[outline.ptsCnt] = outline.pts[i];
170     outline.types[outline.ptsCnt] = SW_CURVE_TYPE_POINT;
171     ++outline.ptsCnt;
172
173     outline.opened = false;
174 }
175
176
177 static void _initBBox(SwBBox& bbox)
178 {
179     bbox.min.x = bbox.min.y = 0;
180     bbox.max.x = bbox.max.y = 0;
181 }
182
183
184 static bool _updateBBox(SwOutline* outline, SwBBox& bbox)
185 {
186     if (!outline) return false;
187
188     auto pt = outline->pts;
189     assert(pt);
190
191     if (outline->ptsCnt <= 0) {
192         _initBBox(bbox);
193         return false;
194     }
195
196     auto xMin = pt->x;
197     auto xMax = pt->x;
198     auto yMin = pt->y;
199     auto yMax = pt->y;
200
201     ++pt;
202
203     for(uint32_t i = 1; i < outline->ptsCnt; ++i, ++pt) {
204         assert(pt);
205         if (xMin > pt->x) xMin = pt->x;
206         if (xMax < pt->x) xMax = pt->x;
207         if (yMin > pt->y) yMin = pt->y;
208         if (yMax < pt->y) yMax = pt->y;
209     }
210     bbox.min.x = xMin >> 6;
211     bbox.max.x = (xMax + 63) >> 6;
212     bbox.min.y = yMin >> 6;
213     bbox.max.y = (yMax + 63) >> 6;
214
215     if (xMax - xMin < 1 || yMax - yMin < 1) return false;
216
217     return true;
218 }
219
220
221 static bool _checkValid(const SwOutline* outline, const SwBBox& bbox, const SwSize& clip)
222 {
223     assert(outline);
224
225     if (outline->ptsCnt == 0 || outline->cntrsCnt <= 0) return false;
226
227     //Check boundary
228     if ((bbox.min.x > clip.w || bbox.min.y > clip.h) ||
229         (bbox.min.x + bbox.max.x < 0) ||
230         (bbox.min.y + bbox.max.y < 0)) return false;
231
232     return true;
233 }
234
235
236 static void _transformOutline(SwOutline* outline, const RenderTransform* transform)
237 {
238     assert(outline);
239
240     if (!transform) return;
241
242     for(uint32_t i = 0; i < outline->ptsCnt; ++i) {
243         auto dx = static_cast<float>(outline->pts[i].x >> 6);
244         auto dy = static_cast<float>(outline->pts[i].y >> 6);
245         auto tx = dx * transform->e11 + dy * transform->e12 + transform->e31;
246         auto ty = dx * transform->e21 + dy * transform->e22 + transform->e32;
247         auto pt = Point{tx, ty};
248         outline->pts[i] = TO_SWPOINT(&pt);
249     }
250 }
251
252
253 static void _dashLineTo(SwDashStroke& dash, const Point* to)
254 {
255     _growOutlinePoint(*dash.outline, dash.outline->ptsCnt >> 1);
256     _growOutlineContour(*dash.outline, dash.outline->cntrsCnt >> 1);
257
258     Line cur = {dash.ptCur, *to};
259     auto len = _lineLength(cur.pt1, cur.pt2);
260
261     if (len < dash.curLen) {
262         dash.curLen -= len;
263         if (!dash.curOpGap) {
264             _outlineMoveTo(*dash.outline, &dash.ptCur);
265             _outlineLineTo(*dash.outline, to);
266         }
267     } else {
268         while (len > dash.curLen) {
269             len -= dash.curLen;
270             Line left, right;
271             _lineSplitAt(cur, dash.curLen, left, right);;
272             dash.curIdx = (dash.curIdx + 1) % dash.cnt;
273             if (!dash.curOpGap) {
274                 _outlineMoveTo(*dash.outline, &left.pt1);
275                 _outlineLineTo(*dash.outline, &left.pt2);
276             }
277             dash.curLen = dash.pattern[dash.curIdx];
278             dash.curOpGap = !dash.curOpGap;
279             cur = right;
280             dash.ptCur = cur.pt1;
281         }
282         //leftovers
283         dash.curLen -= len;
284         if (!dash.curOpGap) {    
285             _outlineMoveTo(*dash.outline, &cur.pt1);
286             _outlineLineTo(*dash.outline, &cur.pt2);
287         }
288         if (dash.curLen < 1) {
289             //move to next dash
290             dash.curIdx = (dash.curIdx + 1) % dash.cnt;
291             dash.curLen = dash.pattern[dash.curIdx];
292             dash.curOpGap = !dash.curOpGap;
293         }
294     }
295     dash.ptCur = *to;
296 }
297
298
299 static void _dashCubicTo(SwDashStroke& dash, const Point* ctrl1, const Point* ctrl2, const Point* to)
300 {
301     _growOutlinePoint(*dash.outline, dash.outline->ptsCnt >> 1);
302     _growOutlineContour(*dash.outline, dash.outline->cntrsCnt >> 1);
303
304     Bezier cur = { dash.ptCur, *ctrl1, *ctrl2, *to};
305     auto len = bezLength(cur);
306
307     if (len < dash.curLen) {
308         dash.curLen -= len;
309         if (!dash.curOpGap) {
310             _outlineMoveTo(*dash.outline, &dash.ptCur);
311             _outlineCubicTo(*dash.outline, ctrl1, ctrl2, to);
312         }
313     } else {
314         while (len > dash.curLen) {
315             Bezier left, right;
316             len -= dash.curLen;
317             bezSplitAt(cur, dash.curLen, left, right);
318             dash.curIdx = (dash.curIdx + 1) % dash.cnt;
319             if (!dash.curOpGap) {
320                 _outlineMoveTo(*dash.outline, &left.start);
321                 _outlineCubicTo(*dash.outline, &left.ctrl1, &left.ctrl2, &left.end);
322             }
323             dash.curLen = dash.pattern[dash.curIdx];
324             dash.curOpGap = !dash.curOpGap;
325             cur = right;
326             dash.ptCur = right.start;
327         }
328         //leftovers
329         dash.curLen -= len;
330         if (!dash.curOpGap) {
331             _outlineMoveTo(*dash.outline, &cur.start);
332             _outlineCubicTo(*dash.outline, &cur.ctrl1, &cur.ctrl2, &cur.end);
333         }
334         if (dash.curLen < 1) {
335             //move to next dash
336             dash.curIdx = (dash.curIdx + 1) % dash.cnt;
337             dash.curLen = dash.pattern[dash.curIdx];
338             dash.curOpGap = !dash.curOpGap;
339         }
340     }
341     dash.ptCur = *to;
342 }
343
344
345 SwOutline* _genDashOutline(const Shape& shape)
346 {
347     const PathCommand* cmds = nullptr;
348     auto cmdCnt = shape.pathCommands(&cmds);
349
350     const Point* pts = nullptr;
351     auto ptsCnt = shape.pathCoords(&pts);
352
353     //No actual shape data
354     if (cmdCnt == 0 || ptsCnt == 0) return nullptr;
355
356     SwDashStroke dash;
357     dash.curIdx = 0;
358     dash.curLen = 0;
359     dash.ptStart = {0, 0};
360     dash.ptCur = {0, 0};
361     dash.curOpGap = false;
362
363     const float* pattern;
364     dash.cnt = shape.strokeDash(&pattern);
365     assert(dash.cnt > 0 && pattern);
366
367     //Is it safe to mutual exclusive?
368     dash.pattern = const_cast<float*>(pattern);
369     dash.outline = static_cast<SwOutline*>(calloc(1, sizeof(SwOutline)));
370     assert(dash.outline);
371     dash.outline->opened = true;
372
373     //smart reservation
374     auto outlinePtsCnt = 0;
375     auto outlineCntrsCnt = 0;
376
377     for (uint32_t i = 0; i < cmdCnt; ++i) {
378         switch(*(cmds + i)) {
379             case PathCommand::Close: {
380                 ++outlinePtsCnt;
381                 break;
382             }
383             case PathCommand::MoveTo: {
384                 ++outlineCntrsCnt;
385                 ++outlinePtsCnt;
386                 break;
387             }
388             case PathCommand::LineTo: {
389                 ++outlinePtsCnt;
390                 break;
391             }
392             case PathCommand::CubicTo: {
393                 outlinePtsCnt += 3;
394                 break;
395             }
396         }
397     }
398
399     ++outlinePtsCnt;    //for close
400     ++outlineCntrsCnt;  //for end
401
402     //Reserve Approximitely 20x...
403     _growOutlinePoint(*dash.outline, outlinePtsCnt * 20);
404     _growOutlineContour(*dash.outline, outlineCntrsCnt * 20);
405
406     while (cmdCnt-- > 0) {
407         switch(*cmds) {
408             case PathCommand::Close: {
409                 _dashLineTo(dash, &dash.ptStart);
410                 break;
411             }
412             case PathCommand::MoveTo: {
413                 //reset the dash
414                 dash.curIdx = 0;
415                 dash.curLen = *dash.pattern;
416                 dash.curOpGap = false;
417                 dash.ptStart = dash.ptCur = *pts;
418                 ++pts;
419                 break;
420             }
421             case PathCommand::LineTo: {
422                 _dashLineTo(dash, pts);
423                 ++pts;
424                 break;
425             }
426             case PathCommand::CubicTo: {
427                 _dashCubicTo(dash, pts, pts + 1, pts + 2);
428                 pts += 3;
429                 break;
430             }
431         }
432         ++cmds;
433     }
434
435     _outlineEnd(*dash.outline);
436
437     return dash.outline;
438 }
439
440
441 /************************************************************************/
442 /* External Class Implementation                                        */
443 /************************************************************************/
444
445 bool shapeGenRle(SwShape& shape, const Shape& sdata, const SwSize& clip, const RenderTransform* transform)
446 {
447     if (!shapeGenOutline(shape, sdata)) return false;
448
449     _transformOutline(shape.outline, transform);
450
451     if (!_updateBBox(shape.outline, shape.bbox)) goto end;
452
453     if (!_checkValid(shape.outline, shape.bbox, clip)) goto end;
454
455     shape.rle = rleRender(shape.outline, shape.bbox, clip);
456 end:
457     if (shape.rle) return true;
458     return false;
459 }
460
461
462 void shapeDelOutline(SwShape& shape)
463 {
464     auto outline = shape.outline;
465     _delOutline(outline);
466     shape.outline = nullptr;
467 }
468
469
470 void shapeReset(SwShape& shape)
471 {
472     shapeDelOutline(shape);
473     rleFree(shape.rle);
474     shape.rle = nullptr;
475     _initBBox(shape.bbox);
476 }
477
478
479 bool shapeGenOutline(SwShape& shape, const Shape& sdata)
480 {
481     const PathCommand* cmds = nullptr;
482     auto cmdCnt = sdata.pathCommands(&cmds);
483
484     const Point* pts = nullptr;
485     auto ptsCnt = sdata.pathCoords(&pts);
486
487     //No actual shape data
488     if (cmdCnt == 0 || ptsCnt == 0) return false;
489
490     //smart reservation
491     auto outlinePtsCnt = 0;
492     auto outlineCntrsCnt = 0;
493
494     for (uint32_t i = 0; i < cmdCnt; ++i) {
495         switch(*(cmds + i)) {
496             case PathCommand::Close: {
497                 ++outlinePtsCnt;
498                 break;
499             }
500             case PathCommand::MoveTo: {
501                 ++outlineCntrsCnt;
502                 ++outlinePtsCnt;
503                 break;
504             }
505             case PathCommand::LineTo: {
506                 ++outlinePtsCnt;
507                 break;
508             }
509             case PathCommand::CubicTo: {
510                 outlinePtsCnt += 3;
511                 break;
512             }
513         }
514     }
515
516     ++outlinePtsCnt;    //for close
517     ++outlineCntrsCnt;  //for end
518
519     auto outline = shape.outline;
520     if (!outline) outline = static_cast<SwOutline*>(calloc(1, sizeof(SwOutline)));
521     assert(outline);
522     outline->opened = true;
523
524     _growOutlinePoint(*outline, outlinePtsCnt);
525     _growOutlineContour(*outline, outlineCntrsCnt);
526
527     //Generate Outlines
528     while (cmdCnt-- > 0) {
529         switch(*cmds) {
530             case PathCommand::Close: {
531                 _outlineClose(*outline);
532                 break;
533             }
534             case PathCommand::MoveTo: {
535                 _outlineMoveTo(*outline, pts);
536                 ++pts;
537                 break;
538             }
539             case PathCommand::LineTo: {
540                 _outlineLineTo(*outline, pts);
541                 ++pts;
542                 break;
543             }
544             case PathCommand::CubicTo: {
545                 _outlineCubicTo(*outline, pts, pts + 1, pts + 2);
546                 pts += 3;
547                 break;
548             }
549         }
550         ++cmds;
551     }
552
553     _outlineEnd(*outline);
554
555     //FIXME:
556     //outline->flags = SwOutline::FillRule::Winding;
557
558     shape.outline = outline;
559
560     return true;
561 }
562
563
564 void shapeFree(SwShape* shape)
565 {
566     assert(shape);
567
568     shapeDelOutline(*shape);
569     rleFree(shape->rle);
570
571     if (shape->stroke) {
572         rleFree(shape->strokeRle);
573         strokeFree(shape->stroke);
574     }
575
576     free(shape);
577 }
578
579
580 void shapeDelStroke(SwShape& shape)
581 {
582     if (!shape.stroke) return;
583     rleFree(shape.strokeRle);
584     shape.strokeRle = nullptr;
585     strokeFree(shape.stroke);
586     shape.stroke = nullptr;
587 }
588
589
590 void shapeResetStroke(SwShape& shape, const Shape& sdata)
591 {
592     if (!shape.stroke) shape.stroke = static_cast<SwStroke*>(calloc(1, sizeof(SwStroke)));
593     auto stroke = shape.stroke;
594     assert(stroke);
595
596     strokeReset(*stroke, sdata);
597
598     rleFree(shape.strokeRle);
599     shape.strokeRle = nullptr;
600 }
601
602
603 bool shapeGenStrokeRle(SwShape& shape, const Shape& sdata, const SwSize& clip)
604 {
605     SwOutline* shapeOutline = nullptr;
606
607     //Dash Style Stroke
608     if (sdata.strokeDash(nullptr) > 0) {
609         shapeOutline = _genDashOutline(sdata);
610         if (!shapeOutline) return false;
611
612     //Normal Style stroke
613     } else {
614         if (!shape.outline) {
615             if (!shapeGenOutline(shape, sdata)) return false;
616         }
617         shapeOutline = shape.outline;
618     }
619
620     if (!strokeParseOutline(*shape.stroke, *shapeOutline)) return false;
621
622     auto strokeOutline = strokeExportOutline(*shape.stroke);
623     if (!strokeOutline) return false;
624
625     SwBBox bbox;
626     _updateBBox(strokeOutline, bbox);
627
628     if (!_checkValid(strokeOutline, bbox, clip)) return false;
629
630     shape.strokeRle = rleRender(strokeOutline, bbox, clip);
631
632     _delOutline(strokeOutline);
633
634     return true;
635 }
636
637
638 bool shapeGenFillColors(SwShape& shape, const Fill* fill, const RenderTransform* transform, bool ctable)
639 {
640     assert(fill);
641
642     fillGenColorTable(shape.fill, fill, transform, ctable);
643     return true;
644 }
645
646
647 void shapeResetFill(SwShape& shape, const Fill* fill)
648 {
649     assert(fill);
650
651     if (!shape.fill) shape.fill = static_cast<SwFill*>(calloc(1, sizeof(SwFill)));
652     assert(shape.fill);
653
654     fillReset(shape.fill, fill);
655 }
656
657
658 void shapeDelFill(SwShape& shape)
659 {
660     if (!shape.fill) return;
661     fillFree(shape.fill);
662     shape.fill = nullptr;
663 }
664
665
666 #endif /* _TVG_SW_SHAPE_H_ */