diff --git a/src/loaders/lottie/tvgLottieBuilder.cpp b/src/loaders/lottie/tvgLottieBuilder.cpp index f5a1b893..38322d59 100644 --- a/src/loaders/lottie/tvgLottieBuilder.cpp +++ b/src/loaders/lottie/tvgLottieBuilder.cpp @@ -129,6 +129,42 @@ static void _rotationZ(Matrix* m, float degree) } +static void _skew(Matrix* m, float angleDeg, float axisDeg) +{ + auto angle = -mathDeg2Rad(angleDeg); + float tanVal = tanf(angle); + + axisDeg = fmod(axisDeg, 180.0f); + if (fabsf(axisDeg) < 0.01f || fabsf(axisDeg - 180.0f) < 0.01f || fabsf(axisDeg + 180.0f) < 0.01f) { + float cosVal = cosf(mathDeg2Rad(axisDeg)); + auto B = cosVal * cosVal * tanVal; + m->e12 += B * m->e11; + m->e22 += B * m->e21; + return; + } else if (fabsf(axisDeg - 90.0f) < 0.01f || fabsf(axisDeg + 90.0f) < 0.01f) { + float sinVal = -sinf(mathDeg2Rad(axisDeg)); + auto C = sinVal * sinVal * tanVal; + m->e11 -= C * m->e12; + m->e21 -= C * m->e22; + return; + } + + auto axis = -mathDeg2Rad(axisDeg); + float cosVal = cosf(axis); + float sinVal = sinf(axis); + auto A = sinVal * cosVal * tanVal; + auto B = cosVal * cosVal * tanVal; + auto C = sinVal * sinVal * tanVal; + + auto e11 = m->e11; + auto e21 = m->e21; + m->e11 = (1.0f - A) * e11 - C * m->e12; + m->e12 = B * e11 + (1.0f + A) * m->e12; + m->e21 = (1.0f - A) * e21 - C * m->e22; + m->e22 = B * e21 + (1.0f + A) * m->e22; +} + + static bool _updateTransform(LottieTransform* transform, float frameNo, bool autoOrient, Matrix& matrix, uint8_t& opacity, LottieExpressions* exps) { mathIdentity(&matrix); @@ -154,6 +190,16 @@ static bool _updateTransform(LottieTransform* transform, float frameNo, bool aut _rotateX(&matrix, transform->rotationEx->x(frameNo, exps)); } + auto skewAngle = transform->skewAngle(frameNo, exps); + if (fabsf(skewAngle) > 0.01f) { + // For angles where tangent explodes, the shape degenerates into an infinitely thin line. + // This is handled by zeroing out the matrix due to finite numerical precision. + skewAngle = fmod(skewAngle, 180.0f); + if (fabsf(skewAngle - 90.0f) < 0.01f || fabsf(skewAngle + 90.0f) < 0.01f) return false; + auto skewAxis = transform->skewAxis(frameNo, exps); + _skew(&matrix, skewAngle, skewAxis); + } + auto scale = transform->scale(frameNo, exps); mathScaleR(&matrix, scale.x * 0.01f, scale.y * 0.01f); diff --git a/src/loaders/lottie/tvgLottieModel.h b/src/loaders/lottie/tvgLottieModel.h index 75edd061..80d39534 100644 --- a/src/loaders/lottie/tvgLottieModel.h +++ b/src/loaders/lottie/tvgLottieModel.h @@ -338,6 +338,8 @@ struct LottieTransform : LottieObject LottiePoint scale = Point{100.0f, 100.0f}; LottiePoint anchor = Point{0.0f, 0.0f}; LottieOpacity opacity = 255; + LottieFloat skewAngle = 0.0f; + LottieFloat skewAxis = 0.0f; SeparateCoord* coords = nullptr; //either a position or separate coordinates RotationEx* rotationEx = nullptr; //extension for 3d rotation diff --git a/src/loaders/lottie/tvgLottieParser.cpp b/src/loaders/lottie/tvgLottieParser.cpp index 7a102783..98e9db42 100644 --- a/src/loaders/lottie/tvgLottieParser.cpp +++ b/src/loaders/lottie/tvgLottieParser.cpp @@ -585,8 +585,8 @@ LottieTransform* LottieParser::parseTransform(bool ddd) else if (transform->rotationEx && KEY_AS("ry")) parseProperty(transform->rotationEx->y); else if (transform->rotationEx && KEY_AS("rz")) parseProperty(transform->rotation); else if (KEY_AS("nm")) transform->name = getStringCopy(); - //else if (KEY_AS("sk")) //TODO: skew - //else if (KEY_AS("sa")) //TODO: skew axis + else if (KEY_AS("sk")) parseProperty(transform->skewAngle); + else if (KEY_AS("sa")) parseProperty(transform->skewAxis); else skip(key); } transform->prepare();