This commit is contained in:
Isaac 2024-05-17 00:20:40 +04:00
parent c9a651691a
commit 3a5585c1fd
10 changed files with 201 additions and 77 deletions

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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