From d00b727cd45d2cdb85820f270c41f5f64e4dd492 Mon Sep 17 00:00:00 2001 From: Hermet Park Date: Wed, 18 Dec 2024 12:24:28 +0900 Subject: [PATCH] lottie: support stroke layer effect issue: https://github.com/thorvg/thorvg/issues/2718 --- src/loaders/lottie/tvgLottieBuilder.cpp | 63 ++++++++++++++++++++++++- src/loaders/lottie/tvgLottieBuilder.h | 3 +- src/loaders/lottie/tvgLottieModel.h | 51 +++++++++++++------- src/loaders/lottie/tvgLottieParser.cpp | 43 +++++++++++++++-- src/loaders/lottie/tvgLottieParser.h | 1 + src/loaders/lottie/tvgLottieProperty.h | 4 +- 6 files changed, 138 insertions(+), 27 deletions(-) diff --git a/src/loaders/lottie/tvgLottieBuilder.cpp b/src/loaders/lottie/tvgLottieBuilder.cpp index c0b824ab..14c81e89 100644 --- a/src/loaders/lottie/tvgLottieBuilder.cpp +++ b/src/loaders/lottie/tvgLottieBuilder.cpp @@ -1225,7 +1225,7 @@ void LottieBuilder::updateText(LottieLayer* layer, float frameNo) } -void LottieBuilder::updateMaskings(LottieLayer* layer, float frameNo) +void LottieBuilder::updateMasks(LottieLayer* layer, float frameNo) { if (layer->masks.count == 0) return; @@ -1311,6 +1311,60 @@ bool LottieBuilder::updateMatte(LottieComposition* comp, float frameNo, Scene* s } +void LottieBuilder::updateStrokeEffect(LottieLayer* layer, LottieFxStroke* effect, float frameNo) +{ + if (layer->masks.count == 0) return; + + auto shape = layer->pooling(); + shape->reset(); + + //FIXME: all mask + if (effect->allMask(frameNo)) { + for (auto m = layer->masks.begin(); m < layer->masks.end(); ++m) { + auto mask = *m; + mask->pathset(frameNo, P(shape)->rs.path.cmds, P(shape)->rs.path.pts, nullptr, nullptr, nullptr, exps); + } + //A specific mask + } else { + auto idx = static_cast(effect->mask(frameNo) - 1); + if (idx < 0 || idx >= layer->masks.count) return; + auto mask = layer->masks[idx]; + mask->pathset(frameNo, P(shape)->rs.path.cmds, P(shape)->rs.path.pts, nullptr, nullptr, nullptr, exps); + } + + shape->transform(layer->cache.matrix); + shape->strokeTrim(effect->begin(frameNo) * 0.01f, effect->end(frameNo) * 0.01f); + shape->stroke(255, 255, 255, (int)(effect->opacity(frameNo) * 255.0f)); + shape->stroke(StrokeJoin::Round); + shape->stroke(StrokeCap::Round); + + auto size = effect->size(frameNo) * 2.0f; + shape->stroke(size); + + //fill the color to the layer shapes if any + auto color = effect->color(frameNo); + if (color.rgb[0] != 255 || color.rgb[1] != 255 || color.rgb[2] != 255) { + auto accessor = tvg::Accessor::gen(); + auto stroke = (layer->type == LottieLayer::Type::Shape) ? true : false; + auto f = [color, size, stroke](const tvg::Paint* paint, void* data) -> bool { + if (paint->type() == tvg::Type::Shape) { + auto shape = (tvg::Shape*) paint; + //expand shape to fill the stroke region + if (stroke) { + shape->stroke(size); + shape->stroke(color.rgb[0], color.rgb[1], color.rgb[2], 255); + } + shape->fill(color.rgb[0], color.rgb[1], color.rgb[2], 255); + } + return true; + }; + accessor->set(layer->scene, f, nullptr); + } + + layer->scene->composite(cast(shape), CompositeMethod::AlphaMask); +} + + void LottieBuilder::updateEffect(LottieLayer* layer, float frameNo) { constexpr int QUALITY = 25; @@ -1334,6 +1388,11 @@ void LottieBuilder::updateEffect(LottieLayer* layer, float frameNo) layer->scene->push(SceneEffect::Fill, color.rgb[0], color.rgb[1], color.rgb[2], (int)(255.0f * effect->opacity(frameNo))); break; } + case LottieEffect::Stroke: { + auto effect = static_cast(*ef); + updateStrokeEffect(layer, effect, frameNo); + break; + } case LottieEffect::Tritone: { auto effect = static_cast(*ef); auto dark = effect->dark(frameNo); @@ -1411,7 +1470,7 @@ void LottieBuilder::updateLayer(LottieComposition* comp, Scene* scene, LottieLay } } - updateMaskings(layer, frameNo); + updateMasks(layer, frameNo); layer->scene->blend(layer->blendMethod); diff --git a/src/loaders/lottie/tvgLottieBuilder.h b/src/loaders/lottie/tvgLottieBuilder.h index cb628e86..c5155715 100644 --- a/src/loaders/lottie/tvgLottieBuilder.h +++ b/src/loaders/lottie/tvgLottieBuilder.h @@ -103,6 +103,7 @@ struct LottieBuilder void build(LottieComposition* comp); private: + void updateStrokeEffect(LottieLayer* layer, LottieFxStroke* effect, float frameNo); void updateEffect(LottieLayer* layer, float frameNo); void updateLayer(LottieComposition* comp, Scene* scene, LottieLayer* layer, float frameNo); bool updateMatte(LottieComposition* comp, float frameNo, Scene* scene, LottieLayer* layer); @@ -110,7 +111,7 @@ private: void updateSolid(LottieLayer* layer); void updateImage(LottieGroup* layer); void updateText(LottieLayer* layer, float frameNo); - void updateMaskings(LottieLayer* layer, float frameNo); + void updateMasks(LottieLayer* layer, float frameNo); void updateTransform(LottieLayer* layer, float frameNo); void updateChildren(LottieGroup* parent, float frameNo, Inlist& contexts); void updateGroup(LottieGroup* parent, LottieObject** child, float frameNo, Inlist& pcontexts, RenderContext* ctx); diff --git a/src/loaders/lottie/tvgLottieModel.h b/src/loaders/lottie/tvgLottieModel.h index e1c95589..b4ebc60e 100644 --- a/src/loaders/lottie/tvgLottieModel.h +++ b/src/loaders/lottie/tvgLottieModel.h @@ -76,10 +76,9 @@ struct LottieStroke StrokeJoin join = StrokeJoin::Round; }; - struct LottieEffect { - enum Type : uint8_t {Tint = 20, Fill, Tritone = 23, DropShadow = 25, GaussianBlur = 29}; + enum Type : uint8_t {Tint = 20, Fill, Stroke, Tritone, DropShadow = 25, GaussianBlur = 29}; virtual ~LottieEffect() {} Type type; @@ -88,13 +87,13 @@ struct LottieEffect struct LottieFxFill : LottieEffect { - //int16_t mask layer - //LottieDropDown allMask? + //LottieInteger mask; + //LottieInteger allMask; LottieColor color; - //LottieDropDown inverted - //LottieSlider horizontalFeather - //LottieSlider verticalFeather - LottieSlider opacity; + //LottieInteger invert; + //LottieSlider hFeather; + //LottieSlider vFeather; + LottieFloat opacity; LottieFxFill() { @@ -102,11 +101,31 @@ struct LottieFxFill : LottieEffect } }; +struct LottieFxStroke : LottieEffect +{ + LottieInteger mask; + LottieInteger allMask; + //LottieInteger sequential; + LottieColor color; + LottieFloat size; + //LottieFloat hardness; //should support with the blurness? + LottieFloat opacity; + LottieFloat begin; + LottieFloat end; + //LottieFloat space; + //LottieInteger style; + + LottieFxStroke() + { + type = LottieEffect::Stroke; + } +}; + struct LottieFxTint : LottieEffect { LottieColor black; LottieColor white; - LottieSlider intensity; + LottieFloat intensity; LottieFxTint() { @@ -129,10 +148,10 @@ struct LottieFxTritone : LottieEffect struct LottieFxDropShadow : LottieEffect { LottieColor color; - LottieSlider opacity = 0; - LottieAngle angle = 0.0f; - LottieSlider distance = 0.0f; - LottieSlider blurness = 0.0f; + LottieFloat opacity = 0; + LottieFloat angle = 0.0f; + LottieFloat distance = 0.0f; + LottieFloat blurness = 0.0f; LottieFxDropShadow() { @@ -142,9 +161,9 @@ struct LottieFxDropShadow : LottieEffect struct LottieFxGaussianBlur : LottieEffect { - LottieSlider blurness = 0.0f; - LottieCheckbox direction = 0; - LottieCheckbox wrap = 0; + LottieFloat blurness = 0.0f; + LottieInteger direction = 0; + LottieInteger wrap = 0; LottieFxGaussianBlur() { diff --git a/src/loaders/lottie/tvgLottieParser.cpp b/src/loaders/lottie/tvgLottieParser.cpp index 14a92fc2..b0fbf41e 100644 --- a/src/loaders/lottie/tvgLottieParser.cpp +++ b/src/loaders/lottie/tvgLottieParser.cpp @@ -62,6 +62,7 @@ LottieEffect* LottieParser::getEffect(int type) switch (type) { case LottieEffect::Tint: return new LottieFxTint; case LottieEffect::Fill: return new LottieFxFill; + case LottieEffect::Stroke: return new LottieFxStroke; case LottieEffect::Tritone: return new LottieFxTritone; case LottieEffect::DropShadow: return new LottieFxDropShadow; case LottieEffect::GaussianBlur: return new LottieFxGaussianBlur; @@ -1261,7 +1262,7 @@ void LottieParser::parseMasks(LottieLayer* layer) void LottieParser::parseTint(LottieFxTint* effect) { - int idx = 0; //black -> white -> intenstiy + int idx = 0; enterArray(); while (nextArrayValue()) { enterObject(); @@ -1285,7 +1286,7 @@ void LottieParser::parseTint(LottieFxTint* effect) void LottieParser::parseTritone(LottieFxTritone* effect) { - int idx = 0; //bright, midtone, dark + int idx = 0; enterArray(); while (nextArrayValue()) { enterObject(); @@ -1309,7 +1310,7 @@ void LottieParser::parseTritone(LottieFxTritone* effect) void LottieParser::parseFill(LottieFxFill* effect) { - int idx = 0; //fill mask -> all mask -> color -> invert -> h feather -> v feather -> opacity + int idx = 0; enterArray(); while (nextArrayValue()) { enterObject(); @@ -1332,7 +1333,7 @@ void LottieParser::parseFill(LottieFxFill* effect) void LottieParser::parseGaussianBlur(LottieFxGaussianBlur* effect) { - int idx = 0; //blurness -> direction -> wrap + int idx = 0; enterArray(); while (nextArrayValue()) { enterObject(); @@ -1356,7 +1357,7 @@ void LottieParser::parseGaussianBlur(LottieFxGaussianBlur* effect) void LottieParser::parseDropShadow(LottieFxDropShadow* effect) { - int idx = 0; //color -> opacity -> angle -> distance -> blur + int idx = 0; enterArray(); while (nextArrayValue()) { enterObject(); @@ -1380,6 +1381,34 @@ void LottieParser::parseDropShadow(LottieFxDropShadow* effect) } +void LottieParser::parseStroke(LottieFxStroke* effect) +{ + int idx = 0; + enterArray(); + while (nextArrayValue()) { + enterObject(); + while (auto key = nextObjectKey()) { + if (KEY_AS("v")) { + enterObject(); + while (auto key = nextObjectKey()) { + if (KEY_AS("k")) { + if (idx == 0) parsePropertyInternal(effect->mask); + else if (idx == 1) parsePropertyInternal(effect->allMask); + else if (idx == 3) parsePropertyInternal(effect->color); + else if (idx == 4) parsePropertyInternal(effect->size); + else if (idx == 6) parsePropertyInternal(effect->opacity); + else if (idx == 7) parsePropertyInternal(effect->begin); + else if (idx == 8) parsePropertyInternal(effect->end); + else skip(key); + ++idx; + } else skip(key); + } + } else skip(key); + } + } +} + + void LottieParser::parseEffect(LottieEffect* effect) { switch (effect->type) { @@ -1391,6 +1420,10 @@ void LottieParser::parseEffect(LottieEffect* effect) parseFill(static_cast(effect)); break; } + case LottieEffect::Stroke: { + parseStroke(static_cast(effect)); + break; + } case LottieEffect::Tritone: { parseTritone(static_cast(effect)); break; diff --git a/src/loaders/lottie/tvgLottieParser.h b/src/loaders/lottie/tvgLottieParser.h index eb9ad962..af5a42ba 100644 --- a/src/loaders/lottie/tvgLottieParser.h +++ b/src/loaders/lottie/tvgLottieParser.h @@ -98,6 +98,7 @@ private: LottieFont* parseFont(); LottieMarker* parseMarker(); + void parseStroke(LottieFxStroke* effect); void parseTritone(LottieFxTritone* effect); void parseTint(LottieFxTint* effect); void parseFill(LottieFxFill* effect); diff --git a/src/loaders/lottie/tvgLottieProperty.h b/src/loaders/lottie/tvgLottieProperty.h index b8a33f83..ed27f183 100644 --- a/src/loaders/lottie/tvgLottieProperty.h +++ b/src/loaders/lottie/tvgLottieProperty.h @@ -920,8 +920,6 @@ using LottiePoint = LottieGenericProperty; using LottieFloat = LottieGenericProperty; using LottieOpacity = LottieGenericProperty; using LottieColor = LottieGenericProperty; -using LottieSlider = LottieFloat; -using LottieAngle = LottieFloat; -using LottieCheckbox = LottieGenericProperty; +using LottieInteger = LottieGenericProperty; #endif //_TVG_LOTTIE_PROPERTY_H_