sw_engine: optimize othogonal rectangle drawing.
[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         if (xMin > pt->x) xMin = pt->x;
205         if (xMax < pt->x) xMax = pt->x;
206         if (yMin > pt->y) yMin = pt->y;
207         if (yMax < pt->y) yMax = pt->y;
208     }
209     bbox.min.x = xMin >> 6;
210     bbox.max.x = (xMax + 63) >> 6;
211     bbox.min.y = yMin >> 6;
212     bbox.max.y = (yMax + 63) >> 6;
213
214     if (xMax - xMin < 1 && yMax - yMin < 1) return false;
215
216     return true;
217 }
218
219
220 static bool _checkValid(const SwOutline* outline, const SwBBox& bbox, const SwSize& clip)
221 {
222     assert(outline);
223
224     if (outline->ptsCnt == 0 || outline->cntrsCnt <= 0) return false;
225
226     //Check boundary
227     if (bbox.min.x >= clip.w || bbox.min.y >= clip.h || bbox.max.x <= 0 || bbox.max.y <= 0) return false;
228
229     return true;
230 }
231
232
233 static void _transformOutline(SwOutline* outline, const Matrix* transform)
234 {
235     if (!transform) return;
236
237     assert(outline);
238
239     for(uint32_t i = 0; i < outline->ptsCnt; ++i) {
240         auto dx = static_cast<float>(outline->pts[i].x >> 6);
241         auto dy = static_cast<float>(outline->pts[i].y >> 6);
242         auto tx = dx * transform->e11 + dy * transform->e12 + transform->e31;
243         auto ty = dx * transform->e21 + dy * transform->e22 + transform->e32;
244         auto pt = Point{round(tx), round(ty)};
245         outline->pts[i] = TO_SWPOINT(&pt);
246     }
247 }
248
249
250 static void _dashLineTo(SwDashStroke& dash, const Point* to)
251 {
252     _growOutlinePoint(*dash.outline, dash.outline->ptsCnt >> 1);
253     _growOutlineContour(*dash.outline, dash.outline->cntrsCnt >> 1);
254
255     Line cur = {dash.ptCur, *to};
256     auto len = _lineLength(cur.pt1, cur.pt2);
257
258     if (len < dash.curLen) {
259         dash.curLen -= len;
260         if (!dash.curOpGap) {
261             _outlineMoveTo(*dash.outline, &dash.ptCur);
262             _outlineLineTo(*dash.outline, to);
263         }
264     } else {
265         while (len > dash.curLen) {
266             len -= dash.curLen;
267             Line left, right;
268             _lineSplitAt(cur, dash.curLen, left, right);;
269             dash.curIdx = (dash.curIdx + 1) % dash.cnt;
270             if (!dash.curOpGap) {
271                 _outlineMoveTo(*dash.outline, &left.pt1);
272                 _outlineLineTo(*dash.outline, &left.pt2);
273             }
274             dash.curLen = dash.pattern[dash.curIdx];
275             dash.curOpGap = !dash.curOpGap;
276             cur = right;
277             dash.ptCur = cur.pt1;
278         }
279         //leftovers
280         dash.curLen -= len;
281         if (!dash.curOpGap) {    
282             _outlineMoveTo(*dash.outline, &cur.pt1);
283             _outlineLineTo(*dash.outline, &cur.pt2);
284         }
285         if (dash.curLen < 1) {
286             //move to next dash
287             dash.curIdx = (dash.curIdx + 1) % dash.cnt;
288             dash.curLen = dash.pattern[dash.curIdx];
289             dash.curOpGap = !dash.curOpGap;
290         }
291     }
292     dash.ptCur = *to;
293 }
294
295
296 static void _dashCubicTo(SwDashStroke& dash, const Point* ctrl1, const Point* ctrl2, const Point* to)
297 {
298     _growOutlinePoint(*dash.outline, dash.outline->ptsCnt >> 1);
299     _growOutlineContour(*dash.outline, dash.outline->cntrsCnt >> 1);
300
301     Bezier cur = { dash.ptCur, *ctrl1, *ctrl2, *to};
302     auto len = bezLength(cur);
303
304     if (len < dash.curLen) {
305         dash.curLen -= len;
306         if (!dash.curOpGap) {
307             _outlineMoveTo(*dash.outline, &dash.ptCur);
308             _outlineCubicTo(*dash.outline, ctrl1, ctrl2, to);
309         }
310     } else {
311         while (len > dash.curLen) {
312             Bezier left, right;
313             len -= dash.curLen;
314             bezSplitAt(cur, dash.curLen, left, right);
315             dash.curIdx = (dash.curIdx + 1) % dash.cnt;
316             if (!dash.curOpGap) {
317                 _outlineMoveTo(*dash.outline, &left.start);
318                 _outlineCubicTo(*dash.outline, &left.ctrl1, &left.ctrl2, &left.end);
319             }
320             dash.curLen = dash.pattern[dash.curIdx];
321             dash.curOpGap = !dash.curOpGap;
322             cur = right;
323             dash.ptCur = right.start;
324         }
325         //leftovers
326         dash.curLen -= len;
327         if (!dash.curOpGap) {
328             _outlineMoveTo(*dash.outline, &cur.start);
329             _outlineCubicTo(*dash.outline, &cur.ctrl1, &cur.ctrl2, &cur.end);
330         }
331         if (dash.curLen < 1) {
332             //move to next dash
333             dash.curIdx = (dash.curIdx + 1) % dash.cnt;
334             dash.curLen = dash.pattern[dash.curIdx];
335             dash.curOpGap = !dash.curOpGap;
336         }
337     }
338     dash.ptCur = *to;
339 }
340
341
342 SwOutline* _genDashOutline(const Shape* sdata)
343 {
344     assert(sdata);
345
346     const PathCommand* cmds = nullptr;
347     auto cmdCnt = sdata->pathCommands(&cmds);
348
349     const Point* pts = nullptr;
350     auto ptsCnt = sdata->pathCoords(&pts);
351
352     //No actual shape data
353     if (cmdCnt == 0 || ptsCnt == 0) return nullptr;
354
355     SwDashStroke dash;
356     dash.curIdx = 0;
357     dash.curLen = 0;
358     dash.ptStart = {0, 0};
359     dash.ptCur = {0, 0};
360     dash.curOpGap = false;
361
362     const float* pattern;
363     dash.cnt = sdata->strokeDash(&pattern);
364     assert(dash.cnt > 0 && pattern);
365
366     //Is it safe to mutual exclusive?
367     dash.pattern = const_cast<float*>(pattern);
368     dash.outline = static_cast<SwOutline*>(calloc(1, sizeof(SwOutline)));
369     assert(dash.outline);
370     dash.outline->opened = true;
371
372     //smart reservation
373     auto outlinePtsCnt = 0;
374     auto outlineCntrsCnt = 0;
375
376     for (uint32_t i = 0; i < cmdCnt; ++i) {
377         switch(*(cmds + i)) {
378             case PathCommand::Close: {
379                 ++outlinePtsCnt;
380                 break;
381             }
382             case PathCommand::MoveTo: {
383                 ++outlineCntrsCnt;
384                 ++outlinePtsCnt;
385                 break;
386             }
387             case PathCommand::LineTo: {
388                 ++outlinePtsCnt;
389                 break;
390             }
391             case PathCommand::CubicTo: {
392                 outlinePtsCnt += 3;
393                 break;
394             }
395         }
396     }
397
398     ++outlinePtsCnt;    //for close
399     ++outlineCntrsCnt;  //for end
400
401     //Reserve Approximitely 20x...
402     _growOutlinePoint(*dash.outline, outlinePtsCnt * 20);
403     _growOutlineContour(*dash.outline, outlineCntrsCnt * 20);
404
405     while (cmdCnt-- > 0) {
406         switch(*cmds) {
407             case PathCommand::Close: {
408                 _dashLineTo(dash, &dash.ptStart);
409                 break;
410             }
411             case PathCommand::MoveTo: {
412                 //reset the dash
413                 dash.curIdx = 0;
414                 dash.curLen = *dash.pattern;
415                 dash.curOpGap = false;
416                 dash.ptStart = dash.ptCur = *pts;
417                 ++pts;
418                 break;
419             }
420             case PathCommand::LineTo: {
421                 _dashLineTo(dash, pts);
422                 ++pts;
423                 break;
424             }
425             case PathCommand::CubicTo: {
426                 _dashCubicTo(dash, pts, pts + 1, pts + 2);
427                 pts += 3;
428                 break;
429             }
430         }
431         ++cmds;
432     }
433
434     _outlineEnd(*dash.outline);
435
436     return dash.outline;
437 }
438
439
440 bool _fastTrack(const SwOutline* outline)
441 {
442     //Fast Track: Othogonal rectangle?
443     if (outline->ptsCnt != 5) return false;
444
445     auto pt1 = outline->pts + 0;
446     auto pt2 = outline->pts + 1;
447     auto pt3 = outline->pts + 2;
448     auto pt4 = outline->pts + 3;
449
450     auto min1 = pt1->y < pt3->y ? pt1 : pt3;
451     auto min2 = pt2->y < pt4->y ? pt2 : pt4;
452     if (min1->y != min2->y) return false;
453
454     SwCoord len1 = pow(pt1->x - pt3->x, 2) + pow(pt1->y - pt3->y, 2);
455     SwCoord len2 = pow(pt2->x - pt4->x, 2) + pow(pt2->y - pt4->y, 2);
456     if (len1 == len2) return true;
457
458     return false;
459 }
460
461
462 /************************************************************************/
463 /* External Class Implementation                                        */
464 /************************************************************************/
465
466 bool shapeGenRle(SwShape& shape, const Shape* sdata, const SwSize& clip, const Matrix* transform)
467 {
468     if (!shapeGenOutline(shape, sdata)) return false;
469
470     _transformOutline(shape.outline, transform);
471
472     if (!_updateBBox(shape.outline, shape.bbox)) goto end;
473
474     if (!_checkValid(shape.outline, shape.bbox, clip)) goto end;
475
476     //Case: Fast Track Rectangle Drawing
477     if ((shape.rect = _fastTrack(shape.outline))) return true;
478
479     //Case: Stroke Line
480     if (shape.outline->opened) return true;
481
482     shape.rle = rleRender(shape.outline, shape.bbox, clip);
483 end:
484     if (shape.rle) return true;
485     return false;
486 }
487
488
489 void shapeDelOutline(SwShape& shape)
490 {
491     auto outline = shape.outline;
492     _delOutline(outline);
493     shape.outline = nullptr;
494 }
495
496
497 void shapeReset(SwShape& shape)
498 {
499     shapeDelOutline(shape);
500     rleFree(shape.rle);
501     shape.rle = nullptr;
502     shape.rect = false;
503     _initBBox(shape.bbox);
504 }
505
506
507 bool shapeGenOutline(SwShape& shape, const Shape* sdata)
508 {
509     assert(sdata);
510
511     const PathCommand* cmds = nullptr;
512     auto cmdCnt = sdata->pathCommands(&cmds);
513
514     const Point* pts = nullptr;
515     auto ptsCnt = sdata->pathCoords(&pts);
516
517     //No actual shape data
518     if (cmdCnt == 0 || ptsCnt == 0) return false;
519
520     //smart reservation
521     auto outlinePtsCnt = 0;
522     auto outlineCntrsCnt = 0;
523
524     for (uint32_t i = 0; i < cmdCnt; ++i) {
525         switch(*(cmds + i)) {
526             case PathCommand::Close: {
527                 ++outlinePtsCnt;
528                 break;
529             }
530             case PathCommand::MoveTo: {
531                 ++outlineCntrsCnt;
532                 ++outlinePtsCnt;
533                 break;
534             }
535             case PathCommand::LineTo: {
536                 ++outlinePtsCnt;
537                 break;
538             }
539             case PathCommand::CubicTo: {
540                 outlinePtsCnt += 3;
541                 break;
542             }
543         }
544     }
545
546     ++outlinePtsCnt;    //for close
547     ++outlineCntrsCnt;  //for end
548
549     auto outline = shape.outline;
550     if (!outline) outline = static_cast<SwOutline*>(calloc(1, sizeof(SwOutline)));
551     assert(outline);
552     outline->opened = true;
553
554     _growOutlinePoint(*outline, outlinePtsCnt);
555     _growOutlineContour(*outline, outlineCntrsCnt);
556
557     auto closed = false;
558
559     //Generate Outlines
560     while (cmdCnt-- > 0) {
561         switch(*cmds) {
562             case PathCommand::Close: {
563                 _outlineClose(*outline);
564                 closed = true;
565                 break;
566             }
567             case PathCommand::MoveTo: {
568                 _outlineMoveTo(*outline, pts);
569                 ++pts;
570                 break;
571             }
572             case PathCommand::LineTo: {
573                 _outlineLineTo(*outline, pts);
574                 ++pts;
575                 break;
576             }
577             case PathCommand::CubicTo: {
578                 _outlineCubicTo(*outline, pts, pts + 1, pts + 2);
579                 pts += 3;
580                 break;
581             }
582         }
583         ++cmds;
584     }
585
586     _outlineEnd(*outline);
587
588     if (closed) outline->opened = false;
589
590     //FIXME:
591     //outline->flags = SwOutline::FillRule::Winding;
592
593     shape.outline = outline;
594
595     return true;
596 }
597
598
599 void shapeFree(SwShape& shape)
600 {
601     shapeDelOutline(shape);
602     rleFree(shape.rle);
603     shapeDelFill(shape);
604
605     if (shape.stroke) {
606         rleFree(shape.strokeRle);
607         strokeFree(shape.stroke);
608     }
609 }
610
611
612 void shapeDelStroke(SwShape& shape)
613 {
614     if (!shape.stroke) return;
615     rleFree(shape.strokeRle);
616     shape.strokeRle = nullptr;
617     strokeFree(shape.stroke);
618     shape.stroke = nullptr;
619 }
620
621
622 void shapeResetStroke(SwShape& shape, const Shape* sdata)
623 {
624     if (!shape.stroke) shape.stroke = static_cast<SwStroke*>(calloc(1, sizeof(SwStroke)));
625     auto stroke = shape.stroke;
626     assert(stroke);
627
628     strokeReset(*stroke, sdata);
629
630     rleFree(shape.strokeRle);
631     shape.strokeRle = nullptr;
632 }
633
634
635 bool shapeGenStrokeRle(SwShape& shape, const Shape* sdata, const SwSize& clip)
636 {
637     assert(sdata);
638
639     SwOutline* shapeOutline = nullptr;
640
641     //Dash Style Stroke
642     if (sdata->strokeDash(nullptr) > 0) {
643         shapeOutline = _genDashOutline(sdata);
644         if (!shapeOutline) return false;
645
646     //Normal Style stroke
647     } else {
648         if (!shape.outline) {
649             if (!shapeGenOutline(shape, sdata)) return false;
650         }
651         shapeOutline = shape.outline;
652     }
653
654     if (!strokeParseOutline(*shape.stroke, *shapeOutline)) return false;
655
656     auto strokeOutline = strokeExportOutline(*shape.stroke);
657     if (!strokeOutline) return false;
658
659     SwBBox bbox;
660     _updateBBox(strokeOutline, bbox);
661
662     if (!_checkValid(strokeOutline, bbox, clip)) return false;
663
664     shape.strokeRle = rleRender(strokeOutline, bbox, clip);
665
666     _delOutline(strokeOutline);
667
668     return true;
669 }
670
671
672 bool shapeGenFillColors(SwShape& shape, const Fill* fill, const Matrix* transform, bool ctable)
673 {
674     return fillGenColorTable(shape.fill, fill, transform, ctable);
675 }
676
677
678 void shapeResetFill(SwShape& shape)
679 {
680     if (!shape.fill) shape.fill = static_cast<SwFill*>(calloc(1, sizeof(SwFill)));
681     assert(shape.fill);
682
683     fillReset(shape.fill);
684 }
685
686
687 void shapeDelFill(SwShape& shape)
688 {
689     if (!shape.fill) return;
690     fillFree(shape.fill);
691     shape.fill = nullptr;
692 }
693
694
695 #endif /* _TVG_SW_SHAPE_H_ */