ef78f63b8f7d1419e35b8b4a9f6c396ae907bffe
[platform/core/uifw/lottie-player.git] / src / vector / vdasher.cpp
1 /*
2  * Copyright (c) 2018 Samsung Electronics Co., Ltd. All rights reserved.
3  *
4  * Licensed under the Flora License, Version 1.1 (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://floralicense.org/license/
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 #include "vdasher.h"
18 #include "vbezier.h"
19 #include "vline.h"
20
21 V_BEGIN_NAMESPACE
22
23 VDasher::VDasher(const float *dashArray, int size)
24 {
25     mDashArray = reinterpret_cast<const VDasher::Dash *>(dashArray);
26     mArraySize = size / 2;
27     if (size % 2)
28         mDashOffset = dashArray[size - 1];
29     mIndex = 0;
30     mCurrentLength = 0;
31     mDiscard = false;
32 }
33
34 void VDasher::moveTo(const VPointF &p)
35 {
36     mDiscard = false;
37     mStartNewSegment = true;
38     mCurPt = p;
39     mIndex = 0;
40
41     if (!vCompare(mDashOffset, 0.0f)) {
42         float totalLength = 0.0;
43         for (int i = 0; i < mArraySize; i++) {
44             totalLength = mDashArray[i].length + mDashArray[i].gap;
45         }
46         float normalizeLen = fmod(mDashOffset, totalLength);
47         if (normalizeLen < 0.0) {
48             normalizeLen = totalLength + normalizeLen;
49         }
50         // now the length is less than total length and +ve
51         // findout the current dash index , dashlength and gap.
52         for (int i = 0; i < mArraySize; i++) {
53             if (normalizeLen < mDashArray[i].length) {
54                 mIndex = i;
55                 mCurrentLength = mDashArray[i].length - normalizeLen;
56                 mDiscard = false;
57                 break;
58             }
59             normalizeLen -= mDashArray[i].length;
60             if (normalizeLen < mDashArray[i].gap) {
61                 mIndex = i;
62                 mCurrentLength = mDashArray[i].gap - normalizeLen;
63                 mDiscard = true;
64                 break;
65             }
66             normalizeLen -= mDashArray[i].gap;
67         }
68     } else {
69         mCurrentLength = mDashArray[mIndex].length;
70     }
71     if (vIsZero(mCurrentLength)) updateActiveSegment();
72 }
73
74 void VDasher::addLine(const VPointF &p)
75 {
76    if (mDiscard) return;
77
78    if (mStartNewSegment) {
79         mResult.moveTo(mCurPt);
80         mStartNewSegment = false;
81    }
82    mResult.lineTo(p);
83 }
84
85 void VDasher::updateActiveSegment()
86 {
87     mStartNewSegment = true;
88
89     if (mDiscard) {
90         mDiscard = false;
91         mIndex = (mIndex + 1) % mArraySize;
92         mCurrentLength = mDashArray[mIndex].length;
93     } else {
94         mDiscard = true;
95         mCurrentLength = mDashArray[mIndex].gap;
96     }
97     if (vIsZero(mCurrentLength)) updateActiveSegment();
98 }
99
100 void VDasher::lineTo(const VPointF &p)
101 {
102     VLine left, right;
103     VLine line(mCurPt, p);
104     float length = line.length();
105
106     if (length <= mCurrentLength) {
107         mCurrentLength -= length;
108         addLine(p);
109     } else {
110         while (length > mCurrentLength) {
111             length -= mCurrentLength;
112             line.splitAtLength(mCurrentLength, left, right);
113
114             addLine(left.p2());
115             updateActiveSegment();
116
117             line = right;
118             mCurPt = line.p1();
119         }
120         // handle remainder
121         if (length > 1.0) {
122             mCurrentLength -= length;
123             addLine(line.p2());
124         }
125     }
126
127     if (mCurrentLength < 1.0) updateActiveSegment();
128
129     mCurPt = p;
130 }
131
132 void VDasher::addCubic(const VPointF &cp1, const VPointF &cp2, const VPointF &e)
133 {
134     if (mDiscard) return;
135
136     if (mStartNewSegment) {
137         mResult.moveTo(mCurPt);
138         mStartNewSegment = false;
139     }
140     mResult.cubicTo(cp1, cp2, e);
141 }
142
143 void VDasher::cubicTo(const VPointF &cp1, const VPointF &cp2, const VPointF &e)
144 {
145     VBezier left, right;
146     float   bezLen = 0.0;
147     VBezier b = VBezier::fromPoints(mCurPt, cp1, cp2, e);
148     bezLen = b.length();
149
150     if (bezLen <= mCurrentLength) {
151         mCurrentLength -= bezLen;
152         addCubic(cp1, cp2, e);
153     } else {
154         while (bezLen > mCurrentLength) {
155             bezLen -= mCurrentLength;
156             b.splitAtLength(mCurrentLength, &left, &right);
157
158             addCubic(left.pt2(), left.pt3(), left.pt4());
159             updateActiveSegment();
160
161             b = right;
162             mCurPt = b.pt1();
163         }
164         // handle remainder
165         if (bezLen > 1.0) {
166             mCurrentLength -= bezLen;
167             addCubic(b.pt2(), b.pt3(), b.pt4());
168         }
169     }
170
171     if (mCurrentLength < 1.0) updateActiveSegment();
172
173     mCurPt = e;
174 }
175
176 VPath VDasher::dashed(const VPath &path)
177 {
178     if (path.empty()) return VPath();
179
180     mResult = VPath();
181     mIndex = 0;
182     const std::vector<VPath::Element> &elms = path.elements();
183     const std::vector<VPointF> &       pts = path.points();
184     const VPointF *                    ptPtr = pts.data();
185
186     for (auto &i : elms) {
187         switch (i) {
188         case VPath::Element::MoveTo: {
189             moveTo(*ptPtr++);
190             break;
191         }
192         case VPath::Element::LineTo: {
193             lineTo(*ptPtr++);
194             break;
195         }
196         case VPath::Element::CubicTo: {
197             cubicTo(*ptPtr, *(ptPtr + 1), *(ptPtr + 2));
198             ptPtr += 3;
199             break;
200         }
201         case VPath::Element::Close: {
202             // The point is already joined to start point in VPath
203             // no need to do anything here.
204             break;
205         }
206         default:
207             break;
208         }
209     }
210     return std::move(mResult);
211 }
212
213 V_END_NAMESPACE