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
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) {
|
||||
std::vector<TransformedPath> mappedPaths;
|
||||
|
||||
@ -32,6 +61,7 @@ static std::vector<TransformedPath> collectPaths(std::shared_ptr<lottie::RenderT
|
||||
if (item->path) {
|
||||
mappedPaths.emplace_back(item->path.value(), effectiveTransform);
|
||||
}
|
||||
assert(!item->trimParams);
|
||||
|
||||
for (size_t i = 0; i < maxSubitem; i++) {
|
||||
auto &subItem = item->subItems[i];
|
||||
@ -66,17 +96,8 @@ static void processRenderContentItem(std::shared_ptr<RenderTreeNodeContentItem>
|
||||
int drawContentDescendants = 0;
|
||||
|
||||
for (const auto &shadingVariant : contentItem->shadings) {
|
||||
std::vector<lottie::BezierPath> itemPaths;
|
||||
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));
|
||||
}
|
||||
}
|
||||
lottie::CGRect shapeBounds = collectPathBoundingBoxes(contentItem, shadingVariant->subItemLimit, lottie::CATransform3D::identity(), true);
|
||||
|
||||
CGRect shapeBounds = bezierPathsBoundingBoxParallel(bezierPathsBoundingBoxContext, itemPaths);
|
||||
if (shadingVariant->stroke) {
|
||||
shapeBounds = shapeBounds.insetBy(-shadingVariant->stroke->lineWidth / 2.0, -shadingVariant->stroke->lineWidth / 2.0);
|
||||
} else if (shadingVariant->fill) {
|
||||
@ -95,7 +116,8 @@ static void processRenderContentItem(std::shared_ptr<RenderTreeNodeContentItem>
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
|
@ -144,6 +144,7 @@ public:
|
||||
|
||||
CGRect bezierPathsBoundingBox(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);
|
||||
|
||||
|
@ -424,6 +424,7 @@ public:
|
||||
double alpha = 0.0;
|
||||
std::optional<TrimParams> trimParams;
|
||||
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<RenderTreeNodeContentItem>> subItems;
|
||||
|
||||
|
@ -82,7 +82,7 @@ public:
|
||||
return _contentsLayer;
|
||||
}
|
||||
|
||||
void displayWithFrame(double frame, bool forceUpdates) {
|
||||
void displayWithFrame(double frame, bool forceUpdates, BezierPathsBoundingBoxContext &boundingBoxContext) {
|
||||
_transformNode->updateTree(frame, forceUpdates);
|
||||
|
||||
bool layerVisible = isInRangeOrEqual(frame, _inFrame, _outFrame);
|
||||
@ -93,14 +93,14 @@ public:
|
||||
|
||||
/// Only update contents if current time is within the layers time bounds.
|
||||
if (layerVisible) {
|
||||
displayContentsWithFrame(frame, forceUpdates);
|
||||
displayContentsWithFrame(frame, forceUpdates, boundingBoxContext);
|
||||
if (_maskLayer) {
|
||||
_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
|
||||
}
|
||||
|
||||
@ -151,11 +151,11 @@ public:
|
||||
return _timeStretch;
|
||||
}
|
||||
|
||||
virtual std::shared_ptr<RenderTreeNode> renderTreeNode() {
|
||||
virtual std::shared_ptr<RenderTreeNode> renderTreeNode(BezierPathsBoundingBoxContext &boundingBoxContext) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
virtual void updateRenderTree() {
|
||||
virtual void updateRenderTree(BezierPathsBoundingBoxContext &boundingBoxContext) {
|
||||
}
|
||||
|
||||
public:
|
||||
|
@ -86,7 +86,7 @@ public:
|
||||
return result;
|
||||
}
|
||||
|
||||
virtual void displayContentsWithFrame(double frame, bool forceUpdates) override {
|
||||
virtual void displayContentsWithFrame(double frame, bool forceUpdates, BezierPathsBoundingBoxContext &boundingBoxContext) override {
|
||||
double localFrame = 0.0;
|
||||
if (_remappingNode) {
|
||||
_remappingNode->update(frame);
|
||||
@ -96,11 +96,11 @@ public:
|
||||
}
|
||||
|
||||
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) {
|
||||
_contentsTreeNode = std::make_shared<RenderTreeNode>(
|
||||
CGRect(0.0, 0.0, 0.0, 0.0),
|
||||
@ -120,7 +120,7 @@ public:
|
||||
std::shared_ptr<RenderTreeNode> maskNode;
|
||||
bool invertMask = false;
|
||||
if (_matteLayer) {
|
||||
maskNode = _matteLayer->renderTreeNode();
|
||||
maskNode = _matteLayer->renderTreeNode(boundingBoxContext);
|
||||
if (maskNode && _matteType.has_value() && _matteType.value() == MatteType::Invert) {
|
||||
invertMask = true;
|
||||
}
|
||||
@ -148,7 +148,7 @@ public:
|
||||
}
|
||||
}
|
||||
if (found) {
|
||||
auto node = animationLayer->renderTreeNode();
|
||||
auto node = animationLayer->renderTreeNode(boundingBoxContext);
|
||||
if (node) {
|
||||
renderTreeSubnodes.push_back(node);
|
||||
}
|
||||
@ -177,9 +177,9 @@ public:
|
||||
return _renderTreeNode;
|
||||
}
|
||||
|
||||
virtual void updateRenderTree() override {
|
||||
virtual void updateRenderTree(BezierPathsBoundingBoxContext &boundingBoxContext) override {
|
||||
if (_matteLayer) {
|
||||
_matteLayer->updateRenderTree();
|
||||
_matteLayer->updateRenderTree(boundingBoxContext);
|
||||
}
|
||||
|
||||
for (const auto &animationLayer : _animationLayers) {
|
||||
@ -191,7 +191,7 @@ public:
|
||||
}
|
||||
}
|
||||
if (found) {
|
||||
animationLayer->updateRenderTree();
|
||||
animationLayer->updateRenderTree(boundingBoxContext);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -506,8 +506,9 @@ public:
|
||||
}
|
||||
virtual ~PathOutput() = default;
|
||||
|
||||
virtual void update(AnimationFrameTime frameTime) = 0;
|
||||
virtual void update(AnimationFrameTime frameTime, BezierPathsBoundingBoxContext &boundingBoxContext) = 0;
|
||||
virtual BezierPath const *currentPath() = 0;
|
||||
virtual CGRect const ¤tPathBounds() = 0;
|
||||
};
|
||||
|
||||
class StaticPathOutput : public PathOutput {
|
||||
@ -516,15 +517,26 @@ public:
|
||||
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 {
|
||||
return &resolvedPath;
|
||||
}
|
||||
|
||||
virtual CGRect const ¤tPathBounds() override {
|
||||
return resolvedPathBounds;
|
||||
}
|
||||
|
||||
private:
|
||||
BezierPath resolvedPath;
|
||||
|
||||
bool isPathBoundsResolved = false;
|
||||
CGRect resolvedPathBounds = CGRect(0.0, 0.0, 0.0, 0.0);
|
||||
};
|
||||
|
||||
class ShapePathOutput : public PathOutput {
|
||||
@ -533,9 +545,10 @@ public:
|
||||
path(shape.path.keyframes) {
|
||||
}
|
||||
|
||||
virtual void update(AnimationFrameTime frameTime) override {
|
||||
virtual void update(AnimationFrameTime frameTime, BezierPathsBoundingBoxContext &boundingBoxContext) override {
|
||||
if (!hasValidData || path.hasUpdate(frameTime)) {
|
||||
path.update(frameTime, resolvedPath);
|
||||
resolvedPathBounds = bezierPathsBoundingBoxParallel(boundingBoxContext, resolvedPath);
|
||||
}
|
||||
|
||||
hasValidData = true;
|
||||
@ -545,12 +558,17 @@ public:
|
||||
return &resolvedPath;
|
||||
}
|
||||
|
||||
virtual CGRect const ¤tPathBounds() override {
|
||||
return resolvedPathBounds;
|
||||
}
|
||||
|
||||
private:
|
||||
bool hasValidData = false;
|
||||
|
||||
BezierPathKeyframeInterpolator path;
|
||||
|
||||
BezierPath resolvedPath;
|
||||
CGRect resolvedPathBounds = CGRect(0.0, 0.0, 0.0, 0.0);
|
||||
};
|
||||
|
||||
class RectanglePathOutput : public PathOutput {
|
||||
@ -562,7 +580,7 @@ public:
|
||||
cornerRadius(rectangle.cornerRadius.keyframes) {
|
||||
}
|
||||
|
||||
virtual void update(AnimationFrameTime frameTime) override {
|
||||
virtual void update(AnimationFrameTime frameTime, BezierPathsBoundingBoxContext &boundingBoxContext) override {
|
||||
bool hasUpdates = false;
|
||||
|
||||
if (!hasValidData || position.hasUpdate(frameTime)) {
|
||||
@ -580,6 +598,7 @@ public:
|
||||
|
||||
if (hasUpdates) {
|
||||
ValueInterpolator<BezierPath>::setInplace(makeRectangleBezierPath(Vector2D(positionValue.x, positionValue.y), Vector2D(sizeValue.x, sizeValue.y), cornerRadiusValue, direction), resolvedPath);
|
||||
resolvedPathBounds = bezierPathsBoundingBoxParallel(boundingBoxContext, resolvedPath);
|
||||
}
|
||||
|
||||
hasValidData = true;
|
||||
@ -589,6 +608,10 @@ public:
|
||||
return &resolvedPath;
|
||||
}
|
||||
|
||||
virtual CGRect const ¤tPathBounds() override {
|
||||
return resolvedPathBounds;
|
||||
}
|
||||
|
||||
private:
|
||||
bool hasValidData = false;
|
||||
|
||||
@ -604,6 +627,7 @@ public:
|
||||
double cornerRadiusValue = 0.0;
|
||||
|
||||
BezierPath resolvedPath;
|
||||
CGRect resolvedPathBounds = CGRect(0.0, 0.0, 0.0, 0.0);
|
||||
};
|
||||
|
||||
class EllipsePathOutput : public PathOutput {
|
||||
@ -614,7 +638,7 @@ public:
|
||||
size(ellipse.size.keyframes) {
|
||||
}
|
||||
|
||||
virtual void update(AnimationFrameTime frameTime) override {
|
||||
virtual void update(AnimationFrameTime frameTime, BezierPathsBoundingBoxContext &boundingBoxContext) override {
|
||||
bool hasUpdates = false;
|
||||
|
||||
if (!hasValidData || position.hasUpdate(frameTime)) {
|
||||
@ -628,6 +652,7 @@ public:
|
||||
|
||||
if (hasUpdates) {
|
||||
ValueInterpolator<BezierPath>::setInplace(makeEllipseBezierPath(Vector2D(sizeValue.x, sizeValue.y), Vector2D(positionValue.x, positionValue.y), direction), resolvedPath);
|
||||
resolvedPathBounds = bezierPathsBoundingBoxParallel(boundingBoxContext, resolvedPath);
|
||||
}
|
||||
|
||||
hasValidData = true;
|
||||
@ -637,6 +662,10 @@ public:
|
||||
return &resolvedPath;
|
||||
}
|
||||
|
||||
virtual CGRect const ¤tPathBounds() override {
|
||||
return resolvedPathBounds;
|
||||
}
|
||||
|
||||
private:
|
||||
bool hasValidData = false;
|
||||
|
||||
@ -649,6 +678,7 @@ public:
|
||||
Vector3D sizeValue = Vector3D(0.0, 0.0, 0.0);
|
||||
|
||||
BezierPath resolvedPath;
|
||||
CGRect resolvedPathBounds = CGRect(0.0, 0.0, 0.0, 0.0);
|
||||
};
|
||||
|
||||
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;
|
||||
|
||||
if (!hasValidData || position.hasUpdate(frameTime)) {
|
||||
@ -715,6 +745,7 @@ public:
|
||||
|
||||
if (hasUpdates) {
|
||||
ValueInterpolator<BezierPath>::setInplace(makeStarBezierPath(Vector2D(positionValue.x, positionValue.y), outerRadiusValue, innerRadiusValue, outerRoundednessValue, innerRoundednessValue, pointsValue, rotationValue, direction), resolvedPath);
|
||||
resolvedPathBounds = bezierPathsBoundingBoxParallel(boundingBoxContext, resolvedPath);
|
||||
}
|
||||
|
||||
hasValidData = true;
|
||||
@ -724,6 +755,10 @@ public:
|
||||
return &resolvedPath;
|
||||
}
|
||||
|
||||
virtual CGRect const ¤tPathBounds() override {
|
||||
return resolvedPathBounds;
|
||||
}
|
||||
|
||||
private:
|
||||
bool hasValidData = false;
|
||||
|
||||
@ -751,6 +786,7 @@ public:
|
||||
double pointsValue = 0.0;
|
||||
|
||||
BezierPath resolvedPath;
|
||||
CGRect resolvedPathBounds = CGRect(0.0, 0.0, 0.0, 0.0);
|
||||
};
|
||||
|
||||
class TransformOutput {
|
||||
@ -903,11 +939,7 @@ public:
|
||||
std::shared_ptr<RenderTreeNodeContentItem> _contentItem;
|
||||
|
||||
private:
|
||||
bool hasTrims(size_t subItemLimit) {
|
||||
return false;
|
||||
}
|
||||
|
||||
std::vector<TransformedPath> collectPaths(size_t subItemLimit, CATransform3D const &parentTransform, bool skipApplyTransform, bool &hasTrims) {
|
||||
std::vector<TransformedPath> collectPaths(size_t subItemLimit, CATransform3D const &parentTransform, bool skipApplyTransform) {
|
||||
std::vector<TransformedPath> mappedPaths;
|
||||
|
||||
//TODO:remove skipApplyTransform
|
||||
@ -930,10 +962,9 @@ public:
|
||||
currentTrim = trims[0]->trimParams();
|
||||
}
|
||||
|
||||
auto subItemPaths = subItem->collectPaths(INT32_MAX, effectiveTransform, false, hasTrims);
|
||||
auto subItemPaths = subItem->collectPaths(INT32_MAX, effectiveTransform, false);
|
||||
|
||||
if (currentTrim) {
|
||||
hasTrims = true;
|
||||
CompoundBezierPath tempPath;
|
||||
for (auto &path : subItemPaths) {
|
||||
tempPath.appendPath(path.path.copyUsingTransform(path.transform));
|
||||
@ -1007,23 +1038,22 @@ public:
|
||||
|
||||
if (isGroup && !subItems.empty()) {
|
||||
std::vector<std::shared_ptr<RenderTreeNode>> subItemNodes;
|
||||
for (int i = (int)subItems.size() - 1; i >= 0; i--) {
|
||||
subItems[i]->initializeRenderChildren();
|
||||
_contentItem->subItems.push_back(subItems[i]->_contentItem);
|
||||
for (const auto &subItem : subItems) {
|
||||
subItem->initializeRenderChildren();
|
||||
_contentItem->subItems.push_back(subItem->_contentItem);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public:
|
||||
void updateFrame(AnimationFrameTime frameTime) {
|
||||
void updateFrame(AnimationFrameTime frameTime, BezierPathsBoundingBoxContext &boundingBoxContext) {
|
||||
if (transform) {
|
||||
transform->update(frameTime);
|
||||
}
|
||||
|
||||
if (path) {
|
||||
path->update(frameTime);
|
||||
} else {
|
||||
_contentItem->path = std::nullopt;
|
||||
path->update(frameTime, boundingBoxContext);
|
||||
_contentItem->pathBoundingBox = path->currentPathBounds();
|
||||
}
|
||||
for (const auto &trim : trims) {
|
||||
trim->update(frameTime);
|
||||
@ -1039,10 +1069,24 @@ public:
|
||||
}
|
||||
|
||||
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) {
|
||||
CATransform3D containerTransform = CATransform3D::identity();
|
||||
double containerOpacity = 1.0;
|
||||
@ -1053,6 +1097,10 @@ public:
|
||||
_contentItem->transform = containerTransform;
|
||||
_contentItem->alpha = containerOpacity;
|
||||
|
||||
if (!trims.empty()) {
|
||||
_contentItem->trimParams = trims[0]->trimParams();
|
||||
}
|
||||
|
||||
for (int i = 0; i < shadings.size(); i++) {
|
||||
const auto &shadingVariant = shadings[i];
|
||||
|
||||
@ -1066,11 +1114,9 @@ public:
|
||||
currentTrim = trims[0];
|
||||
}*/
|
||||
|
||||
bool hasTrims = false;
|
||||
if (parentTrim) {
|
||||
CompoundBezierPath compoundPath;
|
||||
hasTrims = true;
|
||||
auto paths = collectPaths(shadingVariant.subItemLimit, CATransform3D::identity(), true, hasTrims);
|
||||
auto paths = collectPaths(shadingVariant.subItemLimit, CATransform3D::identity(), true);
|
||||
for (const auto &path : paths) {
|
||||
compoundPath.appendPath(path.path.copyUsingTransform(path.transform));
|
||||
}
|
||||
@ -1083,21 +1129,20 @@ public:
|
||||
}
|
||||
_contentItem->shadings[i]->explicitPath = resultPaths;
|
||||
} else {
|
||||
CompoundBezierPath compoundPath;
|
||||
auto paths = collectPaths(shadingVariant.subItemLimit, CATransform3D::identity(), true, hasTrims);
|
||||
for (const auto &path : paths) {
|
||||
compoundPath.appendPath(path.path.copyUsingTransform(path.transform));
|
||||
}
|
||||
std::vector<BezierPath> resultPaths;
|
||||
for (const auto &path : compoundPath.paths) {
|
||||
resultPaths.push_back(path);
|
||||
}
|
||||
|
||||
if (hasTrims) {
|
||||
if (hasTrims()) {
|
||||
CompoundBezierPath compoundPath;
|
||||
auto paths = collectPaths(shadingVariant.subItemLimit, CATransform3D::identity(), true);
|
||||
for (const auto &path : paths) {
|
||||
compoundPath.appendPath(path.path.copyUsingTransform(path.transform));
|
||||
}
|
||||
std::vector<BezierPath> resultPaths;
|
||||
for (const auto &path : compoundPath.paths) {
|
||||
resultPaths.push_back(path);
|
||||
}
|
||||
|
||||
_contentItem->shadings[i]->explicitPath = resultPaths;
|
||||
} else {
|
||||
_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);
|
||||
}
|
||||
|
||||
void ShapeCompositionLayer::displayContentsWithFrame(double frame, bool forceUpdates) {
|
||||
void ShapeCompositionLayer::displayContentsWithFrame(double frame, bool forceUpdates, BezierPathsBoundingBoxContext &boundingBoxContext) {
|
||||
_frameTime = frame;
|
||||
_frameTimeInitialized = true;
|
||||
_contentTree->itemTree->updateFrame(_frameTime);
|
||||
_contentTree->itemTree->updateFrame(_frameTime, boundingBoxContext);
|
||||
_contentTree->itemTree->updateContents(std::nullopt);
|
||||
}
|
||||
|
||||
std::shared_ptr<RenderTreeNode> ShapeCompositionLayer::renderTreeNode() {
|
||||
std::shared_ptr<RenderTreeNode> ShapeCompositionLayer::renderTreeNode(BezierPathsBoundingBoxContext &boundingBoxContext) {
|
||||
if (!_frameTimeInitialized) {
|
||||
_frameTime = 0.0;
|
||||
_frameTimeInitialized = true;
|
||||
_contentTree->itemTree->updateFrame(_frameTime);
|
||||
_contentTree->itemTree->updateFrame(_frameTime, boundingBoxContext);
|
||||
_contentTree->itemTree->updateContents(std::nullopt);
|
||||
}
|
||||
|
||||
@ -1324,7 +1369,7 @@ std::shared_ptr<RenderTreeNode> ShapeCompositionLayer::renderTreeNode() {
|
||||
std::shared_ptr<RenderTreeNode> maskNode;
|
||||
bool invertMask = false;
|
||||
if (_matteLayer) {
|
||||
maskNode = _matteLayer->renderTreeNode();
|
||||
maskNode = _matteLayer->renderTreeNode(boundingBoxContext);
|
||||
if (maskNode && _matteType.has_value() && _matteType.value() == MatteType::Invert) {
|
||||
invertMask = true;
|
||||
}
|
||||
@ -1346,9 +1391,9 @@ std::shared_ptr<RenderTreeNode> ShapeCompositionLayer::renderTreeNode() {
|
||||
return _renderTreeNode;
|
||||
}
|
||||
|
||||
void ShapeCompositionLayer::updateRenderTree() {
|
||||
void ShapeCompositionLayer::updateRenderTree(BezierPathsBoundingBoxContext &boundingBoxContext) {
|
||||
if (_matteLayer) {
|
||||
_matteLayer->updateRenderTree();
|
||||
_matteLayer->updateRenderTree(boundingBoxContext);
|
||||
}
|
||||
|
||||
_contentRenderTreeNode->_bounds = _contentsLayer->bounds();
|
||||
|
@ -16,9 +16,9 @@ public:
|
||||
ShapeCompositionLayer(std::shared_ptr<ShapeLayerModel> const &shapeLayer);
|
||||
ShapeCompositionLayer(std::shared_ptr<SolidLayerModel> const &solidLayer);
|
||||
|
||||
virtual void displayContentsWithFrame(double frame, bool forceUpdates) override;
|
||||
virtual std::shared_ptr<RenderTreeNode> renderTreeNode() override;
|
||||
virtual void updateRenderTree() override;
|
||||
virtual void displayContentsWithFrame(double frame, bool forceUpdates, BezierPathsBoundingBoxContext &boundingBoxContext) override;
|
||||
virtual std::shared_ptr<RenderTreeNode> renderTreeNode(BezierPathsBoundingBoxContext &boundingBoxContext) override;
|
||||
virtual void updateRenderTree(BezierPathsBoundingBoxContext &boundingBoxContext) override;
|
||||
|
||||
private:
|
||||
std::shared_ptr<ShapeLayerPresentationTree> _contentTree;
|
||||
|
@ -42,7 +42,7 @@ public:
|
||||
_fontProvider = fontProvider;
|
||||
}
|
||||
|
||||
virtual void displayContentsWithFrame(double frame, bool forceUpdates) override {
|
||||
virtual void displayContentsWithFrame(double frame, bool forceUpdates, BezierPathsBoundingBoxContext &boundingBoxContext) override {
|
||||
if (!_textDocument) {
|
||||
return;
|
||||
}
|
||||
|
@ -103,7 +103,7 @@ public:
|
||||
newFrame = floor(newFrame);
|
||||
}
|
||||
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.
|
||||
void forceDisplayUpdate() {
|
||||
for (const auto &layer : _animationLayers) {
|
||||
layer->displayWithFrame(currentFrame(), true);
|
||||
layer->displayWithFrame(currentFrame(), true, _boundingBoxContext);
|
||||
}
|
||||
}
|
||||
|
||||
@ -193,7 +193,7 @@ public:
|
||||
_currentFrame = currentFrame;
|
||||
|
||||
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) {
|
||||
auto node = animationLayer->renderTreeNode();
|
||||
auto node = animationLayer->renderTreeNode(_boundingBoxContext);
|
||||
if (node) {
|
||||
subnodes.push_back(node);
|
||||
}
|
||||
@ -264,7 +264,7 @@ public:
|
||||
}
|
||||
}
|
||||
if (found) {
|
||||
animationLayer->updateRenderTree();
|
||||
animationLayer->updateRenderTree(_boundingBoxContext);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -288,6 +288,8 @@ private:
|
||||
std::shared_ptr<LayerFontProvider> _layerFontProvider;
|
||||
|
||||
std::shared_ptr<RenderTreeNode> _renderTreeNode;
|
||||
|
||||
BezierPathsBoundingBoxContext _boundingBoxContext;
|
||||
};
|
||||
|
||||
}
|
||||
|
@ -582,6 +582,58 @@ CGRect bezierPathsBoundingBoxParallel(BezierPathsBoundingBoxContext &context, st
|
||||
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) {
|
||||
int pointCount = 0;
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user