mirror of
https://github.com/thorvg/thorvg.git
synced 2025-06-13 19:44:28 +00:00
lottie: roundness refactored based on #2295
This commit is contained in:
parent
a7eae339a5
commit
448d84ffb7
7 changed files with 268 additions and 174 deletions
|
@ -9,6 +9,7 @@ source_file = [
|
|||
'tvgLottieInterpolator.h',
|
||||
'tvgLottieLoader.h',
|
||||
'tvgLottieModel.h',
|
||||
'tvgLottieModifier.h',
|
||||
'tvgLottieParser.h',
|
||||
'tvgLottieParserHandler.h',
|
||||
'tvgLottieProperty.h',
|
||||
|
@ -19,6 +20,7 @@ source_file = [
|
|||
'tvgLottieInterpolator.cpp',
|
||||
'tvgLottieLoader.cpp',
|
||||
'tvgLottieModel.cpp',
|
||||
'tvgLottieModifier.cpp',
|
||||
'tvgLottieParserHandler.cpp',
|
||||
'tvgLottieParser.cpp'
|
||||
]
|
||||
|
|
|
@ -469,21 +469,21 @@ void LottieBuilder::updateRect(LottieGroup* parent, LottieObject** child, float
|
|||
|
||||
auto position = rect->position(frameNo, exps);
|
||||
auto size = rect->size(frameNo, exps);
|
||||
auto roundness = rect->radius(frameNo, exps);
|
||||
if (roundness == 0.0f) {
|
||||
if (ctx->roundness > ROUNDNESS_EPSILON) roundness = std::min(ctx->roundness, std::max(size.x, size.y) * 0.5f);
|
||||
auto r = rect->radius(frameNo, exps);
|
||||
if (r == 0.0f) {
|
||||
if (ctx->roundness) ctx->roundness->modifyRect(size, r);
|
||||
} else {
|
||||
roundness = std::min({roundness, size.x * 0.5f, size.y * 0.5f});
|
||||
r = std::min({r, size.x * 0.5f, size.y * 0.5f});
|
||||
}
|
||||
|
||||
if (!ctx->repeaters.empty()) {
|
||||
auto shape = rect->pooling();
|
||||
shape->reset();
|
||||
_appendRect(shape, position.x - size.x * 0.5f, position.y - size.y * 0.5f, size.x, size.y, roundness, ctx->transform, rect->clockwise);
|
||||
_appendRect(shape, position.x - size.x * 0.5f, position.y - size.y * 0.5f, size.x, size.y, r, ctx->transform, rect->clockwise);
|
||||
_repeat(parent, shape, ctx);
|
||||
} else {
|
||||
_draw(parent, rect, ctx);
|
||||
_appendRect(ctx->merging, position.x - size.x * 0.5f, position.y - size.y * 0.5f, size.x, size.y, roundness, ctx->transform, rect->clockwise);
|
||||
_appendRect(ctx->merging, position.x - size.x * 0.5f, position.y - size.y * 0.5f, size.x, size.y, r, ctx->transform, rect->clockwise);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -563,70 +563,7 @@ void LottieBuilder::updatePath(LottieGroup* parent, LottieObject** child, float
|
|||
}
|
||||
|
||||
|
||||
static void _applyRoundedCorner(Shape* star, Shape* merging, float outerRoundness, float roundness, bool hasRoundness)
|
||||
{
|
||||
static constexpr auto ROUNDED_POLYSTAR_MAGIC_NUMBER = 0.47829f;
|
||||
|
||||
auto cmdCnt = star->pathCommands(nullptr);
|
||||
const Point *pts = nullptr;
|
||||
auto ptsCnt = star->pathCoords(&pts);
|
||||
|
||||
auto len = length(pts[1] - pts[2]);
|
||||
auto r = len > 0.0f ? ROUNDED_POLYSTAR_MAGIC_NUMBER * std::min(len * 0.5f, roundness) / len : 0.0f;
|
||||
|
||||
if (hasRoundness) {
|
||||
P(merging)->rs.path.cmds.grow((uint32_t)(1.5 * cmdCnt));
|
||||
P(merging)->rs.path.pts.grow((uint32_t)(4.5 * cmdCnt));
|
||||
|
||||
int start = 3 * tvg::zero(outerRoundness);
|
||||
merging->moveTo(pts[start].x, pts[start].y);
|
||||
|
||||
for (uint32_t i = 1 + start; i < ptsCnt; i += 6) {
|
||||
auto& prev = pts[i];
|
||||
auto& curr = pts[i + 2];
|
||||
auto& next = (i < ptsCnt - start) ? pts[i + 4] : pts[2];
|
||||
auto& nextCtrl = (i < ptsCnt - start) ? pts[i + 5] : pts[3];
|
||||
auto dNext = r * (curr - next);
|
||||
auto dPrev = r * (curr - prev);
|
||||
|
||||
auto p0 = curr - 2.0f * dPrev;
|
||||
auto p1 = curr - dPrev;
|
||||
auto p2 = curr - dNext;
|
||||
auto p3 = curr - 2.0f * dNext;
|
||||
|
||||
merging->cubicTo(prev.x, prev.y, p0.x, p0.y, p0.x, p0.y);
|
||||
merging->cubicTo(p1.x, p1.y, p2.x, p2.y, p3.x, p3.y);
|
||||
merging->cubicTo(p3.x, p3.y, next.x, next.y, nextCtrl.x, nextCtrl.y);
|
||||
}
|
||||
} else {
|
||||
P(merging)->rs.path.cmds.grow(2 * cmdCnt);
|
||||
P(merging)->rs.path.pts.grow(4 * cmdCnt);
|
||||
|
||||
auto dPrev = r * (pts[1] - pts[0]);
|
||||
auto p = pts[0] + 2.0f * dPrev;
|
||||
merging->moveTo(p.x, p.y);
|
||||
|
||||
for (uint32_t i = 1; i < ptsCnt; ++i) {
|
||||
auto& curr = pts[i];
|
||||
auto& next = (i == ptsCnt - 1) ? pts[1] : pts[i + 1];
|
||||
auto dNext = r * (curr - next);
|
||||
|
||||
auto p0 = curr - 2.0f * dPrev;
|
||||
auto p1 = curr - dPrev;
|
||||
auto p2 = curr - dNext;
|
||||
auto p3 = curr - 2.0f * dNext;
|
||||
|
||||
merging->lineTo(p0.x, p0.y);
|
||||
merging->cubicTo(p1.x, p1.y, p2.x, p2.y, p3.x, p3.y);
|
||||
|
||||
dPrev = -1.0f * dNext;
|
||||
}
|
||||
}
|
||||
merging->close();
|
||||
}
|
||||
|
||||
|
||||
static void _updateStar(LottieGroup* parent, LottiePolyStar* star, Matrix* transform, float roundness, float frameNo, Shape* merging, LottieExpressions* exps)
|
||||
static void _updateStar(TVG_UNUSED LottieGroup* parent, LottiePolyStar* star, Matrix* transform, const LottieRoundnessModifier* roundness, float frameNo, Shape* merging, LottieExpressions* exps)
|
||||
{
|
||||
static constexpr auto POLYSTAR_MAGIC_NUMBER = 0.47829f / 0.28f;
|
||||
|
||||
|
@ -645,7 +582,7 @@ static void _updateStar(LottieGroup* parent, LottiePolyStar* star, Matrix* trans
|
|||
auto numPoints = size_t(ceilf(ptsCnt) * 2);
|
||||
auto direction = star->clockwise ? 1.0f : -1.0f;
|
||||
auto hasRoundness = false;
|
||||
bool roundedCorner = (roundness > ROUNDNESS_EPSILON) && (tvg::zero(innerRoundness) || tvg::zero(outerRoundness));
|
||||
bool roundedCorner = roundness && (tvg::zero(innerRoundness) || tvg::zero(outerRoundness));
|
||||
|
||||
Shape* shape;
|
||||
if (roundedCorner) {
|
||||
|
@ -742,11 +679,11 @@ static void _updateStar(LottieGroup* parent, LottiePolyStar* star, Matrix* trans
|
|||
}
|
||||
shape->close();
|
||||
|
||||
if (roundedCorner) _applyRoundedCorner(shape, merging, outerRoundness, roundness, hasRoundness);
|
||||
if (roundedCorner) roundness->modifyPolystar(P(shape)->rs.path.cmds, P(shape)->rs.path.pts, P(merging)->rs.path.cmds, P(merging)->rs.path.pts, outerRoundness, hasRoundness);
|
||||
}
|
||||
|
||||
|
||||
static void _updatePolygon(LottieGroup* parent, LottiePolyStar* star, Matrix* transform, float roundness, float frameNo, Shape* merging, LottieExpressions* exps)
|
||||
static void _updatePolygon(LottieGroup* parent, LottiePolyStar* star, Matrix* transform, const LottieRoundnessModifier* roundness, float frameNo, Shape* merging, LottieExpressions* exps)
|
||||
{
|
||||
static constexpr auto POLYGON_MAGIC_NUMBER = 0.25f;
|
||||
|
||||
|
@ -758,7 +695,7 @@ static void _updatePolygon(LottieGroup* parent, LottiePolyStar* star, Matrix* tr
|
|||
auto anglePerPoint = 2.0f * MATH_PI / float(ptsCnt);
|
||||
auto direction = star->clockwise ? 1.0f : -1.0f;
|
||||
auto hasRoundness = !tvg::zero(outerRoundness);
|
||||
bool roundedCorner = roundness > ROUNDNESS_EPSILON && !hasRoundness;
|
||||
bool roundedCorner = roundness && !hasRoundness;
|
||||
auto x = radius * cosf(angle);
|
||||
auto y = radius * sinf(angle);
|
||||
|
||||
|
@ -820,7 +757,7 @@ static void _updatePolygon(LottieGroup* parent, LottiePolyStar* star, Matrix* tr
|
|||
}
|
||||
shape->close();
|
||||
|
||||
if (roundedCorner) _applyRoundedCorner(shape, merging, 0.0f, roundness, false);
|
||||
if (roundedCorner) roundness->modifyPolystar(P(shape)->rs.path.cmds, P(shape)->rs.path.pts, P(merging)->rs.path.cmds, P(merging)->rs.path.pts, 0.0f, false);
|
||||
}
|
||||
|
||||
|
||||
|
@ -856,9 +793,12 @@ void LottieBuilder::updatePolystar(LottieGroup* parent, LottieObject** child, fl
|
|||
|
||||
void LottieBuilder::updateRoundedCorner(TVG_UNUSED LottieGroup* parent, LottieObject** child, float frameNo, TVG_UNUSED Inlist<RenderContext>& contexts, RenderContext* ctx)
|
||||
{
|
||||
auto roundedCorner= static_cast<LottieRoundedCorner*>(*child);
|
||||
auto roundness = roundedCorner->radius(frameNo, exps);
|
||||
if (ctx->roundness < roundness) ctx->roundness = roundness;
|
||||
auto roundedCorner = static_cast<LottieRoundedCorner*>(*child);
|
||||
auto r = roundedCorner->radius(frameNo, exps);
|
||||
if (r < LottieRoundnessModifier::ROUNDNESS_EPSILON) return;
|
||||
|
||||
if (!ctx->roundness) ctx->roundness = new LottieRoundnessModifier(r);
|
||||
else if (ctx->roundness->r < r) ctx->roundness->r = r;
|
||||
}
|
||||
|
||||
|
||||
|
@ -1068,7 +1008,7 @@ void LottieBuilder::updateText(LottieLayer* layer, float frameNo)
|
|||
for (auto g = glyph->children.begin(); g < glyph->children.end(); ++g) {
|
||||
auto group = static_cast<LottieGroup*>(*g);
|
||||
for (auto p = group->children.begin(); p < group->children.end(); ++p) {
|
||||
if (static_cast<LottiePath*>(*p)->pathset(frameNo, P(shape)->rs.path.cmds, P(shape)->rs.path.pts, nullptr, 0.0f)) {
|
||||
if (static_cast<LottiePath*>(*p)->pathset(frameNo, P(shape)->rs.path.cmds, P(shape)->rs.path.pts, nullptr, nullptr)) {
|
||||
P(shape)->update(RenderUpdateFlag::Path);
|
||||
}
|
||||
}
|
||||
|
@ -1157,7 +1097,7 @@ void LottieBuilder::updateMaskings(LottieLayer* layer, float frameNo)
|
|||
pShape->reset();
|
||||
pShape->fill(255, 255, 255, pMask->opacity(frameNo));
|
||||
pShape->transform(layer->cache.matrix);
|
||||
pMask->pathset(frameNo, P(pShape)->rs.path.cmds, P(pShape)->rs.path.pts, nullptr, 0.0f, exps);
|
||||
pMask->pathset(frameNo, P(pShape)->rs.path.cmds, P(pShape)->rs.path.pts, nullptr, nullptr, exps);
|
||||
|
||||
if (pMethod == CompositeMethod::SubtractMask || pMethod == CompositeMethod::InvAlphaMask) {
|
||||
layer->scene->composite(tvg::cast(pShape), CompositeMethod::InvAlphaMask);
|
||||
|
@ -1173,14 +1113,14 @@ void LottieBuilder::updateMaskings(LottieLayer* layer, float frameNo)
|
|||
|
||||
//Append the mask shape
|
||||
if (pMethod == method && (method == CompositeMethod::SubtractMask || method == CompositeMethod::DifferenceMask)) {
|
||||
mask->pathset(frameNo, P(pShape)->rs.path.cmds, P(pShape)->rs.path.pts, nullptr, 0.0f, exps);
|
||||
mask->pathset(frameNo, P(pShape)->rs.path.cmds, P(pShape)->rs.path.pts, nullptr, nullptr, exps);
|
||||
//Chain composition
|
||||
} else {
|
||||
auto shape = layer->pooling();
|
||||
shape->reset();
|
||||
shape->fill(255, 255, 255, mask->opacity(frameNo));
|
||||
shape->transform(layer->cache.matrix);
|
||||
mask->pathset(frameNo, P(shape)->rs.path.cmds, P(shape)->rs.path.pts, nullptr, 0.0f, exps);
|
||||
mask->pathset(frameNo, P(shape)->rs.path.cmds, P(shape)->rs.path.pts, nullptr, nullptr, exps);
|
||||
pShape->composite(tvg::cast(shape), method);
|
||||
pShape = shape;
|
||||
pMethod = method;
|
||||
|
|
|
@ -28,6 +28,7 @@
|
|||
#include "tvgPaint.h"
|
||||
#include "tvgShape.h"
|
||||
#include "tvgLottieExpressions.h"
|
||||
#include "tvgLottieModifier.h"
|
||||
|
||||
struct LottieComposition;
|
||||
|
||||
|
@ -55,7 +56,7 @@ struct RenderContext
|
|||
LottieObject** begin = nullptr; //iteration entry point
|
||||
Array<RenderRepeater> repeaters;
|
||||
Matrix* transform = nullptr;
|
||||
float roundness = 0.0f;
|
||||
LottieRoundnessModifier* roundness = nullptr;
|
||||
bool fragmenting = false; //render context has been fragmented by filling
|
||||
bool reqFragment = false; //requirement to fragment the render context
|
||||
|
||||
|
@ -70,6 +71,7 @@ struct RenderContext
|
|||
{
|
||||
PP(propagator)->unref();
|
||||
free(transform);
|
||||
delete(roundness);
|
||||
}
|
||||
|
||||
RenderContext(const RenderContext& rhs, Shape* propagator, bool mergeable = false)
|
||||
|
@ -78,7 +80,7 @@ struct RenderContext
|
|||
PP(propagator)->ref();
|
||||
this->propagator = propagator;
|
||||
this->repeaters = rhs.repeaters;
|
||||
this->roundness = rhs.roundness;
|
||||
if (rhs.roundness) this->roundness = new LottieRoundnessModifier(rhs.roundness->r);
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
@ -29,6 +29,7 @@
|
|||
struct LottieExpression;
|
||||
struct LottieComposition;
|
||||
struct LottieLayer;
|
||||
struct LottieRoundnessModifier;
|
||||
|
||||
#ifdef THORVG_LOTTIE_EXPRESSIONS_SUPPORT
|
||||
|
||||
|
@ -110,7 +111,7 @@ public:
|
|||
}
|
||||
|
||||
template<typename Property>
|
||||
bool result(float frameNo, Array<PathCommand>& cmds, Array<Point>& pts, Matrix* transform, float roundness, LottieExpression* exp)
|
||||
bool result(float frameNo, Array<PathCommand>& cmds, Array<Point>& pts, Matrix* transform, const LottieRoundnessModifier* roundness, LottieExpression* exp)
|
||||
{
|
||||
auto bm_rt = evaluate(frameNo, exp);
|
||||
if (jerry_value_is_undefined(bm_rt)) return false;
|
||||
|
@ -155,7 +156,7 @@ struct LottieExpressions
|
|||
template<typename Property> bool result(TVG_UNUSED float, TVG_UNUSED Point&, LottieExpression*) { return false; }
|
||||
template<typename Property> bool result(TVG_UNUSED float, TVG_UNUSED RGB24&, TVG_UNUSED LottieExpression*) { return false; }
|
||||
template<typename Property> bool result(TVG_UNUSED float, TVG_UNUSED Fill*, TVG_UNUSED LottieExpression*) { return false; }
|
||||
template<typename Property> bool result(TVG_UNUSED float, TVG_UNUSED Array<PathCommand>&, TVG_UNUSED Array<Point>&, TVG_UNUSED Matrix* transform, TVG_UNUSED float, TVG_UNUSED LottieExpression*) { return false; }
|
||||
template<typename Property> bool result(TVG_UNUSED float, TVG_UNUSED Array<PathCommand>&, TVG_UNUSED Array<Point>&, TVG_UNUSED Matrix* transform, TVG_UNUSED const LottieRoundnessModifier*, TVG_UNUSED LottieExpression*) { return false; }
|
||||
void update(TVG_UNUSED float) {}
|
||||
static LottieExpressions* instance() { return nullptr; }
|
||||
static void retrieve(TVG_UNUSED LottieExpressions* instance) {}
|
||||
|
|
180
src/loaders/lottie/tvgLottieModifier.cpp
Normal file
180
src/loaders/lottie/tvgLottieModifier.cpp
Normal file
|
@ -0,0 +1,180 @@
|
|||
/*
|
||||
* Copyright (c) 2024 the ThorVG project. All rights reserved.
|
||||
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
#include "tvgLottieModifier.h"
|
||||
#include "tvgMath.h"
|
||||
|
||||
|
||||
/************************************************************************/
|
||||
/* Internal Class Implementation */
|
||||
/************************************************************************/
|
||||
|
||||
static void _roundCorner(Array<PathCommand>& cmds, Array<Point>& pts, const Point& prev, const Point& curr, const Point& next, float r)
|
||||
{
|
||||
auto lenPrev = length(prev - curr);
|
||||
auto rPrev = lenPrev > 0.0f ? 0.5f * std::min(lenPrev * 0.5f, r) / lenPrev : 0.0f;
|
||||
auto lenNext = length(next - curr);
|
||||
auto rNext = lenNext > 0.0f ? 0.5f * std::min(lenNext * 0.5f, r) / lenNext : 0.0f;
|
||||
|
||||
auto dPrev = rPrev * (curr - prev);
|
||||
auto dNext = rNext * (curr - next);
|
||||
|
||||
pts.push(curr - 2.0f * dPrev);
|
||||
pts.push(curr - dPrev);
|
||||
pts.push(curr - dNext);
|
||||
pts.push(curr - 2.0f * dNext);
|
||||
cmds.push(PathCommand::LineTo);
|
||||
cmds.push(PathCommand::CubicTo);
|
||||
}
|
||||
|
||||
/************************************************************************/
|
||||
/* External Class Implementation */
|
||||
/************************************************************************/
|
||||
|
||||
bool LottieRoundnessModifier::modifyPath(const PathCommand* inCmds, uint32_t inCmdsCnt, const Point* inPts, uint32_t inPtsCnt, Array<PathCommand>& outCmds, Array<Point>& outPts, Matrix* transform) const
|
||||
{
|
||||
outCmds.reserve(inCmdsCnt * 2);
|
||||
outPts.reserve((uint32_t)(inPtsCnt * 1.5));
|
||||
auto ptsCnt = outPts.count;
|
||||
|
||||
uint32_t startIndex = 0;
|
||||
for (uint32_t iCmds = 0, iPts = 0; iCmds < inCmdsCnt; ++iCmds) {
|
||||
switch (inCmds[iCmds]) {
|
||||
case PathCommand::MoveTo: {
|
||||
startIndex = outPts.count;
|
||||
outCmds.push(PathCommand::MoveTo);
|
||||
outPts.push(inPts[iPts++]);
|
||||
break;
|
||||
}
|
||||
case PathCommand::CubicTo: {
|
||||
auto& prev = inPts[iPts - 1];
|
||||
auto& curr = inPts[iPts + 2];
|
||||
if (iCmds < inCmdsCnt - 1 &&
|
||||
tvg::zero(inPts[iPts - 1] - inPts[iPts]) &&
|
||||
tvg::zero(inPts[iPts + 1] - inPts[iPts + 2])) {
|
||||
if (inCmds[iCmds + 1] == PathCommand::CubicTo &&
|
||||
tvg::zero(inPts[iPts + 2] - inPts[iPts + 3]) &&
|
||||
tvg::zero(inPts[iPts + 4] - inPts[iPts + 5])) {
|
||||
_roundCorner(outCmds, outPts, prev, curr, inPts[iPts + 5], r);
|
||||
iPts += 3;
|
||||
break;
|
||||
} else if (inCmds[iCmds + 1] == PathCommand::Close) {
|
||||
_roundCorner(outCmds, outPts, prev, curr, inPts[2], r);
|
||||
outPts[startIndex] = outPts.last();
|
||||
iPts += 3;
|
||||
break;
|
||||
}
|
||||
}
|
||||
outCmds.push(PathCommand::CubicTo);
|
||||
outPts.push(inPts[iPts++]);
|
||||
outPts.push(inPts[iPts++]);
|
||||
outPts.push(inPts[iPts++]);
|
||||
break;
|
||||
}
|
||||
case PathCommand::Close: {
|
||||
outCmds.push(PathCommand::Close);
|
||||
break;
|
||||
}
|
||||
default: break;
|
||||
}
|
||||
}
|
||||
if (transform) {
|
||||
for (auto i = ptsCnt; i < outPts.count; ++i) {
|
||||
outPts[i] *= *transform;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
bool LottieRoundnessModifier::modifyPolystar(TVG_UNUSED const Array<PathCommand>& inCmds, const Array<Point>& inPts, Array<PathCommand>& outCmds, Array<Point>& outPts, float outerRoundness, bool hasRoundness) const
|
||||
{
|
||||
static constexpr auto ROUNDED_POLYSTAR_MAGIC_NUMBER = 0.47829f;
|
||||
|
||||
auto len = length(inPts[1] - inPts[2]);
|
||||
auto r = len > 0.0f ? ROUNDED_POLYSTAR_MAGIC_NUMBER * std::min(len * 0.5f, this->r) / len : 0.0f;
|
||||
|
||||
if (hasRoundness) {
|
||||
outCmds.grow((uint32_t)(1.5 * inCmds.count));
|
||||
outPts.grow((uint32_t)(4.5 * inCmds.count));
|
||||
|
||||
int start = 3 * tvg::zero(outerRoundness);
|
||||
outCmds.push(PathCommand::MoveTo);
|
||||
outPts.push(inPts[start]);
|
||||
|
||||
for (uint32_t i = 1 + start; i < inPts.count; i += 6) {
|
||||
auto& prev = inPts[i];
|
||||
auto& curr = inPts[i + 2];
|
||||
auto& next = (i < inPts.count - start) ? inPts[i + 4] : inPts[2];
|
||||
auto& nextCtrl = (i < inPts.count - start) ? inPts[i + 5] : inPts[3];
|
||||
auto dNext = r * (curr - next);
|
||||
auto dPrev = r * (curr - prev);
|
||||
|
||||
auto p0 = curr - 2.0f * dPrev;
|
||||
auto p1 = curr - dPrev;
|
||||
auto p2 = curr - dNext;
|
||||
auto p3 = curr - 2.0f * dNext;
|
||||
|
||||
outCmds.push(PathCommand::CubicTo);
|
||||
outPts.push(prev); outPts.push(p0); outPts.push(p0);
|
||||
outCmds.push(PathCommand::CubicTo);
|
||||
outPts.push(p1); outPts.push(p2); outPts.push(p3);
|
||||
outCmds.push(PathCommand::CubicTo);
|
||||
outPts.push(p3); outPts.push(next); outPts.push(nextCtrl);
|
||||
}
|
||||
} else {
|
||||
outCmds.grow(2 * inCmds.count);
|
||||
outPts.grow(4 * inCmds.count);
|
||||
|
||||
auto dPrev = r * (inPts[1] - inPts[0]);
|
||||
auto p = inPts[0] + 2.0f * dPrev;
|
||||
outCmds.push(PathCommand::MoveTo);
|
||||
outPts.push(p);
|
||||
|
||||
for (uint32_t i = 1; i < inPts.count; ++i) {
|
||||
auto& curr = inPts[i];
|
||||
auto& next = (i == inPts.count - 1) ? inPts[1] : inPts[i + 1];
|
||||
auto dNext = r * (curr - next);
|
||||
|
||||
auto p0 = curr - 2.0f * dPrev;
|
||||
auto p1 = curr - dPrev;
|
||||
auto p2 = curr - dNext;
|
||||
auto p3 = curr - 2.0f * dNext;
|
||||
|
||||
outCmds.push(PathCommand::LineTo);
|
||||
outPts.push(p0);
|
||||
outCmds.push(PathCommand::CubicTo);
|
||||
outPts.push(p1); outPts.push(p2); outPts.push(p3);
|
||||
|
||||
dPrev = -1.0f * dNext;
|
||||
}
|
||||
}
|
||||
outCmds.push(PathCommand::Close);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
bool LottieRoundnessModifier::modifyRect(const Point& size, float& r) const
|
||||
{
|
||||
r = std::min(this->r, std::max(size.x, size.y) * 0.5f);
|
||||
return true;
|
||||
}
|
43
src/loaders/lottie/tvgLottieModifier.h
Normal file
43
src/loaders/lottie/tvgLottieModifier.h
Normal file
|
@ -0,0 +1,43 @@
|
|||
/*
|
||||
* Copyright (c) 2024 the ThorVG project. All rights reserved.
|
||||
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
#ifndef _TVG_LOTTIE_MODIFIER_H_
|
||||
#define _TVG_LOTTIE_MODIFIER_H_
|
||||
|
||||
#include "tvgCommon.h"
|
||||
#include "tvgArray.h"
|
||||
|
||||
|
||||
struct LottieRoundnessModifier
|
||||
{
|
||||
static constexpr float ROUNDNESS_EPSILON = 1.0f;
|
||||
float r;
|
||||
|
||||
LottieRoundnessModifier(float r) : r(r) {};
|
||||
|
||||
bool modifyPath(const PathCommand* inCmds, uint32_t inCmdsCnt, const Point* inPts, uint32_t inPtsCnt, Array<PathCommand>& outCmds, Array<Point>& outPts, Matrix* transform) const;
|
||||
bool modifyPolystar(const Array<PathCommand>& inCmds, const Array<Point>& inPts, Array<PathCommand>& outCmds, Array<Point>& outPts, float outerRoundness, bool hasRoundness) const;
|
||||
bool modifyRect(const Point& size, float& r) const;
|
||||
};
|
||||
|
||||
|
||||
#endif
|
|
@ -28,8 +28,8 @@
|
|||
#include "tvgLottieCommon.h"
|
||||
#include "tvgLottieInterpolator.h"
|
||||
#include "tvgLottieExpressions.h"
|
||||
#include "tvgLottieModifier.h"
|
||||
|
||||
#define ROUNDNESS_EPSILON 1.0f
|
||||
|
||||
struct LottieFont;
|
||||
struct LottieLayer;
|
||||
|
@ -178,81 +178,6 @@ static void _copy(PathSet* pathset, Array<PathCommand>& outCmds)
|
|||
}
|
||||
|
||||
|
||||
static void _roundCorner(Array<PathCommand>& cmds, Array<Point>& pts, const Point& prev, const Point& curr, const Point& next, float roundness)
|
||||
{
|
||||
auto lenPrev = length(prev - curr);
|
||||
auto rPrev = lenPrev > 0.0f ? 0.5f * std::min(lenPrev * 0.5f, roundness) / lenPrev : 0.0f;
|
||||
auto lenNext = length(next - curr);
|
||||
auto rNext = lenNext > 0.0f ? 0.5f * std::min(lenNext * 0.5f, roundness) / lenNext : 0.0f;
|
||||
|
||||
auto dPrev = rPrev * (curr - prev);
|
||||
auto dNext = rNext * (curr - next);
|
||||
|
||||
pts.push(curr - 2.0f * dPrev);
|
||||
pts.push(curr - dPrev);
|
||||
pts.push(curr - dNext);
|
||||
pts.push(curr - 2.0f * dNext);
|
||||
cmds.push(PathCommand::LineTo);
|
||||
cmds.push(PathCommand::CubicTo);
|
||||
}
|
||||
|
||||
|
||||
static bool _modifier(Point* inPts, uint32_t inPtsCnt, PathCommand* inCmds, uint32_t inCmdsCnt, Array<PathCommand>& cmds, Array<Point>& pts, Matrix* transform, float roundness)
|
||||
{
|
||||
cmds.reserve(inCmdsCnt * 2);
|
||||
pts.reserve((uint16_t)(inPtsCnt * 1.5));
|
||||
auto ptsCnt = pts.count;
|
||||
|
||||
auto startIndex = 0;
|
||||
for (uint32_t iCmds = 0, iPts = 0; iCmds < inCmdsCnt; ++iCmds) {
|
||||
switch (inCmds[iCmds]) {
|
||||
case PathCommand::MoveTo: {
|
||||
startIndex = pts.count;
|
||||
cmds.push(PathCommand::MoveTo);
|
||||
pts.push(inPts[iPts++]);
|
||||
break;
|
||||
}
|
||||
case PathCommand::CubicTo: {
|
||||
auto& prev = inPts[iPts - 1];
|
||||
auto& curr = inPts[iPts + 2];
|
||||
if (iCmds < inCmdsCnt - 1 &&
|
||||
tvg::zero(inPts[iPts - 1] - inPts[iPts]) &&
|
||||
tvg::zero(inPts[iPts + 1] - inPts[iPts + 2])) {
|
||||
if (inCmds[iCmds + 1] == PathCommand::CubicTo &&
|
||||
tvg::zero(inPts[iPts + 2] - inPts[iPts + 3]) &&
|
||||
tvg::zero(inPts[iPts + 4] - inPts[iPts + 5])) {
|
||||
_roundCorner(cmds, pts, prev, curr, inPts[iPts + 5], roundness);
|
||||
iPts += 3;
|
||||
break;
|
||||
} else if (inCmds[iCmds + 1] == PathCommand::Close) {
|
||||
_roundCorner(cmds, pts, prev, curr, inPts[2], roundness);
|
||||
pts[startIndex] = pts.last();
|
||||
iPts += 3;
|
||||
break;
|
||||
}
|
||||
}
|
||||
cmds.push(PathCommand::CubicTo);
|
||||
pts.push(inPts[iPts++]);
|
||||
pts.push(inPts[iPts++]);
|
||||
pts.push(inPts[iPts++]);
|
||||
break;
|
||||
}
|
||||
case PathCommand::Close: {
|
||||
cmds.push(PathCommand::Close);
|
||||
break;
|
||||
}
|
||||
default: break;
|
||||
}
|
||||
}
|
||||
if (transform) {
|
||||
for (auto i = ptsCnt; i < pts.count; ++i) {
|
||||
pts[i] *= *transform;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
template<typename T>
|
||||
uint32_t _bsearch(T* frames, float frameNo)
|
||||
{
|
||||
|
@ -482,7 +407,7 @@ struct LottiePathSet : LottieProperty
|
|||
return (*frames)[frames->count];
|
||||
}
|
||||
|
||||
bool operator()(float frameNo, Array<PathCommand>& cmds, Array<Point>& pts, Matrix* transform, float roundness)
|
||||
bool operator()(float frameNo, Array<PathCommand>& cmds, Array<Point>& pts, Matrix* transform, const LottieRoundnessModifier* roundness)
|
||||
{
|
||||
PathSet* path = nullptr;
|
||||
LottieScalarFrame<PathSet>* frame = nullptr;
|
||||
|
@ -507,7 +432,8 @@ struct LottiePathSet : LottieProperty
|
|||
}
|
||||
|
||||
if (!interpolate) {
|
||||
if (roundness > ROUNDNESS_EPSILON) return _modifier(path->pts, path->ptsCnt, path->cmds, path->cmdsCnt, cmds, pts, transform, roundness);
|
||||
if (roundness) return roundness->modifyPath(path->cmds, path->cmdsCnt, path->pts, path->ptsCnt, cmds, pts, transform);
|
||||
|
||||
_copy(path, cmds);
|
||||
_copy(path, pts, transform);
|
||||
return true;
|
||||
|
@ -516,29 +442,29 @@ struct LottiePathSet : LottieProperty
|
|||
auto s = frame->value.pts;
|
||||
auto e = (frame + 1)->value.pts;
|
||||
|
||||
if (roundness > ROUNDNESS_EPSILON) {
|
||||
if (roundness) {
|
||||
auto interpPts = (Point*)malloc(frame->value.ptsCnt * sizeof(Point));
|
||||
auto p = interpPts;
|
||||
for (auto i = 0; i < frame->value.ptsCnt; ++i, ++s, ++e, ++p) {
|
||||
*p = lerp(*s, *e, t);
|
||||
if (transform) *p *= *transform;
|
||||
}
|
||||
_modifier(interpPts, frame->value.ptsCnt, frame->value.cmds, frame->value.cmdsCnt, cmds, pts, nullptr, roundness);
|
||||
roundness->modifyPath(frame->value.cmds, frame->value.cmdsCnt, interpPts, frame->value.ptsCnt, cmds, pts, nullptr);
|
||||
free(interpPts);
|
||||
return true;
|
||||
} else {
|
||||
for (auto i = 0; i < frame->value.ptsCnt; ++i, ++s, ++e) {
|
||||
auto pt = lerp(*s, *e, t);
|
||||
if (transform) pt *= *transform;
|
||||
pts.push(pt);
|
||||
}
|
||||
_copy(&frame->value, cmds);
|
||||
}
|
||||
|
||||
for (auto i = 0; i < frame->value.ptsCnt; ++i, ++s, ++e) {
|
||||
auto pt = lerp(*s, *e, t);
|
||||
if (transform) pt *= *transform;
|
||||
pts.push(pt);
|
||||
}
|
||||
_copy(&frame->value, cmds);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
bool operator()(float frameNo, Array<PathCommand>& cmds, Array<Point>& pts, Matrix* transform, float roundness, LottieExpressions* exps)
|
||||
bool operator()(float frameNo, Array<PathCommand>& cmds, Array<Point>& pts, Matrix* transform, const LottieRoundnessModifier* roundness, LottieExpressions* exps)
|
||||
{
|
||||
if (exps && exp) {
|
||||
if (exp->loop.mode != LottieExpression::LoopMode::None) frameNo = _loop(frames, frameNo, exp);
|
||||
|
|
Loading…
Add table
Reference in a new issue