Refactoring

This commit is contained in:
Isaac 2024-05-18 20:51:38 +04:00
parent b6ba0d8f99
commit 7f88fb6ffa
3 changed files with 63 additions and 79 deletions

View File

@ -18,7 +18,7 @@ struct TransformedPath {
} }
}; };
static lottie::CGRect collectPathBoundingBoxes(std::shared_ptr<lottie::RenderTreeNodeContentItem> item, size_t subItemLimit, lottie::CATransform3D const &parentTransform, bool skipApplyTransform) { static lottie::CGRect collectPathBoundingBoxes(std::shared_ptr<lottie::RenderTreeNodeContentItem> item, size_t subItemLimit, lottie::CATransform3D const &parentTransform, bool skipApplyTransform, lottie::BezierPathsBoundingBoxContext &bezierPathsBoundingBoxContext) {
//TODO:remove skipApplyTransform //TODO:remove skipApplyTransform
lottie::CATransform3D effectiveTransform = parentTransform; lottie::CATransform3D effectiveTransform = parentTransform;
if (!skipApplyTransform && item->isGroup) { if (!skipApplyTransform && item->isGroup) {
@ -29,13 +29,17 @@ static lottie::CGRect collectPathBoundingBoxes(std::shared_ptr<lottie::RenderTre
lottie::CGRect boundingBox(0.0, 0.0, 0.0, 0.0); lottie::CGRect boundingBox(0.0, 0.0, 0.0, 0.0);
if (item->path) { if (item->path) {
boundingBox = item->pathBoundingBox.applyingTransform(effectiveTransform); if (item->path->needsBoundsRecalculation) {
item->path->bounds = lottie::bezierPathsBoundingBoxParallel(bezierPathsBoundingBoxContext, item->path->path);
item->path->needsBoundsRecalculation = false;
}
boundingBox = item->path->bounds.applyingTransform(effectiveTransform);
} }
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];
lottie::CGRect subItemBoundingBox = collectPathBoundingBoxes(subItem, INT32_MAX, effectiveTransform, false); lottie::CGRect subItemBoundingBox = collectPathBoundingBoxes(subItem, INT32_MAX, effectiveTransform, false, bezierPathsBoundingBoxContext);
if (boundingBox.empty()) { if (boundingBox.empty()) {
boundingBox = subItemBoundingBox; boundingBox = subItemBoundingBox;
@ -59,7 +63,7 @@ static std::vector<TransformedPath> collectPaths(std::shared_ptr<lottie::RenderT
size_t maxSubitem = std::min(item->subItems.size(), subItemLimit); size_t maxSubitem = std::min(item->subItems.size(), subItemLimit);
if (item->path) { if (item->path) {
mappedPaths.emplace_back(item->path.value(), effectiveTransform); mappedPaths.emplace_back(item->path->path, effectiveTransform);
} }
assert(!item->trimParams); assert(!item->trimParams);
@ -96,7 +100,7 @@ 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) {
lottie::CGRect shapeBounds = collectPathBoundingBoxes(contentItem, shadingVariant->subItemLimit, lottie::CATransform3D::identity(), true); lottie::CGRect shapeBounds = collectPathBoundingBoxes(contentItem, shadingVariant->subItemLimit, lottie::CATransform3D::identity(), true, bezierPathsBoundingBoxContext);
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);

View File

@ -309,6 +309,17 @@ public:
class RenderTreeNodeContentShadingVariant; class RenderTreeNodeContentShadingVariant;
struct RenderTreeNodeContentPath {
public:
explicit RenderTreeNodeContentPath(BezierPath path_) :
path(path_) {
}
BezierPath path;
CGRect bounds = CGRect(0.0, 0.0, 0.0, 0.0);
bool needsBoundsRecalculation = true;
};
class RenderTreeNodeContentItem { class RenderTreeNodeContentItem {
public: public:
enum class ShadingType { enum class ShadingType {
@ -423,8 +434,7 @@ public:
CATransform3D transform = CATransform3D::identity(); CATransform3D transform = CATransform3D::identity();
float alpha = 0.0; float alpha = 0.0;
std::optional<TrimParams> trimParams; std::optional<TrimParams> trimParams;
std::optional<BezierPath> path; std::shared_ptr<RenderTreeNodeContentPath> 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;

View File

@ -506,60 +506,44 @@ public:
} }
virtual ~PathOutput() = default; virtual ~PathOutput() = default;
virtual void update(AnimationFrameTime frameTime, BezierPathsBoundingBoxContext &boundingBoxContext) = 0; virtual void update(AnimationFrameTime frameTime) = 0;
virtual BezierPath const *currentPath() = 0; virtual std::shared_ptr<RenderTreeNodeContentPath> &currentPath() = 0;
virtual CGRect const &currentPathBounds() = 0;
}; };
class StaticPathOutput : public PathOutput { class StaticPathOutput : public PathOutput {
public: public:
explicit StaticPathOutput(BezierPath const &path) : explicit StaticPathOutput(BezierPath const &path) :
resolvedPath(path) { resolvedPath(std::make_shared<RenderTreeNodeContentPath>(path)) {
} }
virtual void update(AnimationFrameTime frameTime, BezierPathsBoundingBoxContext &boundingBoxContext) override { virtual void update(AnimationFrameTime frameTime) override {
if (!isPathBoundsResolved) {
resolvedPathBounds = bezierPathsBoundingBoxParallel(boundingBoxContext, resolvedPath);
isPathBoundsResolved = true;
}
} }
virtual BezierPath const *currentPath() override { virtual std::shared_ptr<RenderTreeNodeContentPath> &currentPath() override {
return &resolvedPath; return resolvedPath;
}
virtual CGRect const &currentPathBounds() override {
return resolvedPathBounds;
} }
private: private:
BezierPath resolvedPath; std::shared_ptr<RenderTreeNodeContentPath> resolvedPath;
bool isPathBoundsResolved = false;
CGRect resolvedPathBounds = CGRect(0.0, 0.0, 0.0, 0.0);
}; };
class ShapePathOutput : public PathOutput { class ShapePathOutput : public PathOutput {
public: public:
explicit ShapePathOutput(Shape const &shape) : explicit ShapePathOutput(Shape const &shape) :
path(shape.path.keyframes) { path(shape.path.keyframes),
resolvedPath(std::make_shared<RenderTreeNodeContentPath>(BezierPath())) {
} }
virtual void update(AnimationFrameTime frameTime, BezierPathsBoundingBoxContext &boundingBoxContext) override { virtual void update(AnimationFrameTime frameTime) override {
if (!hasValidData || path.hasUpdate(frameTime)) { if (!hasValidData || path.hasUpdate(frameTime)) {
path.update(frameTime, resolvedPath); path.update(frameTime, resolvedPath->path);
resolvedPathBounds = bezierPathsBoundingBoxParallel(boundingBoxContext, resolvedPath); resolvedPath->needsBoundsRecalculation = true;
} }
hasValidData = true; hasValidData = true;
} }
virtual BezierPath const *currentPath() override { virtual std::shared_ptr<RenderTreeNodeContentPath> &currentPath() override {
return &resolvedPath; return resolvedPath;
}
virtual CGRect const &currentPathBounds() override {
return resolvedPathBounds;
} }
private: private:
@ -567,8 +551,7 @@ public:
BezierPathKeyframeInterpolator path; BezierPathKeyframeInterpolator path;
BezierPath resolvedPath; std::shared_ptr<RenderTreeNodeContentPath> resolvedPath;
CGRect resolvedPathBounds = CGRect(0.0, 0.0, 0.0, 0.0);
}; };
class RectanglePathOutput : public PathOutput { class RectanglePathOutput : public PathOutput {
@ -577,10 +560,11 @@ public:
direction(rectangle.direction.value_or(PathDirection::Clockwise)), direction(rectangle.direction.value_or(PathDirection::Clockwise)),
position(rectangle.position.keyframes), position(rectangle.position.keyframes),
size(rectangle.size.keyframes), size(rectangle.size.keyframes),
cornerRadius(rectangle.cornerRadius.keyframes) { cornerRadius(rectangle.cornerRadius.keyframes),
resolvedPath(std::make_shared<RenderTreeNodeContentPath>(BezierPath())) {
} }
virtual void update(AnimationFrameTime frameTime, BezierPathsBoundingBoxContext &boundingBoxContext) override { virtual void update(AnimationFrameTime frameTime) override {
bool hasUpdates = false; bool hasUpdates = false;
if (!hasValidData || position.hasUpdate(frameTime)) { if (!hasValidData || position.hasUpdate(frameTime)) {
@ -597,19 +581,15 @@ 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->path);
resolvedPathBounds = bezierPathsBoundingBoxParallel(boundingBoxContext, resolvedPath); resolvedPath->needsBoundsRecalculation = true;
} }
hasValidData = true; hasValidData = true;
} }
virtual BezierPath const *currentPath() override { virtual std::shared_ptr<RenderTreeNodeContentPath> &currentPath() override {
return &resolvedPath; return resolvedPath;
}
virtual CGRect const &currentPathBounds() override {
return resolvedPathBounds;
} }
private: private:
@ -626,8 +606,7 @@ public:
KeyframeInterpolator<Vector1D> cornerRadius; KeyframeInterpolator<Vector1D> cornerRadius;
float cornerRadiusValue = 0.0; float cornerRadiusValue = 0.0;
BezierPath resolvedPath; std::shared_ptr<RenderTreeNodeContentPath> resolvedPath;
CGRect resolvedPathBounds = CGRect(0.0, 0.0, 0.0, 0.0);
}; };
class EllipsePathOutput : public PathOutput { class EllipsePathOutput : public PathOutput {
@ -635,10 +614,11 @@ public:
explicit EllipsePathOutput(Ellipse const &ellipse) : explicit EllipsePathOutput(Ellipse const &ellipse) :
direction(ellipse.direction.value_or(PathDirection::Clockwise)), direction(ellipse.direction.value_or(PathDirection::Clockwise)),
position(ellipse.position.keyframes), position(ellipse.position.keyframes),
size(ellipse.size.keyframes) { size(ellipse.size.keyframes),
resolvedPath(std::make_shared<RenderTreeNodeContentPath>(BezierPath())) {
} }
virtual void update(AnimationFrameTime frameTime, BezierPathsBoundingBoxContext &boundingBoxContext) override { virtual void update(AnimationFrameTime frameTime) override {
bool hasUpdates = false; bool hasUpdates = false;
if (!hasValidData || position.hasUpdate(frameTime)) { if (!hasValidData || position.hasUpdate(frameTime)) {
@ -651,19 +631,15 @@ 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->path);
resolvedPathBounds = bezierPathsBoundingBoxParallel(boundingBoxContext, resolvedPath); resolvedPath->needsBoundsRecalculation = true;
} }
hasValidData = true; hasValidData = true;
} }
virtual BezierPath const *currentPath() override { virtual std::shared_ptr<RenderTreeNodeContentPath> &currentPath() override {
return &resolvedPath; return resolvedPath;
}
virtual CGRect const &currentPathBounds() override {
return resolvedPathBounds;
} }
private: private:
@ -677,8 +653,7 @@ public:
KeyframeInterpolator<Vector3D> size; KeyframeInterpolator<Vector3D> size;
Vector3D sizeValue = Vector3D(0.0, 0.0, 0.0); Vector3D sizeValue = Vector3D(0.0, 0.0, 0.0);
BezierPath resolvedPath; std::shared_ptr<RenderTreeNodeContentPath> resolvedPath;
CGRect resolvedPathBounds = CGRect(0.0, 0.0, 0.0, 0.0);
}; };
class StarPathOutput : public PathOutput { class StarPathOutput : public PathOutput {
@ -689,7 +664,8 @@ public:
outerRadius(star.outerRadius.keyframes), outerRadius(star.outerRadius.keyframes),
outerRoundedness(star.outerRoundness.keyframes), outerRoundedness(star.outerRoundness.keyframes),
rotation(star.rotation.keyframes), rotation(star.rotation.keyframes),
points(star.points.keyframes) { points(star.points.keyframes),
resolvedPath(std::make_shared<RenderTreeNodeContentPath>(BezierPath())) {
if (star.innerRadius.has_value()) { if (star.innerRadius.has_value()) {
innerRadius = std::make_unique<NodeProperty<Vector1D>>(std::make_shared<KeyframeInterpolator<Vector1D>>(star.innerRadius->keyframes)); innerRadius = std::make_unique<NodeProperty<Vector1D>>(std::make_shared<KeyframeInterpolator<Vector1D>>(star.innerRadius->keyframes));
} else { } else {
@ -703,7 +679,7 @@ public:
} }
} }
virtual void update(AnimationFrameTime frameTime, BezierPathsBoundingBoxContext &boundingBoxContext) override { virtual void update(AnimationFrameTime frameTime) override {
bool hasUpdates = false; bool hasUpdates = false;
if (!hasValidData || position.hasUpdate(frameTime)) { if (!hasValidData || position.hasUpdate(frameTime)) {
@ -744,19 +720,15 @@ 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->path);
resolvedPathBounds = bezierPathsBoundingBoxParallel(boundingBoxContext, resolvedPath); resolvedPath->needsBoundsRecalculation = true;
} }
hasValidData = true; hasValidData = true;
} }
virtual BezierPath const *currentPath() override { virtual std::shared_ptr<RenderTreeNodeContentPath> &currentPath() override {
return &resolvedPath; return resolvedPath;
}
virtual CGRect const &currentPathBounds() override {
return resolvedPathBounds;
} }
private: private:
@ -785,8 +757,7 @@ public:
KeyframeInterpolator<Vector1D> points; KeyframeInterpolator<Vector1D> points;
float pointsValue = 0.0; float pointsValue = 0.0;
BezierPath resolvedPath; std::shared_ptr<RenderTreeNodeContentPath> resolvedPath;
CGRect resolvedPathBounds = CGRect(0.0, 0.0, 0.0, 0.0);
}; };
class TransformOutput { class TransformOutput {
@ -951,7 +922,7 @@ public:
size_t maxSubitem = std::min(subItems.size(), subItemLimit); size_t maxSubitem = std::min(subItems.size(), subItemLimit);
if (_contentItem->path) { if (_contentItem->path) {
mappedPaths.emplace_back(_contentItem->path.value(), effectiveTransform); mappedPaths.emplace_back(_contentItem->path->path, effectiveTransform);
} }
for (size_t i = 0; i < maxSubitem; i++) { for (size_t i = 0; i < maxSubitem; i++) {
@ -1012,7 +983,7 @@ public:
_contentItem->isGroup = isGroup; _contentItem->isGroup = isGroup;
if (path) { if (path) {
_contentItem->path = *path->currentPath(); _contentItem->path = path->currentPath();
} }
if (!shadings.empty()) { if (!shadings.empty()) {
@ -1052,8 +1023,7 @@ public:
} }
if (path) { if (path) {
path->update(frameTime, boundingBoxContext); path->update(frameTime);
_contentItem->pathBoundingBox = path->currentPathBounds();
} }
for (const auto &trim : trims) { for (const auto &trim : trims) {
trim->update(frameTime); trim->update(frameTime);