mirror of
https://github.com/thorvg/thorvg.git
synced 2025-06-20 15:03:25 +00:00
lottie: added pucker/bloat support
@Issue: https://github.com/thorvg/thorvg/issues/2681
This commit is contained in:
parent
3426531dd2
commit
44e130f55c
7 changed files with 174 additions and 3 deletions
|
@ -719,6 +719,15 @@ void LottieBuilder::updateOffsetPath(TVG_UNUSED LottieGroup* parent, LottieObjec
|
|||
}
|
||||
|
||||
|
||||
void LottieBuilder::updatePuckerBloat(TVG_UNUSED LottieGroup* parent, LottieObject** child, float frameNo, TVG_UNUSED Inlist<RenderContext>& contexts, RenderContext* ctx)
|
||||
{
|
||||
auto puckerBloat = static_cast<LottiePuckerBloat*>(*child);
|
||||
if (!ctx->puckerBloat) ctx->puckerBloat = new LottiePuckerBloatModifier(buffer, puckerBloat->amount(frameNo, tween, exps));
|
||||
|
||||
ctx->update(ctx->puckerBloat);
|
||||
}
|
||||
|
||||
|
||||
void LottieBuilder::updateRepeater(TVG_UNUSED LottieGroup* parent, LottieObject** child, float frameNo, TVG_UNUSED Inlist<RenderContext>& contexts, RenderContext* ctx)
|
||||
{
|
||||
auto repeater = static_cast<LottieRepeater*>(*child);
|
||||
|
@ -827,6 +836,10 @@ void LottieBuilder::updateChildren(LottieGroup* parent, float frameNo, Inlist<Re
|
|||
updateOffsetPath(parent, child, frameNo, contexts, ctx);
|
||||
break;
|
||||
}
|
||||
case LottieObject::PuckerBloat: {
|
||||
updatePuckerBloat(parent, child, frameNo, contexts, ctx);
|
||||
break;
|
||||
}
|
||||
default: break;
|
||||
}
|
||||
|
||||
|
|
|
@ -58,6 +58,7 @@ struct RenderContext
|
|||
Matrix* transform = nullptr;
|
||||
LottieRoundnessModifier* roundness = nullptr;
|
||||
LottieOffsetModifier* offset = nullptr;
|
||||
LottiePuckerBloatModifier* puckerBloat = nullptr;
|
||||
LottieModifier* modifier = nullptr;
|
||||
RenderFragment fragment = ByNone; //render context has been fragmented
|
||||
bool reqFragment = false; //requirement to fragment the render context
|
||||
|
@ -75,6 +76,7 @@ struct RenderContext
|
|||
delete(transform);
|
||||
delete(roundness);
|
||||
delete(offset);
|
||||
delete(puckerBloat);
|
||||
}
|
||||
|
||||
RenderContext(const RenderContext& rhs, Shape* propagator, bool mergeable = false) : propagator(propagator)
|
||||
|
@ -91,6 +93,10 @@ struct RenderContext
|
|||
offset = new LottieOffsetModifier(rhs.offset->offset, rhs.offset->miterLimit, rhs.offset->join);
|
||||
update(offset);
|
||||
}
|
||||
if (rhs.puckerBloat) {
|
||||
puckerBloat = new LottiePuckerBloatModifier(rhs.puckerBloat->buffer, rhs.puckerBloat->amount);
|
||||
update(puckerBloat);
|
||||
}
|
||||
if (rhs.transform) {
|
||||
transform = new Matrix;
|
||||
*transform = *rhs.transform;
|
||||
|
@ -173,6 +179,7 @@ private:
|
|||
void updateRepeater(LottieGroup* parent, LottieObject** child, float frameNo, Inlist<RenderContext>& contexts, RenderContext* ctx);
|
||||
void updateRoundedCorner(LottieGroup* parent, LottieObject** child, float frameNo, Inlist<RenderContext>& contexts, RenderContext* ctx);
|
||||
void updateOffsetPath(LottieGroup* parent, LottieObject** child, float frameNo, Inlist<RenderContext>& contexts, RenderContext* ctx);
|
||||
void updatePuckerBloat(LottieGroup* parent, LottieObject** child, float frameNo, Inlist<RenderContext>& contexts, RenderContext* ctx);
|
||||
|
||||
RenderPath buffer; //resusable path
|
||||
LottieExpressions* exps;
|
||||
|
|
|
@ -269,7 +269,8 @@ struct LottieObject
|
|||
Text,
|
||||
Repeater,
|
||||
RoundedCorner,
|
||||
OffsetPath
|
||||
OffsetPath,
|
||||
PuckerBloat
|
||||
};
|
||||
|
||||
virtual ~LottieObject()
|
||||
|
@ -554,6 +555,23 @@ struct LottieRoundedCorner : LottieObject
|
|||
};
|
||||
|
||||
|
||||
struct LottiePuckerBloat : LottieObject
|
||||
{
|
||||
LottiePuckerBloat()
|
||||
{
|
||||
LottieObject::type = LottieObject::PuckerBloat;
|
||||
}
|
||||
|
||||
LottieProperty* property(uint16_t ix) override
|
||||
{
|
||||
if (amount.ix == ix) return &amount;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
LottieFloat amount = 0.0f;
|
||||
};
|
||||
|
||||
|
||||
struct LottiePath : LottieShape
|
||||
{
|
||||
LottiePath() : LottieShape(LottieObject::Path) {}
|
||||
|
|
|
@ -102,6 +102,46 @@ static bool _clockwise(Point* pts, uint32_t n)
|
|||
}
|
||||
|
||||
|
||||
static Point _center(const PathCommand* cmds, uint32_t cmdsCount, const Point* pts)
|
||||
{
|
||||
Point center{};
|
||||
auto count = 0;
|
||||
auto p = (Point*)pts;
|
||||
auto startP = p;
|
||||
|
||||
for (uint32_t i = 0; i < cmdsCount; ++i) {
|
||||
switch (cmds[i]) {
|
||||
case PathCommand::MoveTo: {
|
||||
startP = p;
|
||||
++p;
|
||||
break;
|
||||
}
|
||||
case PathCommand::CubicTo: {
|
||||
center = center + *(p - 1) + *p + *(p + 1) + *(p + 2);
|
||||
p += 3;
|
||||
count += 4;
|
||||
break;
|
||||
}
|
||||
case PathCommand::LineTo: {
|
||||
center = center + *(p - 1) + *p;
|
||||
++p;
|
||||
count += 2;
|
||||
break;
|
||||
}
|
||||
case PathCommand::Close: {
|
||||
if (!tvg::zero(*(p - 1) - *startP)) {
|
||||
center = center + *(p - 1) + *startP;
|
||||
count += 2;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return count > 0 ? center / (float)count : Point{0, 0};
|
||||
}
|
||||
|
||||
|
||||
void LottieOffsetModifier::corner(RenderPath& out, Line& line, Line& nextLine, uint32_t movetoOutIndex, bool nextClose)
|
||||
{
|
||||
bool inside{};
|
||||
|
@ -414,4 +454,67 @@ bool LottieOffsetModifier::modifyEllipse(Point& radius)
|
|||
radius.x += offset;
|
||||
radius.y += offset;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
bool LottiePuckerBloatModifier::modifyPath(PathCommand* inCmds, uint32_t inCmdsCnt, Point* inPts, TVG_UNUSED uint32_t inPtsCnt, TVG_UNUSED Matrix* transform, RenderPath& out)
|
||||
{
|
||||
auto& path = next ? (inCmds == buffer[0].cmds.data ? buffer[1] : buffer[0]) : out;
|
||||
if (next) path.clear();
|
||||
|
||||
path.cmds.grow(inCmdsCnt);
|
||||
path.pts.grow(inPtsCnt);
|
||||
|
||||
auto center = _center(inCmds, inCmdsCnt, inPts, inPtsCnt);
|
||||
auto a = amount * 0.01f;
|
||||
auto pts = inPts;
|
||||
auto startPts = pts;
|
||||
|
||||
for (uint32_t i = 0; i < inCmdsCnt; ++i) {
|
||||
switch (inCmds[i]) {
|
||||
case PathCommand::MoveTo: {
|
||||
startPts = pts;
|
||||
path.pts.push(*pts + (center - *pts) * a);
|
||||
path.cmds.push(PathCommand::MoveTo);
|
||||
++pts;
|
||||
break;
|
||||
}
|
||||
case PathCommand::CubicTo: {
|
||||
path.pts.push(*pts - (center - *pts) * a);
|
||||
path.pts.push(*(pts + 1) - (center - *(pts + 1)) * a);
|
||||
path.pts.push(*(pts + 2) + (center - *(pts + 2)) * a);
|
||||
pts += 3;
|
||||
path.cmds.push(PathCommand::CubicTo);
|
||||
break;
|
||||
}
|
||||
case PathCommand::LineTo: {
|
||||
path.pts.push(*(pts - 1) - (center - *(pts - 1)) * a);
|
||||
path.pts.push(*pts - (center - *pts) * a);
|
||||
path.pts.push(*pts + (center - *pts) * a);
|
||||
path.cmds.push(PathCommand::CubicTo);
|
||||
++pts;
|
||||
break;
|
||||
}
|
||||
case PathCommand::Close: {
|
||||
if (!tvg::zero(*(pts - 1) - *startPts)) {
|
||||
path.pts.push(*(pts - 1) - (center - *(pts - 1)) * a);
|
||||
path.pts.push(*startPts - (center - *startPts) * a);
|
||||
path.pts.push(*startPts + (center - *startPts) * a);
|
||||
path.cmds.push(PathCommand::CubicTo);
|
||||
}
|
||||
path.cmds.push(PathCommand::Close);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (next) return next->modifyPath(path.cmds.data, path.cmds.count, path.pts.data, path.pts.count, transform, out);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
bool LottiePuckerBloatModifier::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, nullptr, out);
|
||||
}
|
|
@ -31,7 +31,7 @@
|
|||
|
||||
struct LottieModifier
|
||||
{
|
||||
enum Type : uint8_t {Roundness = 0, Offset};
|
||||
enum Type : uint8_t {Roundness = 0, Offset, PuckerBloat};
|
||||
|
||||
LottieModifier* next = nullptr;
|
||||
Type type;
|
||||
|
@ -106,4 +106,18 @@ private:
|
|||
void corner(RenderPath& out, Line& line, Line& nextLine, uint32_t movetoIndex, bool nextClose);
|
||||
};
|
||||
|
||||
|
||||
struct LottiePuckerBloatModifier : LottieModifier
|
||||
{
|
||||
float amount;
|
||||
|
||||
LottiePuckerBloatModifier(RenderPath* buffer, float a) : LottieModifier(buffer), amount(a)
|
||||
{
|
||||
type = PuckerBloat;
|
||||
}
|
||||
|
||||
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) override;
|
||||
};
|
||||
|
||||
#endif
|
|
@ -723,6 +723,21 @@ LottieRoundedCorner* LottieParser::parseRoundedCorner()
|
|||
}
|
||||
|
||||
|
||||
LottiePuckerBloat* LottieParser::parsePuckerBloat()
|
||||
{
|
||||
auto puckerBloat = new LottiePuckerBloat;
|
||||
|
||||
context.parent = puckerBloat;
|
||||
|
||||
while (auto key = nextObjectKey()) {
|
||||
if (parseCommon(puckerBloat, key)) continue;
|
||||
if (KEY_AS("a")) parseProperty(puckerBloat->amount);
|
||||
else skip();
|
||||
}
|
||||
return puckerBloat;
|
||||
}
|
||||
|
||||
|
||||
void LottieParser::parseColorStop(LottieGradient* gradient)
|
||||
{
|
||||
enterObject();
|
||||
|
@ -871,7 +886,7 @@ LottieObject* LottieParser::parseObject()
|
|||
else if (!strcmp(type, "tm")) return parseTrimpath();
|
||||
else if (!strcmp(type, "rp")) return parseRepeater();
|
||||
else if (!strcmp(type, "mm")) TVGLOG("LOTTIE", "MergePath(mm) is not supported yet");
|
||||
else if (!strcmp(type, "pb")) TVGLOG("LOTTIE", "Puker/Bloat(pb) is not supported yet");
|
||||
else if (!strcmp(type, "pb")) return parsePuckerBloat();
|
||||
else if (!strcmp(type, "tw")) TVGLOG("LOTTIE", "Twist(tw) is not supported yet");
|
||||
else if (!strcmp(type, "op")) return parseOffsetPath();
|
||||
else if (!strcmp(type, "zz")) TVGLOG("LOTTIE", "ZigZag(zz) is not supported yet");
|
||||
|
|
|
@ -89,6 +89,7 @@ private:
|
|||
LottiePath* parsePath();
|
||||
LottiePolyStar* parsePolyStar();
|
||||
LottieRoundedCorner* parseRoundedCorner();
|
||||
LottiePuckerBloat* parsePuckerBloat();
|
||||
LottieGradientFill* parseGradientFill();
|
||||
LottieLayer* parseLayers(LottieLayer* root);
|
||||
LottieMask* parseMask();
|
||||
|
|
Loading…
Add table
Reference in a new issue