mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-06-16 05:55:20 +00:00
Lottie
This commit is contained in:
parent
c9a651691a
commit
3a5585c1fd
@ -18,6 +18,35 @@ struct TransformedPath {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
static lottie::CGRect collectPathBoundingBoxes(std::shared_ptr<lottie::RenderTreeNodeContentItem> item, size_t subItemLimit, lottie::CATransform3D const &parentTransform, bool skipApplyTransform) {
|
||||||
|
//TODO:remove skipApplyTransform
|
||||||
|
lottie::CATransform3D effectiveTransform = parentTransform;
|
||||||
|
if (!skipApplyTransform && item->isGroup) {
|
||||||
|
effectiveTransform = item->transform * effectiveTransform;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t maxSubitem = std::min(item->subItems.size(), subItemLimit);
|
||||||
|
|
||||||
|
lottie::CGRect boundingBox(0.0, 0.0, 0.0, 0.0);
|
||||||
|
if (item->path) {
|
||||||
|
boundingBox = item->pathBoundingBox.applyingTransform(effectiveTransform);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (size_t i = 0; i < maxSubitem; i++) {
|
||||||
|
auto &subItem = item->subItems[i];
|
||||||
|
|
||||||
|
lottie::CGRect subItemBoundingBox = collectPathBoundingBoxes(subItem, INT32_MAX, effectiveTransform, false);
|
||||||
|
|
||||||
|
if (boundingBox.empty()) {
|
||||||
|
boundingBox = subItemBoundingBox;
|
||||||
|
} else {
|
||||||
|
boundingBox = boundingBox.unionWith(subItemBoundingBox);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return boundingBox;
|
||||||
|
}
|
||||||
|
|
||||||
static std::vector<TransformedPath> collectPaths(std::shared_ptr<lottie::RenderTreeNodeContentItem> item, size_t subItemLimit, lottie::CATransform3D const &parentTransform, bool skipApplyTransform) {
|
static std::vector<TransformedPath> collectPaths(std::shared_ptr<lottie::RenderTreeNodeContentItem> item, size_t subItemLimit, lottie::CATransform3D const &parentTransform, bool skipApplyTransform) {
|
||||||
std::vector<TransformedPath> mappedPaths;
|
std::vector<TransformedPath> mappedPaths;
|
||||||
|
|
||||||
@ -32,6 +61,7 @@ static std::vector<TransformedPath> collectPaths(std::shared_ptr<lottie::RenderT
|
|||||||
if (item->path) {
|
if (item->path) {
|
||||||
mappedPaths.emplace_back(item->path.value(), effectiveTransform);
|
mappedPaths.emplace_back(item->path.value(), effectiveTransform);
|
||||||
}
|
}
|
||||||
|
assert(!item->trimParams);
|
||||||
|
|
||||||
for (size_t i = 0; i < maxSubitem; i++) {
|
for (size_t i = 0; i < maxSubitem; i++) {
|
||||||
auto &subItem = item->subItems[i];
|
auto &subItem = item->subItems[i];
|
||||||
@ -66,17 +96,8 @@ static void processRenderContentItem(std::shared_ptr<RenderTreeNodeContentItem>
|
|||||||
int drawContentDescendants = 0;
|
int drawContentDescendants = 0;
|
||||||
|
|
||||||
for (const auto &shadingVariant : contentItem->shadings) {
|
for (const auto &shadingVariant : contentItem->shadings) {
|
||||||
std::vector<lottie::BezierPath> itemPaths;
|
lottie::CGRect shapeBounds = collectPathBoundingBoxes(contentItem, shadingVariant->subItemLimit, lottie::CATransform3D::identity(), true);
|
||||||
if (shadingVariant->explicitPath) {
|
|
||||||
itemPaths = shadingVariant->explicitPath.value();
|
|
||||||
} else {
|
|
||||||
auto rawPaths = collectPaths(contentItem, shadingVariant->subItemLimit, lottie::CATransform3D::identity(), true);
|
|
||||||
for (const auto &rawPath : rawPaths) {
|
|
||||||
itemPaths.push_back(rawPath.path.copyUsingTransform(rawPath.transform));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
CGRect shapeBounds = bezierPathsBoundingBoxParallel(bezierPathsBoundingBoxContext, itemPaths);
|
|
||||||
if (shadingVariant->stroke) {
|
if (shadingVariant->stroke) {
|
||||||
shapeBounds = shapeBounds.insetBy(-shadingVariant->stroke->lineWidth / 2.0, -shadingVariant->stroke->lineWidth / 2.0);
|
shapeBounds = shapeBounds.insetBy(-shadingVariant->stroke->lineWidth / 2.0, -shadingVariant->stroke->lineWidth / 2.0);
|
||||||
} else if (shadingVariant->fill) {
|
} else if (shadingVariant->fill) {
|
||||||
@ -95,7 +116,8 @@ static void processRenderContentItem(std::shared_ptr<RenderTreeNodeContentItem>
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (contentItem->isGroup) {
|
if (contentItem->isGroup) {
|
||||||
for (const auto &subItem : contentItem->subItems) {
|
for (auto it = contentItem->subItems.rbegin(); it != contentItem->subItems.rend(); it++) {
|
||||||
|
const auto &subItem = *it;
|
||||||
processRenderContentItem(subItem, globalSize, currentTransform, bezierPathsBoundingBoxContext);
|
processRenderContentItem(subItem, globalSize, currentTransform, bezierPathsBoundingBoxContext);
|
||||||
|
|
||||||
if (subItem->renderData.isValid) {
|
if (subItem->renderData.isValid) {
|
||||||
@ -491,7 +513,8 @@ static void drawLottieContentItem(std::shared_ptr<lottieRendering::Canvas> paren
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for (const auto &subItem : item->subItems) {
|
for (auto it = item->subItems.rbegin(); it != item->subItems.rend(); it++) {
|
||||||
|
const auto &subItem = *it;
|
||||||
drawLottieContentItem(currentContext, subItem, renderAlpha);
|
drawLottieContentItem(currentContext, subItem, renderAlpha);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -144,6 +144,7 @@ public:
|
|||||||
|
|
||||||
CGRect bezierPathsBoundingBox(std::vector<BezierPath> const &paths);
|
CGRect bezierPathsBoundingBox(std::vector<BezierPath> const &paths);
|
||||||
CGRect bezierPathsBoundingBoxParallel(BezierPathsBoundingBoxContext &context, std::vector<BezierPath> const &paths);
|
CGRect bezierPathsBoundingBoxParallel(BezierPathsBoundingBoxContext &context, std::vector<BezierPath> const &paths);
|
||||||
|
CGRect bezierPathsBoundingBoxParallel(BezierPathsBoundingBoxContext &context, BezierPath const &path);
|
||||||
|
|
||||||
std::vector<BezierPath> trimBezierPaths(std::vector<BezierPath> &sourcePaths, double start, double end, double offset, TrimType type);
|
std::vector<BezierPath> trimBezierPaths(std::vector<BezierPath> &sourcePaths, double start, double end, double offset, TrimType type);
|
||||||
|
|
||||||
|
@ -424,6 +424,7 @@ public:
|
|||||||
double alpha = 0.0;
|
double alpha = 0.0;
|
||||||
std::optional<TrimParams> trimParams;
|
std::optional<TrimParams> trimParams;
|
||||||
std::optional<BezierPath> path;
|
std::optional<BezierPath> path;
|
||||||
|
CGRect pathBoundingBox = CGRect(0.0, 0.0, 0.0, 0.0);
|
||||||
std::vector<std::shared_ptr<RenderTreeNodeContentShadingVariant>> shadings;
|
std::vector<std::shared_ptr<RenderTreeNodeContentShadingVariant>> shadings;
|
||||||
std::vector<std::shared_ptr<RenderTreeNodeContentItem>> subItems;
|
std::vector<std::shared_ptr<RenderTreeNodeContentItem>> subItems;
|
||||||
|
|
||||||
|
@ -82,7 +82,7 @@ public:
|
|||||||
return _contentsLayer;
|
return _contentsLayer;
|
||||||
}
|
}
|
||||||
|
|
||||||
void displayWithFrame(double frame, bool forceUpdates) {
|
void displayWithFrame(double frame, bool forceUpdates, BezierPathsBoundingBoxContext &boundingBoxContext) {
|
||||||
_transformNode->updateTree(frame, forceUpdates);
|
_transformNode->updateTree(frame, forceUpdates);
|
||||||
|
|
||||||
bool layerVisible = isInRangeOrEqual(frame, _inFrame, _outFrame);
|
bool layerVisible = isInRangeOrEqual(frame, _inFrame, _outFrame);
|
||||||
@ -93,14 +93,14 @@ public:
|
|||||||
|
|
||||||
/// Only update contents if current time is within the layers time bounds.
|
/// Only update contents if current time is within the layers time bounds.
|
||||||
if (layerVisible) {
|
if (layerVisible) {
|
||||||
displayContentsWithFrame(frame, forceUpdates);
|
displayContentsWithFrame(frame, forceUpdates, boundingBoxContext);
|
||||||
if (_maskLayer) {
|
if (_maskLayer) {
|
||||||
_maskLayer->updateWithFrame(frame, forceUpdates);
|
_maskLayer->updateWithFrame(frame, forceUpdates);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
virtual void displayContentsWithFrame(double frame, bool forceUpdates) {
|
virtual void displayContentsWithFrame(double frame, bool forceUpdates, BezierPathsBoundingBoxContext &boundingBoxContext) {
|
||||||
/// To be overridden by subclass
|
/// To be overridden by subclass
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -151,11 +151,11 @@ public:
|
|||||||
return _timeStretch;
|
return _timeStretch;
|
||||||
}
|
}
|
||||||
|
|
||||||
virtual std::shared_ptr<RenderTreeNode> renderTreeNode() {
|
virtual std::shared_ptr<RenderTreeNode> renderTreeNode(BezierPathsBoundingBoxContext &boundingBoxContext) {
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
virtual void updateRenderTree() {
|
virtual void updateRenderTree(BezierPathsBoundingBoxContext &boundingBoxContext) {
|
||||||
}
|
}
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
@ -86,7 +86,7 @@ public:
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
virtual void displayContentsWithFrame(double frame, bool forceUpdates) override {
|
virtual void displayContentsWithFrame(double frame, bool forceUpdates, BezierPathsBoundingBoxContext &boundingBoxContext) override {
|
||||||
double localFrame = 0.0;
|
double localFrame = 0.0;
|
||||||
if (_remappingNode) {
|
if (_remappingNode) {
|
||||||
_remappingNode->update(frame);
|
_remappingNode->update(frame);
|
||||||
@ -96,11 +96,11 @@ public:
|
|||||||
}
|
}
|
||||||
|
|
||||||
for (const auto &animationLayer : _animationLayers) {
|
for (const auto &animationLayer : _animationLayers) {
|
||||||
animationLayer->displayWithFrame(localFrame, forceUpdates);
|
animationLayer->displayWithFrame(localFrame, forceUpdates, boundingBoxContext);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
virtual std::shared_ptr<RenderTreeNode> renderTreeNode() override {
|
virtual std::shared_ptr<RenderTreeNode> renderTreeNode(BezierPathsBoundingBoxContext &boundingBoxContext) override {
|
||||||
if (!_renderTreeNode) {
|
if (!_renderTreeNode) {
|
||||||
_contentsTreeNode = std::make_shared<RenderTreeNode>(
|
_contentsTreeNode = std::make_shared<RenderTreeNode>(
|
||||||
CGRect(0.0, 0.0, 0.0, 0.0),
|
CGRect(0.0, 0.0, 0.0, 0.0),
|
||||||
@ -120,7 +120,7 @@ public:
|
|||||||
std::shared_ptr<RenderTreeNode> maskNode;
|
std::shared_ptr<RenderTreeNode> maskNode;
|
||||||
bool invertMask = false;
|
bool invertMask = false;
|
||||||
if (_matteLayer) {
|
if (_matteLayer) {
|
||||||
maskNode = _matteLayer->renderTreeNode();
|
maskNode = _matteLayer->renderTreeNode(boundingBoxContext);
|
||||||
if (maskNode && _matteType.has_value() && _matteType.value() == MatteType::Invert) {
|
if (maskNode && _matteType.has_value() && _matteType.value() == MatteType::Invert) {
|
||||||
invertMask = true;
|
invertMask = true;
|
||||||
}
|
}
|
||||||
@ -148,7 +148,7 @@ public:
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (found) {
|
if (found) {
|
||||||
auto node = animationLayer->renderTreeNode();
|
auto node = animationLayer->renderTreeNode(boundingBoxContext);
|
||||||
if (node) {
|
if (node) {
|
||||||
renderTreeSubnodes.push_back(node);
|
renderTreeSubnodes.push_back(node);
|
||||||
}
|
}
|
||||||
@ -177,9 +177,9 @@ public:
|
|||||||
return _renderTreeNode;
|
return _renderTreeNode;
|
||||||
}
|
}
|
||||||
|
|
||||||
virtual void updateRenderTree() override {
|
virtual void updateRenderTree(BezierPathsBoundingBoxContext &boundingBoxContext) override {
|
||||||
if (_matteLayer) {
|
if (_matteLayer) {
|
||||||
_matteLayer->updateRenderTree();
|
_matteLayer->updateRenderTree(boundingBoxContext);
|
||||||
}
|
}
|
||||||
|
|
||||||
for (const auto &animationLayer : _animationLayers) {
|
for (const auto &animationLayer : _animationLayers) {
|
||||||
@ -191,7 +191,7 @@ public:
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (found) {
|
if (found) {
|
||||||
animationLayer->updateRenderTree();
|
animationLayer->updateRenderTree(boundingBoxContext);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -506,8 +506,9 @@ public:
|
|||||||
}
|
}
|
||||||
virtual ~PathOutput() = default;
|
virtual ~PathOutput() = default;
|
||||||
|
|
||||||
virtual void update(AnimationFrameTime frameTime) = 0;
|
virtual void update(AnimationFrameTime frameTime, BezierPathsBoundingBoxContext &boundingBoxContext) = 0;
|
||||||
virtual BezierPath const *currentPath() = 0;
|
virtual BezierPath const *currentPath() = 0;
|
||||||
|
virtual CGRect const ¤tPathBounds() = 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
class StaticPathOutput : public PathOutput {
|
class StaticPathOutput : public PathOutput {
|
||||||
@ -516,15 +517,26 @@ public:
|
|||||||
resolvedPath(path) {
|
resolvedPath(path) {
|
||||||
}
|
}
|
||||||
|
|
||||||
virtual void update(AnimationFrameTime frameTime) override {
|
virtual void update(AnimationFrameTime frameTime, BezierPathsBoundingBoxContext &boundingBoxContext) override {
|
||||||
|
if (!isPathBoundsResolved) {
|
||||||
|
resolvedPathBounds = bezierPathsBoundingBoxParallel(boundingBoxContext, resolvedPath);
|
||||||
|
isPathBoundsResolved = true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
virtual BezierPath const *currentPath() override {
|
virtual BezierPath const *currentPath() override {
|
||||||
return &resolvedPath;
|
return &resolvedPath;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
virtual CGRect const ¤tPathBounds() override {
|
||||||
|
return resolvedPathBounds;
|
||||||
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
BezierPath resolvedPath;
|
BezierPath resolvedPath;
|
||||||
|
|
||||||
|
bool isPathBoundsResolved = false;
|
||||||
|
CGRect resolvedPathBounds = CGRect(0.0, 0.0, 0.0, 0.0);
|
||||||
};
|
};
|
||||||
|
|
||||||
class ShapePathOutput : public PathOutput {
|
class ShapePathOutput : public PathOutput {
|
||||||
@ -533,9 +545,10 @@ public:
|
|||||||
path(shape.path.keyframes) {
|
path(shape.path.keyframes) {
|
||||||
}
|
}
|
||||||
|
|
||||||
virtual void update(AnimationFrameTime frameTime) override {
|
virtual void update(AnimationFrameTime frameTime, BezierPathsBoundingBoxContext &boundingBoxContext) override {
|
||||||
if (!hasValidData || path.hasUpdate(frameTime)) {
|
if (!hasValidData || path.hasUpdate(frameTime)) {
|
||||||
path.update(frameTime, resolvedPath);
|
path.update(frameTime, resolvedPath);
|
||||||
|
resolvedPathBounds = bezierPathsBoundingBoxParallel(boundingBoxContext, resolvedPath);
|
||||||
}
|
}
|
||||||
|
|
||||||
hasValidData = true;
|
hasValidData = true;
|
||||||
@ -545,12 +558,17 @@ public:
|
|||||||
return &resolvedPath;
|
return &resolvedPath;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
virtual CGRect const ¤tPathBounds() override {
|
||||||
|
return resolvedPathBounds;
|
||||||
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
bool hasValidData = false;
|
bool hasValidData = false;
|
||||||
|
|
||||||
BezierPathKeyframeInterpolator path;
|
BezierPathKeyframeInterpolator path;
|
||||||
|
|
||||||
BezierPath resolvedPath;
|
BezierPath resolvedPath;
|
||||||
|
CGRect resolvedPathBounds = CGRect(0.0, 0.0, 0.0, 0.0);
|
||||||
};
|
};
|
||||||
|
|
||||||
class RectanglePathOutput : public PathOutput {
|
class RectanglePathOutput : public PathOutput {
|
||||||
@ -562,7 +580,7 @@ public:
|
|||||||
cornerRadius(rectangle.cornerRadius.keyframes) {
|
cornerRadius(rectangle.cornerRadius.keyframes) {
|
||||||
}
|
}
|
||||||
|
|
||||||
virtual void update(AnimationFrameTime frameTime) override {
|
virtual void update(AnimationFrameTime frameTime, BezierPathsBoundingBoxContext &boundingBoxContext) override {
|
||||||
bool hasUpdates = false;
|
bool hasUpdates = false;
|
||||||
|
|
||||||
if (!hasValidData || position.hasUpdate(frameTime)) {
|
if (!hasValidData || position.hasUpdate(frameTime)) {
|
||||||
@ -580,6 +598,7 @@ public:
|
|||||||
|
|
||||||
if (hasUpdates) {
|
if (hasUpdates) {
|
||||||
ValueInterpolator<BezierPath>::setInplace(makeRectangleBezierPath(Vector2D(positionValue.x, positionValue.y), Vector2D(sizeValue.x, sizeValue.y), cornerRadiusValue, direction), resolvedPath);
|
ValueInterpolator<BezierPath>::setInplace(makeRectangleBezierPath(Vector2D(positionValue.x, positionValue.y), Vector2D(sizeValue.x, sizeValue.y), cornerRadiusValue, direction), resolvedPath);
|
||||||
|
resolvedPathBounds = bezierPathsBoundingBoxParallel(boundingBoxContext, resolvedPath);
|
||||||
}
|
}
|
||||||
|
|
||||||
hasValidData = true;
|
hasValidData = true;
|
||||||
@ -589,6 +608,10 @@ public:
|
|||||||
return &resolvedPath;
|
return &resolvedPath;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
virtual CGRect const ¤tPathBounds() override {
|
||||||
|
return resolvedPathBounds;
|
||||||
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
bool hasValidData = false;
|
bool hasValidData = false;
|
||||||
|
|
||||||
@ -604,6 +627,7 @@ public:
|
|||||||
double cornerRadiusValue = 0.0;
|
double cornerRadiusValue = 0.0;
|
||||||
|
|
||||||
BezierPath resolvedPath;
|
BezierPath resolvedPath;
|
||||||
|
CGRect resolvedPathBounds = CGRect(0.0, 0.0, 0.0, 0.0);
|
||||||
};
|
};
|
||||||
|
|
||||||
class EllipsePathOutput : public PathOutput {
|
class EllipsePathOutput : public PathOutput {
|
||||||
@ -614,7 +638,7 @@ public:
|
|||||||
size(ellipse.size.keyframes) {
|
size(ellipse.size.keyframes) {
|
||||||
}
|
}
|
||||||
|
|
||||||
virtual void update(AnimationFrameTime frameTime) override {
|
virtual void update(AnimationFrameTime frameTime, BezierPathsBoundingBoxContext &boundingBoxContext) override {
|
||||||
bool hasUpdates = false;
|
bool hasUpdates = false;
|
||||||
|
|
||||||
if (!hasValidData || position.hasUpdate(frameTime)) {
|
if (!hasValidData || position.hasUpdate(frameTime)) {
|
||||||
@ -628,6 +652,7 @@ public:
|
|||||||
|
|
||||||
if (hasUpdates) {
|
if (hasUpdates) {
|
||||||
ValueInterpolator<BezierPath>::setInplace(makeEllipseBezierPath(Vector2D(sizeValue.x, sizeValue.y), Vector2D(positionValue.x, positionValue.y), direction), resolvedPath);
|
ValueInterpolator<BezierPath>::setInplace(makeEllipseBezierPath(Vector2D(sizeValue.x, sizeValue.y), Vector2D(positionValue.x, positionValue.y), direction), resolvedPath);
|
||||||
|
resolvedPathBounds = bezierPathsBoundingBoxParallel(boundingBoxContext, resolvedPath);
|
||||||
}
|
}
|
||||||
|
|
||||||
hasValidData = true;
|
hasValidData = true;
|
||||||
@ -637,6 +662,10 @@ public:
|
|||||||
return &resolvedPath;
|
return &resolvedPath;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
virtual CGRect const ¤tPathBounds() override {
|
||||||
|
return resolvedPathBounds;
|
||||||
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
bool hasValidData = false;
|
bool hasValidData = false;
|
||||||
|
|
||||||
@ -649,6 +678,7 @@ public:
|
|||||||
Vector3D sizeValue = Vector3D(0.0, 0.0, 0.0);
|
Vector3D sizeValue = Vector3D(0.0, 0.0, 0.0);
|
||||||
|
|
||||||
BezierPath resolvedPath;
|
BezierPath resolvedPath;
|
||||||
|
CGRect resolvedPathBounds = CGRect(0.0, 0.0, 0.0, 0.0);
|
||||||
};
|
};
|
||||||
|
|
||||||
class StarPathOutput : public PathOutput {
|
class StarPathOutput : public PathOutput {
|
||||||
@ -673,7 +703,7 @@ public:
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
virtual void update(AnimationFrameTime frameTime) override {
|
virtual void update(AnimationFrameTime frameTime, BezierPathsBoundingBoxContext &boundingBoxContext) override {
|
||||||
bool hasUpdates = false;
|
bool hasUpdates = false;
|
||||||
|
|
||||||
if (!hasValidData || position.hasUpdate(frameTime)) {
|
if (!hasValidData || position.hasUpdate(frameTime)) {
|
||||||
@ -715,6 +745,7 @@ public:
|
|||||||
|
|
||||||
if (hasUpdates) {
|
if (hasUpdates) {
|
||||||
ValueInterpolator<BezierPath>::setInplace(makeStarBezierPath(Vector2D(positionValue.x, positionValue.y), outerRadiusValue, innerRadiusValue, outerRoundednessValue, innerRoundednessValue, pointsValue, rotationValue, direction), resolvedPath);
|
ValueInterpolator<BezierPath>::setInplace(makeStarBezierPath(Vector2D(positionValue.x, positionValue.y), outerRadiusValue, innerRadiusValue, outerRoundednessValue, innerRoundednessValue, pointsValue, rotationValue, direction), resolvedPath);
|
||||||
|
resolvedPathBounds = bezierPathsBoundingBoxParallel(boundingBoxContext, resolvedPath);
|
||||||
}
|
}
|
||||||
|
|
||||||
hasValidData = true;
|
hasValidData = true;
|
||||||
@ -724,6 +755,10 @@ public:
|
|||||||
return &resolvedPath;
|
return &resolvedPath;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
virtual CGRect const ¤tPathBounds() override {
|
||||||
|
return resolvedPathBounds;
|
||||||
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
bool hasValidData = false;
|
bool hasValidData = false;
|
||||||
|
|
||||||
@ -751,6 +786,7 @@ public:
|
|||||||
double pointsValue = 0.0;
|
double pointsValue = 0.0;
|
||||||
|
|
||||||
BezierPath resolvedPath;
|
BezierPath resolvedPath;
|
||||||
|
CGRect resolvedPathBounds = CGRect(0.0, 0.0, 0.0, 0.0);
|
||||||
};
|
};
|
||||||
|
|
||||||
class TransformOutput {
|
class TransformOutput {
|
||||||
@ -903,11 +939,7 @@ public:
|
|||||||
std::shared_ptr<RenderTreeNodeContentItem> _contentItem;
|
std::shared_ptr<RenderTreeNodeContentItem> _contentItem;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
bool hasTrims(size_t subItemLimit) {
|
std::vector<TransformedPath> collectPaths(size_t subItemLimit, CATransform3D const &parentTransform, bool skipApplyTransform) {
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::vector<TransformedPath> collectPaths(size_t subItemLimit, CATransform3D const &parentTransform, bool skipApplyTransform, bool &hasTrims) {
|
|
||||||
std::vector<TransformedPath> mappedPaths;
|
std::vector<TransformedPath> mappedPaths;
|
||||||
|
|
||||||
//TODO:remove skipApplyTransform
|
//TODO:remove skipApplyTransform
|
||||||
@ -930,10 +962,9 @@ public:
|
|||||||
currentTrim = trims[0]->trimParams();
|
currentTrim = trims[0]->trimParams();
|
||||||
}
|
}
|
||||||
|
|
||||||
auto subItemPaths = subItem->collectPaths(INT32_MAX, effectiveTransform, false, hasTrims);
|
auto subItemPaths = subItem->collectPaths(INT32_MAX, effectiveTransform, false);
|
||||||
|
|
||||||
if (currentTrim) {
|
if (currentTrim) {
|
||||||
hasTrims = true;
|
|
||||||
CompoundBezierPath tempPath;
|
CompoundBezierPath tempPath;
|
||||||
for (auto &path : subItemPaths) {
|
for (auto &path : subItemPaths) {
|
||||||
tempPath.appendPath(path.path.copyUsingTransform(path.transform));
|
tempPath.appendPath(path.path.copyUsingTransform(path.transform));
|
||||||
@ -1007,23 +1038,22 @@ public:
|
|||||||
|
|
||||||
if (isGroup && !subItems.empty()) {
|
if (isGroup && !subItems.empty()) {
|
||||||
std::vector<std::shared_ptr<RenderTreeNode>> subItemNodes;
|
std::vector<std::shared_ptr<RenderTreeNode>> subItemNodes;
|
||||||
for (int i = (int)subItems.size() - 1; i >= 0; i--) {
|
for (const auto &subItem : subItems) {
|
||||||
subItems[i]->initializeRenderChildren();
|
subItem->initializeRenderChildren();
|
||||||
_contentItem->subItems.push_back(subItems[i]->_contentItem);
|
_contentItem->subItems.push_back(subItem->_contentItem);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public:
|
public:
|
||||||
void updateFrame(AnimationFrameTime frameTime) {
|
void updateFrame(AnimationFrameTime frameTime, BezierPathsBoundingBoxContext &boundingBoxContext) {
|
||||||
if (transform) {
|
if (transform) {
|
||||||
transform->update(frameTime);
|
transform->update(frameTime);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (path) {
|
if (path) {
|
||||||
path->update(frameTime);
|
path->update(frameTime, boundingBoxContext);
|
||||||
} else {
|
_contentItem->pathBoundingBox = path->currentPathBounds();
|
||||||
_contentItem->path = std::nullopt;
|
|
||||||
}
|
}
|
||||||
for (const auto &trim : trims) {
|
for (const auto &trim : trims) {
|
||||||
trim->update(frameTime);
|
trim->update(frameTime);
|
||||||
@ -1039,10 +1069,24 @@ public:
|
|||||||
}
|
}
|
||||||
|
|
||||||
for (const auto &subItem : subItems) {
|
for (const auto &subItem : subItems) {
|
||||||
subItem->updateFrame(frameTime);
|
subItem->updateFrame(frameTime, boundingBoxContext);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool hasTrims() {
|
||||||
|
if (!trims.empty()) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const auto &subItem : subItems) {
|
||||||
|
if (subItem->hasTrims()) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
void updateContents(std::optional<TrimParams> parentTrim) {
|
void updateContents(std::optional<TrimParams> parentTrim) {
|
||||||
CATransform3D containerTransform = CATransform3D::identity();
|
CATransform3D containerTransform = CATransform3D::identity();
|
||||||
double containerOpacity = 1.0;
|
double containerOpacity = 1.0;
|
||||||
@ -1053,6 +1097,10 @@ public:
|
|||||||
_contentItem->transform = containerTransform;
|
_contentItem->transform = containerTransform;
|
||||||
_contentItem->alpha = containerOpacity;
|
_contentItem->alpha = containerOpacity;
|
||||||
|
|
||||||
|
if (!trims.empty()) {
|
||||||
|
_contentItem->trimParams = trims[0]->trimParams();
|
||||||
|
}
|
||||||
|
|
||||||
for (int i = 0; i < shadings.size(); i++) {
|
for (int i = 0; i < shadings.size(); i++) {
|
||||||
const auto &shadingVariant = shadings[i];
|
const auto &shadingVariant = shadings[i];
|
||||||
|
|
||||||
@ -1066,11 +1114,9 @@ public:
|
|||||||
currentTrim = trims[0];
|
currentTrim = trims[0];
|
||||||
}*/
|
}*/
|
||||||
|
|
||||||
bool hasTrims = false;
|
|
||||||
if (parentTrim) {
|
if (parentTrim) {
|
||||||
CompoundBezierPath compoundPath;
|
CompoundBezierPath compoundPath;
|
||||||
hasTrims = true;
|
auto paths = collectPaths(shadingVariant.subItemLimit, CATransform3D::identity(), true);
|
||||||
auto paths = collectPaths(shadingVariant.subItemLimit, CATransform3D::identity(), true, hasTrims);
|
|
||||||
for (const auto &path : paths) {
|
for (const auto &path : paths) {
|
||||||
compoundPath.appendPath(path.path.copyUsingTransform(path.transform));
|
compoundPath.appendPath(path.path.copyUsingTransform(path.transform));
|
||||||
}
|
}
|
||||||
@ -1083,21 +1129,20 @@ public:
|
|||||||
}
|
}
|
||||||
_contentItem->shadings[i]->explicitPath = resultPaths;
|
_contentItem->shadings[i]->explicitPath = resultPaths;
|
||||||
} else {
|
} else {
|
||||||
CompoundBezierPath compoundPath;
|
if (hasTrims()) {
|
||||||
auto paths = collectPaths(shadingVariant.subItemLimit, CATransform3D::identity(), true, hasTrims);
|
CompoundBezierPath compoundPath;
|
||||||
for (const auto &path : paths) {
|
auto paths = collectPaths(shadingVariant.subItemLimit, CATransform3D::identity(), true);
|
||||||
compoundPath.appendPath(path.path.copyUsingTransform(path.transform));
|
for (const auto &path : paths) {
|
||||||
}
|
compoundPath.appendPath(path.path.copyUsingTransform(path.transform));
|
||||||
std::vector<BezierPath> resultPaths;
|
}
|
||||||
for (const auto &path : compoundPath.paths) {
|
std::vector<BezierPath> resultPaths;
|
||||||
resultPaths.push_back(path);
|
for (const auto &path : compoundPath.paths) {
|
||||||
}
|
resultPaths.push_back(path);
|
||||||
|
}
|
||||||
if (hasTrims) {
|
|
||||||
_contentItem->shadings[i]->explicitPath = resultPaths;
|
_contentItem->shadings[i]->explicitPath = resultPaths;
|
||||||
} else {
|
} else {
|
||||||
_contentItem->shadings[i]->explicitPath = std::nullopt;
|
_contentItem->shadings[i]->explicitPath = std::nullopt;
|
||||||
_contentItem->shadings[i]->explicitPath = resultPaths;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1288,18 +1333,18 @@ CompositionLayer(solidLayer, Vector2D::Zero()) {
|
|||||||
_contentTree = std::make_shared<ShapeLayerPresentationTree>(solidLayer);
|
_contentTree = std::make_shared<ShapeLayerPresentationTree>(solidLayer);
|
||||||
}
|
}
|
||||||
|
|
||||||
void ShapeCompositionLayer::displayContentsWithFrame(double frame, bool forceUpdates) {
|
void ShapeCompositionLayer::displayContentsWithFrame(double frame, bool forceUpdates, BezierPathsBoundingBoxContext &boundingBoxContext) {
|
||||||
_frameTime = frame;
|
_frameTime = frame;
|
||||||
_frameTimeInitialized = true;
|
_frameTimeInitialized = true;
|
||||||
_contentTree->itemTree->updateFrame(_frameTime);
|
_contentTree->itemTree->updateFrame(_frameTime, boundingBoxContext);
|
||||||
_contentTree->itemTree->updateContents(std::nullopt);
|
_contentTree->itemTree->updateContents(std::nullopt);
|
||||||
}
|
}
|
||||||
|
|
||||||
std::shared_ptr<RenderTreeNode> ShapeCompositionLayer::renderTreeNode() {
|
std::shared_ptr<RenderTreeNode> ShapeCompositionLayer::renderTreeNode(BezierPathsBoundingBoxContext &boundingBoxContext) {
|
||||||
if (!_frameTimeInitialized) {
|
if (!_frameTimeInitialized) {
|
||||||
_frameTime = 0.0;
|
_frameTime = 0.0;
|
||||||
_frameTimeInitialized = true;
|
_frameTimeInitialized = true;
|
||||||
_contentTree->itemTree->updateFrame(_frameTime);
|
_contentTree->itemTree->updateFrame(_frameTime, boundingBoxContext);
|
||||||
_contentTree->itemTree->updateContents(std::nullopt);
|
_contentTree->itemTree->updateContents(std::nullopt);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1324,7 +1369,7 @@ std::shared_ptr<RenderTreeNode> ShapeCompositionLayer::renderTreeNode() {
|
|||||||
std::shared_ptr<RenderTreeNode> maskNode;
|
std::shared_ptr<RenderTreeNode> maskNode;
|
||||||
bool invertMask = false;
|
bool invertMask = false;
|
||||||
if (_matteLayer) {
|
if (_matteLayer) {
|
||||||
maskNode = _matteLayer->renderTreeNode();
|
maskNode = _matteLayer->renderTreeNode(boundingBoxContext);
|
||||||
if (maskNode && _matteType.has_value() && _matteType.value() == MatteType::Invert) {
|
if (maskNode && _matteType.has_value() && _matteType.value() == MatteType::Invert) {
|
||||||
invertMask = true;
|
invertMask = true;
|
||||||
}
|
}
|
||||||
@ -1346,9 +1391,9 @@ std::shared_ptr<RenderTreeNode> ShapeCompositionLayer::renderTreeNode() {
|
|||||||
return _renderTreeNode;
|
return _renderTreeNode;
|
||||||
}
|
}
|
||||||
|
|
||||||
void ShapeCompositionLayer::updateRenderTree() {
|
void ShapeCompositionLayer::updateRenderTree(BezierPathsBoundingBoxContext &boundingBoxContext) {
|
||||||
if (_matteLayer) {
|
if (_matteLayer) {
|
||||||
_matteLayer->updateRenderTree();
|
_matteLayer->updateRenderTree(boundingBoxContext);
|
||||||
}
|
}
|
||||||
|
|
||||||
_contentRenderTreeNode->_bounds = _contentsLayer->bounds();
|
_contentRenderTreeNode->_bounds = _contentsLayer->bounds();
|
||||||
|
@ -16,9 +16,9 @@ public:
|
|||||||
ShapeCompositionLayer(std::shared_ptr<ShapeLayerModel> const &shapeLayer);
|
ShapeCompositionLayer(std::shared_ptr<ShapeLayerModel> const &shapeLayer);
|
||||||
ShapeCompositionLayer(std::shared_ptr<SolidLayerModel> const &solidLayer);
|
ShapeCompositionLayer(std::shared_ptr<SolidLayerModel> const &solidLayer);
|
||||||
|
|
||||||
virtual void displayContentsWithFrame(double frame, bool forceUpdates) override;
|
virtual void displayContentsWithFrame(double frame, bool forceUpdates, BezierPathsBoundingBoxContext &boundingBoxContext) override;
|
||||||
virtual std::shared_ptr<RenderTreeNode> renderTreeNode() override;
|
virtual std::shared_ptr<RenderTreeNode> renderTreeNode(BezierPathsBoundingBoxContext &boundingBoxContext) override;
|
||||||
virtual void updateRenderTree() override;
|
virtual void updateRenderTree(BezierPathsBoundingBoxContext &boundingBoxContext) override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
std::shared_ptr<ShapeLayerPresentationTree> _contentTree;
|
std::shared_ptr<ShapeLayerPresentationTree> _contentTree;
|
||||||
|
@ -42,7 +42,7 @@ public:
|
|||||||
_fontProvider = fontProvider;
|
_fontProvider = fontProvider;
|
||||||
}
|
}
|
||||||
|
|
||||||
virtual void displayContentsWithFrame(double frame, bool forceUpdates) override {
|
virtual void displayContentsWithFrame(double frame, bool forceUpdates, BezierPathsBoundingBoxContext &boundingBoxContext) override {
|
||||||
if (!_textDocument) {
|
if (!_textDocument) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -103,7 +103,7 @@ public:
|
|||||||
newFrame = floor(newFrame);
|
newFrame = floor(newFrame);
|
||||||
}
|
}
|
||||||
for (const auto &layer : _animationLayers) {
|
for (const auto &layer : _animationLayers) {
|
||||||
layer->displayWithFrame(newFrame, false);
|
layer->displayWithFrame(newFrame, false, _boundingBoxContext);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -118,7 +118,7 @@ public:
|
|||||||
/// Forces the view to update its drawing.
|
/// Forces the view to update its drawing.
|
||||||
void forceDisplayUpdate() {
|
void forceDisplayUpdate() {
|
||||||
for (const auto &layer : _animationLayers) {
|
for (const auto &layer : _animationLayers) {
|
||||||
layer->displayWithFrame(currentFrame(), true);
|
layer->displayWithFrame(currentFrame(), true, _boundingBoxContext);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -193,7 +193,7 @@ public:
|
|||||||
_currentFrame = currentFrame;
|
_currentFrame = currentFrame;
|
||||||
|
|
||||||
for (size_t i = 0; i < _animationLayers.size(); i++) {
|
for (size_t i = 0; i < _animationLayers.size(); i++) {
|
||||||
_animationLayers[i]->displayWithFrame(_currentFrame, false);
|
_animationLayers[i]->displayWithFrame(_currentFrame, false, _boundingBoxContext);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -230,7 +230,7 @@ public:
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (found) {
|
if (found) {
|
||||||
auto node = animationLayer->renderTreeNode();
|
auto node = animationLayer->renderTreeNode(_boundingBoxContext);
|
||||||
if (node) {
|
if (node) {
|
||||||
subnodes.push_back(node);
|
subnodes.push_back(node);
|
||||||
}
|
}
|
||||||
@ -264,7 +264,7 @@ public:
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (found) {
|
if (found) {
|
||||||
animationLayer->updateRenderTree();
|
animationLayer->updateRenderTree(_boundingBoxContext);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -288,6 +288,8 @@ private:
|
|||||||
std::shared_ptr<LayerFontProvider> _layerFontProvider;
|
std::shared_ptr<LayerFontProvider> _layerFontProvider;
|
||||||
|
|
||||||
std::shared_ptr<RenderTreeNode> _renderTreeNode;
|
std::shared_ptr<RenderTreeNode> _renderTreeNode;
|
||||||
|
|
||||||
|
BezierPathsBoundingBoxContext _boundingBoxContext;
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -582,6 +582,58 @@ CGRect bezierPathsBoundingBoxParallel(BezierPathsBoundingBoxContext &context, st
|
|||||||
return calculateBoundingRectOpt(pointsX, pointsY, pointCount);
|
return calculateBoundingRectOpt(pointsX, pointsY, pointCount);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
CGRect bezierPathsBoundingBoxParallel(BezierPathsBoundingBoxContext &context, BezierPath const &path) {
|
||||||
|
int pointCount = 0;
|
||||||
|
|
||||||
|
float *pointsX = context.pointsX;
|
||||||
|
float *pointsY = context.pointsY;
|
||||||
|
int pointsSize = context.pointsSize;
|
||||||
|
|
||||||
|
PathElement const *pathElements = path.elements().data();
|
||||||
|
int pathElementCount = (int)path.elements().size();
|
||||||
|
|
||||||
|
for (int i = 0; i < pathElementCount; i++) {
|
||||||
|
const auto &element = pathElements[i];
|
||||||
|
|
||||||
|
if (pointsSize < pointCount + 1) {
|
||||||
|
pointsSize = (pointCount + 1) * 2;
|
||||||
|
pointsX = (float *)realloc(pointsX, pointsSize * 4);
|
||||||
|
pointsY = (float *)realloc(pointsY, pointsSize * 4);
|
||||||
|
}
|
||||||
|
pointsX[pointCount] = (float)element.vertex.point.x;
|
||||||
|
pointsY[pointCount] = (float)element.vertex.point.y;
|
||||||
|
pointCount++;
|
||||||
|
|
||||||
|
if (i != 0) {
|
||||||
|
const auto &previousElement = pathElements[i - 1];
|
||||||
|
if (previousElement.vertex.outTangentRelative().isZero() && element.vertex.inTangentRelative().isZero()) {
|
||||||
|
} else {
|
||||||
|
if (pointsSize < pointCount + 1) {
|
||||||
|
pointsSize = (pointCount + 2) * 2;
|
||||||
|
pointsX = (float *)realloc(pointsX, pointsSize * 4);
|
||||||
|
pointsY = (float *)realloc(pointsY, pointsSize * 4);
|
||||||
|
}
|
||||||
|
pointsX[pointCount] = (float)previousElement.vertex.outTangent.x;
|
||||||
|
pointsY[pointCount] = (float)previousElement.vertex.outTangent.y;
|
||||||
|
pointCount++;
|
||||||
|
pointsX[pointCount] = (float)element.vertex.inTangent.x;
|
||||||
|
pointsY[pointCount] = (float)element.vertex.inTangent.y;
|
||||||
|
pointCount++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
context.pointsX = pointsX;
|
||||||
|
context.pointsY = pointsY;
|
||||||
|
context.pointsSize = pointsSize;
|
||||||
|
|
||||||
|
if (pointCount == 0) {
|
||||||
|
return CGRect(0.0, 0.0, 0.0, 0.0);
|
||||||
|
}
|
||||||
|
|
||||||
|
return calculateBoundingRectOpt(pointsX, pointsY, pointCount);
|
||||||
|
}
|
||||||
|
|
||||||
CGRect bezierPathsBoundingBox(std::vector<BezierPath> const &paths) {
|
CGRect bezierPathsBoundingBox(std::vector<BezierPath> const &paths) {
|
||||||
int pointCount = 0;
|
int pointCount = 0;
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user