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