1 /*
2 * Copyright (c) 2020 - 2024 the ThorVG project. All rights reserved.
3
4 * Permission is hereby granted, free of charge, to any person obtaining a copy
5 * of this software and associated documentation files (the "Software"), to deal
6 * in the Software without restriction, including without limitation the rights
7 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8 * copies of the Software, and to permit persons to whom the Software is
9 * furnished to do so, subject to the following conditions:
10
11 * The above copyright notice and this permission notice shall be included in all
12 * copies or substantial portions of the Software.
13
14 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
20 * SOFTWARE.
21 */
22
23 #include "../../lv_conf_internal.h"
24 #if LV_USE_THORVG_INTERNAL
25
26 #include "tvgMath.h"
27 #include "tvgSwCommon.h"
28
29
30 /************************************************************************/
31 /* Internal Class Implementation */
32 /************************************************************************/
33
TO_RADIAN(SwFixed angle)34 static float TO_RADIAN(SwFixed angle)
35 {
36 return (float(angle) / 65536.0f) * (MATH_PI / 180.0f);
37 }
38
39
40 /************************************************************************/
41 /* External Class Implementation */
42 /************************************************************************/
43
mathMean(SwFixed angle1,SwFixed angle2)44 SwFixed mathMean(SwFixed angle1, SwFixed angle2)
45 {
46 return angle1 + mathDiff(angle1, angle2) / 2;
47 }
48
49
mathCubicAngle(const SwPoint * base,SwFixed & angleIn,SwFixed & angleMid,SwFixed & angleOut)50 int mathCubicAngle(const SwPoint* base, SwFixed& angleIn, SwFixed& angleMid, SwFixed& angleOut)
51 {
52 auto d1 = base[2] - base[3];
53 auto d2 = base[1] - base[2];
54 auto d3 = base[0] - base[1];
55
56 if (d1.small()) {
57 if (d2.small()) {
58 if (d3.small()) {
59 angleIn = angleMid = angleOut = 0;
60 return -1; //ignoreable
61 } else {
62 angleIn = angleMid = angleOut = mathAtan(d3);
63 }
64 } else {
65 if (d3.small()) {
66 angleIn = angleMid = angleOut = mathAtan(d2);
67 } else {
68 angleIn = angleMid = mathAtan(d2);
69 angleOut = mathAtan(d3);
70 }
71 }
72 } else {
73 if (d2.small()) {
74 if (d3.small()) {
75 angleIn = angleMid = angleOut = mathAtan(d1);
76 } else {
77 angleIn = mathAtan(d1);
78 angleOut = mathAtan(d3);
79 angleMid = mathMean(angleIn, angleOut);
80 }
81 } else {
82 if (d3.small()) {
83 angleIn = mathAtan(d1);
84 angleMid = angleOut = mathAtan(d2);
85 } else {
86 angleIn = mathAtan(d1);
87 angleMid = mathAtan(d2);
88 angleOut = mathAtan(d3);
89 }
90 }
91 }
92
93 auto theta1 = abs(mathDiff(angleIn, angleMid));
94 auto theta2 = abs(mathDiff(angleMid, angleOut));
95
96 if ((theta1 < (SW_ANGLE_PI / 8)) && (theta2 < (SW_ANGLE_PI / 8))) return 0; //small size
97 return 1;
98 }
99
100
mathMultiply(int64_t a,int64_t b)101 int64_t mathMultiply(int64_t a, int64_t b)
102 {
103 int32_t s = 1;
104
105 //move sign
106 if (a < 0) {
107 a = -a;
108 s = -s;
109 }
110 if (b < 0) {
111 b = -b;
112 s = -s;
113 }
114 int64_t c = (a * b + 0x8000L) >> 16;
115 return (s > 0) ? c : -c;
116 }
117
118
mathDivide(int64_t a,int64_t b)119 int64_t mathDivide(int64_t a, int64_t b)
120 {
121 int32_t s = 1;
122
123 //move sign
124 if (a < 0) {
125 a = -a;
126 s = -s;
127 }
128 if (b < 0) {
129 b = -b;
130 s = -s;
131 }
132 int64_t q = b > 0 ? ((a << 16) + (b >> 1)) / b : 0x7FFFFFFFL;
133 return (s < 0 ? -q : q);
134 }
135
136
mathMulDiv(int64_t a,int64_t b,int64_t c)137 int64_t mathMulDiv(int64_t a, int64_t b, int64_t c)
138 {
139 int32_t s = 1;
140
141 //move sign
142 if (a < 0) {
143 a = -a;
144 s = -s;
145 }
146 if (b < 0) {
147 b = -b;
148 s = -s;
149 }
150 if (c < 0) {
151 c = -c;
152 s = -s;
153 }
154 int64_t d = c > 0 ? (a * b + (c >> 1)) / c : 0x7FFFFFFFL;
155
156 return (s > 0 ? d : -d);
157 }
158
159
mathRotate(SwPoint & pt,SwFixed angle)160 void mathRotate(SwPoint& pt, SwFixed angle)
161 {
162 if (angle == 0 || pt.zero()) return;
163
164 Point v = pt.toPoint();
165
166 auto radian = TO_RADIAN(angle);
167 auto cosv = cosf(radian);
168 auto sinv = sinf(radian);
169
170 pt.x = SwCoord(nearbyint((v.x * cosv - v.y * sinv) * 64.0f));
171 pt.y = SwCoord(nearbyint((v.x * sinv + v.y * cosv) * 64.0f));
172 }
173
174
mathTan(SwFixed angle)175 SwFixed mathTan(SwFixed angle)
176 {
177 if (angle == 0) return 0;
178 return SwFixed(tanf(TO_RADIAN(angle)) * 65536.0f);
179 }
180
181
mathAtan(const SwPoint & pt)182 SwFixed mathAtan(const SwPoint& pt)
183 {
184 if (pt.zero()) return 0;
185 return SwFixed(tvg::atan2(TO_FLOAT(pt.y), TO_FLOAT(pt.x)) * (180.0f / MATH_PI) * 65536.0f);
186 }
187
188
mathSin(SwFixed angle)189 SwFixed mathSin(SwFixed angle)
190 {
191 if (angle == 0) return 0;
192 return mathCos(SW_ANGLE_PI2 - angle);
193 }
194
195
mathCos(SwFixed angle)196 SwFixed mathCos(SwFixed angle)
197 {
198 return SwFixed(cosf(TO_RADIAN(angle)) * 65536.0f);
199 }
200
201
mathLength(const SwPoint & pt)202 SwFixed mathLength(const SwPoint& pt)
203 {
204 if (pt.zero()) return 0;
205
206 //trivial case
207 if (pt.x == 0) return abs(pt.y);
208 if (pt.y == 0) return abs(pt.x);
209
210 auto v = pt.toPoint();
211 //return static_cast<SwFixed>(sqrtf(v.x * v.x + v.y * v.y) * 65536.0f);
212
213 /* approximate sqrt(x*x + y*y) using alpha max plus beta min algorithm.
214 With alpha = 1, beta = 3/8, giving results with the largest error less
215 than 7% compared to the exact value. */
216 if (v.x < 0) v.x = -v.x;
217 if (v.y < 0) v.y = -v.y;
218 return static_cast<SwFixed>((v.x > v.y) ? (v.x + v.y * 0.375f) : (v.y + v.x * 0.375f));
219 }
220
221
mathSplitCubic(SwPoint * base)222 void mathSplitCubic(SwPoint* base)
223 {
224 SwCoord a, b, c, d;
225
226 base[6].x = base[3].x;
227 c = base[1].x;
228 d = base[2].x;
229 base[1].x = a = (base[0].x + c) >> 1;
230 base[5].x = b = (base[3].x + d) >> 1;
231 c = (c + d) >> 1;
232 base[2].x = a = (a + c) >> 1;
233 base[4].x = b = (b + c) >> 1;
234 base[3].x = (a + b) >> 1;
235
236 base[6].y = base[3].y;
237 c = base[1].y;
238 d = base[2].y;
239 base[1].y = a = (base[0].y + c) >> 1;
240 base[5].y = b = (base[3].y + d) >> 1;
241 c = (c + d) >> 1;
242 base[2].y = a = (a + c) >> 1;
243 base[4].y = b = (b + c) >> 1;
244 base[3].y = (a + b) >> 1;
245 }
246
247
mathSplitLine(SwPoint * base)248 void mathSplitLine(SwPoint* base)
249 {
250 base[2] = base[1];
251
252 base[1].x = (base[0].x + base[1].x) >> 1;
253 base[1].y = (base[0].y + base[1].y) >> 1;
254 }
255
256
mathDiff(SwFixed angle1,SwFixed angle2)257 SwFixed mathDiff(SwFixed angle1, SwFixed angle2)
258 {
259 auto delta = angle2 - angle1;
260
261 delta %= SW_ANGLE_2PI;
262 if (delta < 0) delta += SW_ANGLE_2PI;
263 if (delta > SW_ANGLE_PI) delta -= SW_ANGLE_2PI;
264
265 return delta;
266 }
267
268
mathTransform(const Point * to,const Matrix & transform)269 SwPoint mathTransform(const Point* to, const Matrix& transform)
270 {
271 auto tx = to->x * transform.e11 + to->y * transform.e12 + transform.e13;
272 auto ty = to->x * transform.e21 + to->y * transform.e22 + transform.e23;
273
274 return {TO_SWCOORD(tx), TO_SWCOORD(ty)};
275 }
276
277
mathClipBBox(const SwBBox & clipper,SwBBox & clippee)278 bool mathClipBBox(const SwBBox& clipper, SwBBox& clippee)
279 {
280 clippee.max.x = (clippee.max.x < clipper.max.x) ? clippee.max.x : clipper.max.x;
281 clippee.max.y = (clippee.max.y < clipper.max.y) ? clippee.max.y : clipper.max.y;
282 clippee.min.x = (clippee.min.x > clipper.min.x) ? clippee.min.x : clipper.min.x;
283 clippee.min.y = (clippee.min.y > clipper.min.y) ? clippee.min.y : clipper.min.y;
284
285 //Check valid region
286 if (clippee.max.x - clippee.min.x < 1 && clippee.max.y - clippee.min.y < 1) return false;
287
288 //Check boundary
289 if (clippee.min.x >= clipper.max.x || clippee.min.y >= clipper.max.y ||
290 clippee.max.x <= clipper.min.x || clippee.max.y <= clipper.min.y) return false;
291
292 return true;
293 }
294
295
mathUpdateOutlineBBox(const SwOutline * outline,const SwBBox & clipRegion,SwBBox & renderRegion,bool fastTrack)296 bool mathUpdateOutlineBBox(const SwOutline* outline, const SwBBox& clipRegion, SwBBox& renderRegion, bool fastTrack)
297 {
298 if (!outline) return false;
299
300 if (outline->pts.empty() || outline->cntrs.empty()) {
301 renderRegion.reset();
302 return false;
303 }
304
305 auto pt = outline->pts.begin();
306
307 auto xMin = pt->x;
308 auto xMax = pt->x;
309 auto yMin = pt->y;
310 auto yMax = pt->y;
311
312 for (++pt; pt < outline->pts.end(); ++pt) {
313 if (xMin > pt->x) xMin = pt->x;
314 if (xMax < pt->x) xMax = pt->x;
315 if (yMin > pt->y) yMin = pt->y;
316 if (yMax < pt->y) yMax = pt->y;
317 }
318
319 if (fastTrack) {
320 renderRegion.min.x = static_cast<SwCoord>(nearbyint(xMin / 64.0f));
321 renderRegion.max.x = static_cast<SwCoord>(nearbyint(xMax / 64.0f));
322 renderRegion.min.y = static_cast<SwCoord>(nearbyint(yMin / 64.0f));
323 renderRegion.max.y = static_cast<SwCoord>(nearbyint(yMax / 64.0f));
324 } else {
325 renderRegion.min.x = xMin >> 6;
326 renderRegion.max.x = (xMax + 63) >> 6;
327 renderRegion.min.y = yMin >> 6;
328 renderRegion.max.y = (yMax + 63) >> 6;
329 }
330 return mathClipBBox(clipRegion, renderRegion);
331 }
332
333 #endif /* LV_USE_THORVG_INTERNAL */
334
335