2 * Copyright (c) 2020 Samsung Electronics Co., Ltd All Rights Reserved
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
8 * http://www.apache.org/licenses/LICENSE-2.0
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.
17 #ifndef _TVG_SW_SHAPE_H_
18 #define _TVG_SW_SHAPE_H_
20 #include "tvgSwCommon.h"
22 /************************************************************************/
23 /* Internal Class Implementation */
24 /************************************************************************/
42 static float _lineLength(const Point& pt1, const Point& pt2)
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);
54 static void _lineSplitAt(const Line& cur, float at, Line& left, Line& right)
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;
60 left.pt2.x = left.pt1.x + dx;
61 left.pt2.y = left.pt1.y + dy;
67 static void _bezSplit(const Bezier&cur, Bezier& left, Bezier& right)
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;
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;
89 static float _bezLength(const Bezier& cur)
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);
95 if (fabs(len - chord) > FLT_EPSILON) {
96 _bezSplit(cur, left, right);
97 return _bezLength(left) + _bezLength(right);
103 static void _bezSplitLeft(Bezier& cur, float at, Bezier& left)
105 left.start = cur.start;
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);
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
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);
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);
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);
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);
127 static float _bezAt(const Bezier& bz, float at)
129 auto len = _bezLength(bz);
132 if (at >= len) return 1.0f;
139 _bezSplitLeft(right, at, left);
140 auto len2 = _bezLength(left);
142 if (fabs(len2 - len) < FLT_EPSILON) break;
145 at += (biggest - at) * 0.5f;
155 static void _bezSplitAt(const Bezier& cur, float at, Bezier& left, Bezier& right)
158 auto t = _bezAt(right, at);
159 _bezSplitLeft(right, t, left);
163 static void _growOutlineContour(SwOutline& outline, uint32_t n)
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);
172 static void _growOutlinePoint(SwOutline& outline, uint32_t n)
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)));
178 outline.types = static_cast<uint8_t*>(realloc(outline.types, outline.reservedPtsCnt * sizeof(uint8_t)));
179 assert(outline.types);
183 static void _delOutline(SwOutline* outline)
185 if (!outline) return;
187 if (outline->cntrs) free(outline->cntrs);
188 if (outline->pts) free(outline->pts);
189 if (outline->types) free(outline->types);
194 static void _outlineEnd(SwOutline& outline)
196 _growOutlineContour(outline, 1);
197 if (outline.ptsCnt > 0) {
198 outline.cntrs[outline.cntrsCnt] = outline.ptsCnt - 1;
204 static void _outlineMoveTo(SwOutline& outline, const Point* to)
208 _growOutlinePoint(outline, 1);
210 outline.pts[outline.ptsCnt] = TO_SWPOINT(to);
211 outline.types[outline.ptsCnt] = SW_CURVE_TYPE_POINT;
213 if (outline.ptsCnt > 0) {
214 _growOutlineContour(outline, 1);
215 outline.cntrs[outline.cntrsCnt] = outline.ptsCnt - 1;
223 static void _outlineLineTo(SwOutline& outline, const Point* to)
227 _growOutlinePoint(outline, 1);
229 outline.pts[outline.ptsCnt] = TO_SWPOINT(to);
230 outline.types[outline.ptsCnt] = SW_CURVE_TYPE_POINT;
235 static void _outlineCubicTo(SwOutline& outline, const Point* ctrl1, const Point* ctrl2, const Point* to)
237 assert(ctrl1 && ctrl2 && to);
239 _growOutlinePoint(outline, 3);
241 outline.pts[outline.ptsCnt] = TO_SWPOINT(ctrl1);
242 outline.types[outline.ptsCnt] = SW_CURVE_TYPE_CUBIC;
245 outline.pts[outline.ptsCnt] = TO_SWPOINT(ctrl2);
246 outline.types[outline.ptsCnt] = SW_CURVE_TYPE_CUBIC;
249 outline.pts[outline.ptsCnt] = TO_SWPOINT(to);
250 outline.types[outline.ptsCnt] = SW_CURVE_TYPE_POINT;
255 static void _outlineClose(SwOutline& outline)
259 if (outline.cntrsCnt > 0) {
260 i = outline.cntrs[outline.cntrsCnt - 1] + 1;
265 //Make sure there is at least one point in the current path
266 if (outline.ptsCnt == i) {
267 outline.opened = true;
272 _growOutlinePoint(outline, 1);
274 outline.pts[outline.ptsCnt] = outline.pts[i];
275 outline.types[outline.ptsCnt] = SW_CURVE_TYPE_POINT;
278 outline.opened = false;
282 static void _initBBox(SwBBox& bbox)
284 bbox.min.x = bbox.min.y = 0;
285 bbox.max.x = bbox.max.y = 0;
289 static bool _updateBBox(SwOutline* outline, SwBBox& bbox)
291 if (!outline) return false;
293 auto pt = outline->pts;
296 if (outline->ptsCnt <= 0) {
308 for(uint32_t i = 1; i < outline->ptsCnt; ++i, ++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;
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;
320 if (xMax - xMin < 1 || yMax - yMin < 1) return false;
326 static bool _checkValid(const SwOutline* outline, const SwBBox& bbox, const SwSize& clip)
330 if (outline->ptsCnt == 0 || outline->cntrsCnt <= 0) return false;
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;
341 static void _transformOutline(SwOutline* outline, const RenderTransform* transform)
345 if (!transform) return;
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);
358 static void _dashLineTo(SwDashStroke& dash, const Point* to)
360 _growOutlinePoint(*dash.outline, dash.outline->ptsCnt >> 1);
361 _growOutlineContour(*dash.outline, dash.outline->cntrsCnt >> 1);
363 Line cur = {dash.ptCur, *to};
364 auto len = _lineLength(cur.pt1, cur.pt2);
366 if (len < dash.curLen) {
368 if (!dash.curOpGap) {
369 _outlineMoveTo(*dash.outline, &dash.ptCur);
370 _outlineLineTo(*dash.outline, to);
373 while (len > dash.curLen) {
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);
382 dash.curLen = dash.pattern[dash.curIdx];
383 dash.curOpGap = !dash.curOpGap;
385 dash.ptCur = cur.pt1;
389 if (!dash.curOpGap) {
390 _outlineMoveTo(*dash.outline, &cur.pt1);
391 _outlineLineTo(*dash.outline, &cur.pt2);
393 if (dash.curLen < 1) {
395 dash.curIdx = (dash.curIdx + 1) % dash.cnt;
396 dash.curLen = dash.pattern[dash.curIdx];
397 dash.curOpGap = !dash.curOpGap;
404 static void _dashCubicTo(SwDashStroke& dash, const Point* ctrl1, const Point* ctrl2, const Point* to)
406 _growOutlinePoint(*dash.outline, dash.outline->ptsCnt >> 1);
407 _growOutlineContour(*dash.outline, dash.outline->cntrsCnt >> 1);
409 Bezier cur = { dash.ptCur, *ctrl1, *ctrl2, *to};
410 auto len = _bezLength(cur);
412 if (len < dash.curLen) {
414 if (!dash.curOpGap) {
415 _outlineMoveTo(*dash.outline, &dash.ptCur);
416 _outlineCubicTo(*dash.outline, ctrl1, ctrl2, to);
419 while (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);
428 dash.curLen = dash.pattern[dash.curIdx];
429 dash.curOpGap = !dash.curOpGap;
431 dash.ptCur = right.start;
435 if (!dash.curOpGap) {
436 _outlineMoveTo(*dash.outline, &cur.start);
437 _outlineCubicTo(*dash.outline, &cur.ctrl1, &cur.ctrl2, &cur.end);
439 if (dash.curLen < 1) {
441 dash.curIdx = (dash.curIdx + 1) % dash.cnt;
442 dash.curLen = dash.pattern[dash.curIdx];
443 dash.curOpGap = !dash.curOpGap;
450 SwOutline* _genDashOutline(const Shape& shape)
452 const PathCommand* cmds = nullptr;
453 auto cmdCnt = shape.pathCommands(&cmds);
455 const Point* pts = nullptr;
456 auto ptsCnt = shape.pathCoords(&pts);
458 //No actual shape data
459 if (cmdCnt == 0 || ptsCnt == 0) return nullptr;
464 dash.ptStart = {0, 0};
466 dash.curOpGap = false;
468 const float* pattern;
469 dash.cnt = shape.strokeDash(&pattern);
470 assert(dash.cnt > 0 && pattern);
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;
479 auto outlinePtsCnt = 0;
480 auto outlineCntrsCnt = 0;
482 for (uint32_t i = 0; i < cmdCnt; ++i) {
483 switch(*(cmds + i)) {
484 case PathCommand::Close: {
488 case PathCommand::MoveTo: {
493 case PathCommand::LineTo: {
497 case PathCommand::CubicTo: {
504 ++outlinePtsCnt; //for close
505 ++outlineCntrsCnt; //for end
507 //Reserve Approximitely 20x...
508 _growOutlinePoint(*dash.outline, outlinePtsCnt * 20);
509 _growOutlineContour(*dash.outline, outlineCntrsCnt * 20);
511 while (cmdCnt-- > 0) {
513 case PathCommand::Close: {
514 _dashLineTo(dash, &dash.ptStart);
517 case PathCommand::MoveTo: {
520 dash.curLen = *dash.pattern;
521 dash.curOpGap = false;
522 dash.ptStart = dash.ptCur = *pts;
526 case PathCommand::LineTo: {
527 _dashLineTo(dash, pts);
531 case PathCommand::CubicTo: {
532 _dashCubicTo(dash, pts, pts + 1, pts + 2);
540 _outlineEnd(*dash.outline);
546 /************************************************************************/
547 /* External Class Implementation */
548 /************************************************************************/
550 bool shapeGenRle(SwShape& shape, const Shape& sdata, const SwSize& clip, const RenderTransform* transform)
552 if (!shapeGenOutline(shape, sdata)) return false;
554 _transformOutline(shape.outline, transform);
556 if (!_updateBBox(shape.outline, shape.bbox)) goto end;
558 if (!_checkValid(shape.outline, shape.bbox, clip)) goto end;
560 shape.rle = rleRender(shape.outline, shape.bbox, clip);
562 if (shape.rle) return true;
567 void shapeDelOutline(SwShape& shape)
569 auto outline = shape.outline;
570 _delOutline(outline);
571 shape.outline = nullptr;
575 void shapeReset(SwShape& shape)
577 shapeDelOutline(shape);
580 _initBBox(shape.bbox);
584 bool shapeGenOutline(SwShape& shape, const Shape& sdata)
586 const PathCommand* cmds = nullptr;
587 auto cmdCnt = sdata.pathCommands(&cmds);
589 const Point* pts = nullptr;
590 auto ptsCnt = sdata.pathCoords(&pts);
592 //No actual shape data
593 if (cmdCnt == 0 || ptsCnt == 0) return false;
596 auto outlinePtsCnt = 0;
597 auto outlineCntrsCnt = 0;
599 for (uint32_t i = 0; i < cmdCnt; ++i) {
600 switch(*(cmds + i)) {
601 case PathCommand::Close: {
605 case PathCommand::MoveTo: {
610 case PathCommand::LineTo: {
614 case PathCommand::CubicTo: {
621 ++outlinePtsCnt; //for close
622 ++outlineCntrsCnt; //for end
624 auto outline = shape.outline;
625 if (!outline) outline = static_cast<SwOutline*>(calloc(1, sizeof(SwOutline)));
627 outline->opened = true;
629 _growOutlinePoint(*outline, outlinePtsCnt);
630 _growOutlineContour(*outline, outlineCntrsCnt);
633 while (cmdCnt-- > 0) {
635 case PathCommand::Close: {
636 _outlineClose(*outline);
639 case PathCommand::MoveTo: {
640 _outlineMoveTo(*outline, pts);
644 case PathCommand::LineTo: {
645 _outlineLineTo(*outline, pts);
649 case PathCommand::CubicTo: {
650 _outlineCubicTo(*outline, pts, pts + 1, pts + 2);
658 _outlineEnd(*outline);
661 //outline->flags = SwOutline::FillRule::Winding;
663 shape.outline = outline;
669 void shapeFree(SwShape* shape)
673 shapeDelOutline(*shape);
677 rleFree(shape->strokeRle);
678 strokeFree(shape->stroke);
685 void shapeDelStroke(SwShape& shape)
687 if (!shape.stroke) return;
688 rleFree(shape.strokeRle);
689 shape.strokeRle = nullptr;
690 strokeFree(shape.stroke);
691 shape.stroke = nullptr;
695 void shapeResetStroke(SwShape& shape, const Shape& sdata)
697 if (!shape.stroke) shape.stroke = static_cast<SwStroke*>(calloc(1, sizeof(SwStroke)));
698 auto stroke = shape.stroke;
701 strokeReset(*stroke, sdata);
703 rleFree(shape.strokeRle);
704 shape.strokeRle = nullptr;
708 bool shapeGenStrokeRle(SwShape& shape, const Shape& sdata, const SwSize& clip)
710 SwOutline* shapeOutline = nullptr;
713 if (sdata.strokeDash(nullptr) > 0) {
714 shapeOutline = _genDashOutline(sdata);
715 if (!shapeOutline) return false;
717 //Normal Style stroke
719 if (!shape.outline) {
720 if (!shapeGenOutline(shape, sdata)) return false;
722 shapeOutline = shape.outline;
725 if (!strokeParseOutline(*shape.stroke, *shapeOutline)) return false;
727 auto strokeOutline = strokeExportOutline(*shape.stroke);
728 if (!strokeOutline) return false;
731 _updateBBox(strokeOutline, bbox);
733 if (!_checkValid(strokeOutline, bbox, clip)) return false;
735 shape.strokeRle = rleRender(strokeOutline, bbox, clip);
737 _delOutline(strokeOutline);
743 bool shapeGenFillColors(SwShape& shape, const Fill* fill)
747 fillGenColorTable(shape.fill, fill);
752 void shapeResetFill(SwShape& shape, const Fill* fill)
756 if (!shape.fill) shape.fill = static_cast<SwFill*>(calloc(1, sizeof(SwFill)));
759 fillReset(shape.fill, fill);
763 void shapeDelFill(SwShape& shape)
765 if (!shape.fill) return;
766 fillFree(shape.fill);
767 shape.fill = nullptr;
771 #endif /* _TVG_SW_SHAPE_H_ */