mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-06-15 21:45:19 +00:00
Lottie
This commit is contained in:
parent
a5377fd4f5
commit
940619b7cd
@ -8,6 +8,8 @@
|
||||
|
||||
namespace {
|
||||
|
||||
static constexpr float minVisibleAlpha = 0.5f / 255.0f;
|
||||
|
||||
struct TransformedPath {
|
||||
lottie::BezierPath path;
|
||||
lottie::CATransform3D transform;
|
||||
@ -85,10 +87,6 @@ static std::vector<TransformedPath> collectPaths(std::shared_ptr<lottie::RenderT
|
||||
namespace lottie {
|
||||
|
||||
static std::optional<CGRect> getRenderContentItemGlobalRect(std::shared_ptr<RenderTreeNodeContentItem> const &contentItem, lottie::Vector2D const &globalSize, lottie::CATransform3D const &parentTransform, BezierPathsBoundingBoxContext &bezierPathsBoundingBoxContext) {
|
||||
if (!contentItem->renderData.isValid) {
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
auto currentTransform = parentTransform;
|
||||
CATransform3D localTransform = contentItem->transform;
|
||||
currentTransform = localTransform * currentTransform;
|
||||
@ -185,52 +183,8 @@ static std::optional<CGRect> getRenderNodeGlobalRect(std::shared_ptr<RenderTreeN
|
||||
}
|
||||
}
|
||||
|
||||
static void processRenderContentItem(std::shared_ptr<RenderTreeNodeContentItem> const &contentItem, Vector2D const &globalSize, CATransform3D const &parentTransform, BezierPathsBoundingBoxContext &bezierPathsBoundingBoxContext) {
|
||||
auto currentTransform = parentTransform;
|
||||
CATransform3D localTransform = contentItem->transform;
|
||||
currentTransform = localTransform * currentTransform;
|
||||
|
||||
if (!currentTransform.isInvertible()) {
|
||||
contentItem->renderData.isValid = false;
|
||||
return;
|
||||
}
|
||||
|
||||
int drawContentDescendants = 0;
|
||||
for (const auto &shadingVariant : contentItem->shadings) {
|
||||
if (shadingVariant->stroke) {
|
||||
} else if (shadingVariant->fill) {
|
||||
} else {
|
||||
continue;
|
||||
}
|
||||
|
||||
drawContentDescendants += 1;
|
||||
}
|
||||
|
||||
if (contentItem->isGroup) {
|
||||
for (auto it = contentItem->subItems.rbegin(); it != contentItem->subItems.rend(); it++) {
|
||||
const auto &subItem = *it;
|
||||
processRenderContentItem(subItem, globalSize, currentTransform, bezierPathsBoundingBoxContext);
|
||||
|
||||
if (subItem->renderData.isValid) {
|
||||
drawContentDescendants += subItem->renderData.drawContentDescendants;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
for (const auto &subItem : contentItem->subItems) {
|
||||
subItem->renderData.isValid = false;
|
||||
}
|
||||
}
|
||||
|
||||
contentItem->renderData.isValid = true;
|
||||
|
||||
contentItem->renderData.layer.masksToBounds = false;
|
||||
|
||||
contentItem->renderData.drawContentDescendants = drawContentDescendants;
|
||||
contentItem->renderData.isInvertedMatte = false;
|
||||
}
|
||||
|
||||
static void processRenderTree(std::shared_ptr<RenderTreeNode> const &node, Vector2D const &globalSize, CATransform3D const &parentTransform, bool isInvertedMask, BezierPathsBoundingBoxContext &bezierPathsBoundingBoxContext) {
|
||||
if (node->isHidden() || node->alpha() == 0.0f) {
|
||||
static void processRenderTree(std::shared_ptr<RenderTreeNode> const &node, Vector2D const &globalSize, bool isInvertedMask, BezierPathsBoundingBoxContext &bezierPathsBoundingBoxContext) {
|
||||
if (node->isHidden() || node->alpha() < minVisibleAlpha) {
|
||||
node->renderData.isValid = false;
|
||||
return;
|
||||
}
|
||||
@ -242,44 +196,16 @@ static void processRenderTree(std::shared_ptr<RenderTreeNode> const &node, Vecto
|
||||
}
|
||||
}
|
||||
|
||||
auto currentTransform = parentTransform;
|
||||
Vector2D localTranslation(node->position().x + -node->bounds().x, node->position().y + -node->bounds().y);
|
||||
CATransform3D localTransform = node->transform();
|
||||
localTransform = localTransform.translated(localTranslation);
|
||||
currentTransform = localTransform * currentTransform;
|
||||
|
||||
if (!currentTransform.isInvertible()) {
|
||||
node->renderData.isValid = false;
|
||||
return;
|
||||
}
|
||||
|
||||
int drawContentDescendants = 0;
|
||||
if (node->_contentItem) {
|
||||
processRenderContentItem(node->_contentItem, globalSize, currentTransform, bezierPathsBoundingBoxContext);
|
||||
if (node->_contentItem->renderData.isValid) {
|
||||
drawContentDescendants += node->_contentItem->renderData.drawContentDescendants;
|
||||
}
|
||||
}
|
||||
|
||||
bool isInvertedMatte = isInvertedMask;
|
||||
|
||||
for (const auto &item : node->subnodes()) {
|
||||
processRenderTree(item, globalSize, currentTransform, false, bezierPathsBoundingBoxContext);
|
||||
if (item->renderData.isValid) {
|
||||
drawContentDescendants += item->renderData.drawContentDescendants;
|
||||
}
|
||||
}
|
||||
|
||||
bool masksToBounds = node->masksToBounds();
|
||||
if (masksToBounds) {
|
||||
CGRect effectiveGlobalBounds = node->bounds().applyingTransform(currentTransform);
|
||||
if (effectiveGlobalBounds.contains(CGRect(0.0, 0.0, globalSize.x, globalSize.y))) {
|
||||
masksToBounds = false;
|
||||
for (const auto &subnode : node->subnodes()) {
|
||||
processRenderTree(subnode, globalSize, false, bezierPathsBoundingBoxContext);
|
||||
if (subnode->renderData.isValid) {
|
||||
}
|
||||
}
|
||||
|
||||
if (node->mask()) {
|
||||
processRenderTree(node->mask(), globalSize, currentTransform, node->invertMask(), bezierPathsBoundingBoxContext);
|
||||
processRenderTree(node->mask(), globalSize, node->invertMask(), bezierPathsBoundingBoxContext);
|
||||
if (!node->mask()->renderData.isValid) {
|
||||
node->renderData.isValid = false;
|
||||
return;
|
||||
@ -288,9 +214,6 @@ static void processRenderTree(std::shared_ptr<RenderTreeNode> const &node, Vecto
|
||||
|
||||
node->renderData.isValid = true;
|
||||
|
||||
node->renderData.layer.masksToBounds = masksToBounds;
|
||||
|
||||
node->renderData.drawContentDescendants = drawContentDescendants;
|
||||
node->renderData.isInvertedMatte = isInvertedMatte;
|
||||
}
|
||||
|
||||
@ -299,10 +222,6 @@ static void processRenderTree(std::shared_ptr<RenderTreeNode> const &node, Vecto
|
||||
namespace {
|
||||
|
||||
static void drawLottieContentItem(std::shared_ptr<lottieRendering::Canvas> parentContext, std::shared_ptr<lottie::RenderTreeNodeContentItem> item, float parentAlpha, lottie::Vector2D const &globalSize, lottie::CATransform3D const &parentTransform, lottie::BezierPathsBoundingBoxContext &bezierPathsBoundingBoxContext) {
|
||||
if (!item->renderData.isValid) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto currentTransform = parentTransform;
|
||||
lottie::CATransform3D localTransform = item->transform;
|
||||
currentTransform = localTransform * currentTransform;
|
||||
@ -320,7 +239,7 @@ static void drawLottieContentItem(std::shared_ptr<lottieRendering::Canvas> paren
|
||||
std::shared_ptr<lottieRendering::Canvas> tempContext;
|
||||
|
||||
bool needsTempContext = false;
|
||||
needsTempContext = layerAlpha != 1.0 && item->renderData.drawContentDescendants > 1;
|
||||
needsTempContext = layerAlpha != 1.0 && item->drawContentCount > 1;
|
||||
|
||||
std::optional<lottie::CGRect> globalRect;
|
||||
if (needsTempContext) {
|
||||
@ -552,7 +471,7 @@ static void renderLottieRenderNode(std::shared_ptr<lottie::RenderTreeNode> node,
|
||||
float normalizedOpacity = node->alpha();
|
||||
float layerAlpha = ((float)normalizedOpacity) * parentAlpha;
|
||||
|
||||
if (node->isHidden() || normalizedOpacity == 0.0f) {
|
||||
if (node->isHidden() || normalizedOpacity < minVisibleAlpha) {
|
||||
return;
|
||||
}
|
||||
|
||||
@ -568,11 +487,19 @@ static void renderLottieRenderNode(std::shared_ptr<lottie::RenderTreeNode> node,
|
||||
std::shared_ptr<lottieRendering::Canvas> currentContext;
|
||||
std::shared_ptr<lottieRendering::Canvas> tempContext;
|
||||
|
||||
bool masksToBounds = node->masksToBounds();
|
||||
if (masksToBounds) {
|
||||
lottie::CGRect effectiveGlobalBounds = node->bounds().applyingTransform(currentTransform);
|
||||
if (effectiveGlobalBounds.contains(lottie::CGRect(0.0, 0.0, globalSize.x, globalSize.y))) {
|
||||
masksToBounds = false;
|
||||
}
|
||||
}
|
||||
|
||||
bool needsTempContext = false;
|
||||
if (node->mask() && node->mask()->renderData.isValid) {
|
||||
needsTempContext = true;
|
||||
} else {
|
||||
needsTempContext = layerAlpha != 1.0 || node->renderData.layer.masksToBounds;
|
||||
needsTempContext = layerAlpha != 1.0 || masksToBounds;
|
||||
}
|
||||
|
||||
std::optional<lottie::CGRect> globalRect;
|
||||
@ -583,13 +510,13 @@ static void renderLottieRenderNode(std::shared_ptr<lottie::RenderTreeNode> node,
|
||||
return;
|
||||
}
|
||||
|
||||
if ((node->mask() && node->mask()->renderData.isValid) || node->renderData.layer.masksToBounds) {
|
||||
if ((node->mask() && node->mask()->renderData.isValid) || masksToBounds) {
|
||||
auto maskBackingStorage = parentContext->makeLayer((int)(globalRect->width), (int)(globalRect->height));
|
||||
|
||||
maskBackingStorage->concatenate(lottie::CATransform3D::identity().translated(lottie::Vector2D(-globalRect->x, -globalRect->y)));
|
||||
maskBackingStorage->concatenate(currentTransform);
|
||||
|
||||
if (node->renderData.layer.masksToBounds) {
|
||||
if (masksToBounds) {
|
||||
maskBackingStorage->fill(lottie::CGRect(node->bounds().x, node->bounds().y, node->bounds().width, node->bounds().height), lottie::Color(1.0, 1.0, 1.0, 1.0));
|
||||
}
|
||||
if (node->mask() && node->mask()->renderData.isValid) {
|
||||
@ -679,10 +606,6 @@ CGRect getPathNativeBoundingBox(CGPathRef _Nonnull path) {
|
||||
}
|
||||
|
||||
- (UIImage * _Nullable)renderForSize:(CGSize)size useReferenceRendering:(bool)useReferenceRendering {
|
||||
if (!useReferenceRendering) {
|
||||
return nil;
|
||||
}
|
||||
|
||||
LottieAnimation *animation = _animationContainer.animation;
|
||||
std::shared_ptr<lottie::RenderTreeNode> renderNode = [_animationContainer internalGetRootRenderTreeNode];
|
||||
if (!renderNode) {
|
||||
@ -691,7 +614,11 @@ CGRect getPathNativeBoundingBox(CGPathRef _Nonnull path) {
|
||||
|
||||
lottie::CATransform3D rootTransform = lottie::CATransform3D::identity().scaled(lottie::Vector2D(size.width / (float)animation.size.width, size.height / (float)animation.size.height));
|
||||
|
||||
processRenderTree(renderNode, lottie::Vector2D((int)size.width, (int)size.height), rootTransform, false, *_bezierPathsBoundingBoxContext.get());
|
||||
processRenderTree(renderNode, lottie::Vector2D((int)size.width, (int)size.height), false, *_bezierPathsBoundingBoxContext.get());
|
||||
|
||||
if (!useReferenceRendering) {
|
||||
return nil;
|
||||
}
|
||||
|
||||
if (useReferenceRendering) {
|
||||
auto context = std::make_shared<lottieRendering::CanvasImpl>((int)size.width, (int)size.height);
|
||||
|
@ -78,7 +78,7 @@ private final class ReferenceCompareTest {
|
||||
}
|
||||
|
||||
var continueFromName: String?
|
||||
//continueFromName = "1137162165791228042.json"
|
||||
//continueFromName = "4986037051573928320.json"
|
||||
|
||||
let _ = await processAnimationFolderAsync(basePath: bundlePath, path: "", stopOnFailure: true, process: { path, name, alwaysDraw in
|
||||
if let continueFromNameValue = continueFromName {
|
||||
|
@ -14,30 +14,12 @@
|
||||
namespace lottie {
|
||||
|
||||
class ProcessedRenderTreeNodeData {
|
||||
public:
|
||||
struct LayerParams {
|
||||
bool masksToBounds;
|
||||
|
||||
LayerParams(
|
||||
bool masksToBounds_
|
||||
) :
|
||||
masksToBounds(masksToBounds_) {
|
||||
}
|
||||
};
|
||||
|
||||
ProcessedRenderTreeNodeData() :
|
||||
isValid(false),
|
||||
layer(
|
||||
false
|
||||
),
|
||||
drawContentDescendants(false),
|
||||
isInvertedMatte(false) {
|
||||
public:
|
||||
ProcessedRenderTreeNodeData() {
|
||||
}
|
||||
|
||||
bool isValid = false;
|
||||
LayerParams layer;
|
||||
int drawContentDescendants;
|
||||
bool isInvertedMatte;
|
||||
bool isInvertedMatte = false;
|
||||
};
|
||||
|
||||
class RenderableItem {
|
||||
@ -388,6 +370,7 @@ public:
|
||||
std::shared_ptr<RenderTreeNodeContentPath> path;
|
||||
std::vector<std::shared_ptr<RenderTreeNodeContentShadingVariant>> shadings;
|
||||
std::vector<std::shared_ptr<RenderTreeNodeContentItem>> subItems;
|
||||
int drawContentCount = 0;
|
||||
|
||||
ProcessedRenderTreeNodeData renderData;
|
||||
};
|
||||
@ -427,6 +410,9 @@ public:
|
||||
_subnodes(subnodes_),
|
||||
_mask(mask_),
|
||||
_invertMask(invertMask_) {
|
||||
for (const auto &subnode : _subnodes) {
|
||||
drawContentCount += subnode->drawContentCount;
|
||||
}
|
||||
}
|
||||
|
||||
~RenderTreeNode() {
|
||||
@ -477,6 +463,7 @@ public:
|
||||
bool _masksToBounds = false;
|
||||
bool _isHidden = false;
|
||||
std::shared_ptr<RenderTreeNodeContentItem> _contentItem;
|
||||
int drawContentCount = 0;
|
||||
std::vector<std::shared_ptr<RenderTreeNode>> _subnodes;
|
||||
std::shared_ptr<RenderTreeNode> _mask;
|
||||
bool _invertMask = false;
|
||||
|
@ -102,42 +102,6 @@ public:
|
||||
|
||||
virtual std::shared_ptr<RenderTreeNode> renderTreeNode(BezierPathsBoundingBoxContext &boundingBoxContext) override {
|
||||
if (!_renderTreeNode) {
|
||||
_contentsTreeNode = std::make_shared<RenderTreeNode>(
|
||||
CGRect(0.0, 0.0, 0.0, 0.0),
|
||||
Vector2D(0.0, 0.0),
|
||||
CATransform3D::identity(),
|
||||
1.0,
|
||||
false,
|
||||
false,
|
||||
std::vector<std::shared_ptr<RenderTreeNode>>(),
|
||||
nullptr,
|
||||
false
|
||||
);
|
||||
|
||||
std::vector<std::shared_ptr<RenderTreeNode>> subnodes;
|
||||
subnodes.push_back(_contentsTreeNode);
|
||||
|
||||
std::shared_ptr<RenderTreeNode> maskNode;
|
||||
bool invertMask = false;
|
||||
if (_matteLayer) {
|
||||
maskNode = _matteLayer->renderTreeNode(boundingBoxContext);
|
||||
if (maskNode && _matteType.has_value() && _matteType.value() == MatteType::Invert) {
|
||||
invertMask = true;
|
||||
}
|
||||
}
|
||||
|
||||
_renderTreeNode = std::make_shared<RenderTreeNode>(
|
||||
CGRect(0.0, 0.0, 0.0, 0.0),
|
||||
Vector2D(0.0, 0.0),
|
||||
CATransform3D::identity(),
|
||||
1.0,
|
||||
false,
|
||||
false,
|
||||
subnodes,
|
||||
maskNode,
|
||||
invertMask
|
||||
);
|
||||
|
||||
std::vector<std::shared_ptr<RenderTreeNode>> renderTreeSubnodes;
|
||||
for (const auto &animationLayer : _animationLayers) {
|
||||
bool found = false;
|
||||
@ -171,7 +135,41 @@ public:
|
||||
renderTreeValue.push_back(renderTreeContentItem);
|
||||
}
|
||||
|
||||
_contentsTreeNode->_subnodes = renderTreeValue;
|
||||
_contentsTreeNode = std::make_shared<RenderTreeNode>(
|
||||
CGRect(0.0, 0.0, 0.0, 0.0),
|
||||
Vector2D(0.0, 0.0),
|
||||
CATransform3D::identity(),
|
||||
1.0,
|
||||
false,
|
||||
false,
|
||||
renderTreeValue,
|
||||
nullptr,
|
||||
false
|
||||
);
|
||||
|
||||
std::vector<std::shared_ptr<RenderTreeNode>> subnodes;
|
||||
subnodes.push_back(_contentsTreeNode);
|
||||
|
||||
std::shared_ptr<RenderTreeNode> maskNode;
|
||||
bool invertMask = false;
|
||||
if (_matteLayer) {
|
||||
maskNode = _matteLayer->renderTreeNode(boundingBoxContext);
|
||||
if (maskNode && _matteType.has_value() && _matteType.value() == MatteType::Invert) {
|
||||
invertMask = true;
|
||||
}
|
||||
}
|
||||
|
||||
_renderTreeNode = std::make_shared<RenderTreeNode>(
|
||||
CGRect(0.0, 0.0, 0.0, 0.0),
|
||||
Vector2D(0.0, 0.0),
|
||||
CATransform3D::identity(),
|
||||
1.0,
|
||||
false,
|
||||
false,
|
||||
subnodes,
|
||||
maskNode,
|
||||
invertMask
|
||||
);
|
||||
}
|
||||
|
||||
_contentsTreeNode->_bounds = _contentsLayer->bounds();
|
||||
|
@ -994,6 +994,8 @@ public:
|
||||
continue;
|
||||
}
|
||||
|
||||
_contentItem->drawContentCount++;
|
||||
|
||||
auto itemShadingVariant = std::make_shared<RenderTreeNodeContentShadingVariant>();
|
||||
if (shadingVariant.fill) {
|
||||
itemShadingVariant->fill = shadingVariant.fill->fill();
|
||||
@ -1011,6 +1013,7 @@ public:
|
||||
std::vector<std::shared_ptr<RenderTreeNode>> subItemNodes;
|
||||
for (const auto &subItem : subItems) {
|
||||
subItem->initializeRenderChildren();
|
||||
_contentItem->drawContentCount += subItem->_contentItem->drawContentCount;
|
||||
_contentItem->subItems.push_back(subItem->_contentItem);
|
||||
}
|
||||
}
|
||||
@ -1331,9 +1334,9 @@ std::shared_ptr<RenderTreeNode> ShapeCompositionLayer::renderTreeNode(BezierPath
|
||||
false
|
||||
);
|
||||
_contentRenderTreeNode->_contentItem = _contentTree->itemTree->_contentItem;
|
||||
_contentRenderTreeNode->drawContentCount = _contentTree->itemTree->_contentItem->drawContentCount;
|
||||
|
||||
std::vector<std::shared_ptr<RenderTreeNode>> subnodes;
|
||||
//subnodes.push_back(_contentTree->itemTree->renderTree());
|
||||
subnodes.push_back(_contentRenderTreeNode);
|
||||
|
||||
std::shared_ptr<RenderTreeNode> maskNode;
|
||||
|
@ -58,8 +58,6 @@
|
||||
result.isValid = node->renderData.isValid;
|
||||
|
||||
|
||||
result.hasSimpleContents = node->renderData.drawContentDescendants <= 1;
|
||||
result.drawContentDescendants = node->renderData.drawContentDescendants;
|
||||
result.isInvertedMatte = node->renderData.isInvertedMatte;
|
||||
if (node->mask()) {
|
||||
result.maskId = (int64_t)node->mask().get();
|
||||
|
Loading…
x
Reference in New Issue
Block a user