From 9a4f4ef280dae81fbf60730511685c4c9defa0f0 Mon Sep 17 00:00:00 2001 From: Isaac <> Date: Sun, 2 Jun 2024 23:18:15 +0400 Subject: [PATCH] Lottie: refactor trimming --- .../Sources/SoftwareLottieRenderer.mm | 85 +++---------------- .../PublicHeaders/LottieCpp/BezierPath.h | 25 ++++++ .../PublicHeaders/LottieCpp/RenderTreeNode.h | 2 +- .../CompLayers/ShapeCompositionLayer.cpp | 55 +++--------- .../Private/Utility/Primitives/BezierPath.cpp | 42 +++++++++ 5 files changed, 94 insertions(+), 115 deletions(-) diff --git a/Tests/LottieMetalTest/SoftwareLottieRenderer/Sources/SoftwareLottieRenderer.mm b/Tests/LottieMetalTest/SoftwareLottieRenderer/Sources/SoftwareLottieRenderer.mm index c71328ef45..3794f06b0c 100644 --- a/Tests/LottieMetalTest/SoftwareLottieRenderer/Sources/SoftwareLottieRenderer.mm +++ b/Tests/LottieMetalTest/SoftwareLottieRenderer/Sources/SoftwareLottieRenderer.mm @@ -65,6 +65,14 @@ static void enumeratePaths(std::shared_ptr it size_t maxSubitem = std::min(item->subItems.size(), subItemLimit); + if (item->trimmedPaths) { + for (const auto &path : item->trimmedPaths.value()) { + onPath(path, effectiveTransform); + } + + return; + } + if (item->path) { onPath(item->path->path, effectiveTransform); } @@ -234,76 +242,11 @@ static void drawLottieContentItem(std::shared_ptr const for (const auto &shading : item->shadings) { lottieRendering::CanvasPathEnumerator iteratePaths; - if (shading->explicitPath) { - auto itemPaths = shading->explicitPath.value(); - iteratePaths = [itemPaths = itemPaths](std::function iterate) -> void { + iteratePaths = [&](std::function iterate) { + enumeratePaths(item, shading->subItemLimit, lottie::Transform2D::identity(), true, [&](lottie::BezierPath const &sourcePath, lottie::Transform2D const &transform) { + auto path = sourcePath.copyUsingTransform(transform); + lottieRendering::PathCommand pathCommand; - for (const auto &path : itemPaths) { - std::optional previousElement; - for (const auto &element : path.elements()) { - if (previousElement.has_value()) { - if (previousElement->vertex.outTangentRelative().isZero() && element.vertex.inTangentRelative().isZero()) { - pathCommand.type = lottieRendering::PathCommandType::LineTo; - pathCommand.points[0] = CGPointMake(element.vertex.point.x, element.vertex.point.y); - iterate(pathCommand); - } else { - pathCommand.type = lottieRendering::PathCommandType::CurveTo; - pathCommand.points[2] = CGPointMake(element.vertex.point.x, element.vertex.point.y); - pathCommand.points[1] = CGPointMake(element.vertex.inTangent.x, element.vertex.inTangent.y); - pathCommand.points[0] = CGPointMake(previousElement->vertex.outTangent.x, previousElement->vertex.outTangent.y); - iterate(pathCommand); - } - } else { - pathCommand.type = lottieRendering::PathCommandType::MoveTo; - pathCommand.points[0] = CGPointMake(element.vertex.point.x, element.vertex.point.y); - iterate(pathCommand); - } - previousElement = element; - } - if (path.closed().value_or(true)) { - pathCommand.type = lottieRendering::PathCommandType::Close; - iterate(pathCommand); - } - } - }; - } else { - iteratePaths = [&](std::function iterate) { - enumeratePaths(item, shading->subItemLimit, lottie::Transform2D::identity(), true, [&](lottie::BezierPath const &sourcePath, lottie::Transform2D const &transform) { - auto path = sourcePath.copyUsingTransform(transform); - - lottieRendering::PathCommand pathCommand; - std::optional previousElement; - for (const auto &element : path.elements()) { - if (previousElement.has_value()) { - if (previousElement->vertex.outTangentRelative().isZero() && element.vertex.inTangentRelative().isZero()) { - pathCommand.type = lottieRendering::PathCommandType::LineTo; - pathCommand.points[0] = CGPointMake(element.vertex.point.x, element.vertex.point.y); - iterate(pathCommand); - } else { - pathCommand.type = lottieRendering::PathCommandType::CurveTo; - pathCommand.points[2] = CGPointMake(element.vertex.point.x, element.vertex.point.y); - pathCommand.points[1] = CGPointMake(element.vertex.inTangent.x, element.vertex.inTangent.y); - pathCommand.points[0] = CGPointMake(previousElement->vertex.outTangent.x, previousElement->vertex.outTangent.y); - iterate(pathCommand); - } - } else { - pathCommand.type = lottieRendering::PathCommandType::MoveTo; - pathCommand.points[0] = CGPointMake(element.vertex.point.x, element.vertex.point.y); - iterate(pathCommand); - } - previousElement = element; - } - if (path.closed().value_or(true)) { - pathCommand.type = lottieRendering::PathCommandType::Close; - iterate(pathCommand); - } - }); - }; - } - - /*auto iteratePaths = [&](std::function iterate) -> void { - lottieRendering::PathCommand pathCommand; - for (const auto &path : itemPaths) { std::optional previousElement; for (const auto &element : path.elements()) { if (previousElement.has_value()) { @@ -329,8 +272,8 @@ static void drawLottieContentItem(std::shared_ptr const pathCommand.type = lottieRendering::PathCommandType::Close; iterate(pathCommand); } - } - };*/ + }); + }; if (shading->stroke) { if (shading->stroke->shading->type() == lottie::RenderTreeNodeContentItem::ShadingType::Solid) { diff --git a/submodules/TelegramUI/Components/LottieCpp/PublicHeaders/LottieCpp/BezierPath.h b/submodules/TelegramUI/Components/LottieCpp/PublicHeaders/LottieCpp/BezierPath.h index b17f942761..b61a7ce1c2 100644 --- a/submodules/TelegramUI/Components/LottieCpp/PublicHeaders/LottieCpp/BezierPath.h +++ b/submodules/TelegramUI/Components/LottieCpp/PublicHeaders/LottieCpp/BezierPath.h @@ -131,6 +131,31 @@ public: std::shared_ptr _contents; }; +class PathContents { +public: + struct Element { + Vector2D point; + Vector2D cp1; + Vector2D cp2; + + explicit Element(Vector2D const &point_, Vector2D const &cp1_, Vector2D const &cp2_) : + point(point_), + cp1(cp1_), + cp2(cp2_) { + } + }; + +public: + PathContents(BezierPathContents const &bezierPath); + ~PathContents(); + + std::shared_ptr bezierPath() const; + +private: + std::vector _elements; + bool _isClosed = false; +}; + class BezierPathsBoundingBoxContext { public: BezierPathsBoundingBoxContext(); diff --git a/submodules/TelegramUI/Components/LottieCpp/PublicHeaders/LottieCpp/RenderTreeNode.h b/submodules/TelegramUI/Components/LottieCpp/PublicHeaders/LottieCpp/RenderTreeNode.h index 09616bdf0a..7f022c49e9 100644 --- a/submodules/TelegramUI/Components/LottieCpp/PublicHeaders/LottieCpp/RenderTreeNode.h +++ b/submodules/TelegramUI/Components/LottieCpp/PublicHeaders/LottieCpp/RenderTreeNode.h @@ -368,6 +368,7 @@ public: float alpha = 0.0; std::optional trimParams; std::shared_ptr path; + std::optional> trimmedPaths; std::vector> shadings; std::vector> subItems; int drawContentCount = 0; @@ -383,7 +384,6 @@ public: public: std::shared_ptr stroke; std::shared_ptr fill; - std::optional> explicitPath; size_t subItemLimit = 0; }; diff --git a/submodules/TelegramUI/Components/LottieCpp/Sources/Lottie/Private/MainThread/LayerContainers/CompLayers/ShapeCompositionLayer.cpp b/submodules/TelegramUI/Components/LottieCpp/Sources/Lottie/Private/MainThread/LayerContainers/CompLayers/ShapeCompositionLayer.cpp index 9e4eef16f9..39ad7ead6e 100644 --- a/submodules/TelegramUI/Components/LottieCpp/Sources/Lottie/Private/MainThread/LayerContainers/CompLayers/ShapeCompositionLayer.cpp +++ b/submodules/TelegramUI/Components/LottieCpp/Sources/Lottie/Private/MainThread/LayerContainers/CompLayers/ShapeCompositionLayer.cpp @@ -1080,54 +1080,23 @@ public: _contentItem->transform = containerTransform; _contentItem->alpha = containerOpacity; - if (trim) { - _contentItem->trimParams = trim->trimParams(); - } - - for (int i = 0; i < shadings.size(); i++) { - const auto &shadingVariant = shadings[i]; + if (parentTrim) { + _contentItem->trimParams = parentTrim; - if (!(shadingVariant.fill || shadingVariant.stroke)) { - continue; + CompoundBezierPath compoundPath; + auto paths = collectPaths(INT32_MAX, Transform2D::identity(), true); + for (const auto &path : paths) { + compoundPath.appendPath(path.path.copyUsingTransform(path.transform)); } - //std::optional currentTrim = parentTrim; - //TODO:investigate - /*if (!trims.empty()) { - currentTrim = trims[0]; - }*/ + compoundPath = trimCompoundPath(compoundPath, parentTrim->start, parentTrim->end, parentTrim->offset, parentTrim->type); - if (parentTrim) { - CompoundBezierPath compoundPath; - auto paths = collectPaths(shadingVariant.subItemLimit, Transform2D::identity(), true); - for (const auto &path : paths) { - compoundPath.appendPath(path.path.copyUsingTransform(path.transform)); - } - - compoundPath = trimCompoundPath(compoundPath, parentTrim->start, parentTrim->end, parentTrim->offset, parentTrim->type); - - std::vector resultPaths; - for (const auto &path : compoundPath.paths) { - resultPaths.push_back(path); - } - _contentItem->shadings[i]->explicitPath = resultPaths; - } else { - if (hasTrims()) { - CompoundBezierPath compoundPath; - auto paths = collectPaths(shadingVariant.subItemLimit, Transform2D::identity(), true); - for (const auto &path : paths) { - compoundPath.appendPath(path.path.copyUsingTransform(path.transform)); - } - std::vector resultPaths; - for (const auto &path : compoundPath.paths) { - resultPaths.push_back(path); - } - - _contentItem->shadings[i]->explicitPath = resultPaths; - } else { - _contentItem->shadings[i]->explicitPath = std::nullopt; - } + std::vector resultPaths; + for (const auto &path : compoundPath.paths) { + resultPaths.push_back(path); } + + _contentItem->trimmedPaths = resultPaths; } if (isGroup && !subItems.empty()) { diff --git a/submodules/TelegramUI/Components/LottieCpp/Sources/Lottie/Private/Utility/Primitives/BezierPath.cpp b/submodules/TelegramUI/Components/LottieCpp/Sources/Lottie/Private/Utility/Primitives/BezierPath.cpp index 85c50dba1e..f73b382dc1 100644 --- a/submodules/TelegramUI/Components/LottieCpp/Sources/Lottie/Private/Utility/Primitives/BezierPath.cpp +++ b/submodules/TelegramUI/Components/LottieCpp/Sources/Lottie/Private/Utility/Primitives/BezierPath.cpp @@ -687,4 +687,46 @@ CGRect bezierPathsBoundingBox(std::vector const &paths) { return calculateBoundingRectOpt(pointsX, pointsY, pointCount); } +PathContents::PathContents(BezierPathContents const &bezierPath) { + std::optional previousElement; + for (const auto &element : bezierPath.elements) { + if (previousElement.has_value()) { + _elements.emplace_back(element.vertex.point, previousElement->vertex.outTangent, element.vertex.inTangent); + } else { + _elements.emplace_back(element.vertex.point, Vector2D(0.0, 0.0), Vector2D(0.0, 0.0)); + } + previousElement = element; + } + + if (bezierPath.closed.value_or(true)) { + _isClosed = true; + } else { + _isClosed = false; + } +} + +PathContents::~PathContents() { +} + +std::shared_ptr PathContents::bezierPath() const { + auto result = std::make_shared(); + + bool isFirst = true; + for (const auto &element : _elements) { + if (isFirst) { + isFirst = false; + } else { + result->elements.push_back(PathElement(CurveVertex::absolute( + element.point, + element.cp1, + element.cp2 + ))); + } + } + + result->closed = _isClosed; + + return result; +} + }