[WIP] Lottie optimizations

This commit is contained in:
Isaac 2024-05-14 23:55:06 +04:00
parent ea4a203285
commit e926ad9776
10 changed files with 117 additions and 874 deletions

View File

@ -58,18 +58,6 @@ static void processRenderTree(std::shared_ptr<RenderTreeNode> const &node, Vecto
}
}
}
/*if (node->content()) {
RenderTreeNodeContent *shapeContent = node->content().get();
CGRect shapeBounds = bezierPathsBoundingBoxParallel(bezierPathsBoundingBoxContext, shapeContent->paths);
if (shapeContent->stroke) {
shapeBounds = shapeBounds.insetBy(-shapeContent->stroke->lineWidth / 2.0, -shapeContent->stroke->lineWidth / 2.0);
effectiveLocalBounds = shapeBounds;
} else if (shapeContent->fill) {
effectiveLocalBounds = shapeBounds;
}
}*/
bool isInvertedMatte = isInvertedMask;
if (isInvertedMatte) {
@ -95,7 +83,7 @@ static void processRenderTree(std::shared_ptr<RenderTreeNode> const &node, Vecto
if (item->renderData.isValid) {
drawContentDescendants += item->renderData.drawContentDescendants;
if (item->content() || item->_contentItem) {
if (item->_contentItem) {
drawContentDescendants += 1;
}
@ -200,167 +188,6 @@ static void processRenderTree(std::shared_ptr<RenderTreeNode> const &node, Vecto
namespace {
static void drawLottieRenderableItem(std::shared_ptr<lottieRendering::Canvas> context, std::shared_ptr<lottie::RenderTreeNodeContent> item) {
if (item->paths.empty()) {
return;
}
std::shared_ptr<lottie::CGPath> path = lottie::CGPath::makePath();
const auto iterate = [&](LottiePathItem const *pathItem) {
switch (pathItem->type) {
case LottiePathItemTypeMoveTo: {
path->moveTo(lottie::Vector2D(pathItem->points[0].x, pathItem->points[0].y));
break;
}
case LottiePathItemTypeLineTo: {
path->addLineTo(lottie::Vector2D(pathItem->points[0].x, pathItem->points[0].y));
break;
}
case LottiePathItemTypeCurveTo: {
path->addCurveTo(lottie::Vector2D(pathItem->points[2].x, pathItem->points[2].y), lottie::Vector2D(pathItem->points[0].x, pathItem->points[0].y), lottie::Vector2D(pathItem->points[1].x, pathItem->points[1].y));
break;
}
case LottiePathItemTypeClose: {
path->closeSubpath();
break;
}
default: {
break;
}
}
};
LottiePathItem pathItem;
for (const auto &path : item->paths) {
std::optional<lottie::PathElement> previousElement;
for (const auto &element : path.elements()) {
if (previousElement.has_value()) {
if (previousElement->vertex.outTangentRelative().isZero() && element.vertex.inTangentRelative().isZero()) {
pathItem.type = LottiePathItemTypeLineTo;
pathItem.points[0] = CGPointMake(element.vertex.point.x, element.vertex.point.y);
iterate(&pathItem);
} else {
pathItem.type = LottiePathItemTypeCurveTo;
pathItem.points[2] = CGPointMake(element.vertex.point.x, element.vertex.point.y);
pathItem.points[1] = CGPointMake(element.vertex.inTangent.x, element.vertex.inTangent.y);
pathItem.points[0] = CGPointMake(previousElement->vertex.outTangent.x, previousElement->vertex.outTangent.y);
iterate(&pathItem);
}
} else {
pathItem.type = LottiePathItemTypeMoveTo;
pathItem.points[0] = CGPointMake(element.vertex.point.x, element.vertex.point.y);
iterate(&pathItem);
}
previousElement = element;
}
if (path.closed().value_or(true)) {
pathItem.type = LottiePathItemTypeClose;
iterate(&pathItem);
}
}
if (item->stroke) {
if (item->stroke->shading->type() == lottie::RenderTreeNodeContent::ShadingType::Solid) {
lottie::RenderTreeNodeContent::SolidShading *solidShading = (lottie::RenderTreeNodeContent::SolidShading *)item->stroke->shading.get();
lottieRendering::LineJoin lineJoin = lottieRendering::LineJoin::Bevel;
switch (item->stroke->lineJoin) {
case lottie::LineJoin::Bevel: {
lineJoin = lottieRendering::LineJoin::Bevel;
break;
}
case lottie::LineJoin::Round: {
lineJoin = lottieRendering::LineJoin::Round;
break;
}
case lottie::LineJoin::Miter: {
lineJoin = lottieRendering::LineJoin::Miter;
break;
}
default: {
break;
}
}
lottieRendering::LineCap lineCap = lottieRendering::LineCap::Square;
switch (item->stroke->lineCap) {
case lottie::LineCap::Butt: {
lineCap = lottieRendering::LineCap::Butt;
break;
}
case lottie::LineCap::Round: {
lineCap = lottieRendering::LineCap::Round;
break;
}
case lottie::LineCap::Square: {
lineCap = lottieRendering::LineCap::Square;
break;
}
default: {
break;
}
}
std::vector<double> dashPattern;
if (!item->stroke->dashPattern.empty()) {
dashPattern = item->stroke->dashPattern;
}
context->strokePath(path, item->stroke->lineWidth, lineJoin, lineCap, item->stroke->dashPhase, dashPattern, lottieRendering::Color(solidShading->color.r, solidShading->color.g, solidShading->color.b, solidShading->color.a * solidShading->opacity));
} else if (item->stroke->shading->type() == lottie::RenderTreeNodeContent::ShadingType::Gradient) {
//TODO:gradient stroke
}
} else if (item->fill) {
lottieRendering::FillRule rule = lottieRendering::FillRule::NonZeroWinding;
switch (item->fill->rule) {
case lottie::FillRule::EvenOdd: {
rule = lottieRendering::FillRule::EvenOdd;
break;
}
case lottie::FillRule::NonZeroWinding: {
rule = lottieRendering::FillRule::NonZeroWinding;
break;
}
default: {
break;
}
}
if (item->fill->shading->type() == lottie::RenderTreeNodeContent::ShadingType::Solid) {
lottie::RenderTreeNodeContent::SolidShading *solidShading = (lottie::RenderTreeNodeContent::SolidShading *)item->fill->shading.get();
context->fillPath(path, rule, lottieRendering::Color(solidShading->color.r, solidShading->color.g, solidShading->color.b, solidShading->color.a * solidShading->opacity));
} else if (item->fill->shading->type() == lottie::RenderTreeNodeContent::ShadingType::Gradient) {
lottie::RenderTreeNodeContent::GradientShading *gradientShading = (lottie::RenderTreeNodeContent::GradientShading *)item->fill->shading.get();
std::vector<lottieRendering::Color> colors;
std::vector<double> locations;
for (const auto &color : gradientShading->colors) {
colors.push_back(lottieRendering::Color(color.r, color.g, color.b, color.a * gradientShading->opacity));
}
locations = gradientShading->locations;
lottieRendering::Gradient gradient(colors, locations);
lottie::Vector2D start(gradientShading->start.x, gradientShading->start.y);
lottie::Vector2D end(gradientShading->end.x, gradientShading->end.y);
switch (gradientShading->gradientType) {
case lottie::GradientType::Linear: {
context->linearGradientFillPath(path, rule, gradient, start, end);
break;
}
case lottie::GradientType::Radial: {
context->radialGradientFillPath(path, rule, gradient, start, 0.0, start, start.distanceTo(end));
break;
}
default: {
break;
}
}
}
}
}
static void drawLottieContentItem(std::shared_ptr<lottieRendering::Canvas> context, std::shared_ptr<lottie::RenderTreeNodeContentItem> item) {
if (item->shadings.empty()) {
return;
@ -427,8 +254,8 @@ static void drawLottieContentItem(std::shared_ptr<lottieRendering::Canvas> conte
}
if (shading->stroke) {
if (shading->stroke->shading->type() == lottie::RenderTreeNodeContent::ShadingType::Solid) {
lottie::RenderTreeNodeContent::SolidShading *solidShading = (lottie::RenderTreeNodeContent::SolidShading *)shading->stroke->shading.get();
if (shading->stroke->shading->type() == lottie::RenderTreeNodeContentItem::ShadingType::Solid) {
lottie::RenderTreeNodeContentItem::SolidShading *solidShading = (lottie::RenderTreeNodeContentItem::SolidShading *)shading->stroke->shading.get();
if (solidShading->opacity != 0.0) {
lottieRendering::LineJoin lineJoin = lottieRendering::LineJoin::Bevel;
@ -475,7 +302,7 @@ static void drawLottieContentItem(std::shared_ptr<lottieRendering::Canvas> conte
}
context->strokePath(path, shading->stroke->lineWidth, lineJoin, lineCap, shading->stroke->dashPhase, dashPattern, lottieRendering::Color(solidShading->color.r, solidShading->color.g, solidShading->color.b, solidShading->color.a * solidShading->opacity));
} else if (shading->stroke->shading->type() == lottie::RenderTreeNodeContent::ShadingType::Gradient) {
} else if (shading->stroke->shading->type() == lottie::RenderTreeNodeContentItem::ShadingType::Gradient) {
//TODO:gradient stroke
}
}
@ -495,13 +322,13 @@ static void drawLottieContentItem(std::shared_ptr<lottieRendering::Canvas> conte
}
}
if (shading->fill->shading->type() == lottie::RenderTreeNodeContent::ShadingType::Solid) {
lottie::RenderTreeNodeContent::SolidShading *solidShading = (lottie::RenderTreeNodeContent::SolidShading *)shading->fill->shading.get();
if (shading->fill->shading->type() == lottie::RenderTreeNodeContentItem::ShadingType::Solid) {
lottie::RenderTreeNodeContentItem::SolidShading *solidShading = (lottie::RenderTreeNodeContentItem::SolidShading *)shading->fill->shading.get();
if (solidShading->opacity != 0.0) {
context->fillPath(path, rule, lottieRendering::Color(solidShading->color.r, solidShading->color.g, solidShading->color.b, solidShading->color.a * solidShading->opacity));
}
} else if (shading->fill->shading->type() == lottie::RenderTreeNodeContent::ShadingType::Gradient) {
lottie::RenderTreeNodeContent::GradientShading *gradientShading = (lottie::RenderTreeNodeContent::GradientShading *)shading->fill->shading.get();
} else if (shading->fill->shading->type() == lottie::RenderTreeNodeContentItem::ShadingType::Gradient) {
lottie::RenderTreeNodeContentItem::GradientShading *gradientShading = (lottie::RenderTreeNodeContentItem::GradientShading *)shading->fill->shading.get();
if (gradientShading->opacity != 0.0) {
std::vector<lottieRendering::Color> colors;
@ -600,10 +427,7 @@ static void renderLottieRenderNode(std::shared_ptr<lottie::RenderTreeNode> node,
currentContext->setAlpha(renderAlpha);
if (node->content() && (int64_t)"" < 0) {
drawLottieRenderableItem(currentContext, node->content());
}
if (node->_contentItem) {//} && (int64_t)"" < 0) {
if (node->_contentItem) {
drawLottieContentItem(currentContext, node->_contentItem);
}

View File

@ -231,9 +231,9 @@ public:
CGRect bounds;
};
class RenderTreeNodeContentItem;
class RenderTreeNodeContentShadingVariant;
class RenderTreeNodeContent {
class RenderTreeNodeContentItem {
public:
enum class ShadingType {
Solid,
@ -339,20 +339,12 @@ public:
};
public:
RenderTreeNodeContent(
std::vector<BezierPath> paths_,
std::shared_ptr<Stroke> stroke_,
std::shared_ptr<Fill> fill_
) :
paths(paths_),
stroke(stroke_),
fill(fill_) {
RenderTreeNodeContentItem() {
}
public:
std::vector<BezierPath> paths;
std::shared_ptr<Stroke> stroke;
std::shared_ptr<Fill> fill;
bool isGroup = false;
std::vector<std::shared_ptr<RenderTreeNodeContentShadingVariant>> shadings;
};
class RenderTreeNodeContentShadingVariant {
@ -361,24 +353,14 @@ public:
}
public:
std::shared_ptr<RenderTreeNodeContent::Stroke> stroke;
std::shared_ptr<RenderTreeNodeContent::Fill> fill;
std::shared_ptr<RenderTreeNodeContentItem::Stroke> stroke;
std::shared_ptr<RenderTreeNodeContentItem::Fill> fill;
std::optional<std::vector<BezierPath>> explicitPath;
size_t subItemLimit = 0;
bool isGroup = false;
};
class RenderTreeNodeContentItem {
public:
RenderTreeNodeContentItem() {
}
public:
bool isGroup = false;
std::vector<std::shared_ptr<RenderTreeNodeContentShadingVariant>> shadings;
};
class ProcessedRenderTreeNodeData {
public:
struct LayerParams {
@ -468,7 +450,6 @@ public:
double alpha_,
bool masksToBounds_,
bool isHidden_,
std::shared_ptr<RenderTreeNodeContent> content_,
std::vector<std::shared_ptr<RenderTreeNode>> subnodes_,
std::shared_ptr<RenderTreeNode> mask_,
bool invertMask_
@ -479,7 +460,6 @@ public:
_alpha(alpha_),
_masksToBounds(masksToBounds_),
_isHidden(isHidden_),
_content(content_),
_subnodes(subnodes_),
_mask(mask_),
_invertMask(invertMask_) {
@ -513,10 +493,6 @@ public:
return _isHidden;
}
std::shared_ptr<RenderTreeNodeContent> const &content() const {
return _content;
}
std::vector<std::shared_ptr<RenderTreeNode>> const &subnodes() const {
return _subnodes;
}
@ -536,7 +512,6 @@ public:
double _alpha = 1.0;
bool _masksToBounds = false;
bool _isHidden = false;
std::shared_ptr<RenderTreeNodeContent> _content;
std::shared_ptr<RenderTreeNodeContentItem> _contentItem;
std::vector<std::shared_ptr<RenderTreeNode>> _subnodes;
std::shared_ptr<RenderTreeNode> _mask;

View File

@ -109,7 +109,6 @@ public:
1.0,
false,
false,
nullptr,
std::vector<std::shared_ptr<RenderTreeNode>>(),
nullptr,
false
@ -134,7 +133,6 @@ public:
1.0,
false,
false,
nullptr,
subnodes,
maskNode,
invertMask
@ -165,7 +163,6 @@ public:
1.0,
false,
false,
nullptr,
renderTreeSubnodes,
nullptr,
false

View File

@ -25,7 +25,7 @@ public:
~FillOutput() = default;
virtual void update(AnimationFrameTime frameTime) = 0;
virtual std::shared_ptr<RenderTreeNodeContent::Fill> fill() = 0;
virtual std::shared_ptr<RenderTreeNodeContentItem::Fill> fill() = 0;
};
class SolidFillOutput : public FillOutput {
@ -34,6 +34,11 @@ public:
rule(fill.fillRule.value_or(FillRule::NonZeroWinding)),
color(fill.color.keyframes),
opacity(fill.opacity.keyframes) {
auto solid = std::make_shared<RenderTreeNodeContentItem::SolidShading>(Color(0.0, 0.0, 0.0, 0.0), 0.0);
_fill = std::make_shared<RenderTreeNodeContentItem::Fill>(
solid,
rule
);
}
virtual ~SolidFillOutput() = default;
@ -51,16 +56,14 @@ public:
opacityValue = opacity.value(frameTime).value;
}
if (!_fill || hasUpdates) {
auto solid = std::make_shared<RenderTreeNodeContent::SolidShading>(colorValue, opacityValue * 0.01);
_fill = std::make_shared<RenderTreeNodeContent::Fill>(
solid,
rule
);
if (hasUpdates) {
RenderTreeNodeContentItem::SolidShading *solid = (RenderTreeNodeContentItem::SolidShading *)_fill->shading.get();
solid->color = colorValue;
solid->opacity = opacityValue * 0.01;
}
}
virtual std::shared_ptr<RenderTreeNodeContent::Fill> fill() override {
virtual std::shared_ptr<RenderTreeNodeContentItem::Fill> fill() override {
return _fill;
}
@ -73,7 +76,7 @@ public:
KeyframeInterpolator<Vector1D> opacity;
double opacityValue = 0.0;
std::shared_ptr<RenderTreeNodeContent::Fill> _fill;
std::shared_ptr<RenderTreeNodeContentItem::Fill> _fill;
};
class GradientFillOutput : public FillOutput {
@ -86,6 +89,18 @@ public:
startPoint(gradientFill.startPoint.keyframes),
endPoint(gradientFill.endPoint.keyframes),
opacity(gradientFill.opacity.keyframes) {
auto gradient = std::make_shared<RenderTreeNodeContentItem::GradientShading>(
0.0,
gradientType,
std::vector<Color>(),
std::vector<double>(),
Vector2D(0.0, 0.0),
Vector2D(0.0, 0.0)
);
_fill = std::make_shared<RenderTreeNodeContentItem::Fill>(
gradient,
rule
);
}
virtual ~GradientFillOutput() = default;
@ -113,27 +128,21 @@ public:
opacityValue = opacity.value(frameTime).value;
}
if (!_fill || hasUpdates) {
if (hasUpdates) {
std::vector<Color> colors;
std::vector<double> locations;
getGradientParameters(numberOfColors, colorsValue, colors, locations);
auto gradient = std::make_shared<RenderTreeNodeContent::GradientShading>(
opacityValue * 0.01,
gradientType,
colors,
locations,
Vector2D(startPointValue.x, startPointValue.y),
Vector2D(endPointValue.x, endPointValue.y)
);
_fill = std::make_shared<RenderTreeNodeContent::Fill>(
gradient,
rule
);
RenderTreeNodeContentItem::GradientShading *gradient = ((RenderTreeNodeContentItem::GradientShading *)_fill->shading.get());
gradient->opacity = opacityValue * 0.01;
gradient->colors = colors;
gradient->locations = locations;
gradient->start = Vector2D(startPointValue.x, startPointValue.y);
gradient->end = Vector2D(endPointValue.x, endPointValue.y);
}
}
virtual std::shared_ptr<RenderTreeNodeContent::Fill> fill() override {
virtual std::shared_ptr<RenderTreeNodeContentItem::Fill> fill() override {
return _fill;
}
@ -154,7 +163,7 @@ public:
KeyframeInterpolator<Vector1D> opacity;
double opacityValue = 0.0;
std::shared_ptr<RenderTreeNodeContent::Fill> _fill;
std::shared_ptr<RenderTreeNodeContentItem::Fill> _fill;
};
class StrokeOutput {
@ -164,7 +173,7 @@ public:
~StrokeOutput() = default;
virtual void update(AnimationFrameTime frameTime) = 0;
virtual std::shared_ptr<RenderTreeNodeContent::Stroke> stroke() = 0;
virtual std::shared_ptr<RenderTreeNodeContentItem::Stroke> stroke() = 0;
};
class SolidStrokeOutput : public StrokeOutput {
@ -184,6 +193,17 @@ public:
dashPhase = std::make_unique<KeyframeInterpolator<Vector1D>>(dashConfiguration.dashPhase);
}
}
auto solid = std::make_shared<RenderTreeNodeContentItem::SolidShading>(Color(0.0, 0.0, 0.0, 0.0), 0.0);
_stroke = std::make_shared<RenderTreeNodeContentItem::Stroke>(
solid,
0.0,
lineJoin,
lineCap,
miterLimit,
0.0,
std::vector<double>()
);
}
virtual ~SolidStrokeOutput() = default;
@ -220,7 +240,7 @@ public:
}
}
if (!_stroke || hasUpdates) {
if (hasUpdates) {
bool hasNonZeroDashes = false;
if (!dashPatternValue.values.empty()) {
for (const auto &value : dashPatternValue.values) {
@ -231,20 +251,17 @@ public:
}
}
auto solid = std::make_shared<RenderTreeNodeContent::SolidShading>(colorValue, opacityValue * 0.01);
_stroke = std::make_shared<RenderTreeNodeContent::Stroke>(
solid,
widthValue,
lineJoin,
lineCap,
miterLimit,
hasNonZeroDashes ? dashPhaseValue : 0.0,
hasNonZeroDashes ? dashPatternValue.values : std::vector<double>()
);
RenderTreeNodeContentItem::SolidShading *solid = (RenderTreeNodeContentItem::SolidShading *)_stroke->shading.get();
solid->color = colorValue;
solid->opacity = opacityValue * 0.01;
_stroke->lineWidth = widthValue;
_stroke->dashPhase = hasNonZeroDashes ? dashPhaseValue : 0.0;
_stroke->dashPattern = hasNonZeroDashes ? dashPatternValue.values : std::vector<double>();
}
}
virtual std::shared_ptr<RenderTreeNodeContent::Stroke> stroke() override {
virtual std::shared_ptr<RenderTreeNodeContentItem::Stroke> stroke() override {
return _stroke;
}
@ -268,7 +285,7 @@ public:
std::unique_ptr<KeyframeInterpolator<Vector1D>> dashPhase;
double dashPhaseValue = 0.0;
std::shared_ptr<RenderTreeNodeContent::Stroke> _stroke;
std::shared_ptr<RenderTreeNodeContentItem::Stroke> _stroke;
};
class GradientStrokeOutput : public StrokeOutput {
@ -292,6 +309,24 @@ public:
dashPhase = std::make_unique<KeyframeInterpolator<Vector1D>>(dashConfiguration.dashPhase);
}
}
auto gradient = std::make_shared<RenderTreeNodeContentItem::GradientShading>(
0.0,
gradientType,
std::vector<Color>(),
std::vector<double>(),
Vector2D(0.0, 0.0),
Vector2D(0.0, 0.0)
);
_stroke = std::make_shared<RenderTreeNodeContentItem::Stroke>(
gradient,
0.0,
lineJoin,
lineCap,
miterLimit,
0.0,
std::vector<double>()
);
}
virtual ~GradientStrokeOutput() = default;
@ -338,7 +373,7 @@ public:
}
}
if (!_stroke || hasUpdates) {
if (hasUpdates) {
bool hasNonZeroDashes = false;
if (!dashPatternValue.values.empty()) {
for (const auto &value : dashPatternValue.values) {
@ -353,27 +388,20 @@ public:
std::vector<double> locations;
getGradientParameters(numberOfColors, colorsValue, colors, locations);
auto gradient = std::make_shared<RenderTreeNodeContent::GradientShading>(
opacityValue * 0.01,
gradientType,
colors,
locations,
Vector2D(startPointValue.x, startPointValue.y),
Vector2D(endPointValue.x, endPointValue.y)
);
_stroke = std::make_shared<RenderTreeNodeContent::Stroke>(
gradient,
widthValue,
lineJoin,
lineCap,
miterLimit,
hasNonZeroDashes ? dashPhaseValue : 0.0,
hasNonZeroDashes ? dashPatternValue.values : std::vector<double>()
);
RenderTreeNodeContentItem::GradientShading *gradient = ((RenderTreeNodeContentItem::GradientShading *)_stroke->shading.get());
gradient->opacity = opacityValue * 0.01;
gradient->colors = colors;
gradient->locations = locations;
gradient->start = Vector2D(startPointValue.x, startPointValue.y);
gradient->end = Vector2D(endPointValue.x, endPointValue.y);
_stroke->lineWidth = widthValue;
_stroke->dashPhase = hasNonZeroDashes ? dashPhaseValue : 0.0;
_stroke->dashPattern = hasNonZeroDashes ? dashPatternValue.values : std::vector<double>();
}
}
virtual std::shared_ptr<RenderTreeNodeContent::Stroke> stroke() override {
virtual std::shared_ptr<RenderTreeNodeContentItem::Stroke> stroke() override {
return _stroke;
}
@ -406,7 +434,7 @@ public:
std::unique_ptr<KeyframeInterpolator<Vector1D>> dashPhase;
double dashPhaseValue = 0.0;
std::shared_ptr<RenderTreeNodeContent::Stroke> _stroke;
std::shared_ptr<RenderTreeNodeContentItem::Stroke> _stroke;
};
struct TrimParams {
@ -478,8 +506,6 @@ public:
std::shared_ptr<FillOutput> fill;
std::shared_ptr<StrokeOutput> stroke;
size_t subItemLimit = 0;
std::shared_ptr<RenderTreeNode> renderTree;
};
struct TransformedPath {
@ -981,7 +1007,6 @@ public:
1.0,
false,
false,
nullptr,
std::vector<std::shared_ptr<RenderTreeNode>>(),
nullptr,
false
@ -998,38 +1023,12 @@ public:
continue;
}
auto shadingRenderTree = std::make_shared<RenderTreeNode>(
CGRect(0.0, 0.0, 0.0, 0.0),
Vector2D(0.0, 0.0),
CATransform3D::identity(),
1.0,
false,
false,
nullptr,
std::vector<std::shared_ptr<RenderTreeNode>>(),
nullptr,
false
);
shadingVariant.renderTree = shadingRenderTree;
//_renderTree->_subnodes.push_back(shadingRenderTree);
auto itemShadingVariant = std::make_shared<RenderTreeNodeContentShadingVariant>();
if (shadingVariant.fill) {
itemShadingVariant->fill = std::make_shared<RenderTreeNodeContent::Fill>(
nullptr,
FillRule::NonZeroWinding
);
itemShadingVariant->fill = shadingVariant.fill->fill();
}
if (shadingVariant.stroke) {
itemShadingVariant->stroke = std::make_shared<RenderTreeNodeContent::Stroke>(
nullptr,
0.0,
LineJoin::Bevel,
LineCap::Round,
0.0,
0.0,
std::vector<double>()
);
itemShadingVariant->stroke = shadingVariant.stroke->stroke();
}
itemShadingVariant->subItemLimit = shadingVariant.subItemLimit;
@ -1052,7 +1051,6 @@ public:
1.0,
false,
false,
nullptr,
subItemNodes,
nullptr,
false
@ -1126,28 +1124,6 @@ public:
resultPaths.push_back(path);
}
std::shared_ptr<RenderTreeNodeContent> content;
std::shared_ptr<RenderTreeNodeContent::Fill> fill;
if (shadingVariant.fill) {
fill = shadingVariant.fill->fill();
}
std::shared_ptr<RenderTreeNodeContent::Stroke> stroke;
if (shadingVariant.stroke) {
stroke = shadingVariant.stroke->stroke();
}
content = std::make_shared<RenderTreeNodeContent>(
resultPaths,
stroke,
fill
);
shadingVariant.renderTree->_content = content;
_renderTree->_contentItem->shadings[i]->stroke = stroke;
_renderTree->_contentItem->shadings[i]->fill = fill;
_renderTree->_contentItem->shadings[i]->explicitPath = resultPaths;
}
@ -1358,7 +1334,6 @@ std::shared_ptr<RenderTreeNode> ShapeCompositionLayer::renderTreeNode() {
1.0,
false,
false,
nullptr,
renderTreeValue,
nullptr,
false
@ -1383,7 +1358,6 @@ std::shared_ptr<RenderTreeNode> ShapeCompositionLayer::renderTreeNode() {
1.0,
false,
false,
nullptr,
subnodes,
maskNode,
invertMask

View File

@ -243,7 +243,6 @@ public:
1.0,
false,
false,
nullptr,
subnodes,
nullptr,
false

View File

@ -2,298 +2,8 @@
#include "Lottie/Private/MainThread/LayerContainers/MainThreadAnimationLayer.hpp"
#include "LottieAnimationInternal.h"
#include "RenderNode.hpp"
#include "LottieRenderTreeInternal.h"
#include <LottieCpp/VectorsCocoa.h>
namespace lottie {
struct RenderNodeDesc {
struct LayerParams {
CGRect _bounds;
Vector2D _position;
CATransform3D _transform;
double _opacity;
bool _masksToBounds;
bool _isHidden;
LayerParams(
CGRect bounds_,
Vector2D position_,
CATransform3D transform_,
double opacity_,
bool masksToBounds_,
bool isHidden_
) :
_bounds(bounds_),
_position(position_),
_transform(transform_),
_opacity(opacity_),
_masksToBounds(masksToBounds_),
_isHidden(isHidden_) {
}
CGRect bounds() const {
return _bounds;
}
Vector2D position() const {
return _position;
}
CATransform3D transform() const {
return _transform;
}
double opacity() const {
return _opacity;
}
bool masksToBounds() const {
return _masksToBounds;
}
bool isHidden() const {
return _isHidden;
}
};
LayerParams layer;
CGRect globalRect;
CGRect localRect;
CATransform3D globalTransform;
bool drawsContent;
bool renderContent;
int drawContentDescendants;
bool isInvertedMatte;
std::shared_ptr<OutputRenderNode> mask;
explicit RenderNodeDesc(
LayerParams const &layer_,
CGRect const &globalRect_,
CGRect const &localRect_,
CATransform3D const &globalTransform_,
bool drawsContent_,
bool renderContent_,
int drawContentDescendants_,
bool isInvertedMatte_
) :
layer(layer_),
globalRect(globalRect_),
localRect(localRect_),
globalTransform(globalTransform_),
drawsContent(drawsContent_),
renderContent(renderContent_),
drawContentDescendants(drawContentDescendants_),
isInvertedMatte(isInvertedMatte_) {
}
};
static std::shared_ptr<OutputRenderNode> convertRenderTree(std::shared_ptr<RenderTreeNode> const &node, Vector2D const &globalSize, CATransform3D const &parentTransform, bool isInvertedMask, BezierPathsBoundingBoxContext &bezierPathsBoundingBoxContext) {
if (node->isHidden() || node->alpha() == 0.0f) {
return nullptr;
}
if (node->masksToBounds()) {
if (node->bounds().empty()) {
return nullptr;
}
}
auto currentTransform = parentTransform;
Vector2D localTranslation(node->position().x + -node->bounds().x, node->position().y + -node->bounds().y);
CATransform3D localTransform = node->transform();
localTransform = localTransform.translated(localTranslation);
currentTransform = localTransform * currentTransform;
if (!currentTransform.isInvertible()) {
return nullptr;
}
std::optional<CGRect> effectiveLocalBounds;
double alpha = node->alpha();
if (node->content()) {
RenderTreeNodeContent *shapeContent = node->content().get();
CGRect shapeBounds = bezierPathsBoundingBoxParallel(bezierPathsBoundingBoxContext, shapeContent->paths);
if (shapeContent->stroke) {
shapeBounds = shapeBounds.insetBy(-shapeContent->stroke->lineWidth / 2.0, -shapeContent->stroke->lineWidth / 2.0);
effectiveLocalBounds = shapeBounds;
switch (shapeContent->stroke->shading->type()) {
case RenderTreeNodeContent::ShadingType::Solid: {
RenderTreeNodeContent::SolidShading *solidShading = (RenderTreeNodeContent::SolidShading *)shapeContent->stroke->shading.get();
alpha *= solidShading->opacity;
break;
}
case RenderTreeNodeContent::ShadingType::Gradient: {
break;
}
default:
break;
}
} else if (shapeContent->fill) {
effectiveLocalBounds = shapeBounds;
switch (shapeContent->fill->shading->type()) {
case RenderTreeNodeContent::ShadingType::Solid: {
RenderTreeNodeContent::SolidShading *solidShading = (RenderTreeNodeContent::SolidShading *)shapeContent->fill->shading.get();
alpha *= solidShading->opacity;
break;
}
case RenderTreeNodeContent::ShadingType::Gradient: {
RenderTreeNodeContent::GradientShading *gradientShading = (RenderTreeNodeContent::GradientShading *)shapeContent->fill->shading.get();
alpha *= gradientShading->opacity;
break;
}
default:
break;
}
}
}
bool isInvertedMatte = isInvertedMask;
if (isInvertedMatte) {
effectiveLocalBounds = node->bounds();
}
if (effectiveLocalBounds && effectiveLocalBounds->empty()) {
effectiveLocalBounds = std::nullopt;
}
std::optional<CGRect> effectiveLocalRect;
if (effectiveLocalBounds.has_value()) {
effectiveLocalRect = effectiveLocalBounds;
}
std::vector<std::shared_ptr<OutputRenderNode>> subnodes;
std::optional<CGRect> subnodesGlobalRect;
bool masksToBounds = node->masksToBounds();
int drawContentDescendants = 0;
for (const auto &item : node->subnodes()) {
if (const auto subnode = convertRenderTree(item, globalSize, currentTransform, false, bezierPathsBoundingBoxContext)) {
subnodes.push_back(subnode);
drawContentDescendants += subnode->drawContentDescendants;
if (subnode->renderContent) {
drawContentDescendants += 1;
}
if (!subnode->localRect.empty()) {
if (effectiveLocalRect.has_value()) {
effectiveLocalRect = effectiveLocalRect->unionWith(subnode->localRect);
} else {
effectiveLocalRect = subnode->localRect;
}
}
if (subnodesGlobalRect) {
subnodesGlobalRect = subnodesGlobalRect->unionWith(subnode->globalRect);
} else {
subnodesGlobalRect = subnode->globalRect;
}
}
}
if (masksToBounds && effectiveLocalRect.has_value()) {
if (node->bounds().contains(effectiveLocalRect.value())) {
masksToBounds = false;
}
}
std::optional<CGRect> fuzzyGlobalRect;
if (effectiveLocalBounds) {
CGRect effectiveGlobalBounds = effectiveLocalBounds->applyingTransform(currentTransform);
if (fuzzyGlobalRect) {
fuzzyGlobalRect = fuzzyGlobalRect->unionWith(effectiveGlobalBounds);
} else {
fuzzyGlobalRect = effectiveGlobalBounds;
}
}
if (subnodesGlobalRect) {
if (fuzzyGlobalRect) {
fuzzyGlobalRect = fuzzyGlobalRect->unionWith(subnodesGlobalRect.value());
} else {
fuzzyGlobalRect = subnodesGlobalRect;
}
}
if (!fuzzyGlobalRect) {
return nullptr;
}
CGRect globalRect(
std::floor(fuzzyGlobalRect->x),
std::floor(fuzzyGlobalRect->y),
std::ceil(fuzzyGlobalRect->width + fuzzyGlobalRect->x - floor(fuzzyGlobalRect->x)),
std::ceil(fuzzyGlobalRect->height + fuzzyGlobalRect->y - floor(fuzzyGlobalRect->y))
);
if (!CGRect(0.0, 0.0, globalSize.x, globalSize.y).intersects(globalRect)) {
return nullptr;
}
if (masksToBounds && effectiveLocalBounds) {
CGRect effectiveGlobalBounds = effectiveLocalBounds->applyingTransform(currentTransform);
if (effectiveGlobalBounds.contains(CGRect(0.0, 0.0, globalSize.x, globalSize.y))) {
masksToBounds = false;
}
}
std::shared_ptr<OutputRenderNode> maskNode;
if (node->mask()) {
if (const auto maskNodeValue = convertRenderTree(node->mask(), globalSize, currentTransform, node->invertMask(), bezierPathsBoundingBoxContext)) {
if (!maskNodeValue->globalRect.intersects(globalRect)) {
return nullptr;
}
maskNode = maskNodeValue;
} else {
return nullptr;
}
}
CGRect localRect = effectiveLocalRect.value_or(CGRect(0.0, 0.0, 0.0, 0.0)).applyingTransform(localTransform);
return std::make_shared<OutputRenderNode>(
OutputRenderNode::LayerParams(
node->bounds(),
node->position(),
node->transform(),
alpha,
masksToBounds,
node->isHidden()
),
globalRect,
localRect,
currentTransform,
effectiveLocalBounds.has_value(),
node->content(),
drawContentDescendants,
isInvertedMatte,
subnodes,
maskNode
);
}
}
@interface LottieAnimationContainer () {
@public
std::shared_ptr<lottie::MainThreadAnimationLayer> _layer;
@ -326,43 +36,9 @@ static std::shared_ptr<OutputRenderNode> convertRenderTree(std::shared_ptr<Rende
}
- (LottieRenderNode * _Nullable)getCurrentRenderTreeForSize:(CGSize)size {
auto renderNode = _layer->renderTreeNode();
if (!renderNode) {
return nil;
}
if (size.width <= 0.0 || size.height <= 0.0) {
return nil;
}
auto node = convertRenderTree(renderNode, lottie::Vector2D((int)size.width, (int)size.height), lottie::CATransform3D::identity().scaled(lottie::Vector2D(size.width / (double)_animation.size.width, size.height / (double)_animation.size.height)), false, *_bezierPathsBoundingBoxContext.get());
if (node) {
return [[LottieRenderNode alloc] initWithRenderNode:node];
} else {
node = std::make_shared<lottie::OutputRenderNode>(
lottie::OutputRenderNode::LayerParams(
lottie::CGRect(0.0, 0.0, size.width, size.height),
lottie::Vector2D(0.0, 0.0),
lottie::CATransform3D::identity(),
1.0,
false,
false
),
lottie::CGRect(0.0, 0.0, size.width, size.height),
lottie::CGRect(0.0, 0.0, size.width, size.height),
lottie::CATransform3D::identity(),
false,
nullptr,
true,
false,
std::vector<std::shared_ptr<lottie::OutputRenderNode>>(),
nullptr
);
return [[LottieRenderNode alloc] initWithRenderNode:node];
}
}
- (std::shared_ptr<lottie::RenderTreeNode>)internalGetRootRenderTreeNode {
auto renderNode = _layer->renderTreeNode();
return renderNode;

View File

@ -1,5 +1,4 @@
#include "LottieRenderTree.h"
#include "LottieRenderTreeInternal.h"
#include <LottieCpp/CGPath.h>
#include <LottieCpp/CGPathCocoa.h>
@ -7,8 +6,6 @@
#include "Lottie/Public/Primitives/CALayer.hpp"
#include <LottieCpp/VectorsCocoa.h>
#include "RenderNode.hpp"
namespace {
}
@ -192,7 +189,7 @@ static LottieColor lottieColorFromColor(lottie::Color color) {
@implementation LottieRenderContentSolidShading
- (instancetype _Nonnull)initWithSolidShading:(lottie::RenderTreeNodeContent::SolidShading *)solidShading __attribute__((objc_direct)) {
- (instancetype _Nonnull)initWithSolidShading:(lottie::RenderTreeNodeContentItem::SolidShading *)solidShading __attribute__((objc_direct)) {
self = [super init];
if (self != nil) {
_color = lottieColorFromColor(solidShading->color);
@ -214,7 +211,7 @@ static LottieColor lottieColorFromColor(lottie::Color color) {
@implementation LottieRenderContentGradientShading
- (instancetype _Nonnull)initWithGradientShading:(lottie::RenderTreeNodeContent::GradientShading *)gradientShading __attribute__((objc_direct)) {
- (instancetype _Nonnull)initWithGradientShading:(lottie::RenderTreeNodeContentItem::GradientShading *)gradientShading __attribute__((objc_direct)) {
self = [super init];
if (self != nil) {
_opacity = gradientShading->opacity;
@ -258,16 +255,16 @@ static LottieColor lottieColorFromColor(lottie::Color color) {
@implementation LottieRenderContentFill
- (instancetype _Nonnull)initWithFill:(std::shared_ptr<lottie::RenderTreeNodeContent::Fill> const &)fill __attribute__((objc_direct)) {
- (instancetype _Nonnull)initWithFill:(std::shared_ptr<lottie::RenderTreeNodeContentItem::Fill> const &)fill __attribute__((objc_direct)) {
self = [super init];
if (self != nil) {
switch (fill->shading->type()) {
case lottie::RenderTreeNodeContent::ShadingType::Solid: {
_shading = [[LottieRenderContentSolidShading alloc] initWithSolidShading:(lottie::RenderTreeNodeContent::SolidShading *)fill->shading.get()];
case lottie::RenderTreeNodeContentItem::ShadingType::Solid: {
_shading = [[LottieRenderContentSolidShading alloc] initWithSolidShading:(lottie::RenderTreeNodeContentItem::SolidShading *)fill->shading.get()];
break;
}
case lottie::RenderTreeNodeContent::ShadingType::Gradient: {
_shading = [[LottieRenderContentGradientShading alloc] initWithGradientShading:(lottie::RenderTreeNodeContent::GradientShading *)fill->shading.get()];
case lottie::RenderTreeNodeContentItem::ShadingType::Gradient: {
_shading = [[LottieRenderContentGradientShading alloc] initWithGradientShading:(lottie::RenderTreeNodeContentItem::GradientShading *)fill->shading.get()];
break;
}
default: {
@ -302,16 +299,16 @@ static LottieColor lottieColorFromColor(lottie::Color color) {
@implementation LottieRenderContentStroke
- (instancetype _Nonnull)initWithStroke:(std::shared_ptr<lottie::RenderTreeNodeContent::Stroke> const &)stroke __attribute__((objc_direct)) {
- (instancetype _Nonnull)initWithStroke:(std::shared_ptr<lottie::RenderTreeNodeContentItem::Stroke> const &)stroke __attribute__((objc_direct)) {
self = [super init];
if (self != nil) {
switch (stroke->shading->type()) {
case lottie::RenderTreeNodeContent::ShadingType::Solid: {
_shading = [[LottieRenderContentSolidShading alloc] initWithSolidShading:(lottie::RenderTreeNodeContent::SolidShading *)stroke->shading.get()];
case lottie::RenderTreeNodeContentItem::ShadingType::Solid: {
_shading = [[LottieRenderContentSolidShading alloc] initWithSolidShading:(lottie::RenderTreeNodeContentItem::SolidShading *)stroke->shading.get()];
break;
}
case lottie::RenderTreeNodeContent::ShadingType::Gradient: {
_shading = [[LottieRenderContentGradientShading alloc] initWithGradientShading:(lottie::RenderTreeNodeContent::GradientShading *)stroke->shading.get()];
case lottie::RenderTreeNodeContentItem::ShadingType::Gradient: {
_shading = [[LottieRenderContentGradientShading alloc] initWithGradientShading:(lottie::RenderTreeNodeContentItem::GradientShading *)stroke->shading.get()];
break;
}
default: {
@ -392,20 +389,6 @@ static LottieColor lottieColorFromColor(lottie::Color color) {
@implementation LottieRenderContent
- (instancetype _Nonnull)initWithRenderContent:(std::shared_ptr<lottie::RenderTreeNodeContent> const &)content __attribute__((objc_direct)) {
self = [super init];
if (self != nil) {
_path = [[LottiePath alloc] initWithPaths:content->paths];
if (content->stroke) {
_stroke = [[LottieRenderContentStroke alloc] initWithStroke:content->stroke];
}
if (content->fill) {
_fill = [[LottieRenderContentFill alloc] initWithFill:content->fill];
}
}
return self;
}
- (instancetype _Nonnull)initWithPath:(LottiePath * _Nonnull)path stroke:(LottieRenderContentStroke * _Nullable)stroke fill:(LottieRenderContentFill * _Nullable)fill __attribute__((objc_direct)) {
self = [super init];
if (self != nil) {
@ -441,50 +424,3 @@ static LottieColor lottieColorFromColor(lottie::Color color) {
}
@end
@implementation LottieRenderNode (Internal)
- (instancetype _Nonnull)initWithRenderNode:(std::shared_ptr<lottie::OutputRenderNode> const &)renderNode __attribute__((objc_direct)) {
self = [super init];
if (self != nil) {
auto position = renderNode->layer.position();
_position = CGPointMake(position.x, position.y);
auto bounds = renderNode->layer.bounds();
_bounds = CGRectMake(bounds.x, bounds.y, bounds.width, bounds.height);
_transform = lottie::nativeTransform(renderNode->layer.transform());
_opacity = renderNode->layer.opacity();
_masksToBounds = renderNode->layer.masksToBounds();
_isHidden = renderNode->layer.isHidden();
auto globalRect = renderNode->globalRect;
_globalRect = CGRectMake(globalRect.x, globalRect.y, globalRect.width, globalRect.height);
_globalTransform = lottie::nativeTransform(renderNode->globalTransform);
if (renderNode->renderContent) {
_renderContent = [[LottieRenderContent alloc] initWithRenderContent:renderNode->renderContent];
}
_hasSimpleContents = renderNode->drawContentDescendants <= 1;
_isInvertedMatte = renderNode->isInvertedMatte;
if (!renderNode->subnodes.empty()) {
NSMutableArray<LottieRenderNode *> *subnodes = [[NSMutableArray alloc] init];
for (const auto &subnode : renderNode->subnodes) {
[subnodes addObject:[[LottieRenderNode alloc] initWithRenderNode:subnode]];
}
_subnodes = subnodes;
} else {
_subnodes = [[NSArray alloc] init];
}
if (renderNode->mask) {
_mask = [[LottieRenderNode alloc] initWithRenderNode:renderNode->mask];
}
}
return self;
}
@end

View File

@ -1,15 +0,0 @@
#ifndef LottieRenderTreeInternal_h
#define LottieRenderTreeInternal_h
#include <LottieCpp/LottieRenderTree.h>
#import "RenderNode.hpp"
#include <memory>
@interface LottieRenderNode (Internal)
- (instancetype _Nonnull)initWithRenderNode:(std::shared_ptr<lottie::OutputRenderNode> const &)renderNode __attribute__((objc_direct));
@end
#endif /* LottieRenderTreeInternal_h */

View File

@ -1,29 +0,0 @@
#include "RenderNode.hpp"
namespace lottie {
OutputRenderNode::OutputRenderNode(
LayerParams const &layer_,
CGRect const &globalRect_,
CGRect const &localRect_,
CATransform3D const &globalTransform_,
bool drawsContent_,
std::shared_ptr<RenderTreeNodeContent> renderContent_,
int drawContentDescendants_,
bool isInvertedMatte_,
std::vector<std::shared_ptr<OutputRenderNode>> const &subnodes_,
std::shared_ptr<OutputRenderNode> const &mask_
) :
layer(layer_),
globalRect(globalRect_),
localRect(localRect_),
globalTransform(globalTransform_),
drawsContent(drawsContent_),
renderContent(renderContent_),
drawContentDescendants(drawContentDescendants_),
isInvertedMatte(isInvertedMatte_),
subnodes(subnodes_),
mask(mask_) {
}
}

View File

@ -1,94 +0,0 @@
#ifndef RenderNode_hpp
#define RenderNode_hpp
#include "Lottie/Public/Primitives/CALayer.hpp"
namespace lottie {
struct OutputRenderNode {
struct LayerParams {
CGRect _bounds;
Vector2D _position;
CATransform3D _transform;
double _opacity;
bool _masksToBounds;
bool _isHidden;
LayerParams(
CGRect bounds_,
Vector2D position_,
CATransform3D transform_,
double opacity_,
bool masksToBounds_,
bool isHidden_
) :
_bounds(bounds_),
_position(position_),
_transform(transform_),
_opacity(opacity_),
_masksToBounds(masksToBounds_),
_isHidden(isHidden_) {
}
LayerParams(std::shared_ptr<CALayer> const &layer) :
_bounds(layer->bounds()),
_position(layer->position()),
_transform(layer->transform()),
_opacity(layer->opacity()),
_masksToBounds(layer->masksToBounds()),
_isHidden(layer->isHidden()) {
}
CGRect bounds() const {
return _bounds;
}
Vector2D position() const {
return _position;
}
CATransform3D transform() const {
return _transform;
}
double opacity() const {
return _opacity;
}
bool masksToBounds() const {
return _masksToBounds;
}
bool isHidden() const {
return _isHidden;
}
};
LayerParams layer;
CGRect globalRect;
CGRect localRect;
CATransform3D globalTransform;
bool drawsContent;
std::shared_ptr<RenderTreeNodeContent> renderContent;
int drawContentDescendants;
bool isInvertedMatte;
std::vector<std::shared_ptr<OutputRenderNode>> subnodes;
std::shared_ptr<OutputRenderNode> mask;
explicit OutputRenderNode(
LayerParams const &layer_,
CGRect const &globalRect_,
CGRect const &localRect_,
CATransform3D const &globalTransform_,
bool drawsContent_,
std::shared_ptr<RenderTreeNodeContent> renderContent_,
int drawContentDescendants_,
bool isInvertedMatte_,
std::vector<std::shared_ptr<OutputRenderNode>> const &subnodes_,
std::shared_ptr<OutputRenderNode> const &mask_
);
};
}
#endif /* RenderNode_hpp */