This commit is contained in:
Isaac 2024-05-19 00:24:04 +04:00
parent a5377fd4f5
commit 940619b7cd
6 changed files with 74 additions and 161 deletions

View File

@ -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);

View File

@ -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 {

View File

@ -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;

View File

@ -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();

View File

@ -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;

View File

@ -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();