mirror of
https://github.com/thorvg/thorvg.git
synced 2025-06-10 06:34:01 +00:00
sw_engine/math: Enhance trigonometric functions.
Streamlining computations with floating-point operations in rotation thereby improving 'thorvg' speed. Also use the well-optimized posix math functions instead of custom math. Test on my local machine. Lottie: -0.008s (0.073 -> 0.065) Performance: -0.0013s (0.0154 -> 0.0141) Binary: -323
This commit is contained in:
parent
1b15233aeb
commit
81e3025ad9
2 changed files with 32 additions and 208 deletions
|
@ -43,11 +43,16 @@ static double timeStamp()
|
|||
#define SW_ANGLE_PI (180L << 16)
|
||||
#define SW_ANGLE_2PI (SW_ANGLE_PI << 1)
|
||||
#define SW_ANGLE_PI2 (SW_ANGLE_PI >> 1)
|
||||
#define SW_ANGLE_PI4 (SW_ANGLE_PI >> 2)
|
||||
|
||||
using SwCoord = signed long;
|
||||
using SwFixed = signed long long;
|
||||
|
||||
|
||||
static inline float TO_FLOAT(SwCoord val)
|
||||
{
|
||||
return static_cast<float>(val) / 64.0f;
|
||||
}
|
||||
|
||||
struct SwPoint
|
||||
{
|
||||
SwCoord x, y;
|
||||
|
@ -92,6 +97,10 @@ struct SwPoint
|
|||
else return false;
|
||||
}
|
||||
|
||||
Point toPoint() const
|
||||
{
|
||||
return {TO_FLOAT(x), TO_FLOAT(y)};
|
||||
}
|
||||
};
|
||||
|
||||
struct SwSize
|
||||
|
|
|
@ -20,7 +20,7 @@
|
|||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
#include <math.h>
|
||||
#include "tvgMath.h"
|
||||
#include "tvgSwCommon.h"
|
||||
|
||||
|
||||
|
@ -28,173 +28,9 @@
|
|||
/* Internal Class Implementation */
|
||||
/************************************************************************/
|
||||
|
||||
//clz: count leading zero’s
|
||||
#if defined(_MSC_VER) && !defined(__clang__)
|
||||
#include <intrin.h>
|
||||
static uint32_t __inline _clz(uint32_t value)
|
||||
{
|
||||
unsigned long leadingZero = 0;
|
||||
if (_BitScanReverse(&leadingZero, value)) return 31 - leadingZero;
|
||||
else return 32;
|
||||
}
|
||||
#else
|
||||
#define _clz(x) __builtin_clz((x))
|
||||
#endif
|
||||
|
||||
|
||||
constexpr SwFixed CORDIC_FACTOR = 0xDBD95B16UL; //the Cordic shrink factor 0.858785336480436 * 2^32
|
||||
|
||||
//this table was generated for SW_FT_PI = 180L << 16, i.e. degrees
|
||||
constexpr static auto ATAN_MAX = 23;
|
||||
constexpr static SwFixed ATAN_TBL[] = {
|
||||
1740967L, 919879L, 466945L, 234379L, 117304L, 58666L, 29335L,
|
||||
14668L, 7334L, 3667L, 1833L, 917L, 458L, 229L, 115L,
|
||||
57L, 29L, 14L, 7L, 4L, 2L, 1L};
|
||||
|
||||
static inline SwCoord SATURATE(const SwCoord x)
|
||||
static float TO_RADIAN(SwFixed angle)
|
||||
{
|
||||
return (x >> (sizeof(SwCoord) * 8 - 1));
|
||||
}
|
||||
|
||||
|
||||
static inline SwFixed PAD_ROUND(const SwFixed x, int32_t n)
|
||||
{
|
||||
return (((x) + ((n)/2)) & ~((n)-1));
|
||||
}
|
||||
|
||||
|
||||
static SwCoord _downscale(SwFixed x)
|
||||
{
|
||||
//multiply a give value by the CORDIC shrink factor
|
||||
auto s = abs(x);
|
||||
int64_t t = (s * static_cast<int64_t>(CORDIC_FACTOR)) + 0x100000000UL;
|
||||
s = static_cast<SwFixed>(t >> 32);
|
||||
if (x < 0) s = -s;
|
||||
return s;
|
||||
}
|
||||
|
||||
|
||||
static int32_t _normalize(SwPoint& pt)
|
||||
{
|
||||
/* the highest bit in overflow-safe vector components
|
||||
MSB of 0.858785336480436 * sqrt(0.5) * 2^30 */
|
||||
constexpr auto SAFE_MSB = 29;
|
||||
|
||||
auto v = pt;
|
||||
|
||||
//High order bit(MSB)
|
||||
int32_t shift = 31 - _clz(abs(v.x) | abs(v.y));
|
||||
|
||||
if (shift <= SAFE_MSB) {
|
||||
shift = SAFE_MSB - shift;
|
||||
pt.x = static_cast<SwCoord>((unsigned long)v.x << shift);
|
||||
pt.y = static_cast<SwCoord>((unsigned long)v.y << shift);
|
||||
} else {
|
||||
shift -= SAFE_MSB;
|
||||
pt.x = v.x >> shift;
|
||||
pt.y = v.y >> shift;
|
||||
shift = -shift;
|
||||
}
|
||||
return shift;
|
||||
}
|
||||
|
||||
|
||||
static void _polarize(SwPoint& pt)
|
||||
{
|
||||
auto v = pt;
|
||||
SwFixed theta;
|
||||
|
||||
//Get the vector into [-PI/4, PI/4] sector
|
||||
if (v.y > v.x) {
|
||||
if (v.y > -v.x) {
|
||||
auto tmp = v.y;
|
||||
v.y = -v.x;
|
||||
v.x = tmp;
|
||||
theta = SW_ANGLE_PI2;
|
||||
} else {
|
||||
theta = v.y > 0 ? SW_ANGLE_PI : -SW_ANGLE_PI;
|
||||
v.x = -v.x;
|
||||
v.y = -v.y;
|
||||
}
|
||||
} else {
|
||||
if (v.y < -v.x) {
|
||||
theta = -SW_ANGLE_PI2;
|
||||
auto tmp = -v.y;
|
||||
v.y = v.x;
|
||||
v.x = tmp;
|
||||
} else {
|
||||
theta = 0;
|
||||
}
|
||||
}
|
||||
|
||||
auto atan = ATAN_TBL;
|
||||
uint32_t i;
|
||||
SwFixed j;
|
||||
|
||||
//Pseudorotations. with right shifts
|
||||
for (i = 1, j = 1; i < ATAN_MAX; j <<= 1, ++i) {
|
||||
if (v.y > 0) {
|
||||
auto tmp = v.x + ((v.y + j) >> i);
|
||||
v.y = v.y - ((v.x + j) >> i);
|
||||
v.x = tmp;
|
||||
theta += *atan++;
|
||||
} else {
|
||||
auto tmp = v.x - ((v.y + j) >> i);
|
||||
v.y = v.y + ((v.x + j) >> i);
|
||||
v.x = tmp;
|
||||
theta -= *atan++;
|
||||
}
|
||||
}
|
||||
|
||||
//round theta
|
||||
if (theta >= 0) theta = PAD_ROUND(theta, 32);
|
||||
else theta = -PAD_ROUND(-theta, 32);
|
||||
|
||||
pt.x = v.x;
|
||||
pt.y = theta;
|
||||
}
|
||||
|
||||
|
||||
static void _rotate(SwPoint& pt, SwFixed theta)
|
||||
{
|
||||
SwFixed x = pt.x;
|
||||
SwFixed y = pt.y;
|
||||
|
||||
//Rotate inside [-PI/4, PI/4] sector
|
||||
while (theta < -SW_ANGLE_PI4) {
|
||||
auto tmp = y;
|
||||
y = -x;
|
||||
x = tmp;
|
||||
theta += SW_ANGLE_PI2;
|
||||
}
|
||||
|
||||
while (theta > SW_ANGLE_PI4) {
|
||||
auto tmp = -y;
|
||||
y = x;
|
||||
x = tmp;
|
||||
theta -= SW_ANGLE_PI2;
|
||||
}
|
||||
|
||||
auto atan = ATAN_TBL;
|
||||
uint32_t i;
|
||||
SwFixed j;
|
||||
|
||||
for (i = 1, j = 1; i < ATAN_MAX; j <<= 1, ++i) {
|
||||
if (theta < 0) {
|
||||
auto tmp = x + ((y + j) >> i);
|
||||
y = y - ((x + j) >> i);
|
||||
x = tmp;
|
||||
theta += *atan++;
|
||||
} else {
|
||||
auto tmp = x - ((y + j) >> i);
|
||||
y = y + ((x + j) >> i);
|
||||
x = tmp;
|
||||
theta -= *atan++;
|
||||
}
|
||||
}
|
||||
|
||||
pt.x = static_cast<SwCoord>(x);
|
||||
pt.y = static_cast<SwCoord>(y);
|
||||
return (float(angle) / 65536.0f) * (MATH_PI / 180.0f);
|
||||
}
|
||||
|
||||
|
||||
|
@ -320,77 +156,56 @@ int64_t mathMulDiv(int64_t a, int64_t b, int64_t c)
|
|||
|
||||
void mathRotate(SwPoint& pt, SwFixed angle)
|
||||
{
|
||||
if (angle == 0 || (pt.x == 0 && pt.y == 0)) return;
|
||||
if (angle == 0 || pt.zero()) return;
|
||||
|
||||
auto v = pt;
|
||||
auto shift = _normalize(v);
|
||||
Point v = pt.toPoint();
|
||||
|
||||
auto theta = angle;
|
||||
_rotate(v, theta);
|
||||
auto radian = TO_RADIAN(angle);
|
||||
auto cosv = cosf(radian);
|
||||
auto sinv = sinf(radian);
|
||||
|
||||
v.x = _downscale(v.x);
|
||||
v.y = _downscale(v.y);
|
||||
|
||||
if (shift > 0) {
|
||||
auto half = static_cast<int32_t>(1L << (shift - 1));
|
||||
pt.x = (v.x + half + SATURATE(v.x)) >> shift;
|
||||
pt.y = (v.y + half + SATURATE(v.y)) >> shift;
|
||||
} else {
|
||||
shift = -shift;
|
||||
pt.x = static_cast<SwCoord>((unsigned long)v.x << shift);
|
||||
pt.y = static_cast<SwCoord>((unsigned long)v.y << shift);
|
||||
}
|
||||
pt.x = SwCoord(roundf((v.x * cosv - v.y * sinv) * 64.0f));
|
||||
pt.y = SwCoord(roundf((v.x * sinv + v.y * cosv) * 64.0f));
|
||||
}
|
||||
|
||||
|
||||
SwFixed mathTan(SwFixed angle)
|
||||
{
|
||||
SwPoint v = {CORDIC_FACTOR >> 8, 0};
|
||||
_rotate(v, angle);
|
||||
return mathDivide(v.y, v.x);
|
||||
if (angle == 0) return 0;
|
||||
return SwFixed(tanf(TO_RADIAN(angle)) * 65536.0f);
|
||||
}
|
||||
|
||||
|
||||
SwFixed mathAtan(const SwPoint& pt)
|
||||
{
|
||||
if (pt.x == 0 && pt.y == 0) return 0;
|
||||
|
||||
auto v = pt;
|
||||
_normalize(v);
|
||||
_polarize(v);
|
||||
|
||||
return v.y;
|
||||
if (pt.zero()) return 0;
|
||||
return SwFixed(atan2f(TO_FLOAT(pt.y), TO_FLOAT(pt.x)) * (180.0f / MATH_PI) * 65536.0f);
|
||||
}
|
||||
|
||||
|
||||
SwFixed mathSin(SwFixed angle)
|
||||
{
|
||||
if (angle == 0) return 0;
|
||||
return mathCos(SW_ANGLE_PI2 - angle);
|
||||
}
|
||||
|
||||
|
||||
SwFixed mathCos(SwFixed angle)
|
||||
{
|
||||
SwPoint v = {CORDIC_FACTOR >> 8, 0};
|
||||
_rotate(v, angle);
|
||||
return (v.x + 0x80L) >> 8;
|
||||
return SwFixed(cosf(TO_RADIAN(angle)) * 65536.0f);
|
||||
}
|
||||
|
||||
|
||||
SwFixed mathLength(const SwPoint& pt)
|
||||
{
|
||||
auto v = pt;
|
||||
if (pt.zero()) return 0;
|
||||
|
||||
//trivial case
|
||||
if (v.x == 0) return abs(v.y);
|
||||
if (v.y == 0) return abs(v.x);
|
||||
if (pt.x == 0) return abs(pt.y);
|
||||
if (pt.y == 0) return abs(pt.x);
|
||||
|
||||
//general case
|
||||
auto shift = _normalize(v);
|
||||
_polarize(v);
|
||||
v.x = _downscale(v.x);
|
||||
|
||||
if (shift > 0) return (v.x + (static_cast<SwFixed>(1) << (shift -1))) >> shift;
|
||||
return static_cast<SwFixed>((uint32_t)v.x << -shift);
|
||||
auto v = pt.toPoint();
|
||||
return static_cast<SwFixed>(sqrtf(v.x * v.x + v.y * v.y) / 64.0f);
|
||||
}
|
||||
|
||||
|
||||
|
|
Loading…
Add table
Reference in a new issue