mirror of
https://github.com/thorvg/thorvg.git
synced 2025-06-08 05:33:36 +00:00
lottie: revised the shape modifiers
- use a decoration style for path-modification chaining. - use a reusable buffer for intermediate path generation.
This commit is contained in:
parent
f4a2c922be
commit
2a4dfcd10e
6 changed files with 160 additions and 115 deletions
|
@ -233,7 +233,7 @@ static void _updateStroke(LottieStroke* stroke, float frameNo, RenderContext* ct
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
static bool _fragmented(LottieGroup* parent, LottieObject** child, Inlist<RenderContext>& contexts, RenderContext* ctx)
|
bool LottieBuilder::fragmented(LottieGroup* parent, LottieObject** child, Inlist<RenderContext>& contexts, RenderContext* ctx)
|
||||||
{
|
{
|
||||||
if (!ctx->reqFragment) return false;
|
if (!ctx->reqFragment) return false;
|
||||||
if (ctx->fragmenting) return true;
|
if (ctx->fragmenting) return true;
|
||||||
|
@ -249,7 +249,7 @@ static bool _fragmented(LottieGroup* parent, LottieObject** child, Inlist<Render
|
||||||
|
|
||||||
void LottieBuilder::updateSolidStroke(LottieGroup* parent, LottieObject** child, float frameNo, Inlist<RenderContext>& contexts, RenderContext* ctx)
|
void LottieBuilder::updateSolidStroke(LottieGroup* parent, LottieObject** child, float frameNo, Inlist<RenderContext>& contexts, RenderContext* ctx)
|
||||||
{
|
{
|
||||||
if (_fragmented(parent, child, contexts, ctx)) return;
|
if (fragmented(parent, child, contexts, ctx)) return;
|
||||||
|
|
||||||
auto stroke = static_cast<LottieSolidStroke*>(*child);
|
auto stroke = static_cast<LottieSolidStroke*>(*child);
|
||||||
|
|
||||||
|
@ -262,7 +262,7 @@ void LottieBuilder::updateSolidStroke(LottieGroup* parent, LottieObject** child,
|
||||||
|
|
||||||
void LottieBuilder::updateGradientStroke(LottieGroup* parent, LottieObject** child, float frameNo, Inlist<RenderContext>& contexts, RenderContext* ctx)
|
void LottieBuilder::updateGradientStroke(LottieGroup* parent, LottieObject** child, float frameNo, Inlist<RenderContext>& contexts, RenderContext* ctx)
|
||||||
{
|
{
|
||||||
if (_fragmented(parent, child, contexts, ctx)) return;
|
if (fragmented(parent, child, contexts, ctx)) return;
|
||||||
|
|
||||||
auto stroke = static_cast<LottieGradientStroke*>(*child);
|
auto stroke = static_cast<LottieGradientStroke*>(*child);
|
||||||
|
|
||||||
|
@ -274,7 +274,7 @@ void LottieBuilder::updateGradientStroke(LottieGroup* parent, LottieObject** chi
|
||||||
|
|
||||||
void LottieBuilder::updateSolidFill(LottieGroup* parent, LottieObject** child, float frameNo, Inlist<RenderContext>& contexts, RenderContext* ctx)
|
void LottieBuilder::updateSolidFill(LottieGroup* parent, LottieObject** child, float frameNo, Inlist<RenderContext>& contexts, RenderContext* ctx)
|
||||||
{
|
{
|
||||||
if (_fragmented(parent, child, contexts, ctx)) return;
|
if (fragmented(parent, child, contexts, ctx)) return;
|
||||||
|
|
||||||
auto fill = static_cast<LottieSolidFill*>(*child);
|
auto fill = static_cast<LottieSolidFill*>(*child);
|
||||||
|
|
||||||
|
@ -289,7 +289,7 @@ void LottieBuilder::updateSolidFill(LottieGroup* parent, LottieObject** child, f
|
||||||
|
|
||||||
void LottieBuilder::updateGradientFill(LottieGroup* parent, LottieObject** child, float frameNo, Inlist<RenderContext>& contexts, RenderContext* ctx)
|
void LottieBuilder::updateGradientFill(LottieGroup* parent, LottieObject** child, float frameNo, Inlist<RenderContext>& contexts, RenderContext* ctx)
|
||||||
{
|
{
|
||||||
if (_fragmented(parent, child, contexts, ctx)) return;
|
if (fragmented(parent, child, contexts, ctx)) return;
|
||||||
|
|
||||||
auto fill = static_cast<LottieGradientFill*>(*child);
|
auto fill = static_cast<LottieGradientFill*>(*child);
|
||||||
|
|
||||||
|
@ -547,11 +547,11 @@ void LottieBuilder::updatePath(LottieGroup* parent, LottieObject** child, float
|
||||||
if (!ctx->repeaters.empty()) {
|
if (!ctx->repeaters.empty()) {
|
||||||
auto shape = path->pooling();
|
auto shape = path->pooling();
|
||||||
shape->reset();
|
shape->reset();
|
||||||
path->pathset(frameNo, SHAPE(shape)->rs.path, ctx->transform, exps, ctx->roundness, ctx->offset);
|
path->pathset(frameNo, SHAPE(shape)->rs.path, ctx->transform, exps, ctx->modifier);
|
||||||
_repeat(parent, shape, ctx);
|
_repeat(parent, shape, ctx);
|
||||||
} else {
|
} else {
|
||||||
_draw(parent, path, ctx);
|
_draw(parent, path, ctx);
|
||||||
if (path->pathset(frameNo, SHAPE(ctx->merging)->rs.path, ctx->transform, exps, ctx->roundness, ctx->offset)) {
|
if (path->pathset(frameNo, SHAPE(ctx->merging)->rs.path, ctx->transform, exps, ctx->modifier)) {
|
||||||
PAINT(ctx->merging)->update(RenderUpdateFlag::Path);
|
PAINT(ctx->merging)->update(RenderUpdateFlag::Path);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -674,13 +674,7 @@ void LottieBuilder::updateStar(LottiePolyStar* star, float frameNo, Matrix* tran
|
||||||
}
|
}
|
||||||
shape->close();
|
shape->close();
|
||||||
|
|
||||||
if (roundedCorner) {
|
if (ctx->modifier) ctx->modifier->modifyPolystar(SHAPE(shape)->rs.path, SHAPE(merging)->rs.path, outerRoundness, hasRoundness);
|
||||||
if (ctx->offset) {
|
|
||||||
buffer.clear();
|
|
||||||
ctx->roundness->modifyPolystar(SHAPE(shape)->rs.path, buffer, outerRoundness, hasRoundness);
|
|
||||||
ctx->offset->modifyPolystar(buffer, SHAPE(merging)->rs.path);
|
|
||||||
} else ctx->roundness->modifyPolystar(SHAPE(shape)->rs.path, SHAPE(merging)->rs.path, outerRoundness, hasRoundness);
|
|
||||||
} else if (ctx->offset) ctx->offset->modifyPolystar(SHAPE(shape)->rs.path, SHAPE(merging)->rs.path);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -758,13 +752,7 @@ void LottieBuilder::updatePolygon(LottieGroup* parent, LottiePolyStar* star, flo
|
||||||
}
|
}
|
||||||
shape->close();
|
shape->close();
|
||||||
|
|
||||||
if (roundedCorner) {
|
if (ctx->modifier) ctx->modifier->modifyPolystar(SHAPE(shape)->rs.path, SHAPE(merging)->rs.path, 0.0f, false);
|
||||||
if (ctx->offset) {
|
|
||||||
buffer.clear();
|
|
||||||
ctx->roundness->modifyPolystar(SHAPE(shape)->rs.path, buffer, 0.0f, false);
|
|
||||||
ctx->offset->modifyPolystar(buffer, SHAPE(merging)->rs.path);
|
|
||||||
} else ctx->roundness->modifyPolystar(SHAPE(shape)->rs.path, SHAPE(merging)->rs.path, 0.0f, false);
|
|
||||||
} else if (ctx->offset) ctx->offset->modifyPolystar(SHAPE(shape)->rs.path, SHAPE(merging)->rs.path);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -803,8 +791,10 @@ void LottieBuilder::updateRoundedCorner(TVG_UNUSED LottieGroup* parent, LottieOb
|
||||||
auto r = roundedCorner->radius(frameNo, exps);
|
auto r = roundedCorner->radius(frameNo, exps);
|
||||||
if (r < LottieRoundnessModifier::ROUNDNESS_EPSILON) return;
|
if (r < LottieRoundnessModifier::ROUNDNESS_EPSILON) return;
|
||||||
|
|
||||||
if (!ctx->roundness) ctx->roundness = new LottieRoundnessModifier(r);
|
if (!ctx->roundness) ctx->roundness = new LottieRoundnessModifier(&buffer, r);
|
||||||
else if (ctx->roundness->r < r) ctx->roundness->r = r;
|
else if (ctx->roundness->r < r) ctx->roundness->r = r;
|
||||||
|
|
||||||
|
ctx->update(ctx->roundness);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -812,6 +802,8 @@ void LottieBuilder::updateOffsetPath(TVG_UNUSED LottieGroup* parent, LottieObjec
|
||||||
{
|
{
|
||||||
auto offset = static_cast<LottieOffsetPath*>(*child);
|
auto offset = static_cast<LottieOffsetPath*>(*child);
|
||||||
if (!ctx->offset) ctx->offset = new LottieOffsetModifier(offset->offset(frameNo, exps), offset->miterLimit(frameNo, exps), offset->join);
|
if (!ctx->offset) ctx->offset = new LottieOffsetModifier(offset->offset(frameNo, exps), offset->miterLimit(frameNo, exps), offset->join);
|
||||||
|
|
||||||
|
ctx->update(ctx->offset);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -1261,7 +1253,7 @@ void LottieBuilder::updateMasks(LottieLayer* layer, float frameNo)
|
||||||
} else {
|
} else {
|
||||||
//TODO: Once path direction support is implemented, ensure that the direction is ignored here
|
//TODO: Once path direction support is implemented, ensure that the direction is ignored here
|
||||||
auto offset = LottieOffsetModifier(expand);
|
auto offset = LottieOffsetModifier(expand);
|
||||||
mask->pathset(frameNo, SHAPE(pShape)->rs.path, nullptr, exps, nullptr, &offset);
|
mask->pathset(frameNo, SHAPE(pShape)->rs.path, nullptr, exps, &offset);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (fastTrack) return;
|
if (fastTrack) return;
|
||||||
|
@ -1309,7 +1301,7 @@ void LottieBuilder::updateStrokeEffect(LottieLayer* layer, LottieFxStroke* effec
|
||||||
auto idx = static_cast<uint32_t>(effect->mask(frameNo) - 1);
|
auto idx = static_cast<uint32_t>(effect->mask(frameNo) - 1);
|
||||||
if (idx < 0 || idx >= layer->masks.count) return;
|
if (idx < 0 || idx >= layer->masks.count) return;
|
||||||
auto mask = layer->masks[idx];
|
auto mask = layer->masks[idx];
|
||||||
mask->pathset(frameNo, SHAPE(shape)->rs.path, nullptr, exps, nullptr, nullptr);
|
mask->pathset(frameNo, SHAPE(shape)->rs.path, nullptr, exps);
|
||||||
}
|
}
|
||||||
|
|
||||||
shape->transform(layer->cache.matrix);
|
shape->transform(layer->cache.matrix);
|
||||||
|
|
|
@ -57,6 +57,7 @@ struct RenderContext
|
||||||
Matrix* transform = nullptr;
|
Matrix* transform = nullptr;
|
||||||
LottieRoundnessModifier* roundness = nullptr;
|
LottieRoundnessModifier* roundness = nullptr;
|
||||||
LottieOffsetModifier* offset = nullptr;
|
LottieOffsetModifier* offset = nullptr;
|
||||||
|
LottieModifier* modifier = nullptr;
|
||||||
bool fragmenting = false; //render context has been fragmented by filling
|
bool fragmenting = false; //render context has been fragmented by filling
|
||||||
bool reqFragment = false; //requirement to fragment the render context
|
bool reqFragment = false; //requirement to fragment the render context
|
||||||
|
|
||||||
|
@ -81,8 +82,20 @@ struct RenderContext
|
||||||
propagator->ref();
|
propagator->ref();
|
||||||
this->propagator = propagator;
|
this->propagator = propagator;
|
||||||
repeaters = rhs.repeaters;
|
repeaters = rhs.repeaters;
|
||||||
if (rhs.roundness) roundness = new LottieRoundnessModifier(rhs.roundness->r);
|
if (rhs.roundness) {
|
||||||
if (rhs.offset) offset = new LottieOffsetModifier(rhs.offset->offset, rhs.offset->miterLimit, rhs.offset->join);
|
roundness = new LottieRoundnessModifier(rhs.roundness->buffer, rhs.roundness->r);
|
||||||
|
update(roundness);
|
||||||
|
}
|
||||||
|
if (rhs.offset) {
|
||||||
|
offset = new LottieOffsetModifier(rhs.offset->offset, rhs.offset->miterLimit, rhs.offset->join);
|
||||||
|
update(offset);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void update(LottieModifier* next)
|
||||||
|
{
|
||||||
|
if (modifier) modifier = modifier->decorate(next);
|
||||||
|
else modifier = next;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -108,6 +121,7 @@ struct LottieBuilder
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void appendRect(Shape* shape, Point& pos, Point& size, float r, bool clockwise, RenderContext* ctx);
|
void appendRect(Shape* shape, Point& pos, Point& size, float r, bool clockwise, RenderContext* ctx);
|
||||||
|
bool fragmented(LottieGroup* parent, LottieObject** child, Inlist<RenderContext>& contexts, RenderContext* ctx);
|
||||||
|
|
||||||
void updateStrokeEffect(LottieLayer* layer, LottieFxStroke* effect, float frameNo);
|
void updateStrokeEffect(LottieLayer* layer, LottieFxStroke* effect, float frameNo);
|
||||||
void updateEffect(LottieLayer* layer, float frameNo);
|
void updateEffect(LottieLayer* layer, float frameNo);
|
||||||
|
|
|
@ -29,8 +29,7 @@
|
||||||
struct LottieExpression;
|
struct LottieExpression;
|
||||||
struct LottieComposition;
|
struct LottieComposition;
|
||||||
struct LottieLayer;
|
struct LottieLayer;
|
||||||
struct LottieRoundnessModifier;
|
struct LottieModifier;
|
||||||
struct LottieOffsetModifier;
|
|
||||||
|
|
||||||
#ifdef THORVG_LOTTIE_EXPRESSIONS_SUPPORT
|
#ifdef THORVG_LOTTIE_EXPRESSIONS_SUPPORT
|
||||||
|
|
||||||
|
@ -112,13 +111,13 @@ public:
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename Property>
|
template<typename Property>
|
||||||
bool result(float frameNo, RenderPath& out, Matrix* transform, LottieRoundnessModifier* roundness, LottieOffsetModifier* offset, LottieExpression* exp)
|
bool result(float frameNo, RenderPath& out, Matrix* transform, LottieModifier* modifier, LottieExpression* exp)
|
||||||
{
|
{
|
||||||
auto bm_rt = evaluate(frameNo, exp);
|
auto bm_rt = evaluate(frameNo, exp);
|
||||||
if (jerry_value_is_undefined(bm_rt)) return false;
|
if (jerry_value_is_undefined(bm_rt)) return false;
|
||||||
|
|
||||||
if (auto pathset = static_cast<Property*>(jerry_object_get_native_ptr(bm_rt, nullptr))) {
|
if (auto pathset = static_cast<Property*>(jerry_object_get_native_ptr(bm_rt, nullptr))) {
|
||||||
(*pathset)(frameNo, out, transform, nullptr, roundness, offset);
|
(*pathset)(frameNo, out, transform, nullptr, modifier);
|
||||||
}
|
}
|
||||||
jerry_value_free(bm_rt);
|
jerry_value_free(bm_rt);
|
||||||
return true;
|
return true;
|
||||||
|
@ -157,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 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 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 Fill*, TVG_UNUSED LottieExpression*) { return false; }
|
||||||
template<typename Property> bool result(TVG_UNUSED float, TVG_UNUSED RenderPath&, TVG_UNUSED Matrix*, TVG_UNUSED LottieRoundnessModifier*, TVG_UNUSED LottieOffsetModifier*, TVG_UNUSED LottieExpression*) { return false; }
|
template<typename Property> bool result(TVG_UNUSED float, TVG_UNUSED RenderPath&, TVG_UNUSED Matrix*, TVG_UNUSED LottieModifier*, TVG_UNUSED LottieExpression*) { return false; }
|
||||||
void update(TVG_UNUSED float) {}
|
void update(TVG_UNUSED float) {}
|
||||||
static LottieExpressions* instance() { return nullptr; }
|
static LottieExpressions* instance() { return nullptr; }
|
||||||
static void retrieve(TVG_UNUSED LottieExpressions* instance) {}
|
static void retrieve(TVG_UNUSED LottieExpressions* instance) {}
|
||||||
|
|
|
@ -96,7 +96,7 @@ static bool _clockwise(Point* pts, uint32_t n)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void LottieOffsetModifier::corner(RenderPath& out, Line& line, Line& nextLine, uint32_t movetoOutIndex, bool nextClose) const
|
void LottieOffsetModifier::corner(RenderPath& out, Line& line, Line& nextLine, uint32_t movetoOutIndex, bool nextClose)
|
||||||
{
|
{
|
||||||
bool inside{};
|
bool inside{};
|
||||||
Point intersect{};
|
Point intersect{};
|
||||||
|
@ -127,7 +127,7 @@ void LottieOffsetModifier::corner(RenderPath& out, Line& line, Line& nextLine, u
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void LottieOffsetModifier::line(RenderPath& out, PathCommand* inCmds, uint32_t inCmdsCnt, Point* inPts, uint32_t& curPt, uint32_t curCmd, State& state, float offset, bool degenerated) const
|
void LottieOffsetModifier::line(RenderPath& out, PathCommand* inCmds, uint32_t inCmdsCnt, Point* inPts, uint32_t& curPt, uint32_t curCmd, State& state, float offset, bool degenerated)
|
||||||
{
|
{
|
||||||
if (tvg::zero(inPts[curPt - 1] - inPts[curPt])) {
|
if (tvg::zero(inPts[curPt - 1] - inPts[curPt])) {
|
||||||
++curPt;
|
++curPt;
|
||||||
|
@ -172,19 +172,23 @@ void LottieOffsetModifier::line(RenderPath& out, PathCommand* inCmds, uint32_t i
|
||||||
/* External Class Implementation */
|
/* External Class Implementation */
|
||||||
/************************************************************************/
|
/************************************************************************/
|
||||||
|
|
||||||
bool LottieRoundnessModifier::modifyPath(PathCommand* inCmds, uint32_t inCmdsCnt, Point* inPts, uint32_t inPtsCnt, Matrix* transform, RenderPath& out) const
|
bool LottieRoundnessModifier::modifyPath(PathCommand* inCmds, uint32_t inCmdsCnt, Point* inPts, uint32_t inPtsCnt, Matrix* transform, RenderPath& out)
|
||||||
{
|
{
|
||||||
out.cmds.reserve(inCmdsCnt * 2);
|
buffer->clear();
|
||||||
out.pts.reserve((uint32_t)(inPtsCnt * 1.5));
|
|
||||||
auto ptsCnt = out.pts.count;
|
auto& path = (next) ? *buffer : out;
|
||||||
|
|
||||||
|
path.cmds.reserve(inCmdsCnt * 2);
|
||||||
|
path.pts.reserve((uint32_t)(inPtsCnt * 1.5));
|
||||||
|
auto pivot = path.pts.count;
|
||||||
|
|
||||||
uint32_t startIndex = 0;
|
uint32_t startIndex = 0;
|
||||||
for (uint32_t iCmds = 0, iPts = 0; iCmds < inCmdsCnt; ++iCmds) {
|
for (uint32_t iCmds = 0, iPts = 0; iCmds < inCmdsCnt; ++iCmds) {
|
||||||
switch (inCmds[iCmds]) {
|
switch (inCmds[iCmds]) {
|
||||||
case PathCommand::MoveTo: {
|
case PathCommand::MoveTo: {
|
||||||
startIndex = out.pts.count;
|
startIndex = path.pts.count;
|
||||||
out.cmds.push(PathCommand::MoveTo);
|
path.cmds.push(PathCommand::MoveTo);
|
||||||
out.pts.push(inPts[iPts++]);
|
path.pts.push(inPts[iPts++]);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case PathCommand::CubicTo: {
|
case PathCommand::CubicTo: {
|
||||||
|
@ -196,52 +200,59 @@ bool LottieRoundnessModifier::modifyPath(PathCommand* inCmds, uint32_t inCmdsCnt
|
||||||
if (inCmds[iCmds + 1] == PathCommand::CubicTo &&
|
if (inCmds[iCmds + 1] == PathCommand::CubicTo &&
|
||||||
tvg::zero(inPts[iPts + 2] - inPts[iPts + 3]) &&
|
tvg::zero(inPts[iPts + 2] - inPts[iPts + 3]) &&
|
||||||
tvg::zero(inPts[iPts + 4] - inPts[iPts + 5])) {
|
tvg::zero(inPts[iPts + 4] - inPts[iPts + 5])) {
|
||||||
_roundCorner(out.cmds, out.pts, prev, curr, inPts[iPts + 5], r);
|
_roundCorner(path.cmds, path.pts, prev, curr, inPts[iPts + 5], r);
|
||||||
iPts += 3;
|
iPts += 3;
|
||||||
break;
|
break;
|
||||||
} else if (inCmds[iCmds + 1] == PathCommand::Close) {
|
} else if (inCmds[iCmds + 1] == PathCommand::Close) {
|
||||||
_roundCorner(out.cmds, out.pts, prev, curr, inPts[2], r);
|
_roundCorner(path.cmds, path.pts, prev, curr, inPts[2], r);
|
||||||
out.pts[startIndex] = out.pts.last();
|
path.pts[startIndex] = path.pts.last();
|
||||||
iPts += 3;
|
iPts += 3;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
out.cmds.push(PathCommand::CubicTo);
|
path.cmds.push(PathCommand::CubicTo);
|
||||||
out.pts.push(inPts[iPts++]);
|
path.pts.push(inPts[iPts++]);
|
||||||
out.pts.push(inPts[iPts++]);
|
path.pts.push(inPts[iPts++]);
|
||||||
out.pts.push(inPts[iPts++]);
|
path.pts.push(inPts[iPts++]);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case PathCommand::Close: {
|
case PathCommand::Close: {
|
||||||
out.cmds.push(PathCommand::Close);
|
path.cmds.push(PathCommand::Close);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
default: break;
|
default: break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (transform) {
|
if (transform) {
|
||||||
for (auto i = ptsCnt; i < out.pts.count; ++i) {
|
for (auto i = pivot; i < path.pts.count; ++i) {
|
||||||
out.pts[i] *= *transform;
|
path.pts[i] *= *transform;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (next) return next->modifyPath(path.cmds.data, path.cmds.count, path.pts.data, path.pts.count, transform, out);
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
bool LottieRoundnessModifier::modifyPolystar(RenderPath& in, RenderPath& out, float outerRoundness, bool hasRoundness) const
|
bool LottieRoundnessModifier::modifyPolystar(RenderPath& in, RenderPath& out, float outerRoundness, bool hasRoundness)
|
||||||
{
|
{
|
||||||
static constexpr auto ROUNDED_POLYSTAR_MAGIC_NUMBER = 0.47829f;
|
constexpr auto ROUNDED_POLYSTAR_MAGIC_NUMBER = 0.47829f;
|
||||||
|
|
||||||
|
buffer->clear();
|
||||||
|
|
||||||
|
auto& path = (next) ? *buffer : out;
|
||||||
|
|
||||||
auto len = length(in.pts[1] - in.pts[2]);
|
auto len = length(in.pts[1] - in.pts[2]);
|
||||||
auto r = len > 0.0f ? ROUNDED_POLYSTAR_MAGIC_NUMBER * std::min(len * 0.5f, this->r) / len : 0.0f;
|
auto r = len > 0.0f ? ROUNDED_POLYSTAR_MAGIC_NUMBER * std::min(len * 0.5f, this->r) / len : 0.0f;
|
||||||
|
|
||||||
if (hasRoundness) {
|
if (hasRoundness) {
|
||||||
out.cmds.grow((uint32_t)(1.5 * in.cmds.count));
|
path.cmds.grow((uint32_t)(1.5 * in.cmds.count));
|
||||||
out.pts.grow((uint32_t)(4.5 * in.cmds.count));
|
path.pts.grow((uint32_t)(4.5 * in.cmds.count));
|
||||||
|
|
||||||
int start = 3 * tvg::zero(outerRoundness);
|
int start = 3 * tvg::zero(outerRoundness);
|
||||||
out.cmds.push(PathCommand::MoveTo);
|
path.cmds.push(PathCommand::MoveTo);
|
||||||
out.pts.push(in.pts[start]);
|
path.pts.push(in.pts[start]);
|
||||||
|
|
||||||
for (uint32_t i = 1 + start; i < in.pts.count; i += 6) {
|
for (uint32_t i = 1 + start; i < in.pts.count; i += 6) {
|
||||||
auto& prev = in.pts[i];
|
auto& prev = in.pts[i];
|
||||||
|
@ -256,21 +267,21 @@ bool LottieRoundnessModifier::modifyPolystar(RenderPath& in, RenderPath& out, fl
|
||||||
auto p2 = curr - dNext;
|
auto p2 = curr - dNext;
|
||||||
auto p3 = curr - 2.0f * dNext;
|
auto p3 = curr - 2.0f * dNext;
|
||||||
|
|
||||||
out.cmds.push(PathCommand::CubicTo);
|
path.cmds.push(PathCommand::CubicTo);
|
||||||
out.pts.push(prev); out.pts.push(p0); out.pts.push(p0);
|
path.pts.push(prev); path.pts.push(p0); path.pts.push(p0);
|
||||||
out.cmds.push(PathCommand::CubicTo);
|
path.cmds.push(PathCommand::CubicTo);
|
||||||
out.pts.push(p1); out.pts.push(p2); out.pts.push(p3);
|
path.pts.push(p1); path.pts.push(p2); path.pts.push(p3);
|
||||||
out.cmds.push(PathCommand::CubicTo);
|
path.cmds.push(PathCommand::CubicTo);
|
||||||
out.pts.push(p3); out.pts.push(next); out.pts.push(nextCtrl);
|
path.pts.push(p3); path.pts.push(next); path.pts.push(nextCtrl);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
out.cmds.grow(2 * in.cmds.count);
|
path.cmds.grow(2 * in.cmds.count);
|
||||||
out.pts.grow(4 * in.cmds.count);
|
path.pts.grow(4 * in.cmds.count);
|
||||||
|
|
||||||
auto dPrev = r * (in.pts[1] - in.pts[0]);
|
auto dPrev = r * (in.pts[1] - in.pts[0]);
|
||||||
auto p = in.pts[0] + 2.0f * dPrev;
|
auto p = in.pts[0] + 2.0f * dPrev;
|
||||||
out.cmds.push(PathCommand::MoveTo);
|
path.cmds.push(PathCommand::MoveTo);
|
||||||
out.pts.push(p);
|
path.pts.push(p);
|
||||||
|
|
||||||
for (uint32_t i = 1; i < in.pts.count; ++i) {
|
for (uint32_t i = 1; i < in.pts.count; ++i) {
|
||||||
auto& curr = in.pts[i];
|
auto& curr = in.pts[i];
|
||||||
|
@ -282,28 +293,33 @@ bool LottieRoundnessModifier::modifyPolystar(RenderPath& in, RenderPath& out, fl
|
||||||
auto p2 = curr - dNext;
|
auto p2 = curr - dNext;
|
||||||
auto p3 = curr - 2.0f * dNext;
|
auto p3 = curr - 2.0f * dNext;
|
||||||
|
|
||||||
out.cmds.push(PathCommand::LineTo);
|
path.cmds.push(PathCommand::LineTo);
|
||||||
out.pts.push(p0);
|
path.pts.push(p0);
|
||||||
out.cmds.push(PathCommand::CubicTo);
|
path.cmds.push(PathCommand::CubicTo);
|
||||||
out.pts.push(p1); out.pts.push(p2); out.pts.push(p3);
|
path.pts.push(p1); path.pts.push(p2); path.pts.push(p3);
|
||||||
|
|
||||||
dPrev = -1.0f * dNext;
|
dPrev = -1.0f * dNext;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
out.cmds.push(PathCommand::Close);
|
path.cmds.push(PathCommand::Close);
|
||||||
|
|
||||||
|
if (next) return next->modifyPolystar(path, out, outerRoundness, hasRoundness);
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
bool LottieRoundnessModifier::modifyRect(Point& size, float& r) const
|
bool LottieRoundnessModifier::modifyRect(Point& size, float& r)
|
||||||
{
|
{
|
||||||
r = std::min(this->r, std::max(size.x, size.y) * 0.5f);
|
r = std::min(this->r, std::max(size.x, size.y) * 0.5f);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
bool LottieOffsetModifier::modifyPath(PathCommand* inCmds, uint32_t inCmdsCnt, Point* inPts, uint32_t inPtsCnt, RenderPath& out) const
|
bool LottieOffsetModifier::modifyPath(PathCommand* inCmds, uint32_t inCmdsCnt, Point* inPts, uint32_t inPtsCnt, TVG_UNUSED Matrix* transform, RenderPath& out)
|
||||||
{
|
{
|
||||||
|
if (next) TVGERR("LOTTIE", "Offset has a next modifier?");
|
||||||
|
|
||||||
out.cmds.reserve(inCmdsCnt * 2);
|
out.cmds.reserve(inCmdsCnt * 2);
|
||||||
out.pts.reserve(inPtsCnt * (join == StrokeJoin::Round ? 4 : 2));
|
out.pts.reserve(inPtsCnt * (join == StrokeJoin::Round ? 4 : 2));
|
||||||
|
|
||||||
|
@ -376,18 +392,19 @@ bool LottieOffsetModifier::modifyPath(PathCommand* inCmds, uint32_t inCmdsCnt, P
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
bool LottieOffsetModifier::modifyPolystar(RenderPath& in, RenderPath& out) const {
|
bool LottieOffsetModifier::modifyPolystar(RenderPath& in, RenderPath& out, TVG_UNUSED float, TVG_UNUSED bool)
|
||||||
return modifyPath(in.cmds.data, in.cmds.count, in.pts.data, in.pts.count, out);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
bool LottieOffsetModifier::modifyRect(RenderPath& in, RenderPath& out) const
|
|
||||||
{
|
{
|
||||||
return modifyPath(in.cmds.data, in.cmds.count, in.pts.data, in.pts.count, out);
|
return modifyPath(in.cmds.data, in.cmds.count, in.pts.data, in.pts.count, nullptr, out);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
bool LottieOffsetModifier::modifyEllipse(Point& radius) const
|
bool LottieOffsetModifier::modifyRect(RenderPath& in, RenderPath& out)
|
||||||
|
{
|
||||||
|
return modifyPath(in.cmds.data, in.cmds.count, in.pts.data, in.pts.count, nullptr, out);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
bool LottieOffsetModifier::modifyEllipse(Point& radius)
|
||||||
{
|
{
|
||||||
radius.x += offset;
|
radius.x += offset;
|
||||||
radius.y += offset;
|
radius.y += offset;
|
||||||
|
|
|
@ -28,31 +28,69 @@
|
||||||
#include "tvgMath.h"
|
#include "tvgMath.h"
|
||||||
#include "tvgRender.h"
|
#include "tvgRender.h"
|
||||||
|
|
||||||
struct LottieRoundnessModifier
|
|
||||||
|
struct LottieModifier
|
||||||
|
{
|
||||||
|
enum Type : uint8_t {Roundness = 0, Offset};
|
||||||
|
|
||||||
|
LottieModifier* next = nullptr;
|
||||||
|
Type type;
|
||||||
|
|
||||||
|
virtual ~LottieModifier() {}
|
||||||
|
|
||||||
|
virtual bool modifyPath(PathCommand* inCmds, uint32_t inCmdsCnt, Point* inPts, uint32_t inPtsCnt, Matrix* transform, RenderPath& out) = 0;
|
||||||
|
virtual bool modifyPolystar(RenderPath& in, RenderPath& out, float outerRoundness, bool hasRoundness) = 0;
|
||||||
|
|
||||||
|
LottieModifier* decorate(LottieModifier* next)
|
||||||
|
{
|
||||||
|
/* TODO: build the decorative chaining here.
|
||||||
|
currently we only have roundness and offset. */
|
||||||
|
|
||||||
|
//roundness -> offset
|
||||||
|
if (next->type == Roundness) {
|
||||||
|
next->next = this;
|
||||||
|
return next;
|
||||||
|
}
|
||||||
|
|
||||||
|
//just in the order.
|
||||||
|
this->next = next;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
struct LottieRoundnessModifier : LottieModifier
|
||||||
{
|
{
|
||||||
static constexpr float ROUNDNESS_EPSILON = 1.0f;
|
static constexpr float ROUNDNESS_EPSILON = 1.0f;
|
||||||
|
|
||||||
|
RenderPath* buffer;
|
||||||
float r;
|
float r;
|
||||||
|
|
||||||
LottieRoundnessModifier(float r) : r(r) {};
|
LottieRoundnessModifier(RenderPath* buffer, float r) : buffer(buffer), r(r)
|
||||||
|
{
|
||||||
|
type = Roundness;
|
||||||
|
}
|
||||||
|
|
||||||
bool modifyPath(PathCommand* inCmds, uint32_t inCmdsCnt, Point* inPts, uint32_t inPtsCnt, Matrix* transform, RenderPath& out) const;
|
bool modifyPath(PathCommand* inCmds, uint32_t inCmdsCnt, Point* inPts, uint32_t inPtsCnt, Matrix* transform, RenderPath& out) override;
|
||||||
bool modifyPolystar(RenderPath& in, RenderPath& out, float outerRoundness, bool hasRoundness) const;
|
bool modifyPolystar(RenderPath& in, RenderPath& out, float outerRoundness, bool hasRoundness) override;
|
||||||
bool modifyRect(Point& size, float& r) const;
|
bool modifyRect(Point& size, float& r);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
struct LottieOffsetModifier
|
struct LottieOffsetModifier : LottieModifier
|
||||||
{
|
{
|
||||||
float offset;
|
float offset;
|
||||||
float miterLimit;
|
float miterLimit;
|
||||||
StrokeJoin join;
|
StrokeJoin join;
|
||||||
|
|
||||||
LottieOffsetModifier(float offset, float miter = 4.0f, StrokeJoin join = StrokeJoin::Round) : offset(offset), miterLimit(miter), join(join) {}
|
LottieOffsetModifier(float offset, float miter = 4.0f, StrokeJoin join = StrokeJoin::Round) : offset(offset), miterLimit(miter), join(join)
|
||||||
|
{
|
||||||
|
type = Offset;
|
||||||
|
}
|
||||||
|
|
||||||
bool modifyPath(PathCommand* inCmds, uint32_t inCmdsCnt, Point* inPts, uint32_t inPtsCnt, RenderPath& out) const;
|
bool modifyPath(PathCommand* inCmds, uint32_t inCmdsCnt, Point* inPts, uint32_t inPtsCnt, Matrix* transform, RenderPath& out) override;
|
||||||
bool modifyPolystar(RenderPath& in, RenderPath& out) const;
|
bool modifyPolystar(RenderPath& in, RenderPath& out, float outerRoundness, bool hasRoundness) override;
|
||||||
bool modifyRect(RenderPath& in, RenderPath& out) const;
|
bool modifyRect(RenderPath& in, RenderPath& out);
|
||||||
bool modifyEllipse(Point& radius) const;
|
bool modifyEllipse(Point& radius);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
struct State
|
struct State
|
||||||
|
@ -64,8 +102,8 @@ private:
|
||||||
uint32_t movetoInIndex = 0;
|
uint32_t movetoInIndex = 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
void line(RenderPath& out, PathCommand* inCmds, uint32_t inCmdsCnt, Point* inPts, uint32_t& curPt, uint32_t curCmd, State& state, float offset, bool degenerated) const;
|
void line(RenderPath& out, PathCommand* inCmds, uint32_t inCmdsCnt, Point* inPts, uint32_t& curPt, uint32_t curCmd, State& state, float offset, bool degenerated);
|
||||||
void corner(RenderPath& out, Line& line, Line& nextLine, uint32_t movetoIndex, bool nextClose) const;
|
void corner(RenderPath& out, Line& line, Line& nextLine, uint32_t movetoIndex, bool nextClose);
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif
|
#endif
|
|
@ -467,7 +467,7 @@ struct LottiePathSet : LottieProperty
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool modifiedPath(float frameNo, RenderPath& out, Matrix* transform, const LottieRoundnessModifier* roundness, const LottieOffsetModifier* offset)
|
bool modifiedPath(float frameNo, RenderPath& out, Matrix* transform, LottieModifier* modifier)
|
||||||
{
|
{
|
||||||
PathSet* path;
|
PathSet* path;
|
||||||
LottieScalarFrame<PathSet>* frame;
|
LottieScalarFrame<PathSet>* frame;
|
||||||
|
@ -475,17 +475,7 @@ struct LottiePathSet : LottieProperty
|
||||||
float t;
|
float t;
|
||||||
|
|
||||||
if (dispatch(frameNo, path, frame, t)) {
|
if (dispatch(frameNo, path, frame, t)) {
|
||||||
if (roundness) {
|
if (modifier) return modifier->modifyPath(path->cmds, path->cmdsCnt, path->pts, path->ptsCnt, transform, out);
|
||||||
if (offset) {
|
|
||||||
temp.cmds = Array<PathCommand>(path->cmdsCnt);
|
|
||||||
temp.pts = Array<Point>(path->ptsCnt);
|
|
||||||
roundness->modifyPath(path->cmds, path->cmdsCnt, path->pts, path->ptsCnt, transform, temp);
|
|
||||||
return offset->modifyPath(temp.cmds.data, temp.cmds.count, temp.pts.data, temp.pts.count, out);
|
|
||||||
}
|
|
||||||
return roundness->modifyPath(path->cmds, path->cmdsCnt, path->pts, path->ptsCnt, transform, out);
|
|
||||||
}
|
|
||||||
if (offset) return offset->modifyPath(path->cmds, path->cmdsCnt, path->pts, path->ptsCnt, out);
|
|
||||||
|
|
||||||
_copy(path, out.cmds);
|
_copy(path, out.cmds);
|
||||||
_copy(path, out.pts, transform);
|
_copy(path, out.pts, transform);
|
||||||
return true;
|
return true;
|
||||||
|
@ -502,12 +492,7 @@ struct LottiePathSet : LottieProperty
|
||||||
if (transform) *p *= *transform;
|
if (transform) *p *= *transform;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (roundness) {
|
if (modifier) modifier->modifyPath(frame->value.cmds, frame->value.cmdsCnt, interpPts, frame->value.ptsCnt, nullptr, out);
|
||||||
if (offset) {
|
|
||||||
roundness->modifyPath(frame->value.cmds, frame->value.cmdsCnt, interpPts, frame->value.ptsCnt, nullptr, temp);
|
|
||||||
offset->modifyPath(temp.cmds.data, temp.cmds.count, temp.pts.data, temp.pts.count, out);
|
|
||||||
} else roundness->modifyPath(frame->value.cmds, frame->value.cmdsCnt, interpPts, frame->value.ptsCnt, nullptr, out);
|
|
||||||
} else if (offset) offset->modifyPath(frame->value.cmds, frame->value.cmdsCnt, interpPts, frame->value.ptsCnt, out);
|
|
||||||
|
|
||||||
free(interpPts);
|
free(interpPts);
|
||||||
|
|
||||||
|
@ -539,15 +524,15 @@ struct LottiePathSet : LottieProperty
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool operator()(float frameNo, RenderPath& out, Matrix* transform, LottieExpressions* exps, LottieRoundnessModifier* roundness = nullptr, LottieOffsetModifier* offset = nullptr)
|
bool operator()(float frameNo, RenderPath& out, Matrix* transform, LottieExpressions* exps, LottieModifier* modifier = nullptr)
|
||||||
{
|
{
|
||||||
//overriding with expressions
|
//overriding with expressions
|
||||||
if (exps && exp) {
|
if (exps && exp) {
|
||||||
frameNo = _loop(frames, frameNo, exp);
|
frameNo = _loop(frames, frameNo, exp);
|
||||||
if (exps->result<LottiePathSet>(frameNo, out, transform, roundness, offset, exp)) return true;
|
if (exps->result<LottiePathSet>(frameNo, out, transform, modifier, exp)) return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (roundness || offset) return modifiedPath(frameNo, out, transform, roundness, offset);
|
if (modifier) return modifiedPath(frameNo, out, transform, modifier);
|
||||||
else return defaultPath(frameNo, out, transform);
|
else return defaultPath(frameNo, out, transform);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Add table
Reference in a new issue