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