Lottie: refactor trimming

This commit is contained in:
Isaac 2024-06-02 23:18:15 +04:00
parent 9c0be8d8ee
commit 9a4f4ef280
5 changed files with 94 additions and 115 deletions

View File

@ -65,6 +65,14 @@ static void enumeratePaths(std::shared_ptr<lottie::RenderTreeNodeContentItem> it
size_t maxSubitem = std::min(item->subItems.size(), subItemLimit);
if (item->trimmedPaths) {
for (const auto &path : item->trimmedPaths.value()) {
onPath(path, effectiveTransform);
}
return;
}
if (item->path) {
onPath(item->path->path, effectiveTransform);
}
@ -234,39 +242,6 @@ static void drawLottieContentItem(std::shared_ptr<lottieRendering::Canvas> const
for (const auto &shading : item->shadings) {
lottieRendering::CanvasPathEnumerator iteratePaths;
if (shading->explicitPath) {
auto itemPaths = shading->explicitPath.value();
iteratePaths = [itemPaths = itemPaths](std::function<void(lottieRendering::PathCommand const &)> iterate) -> void {
lottieRendering::PathCommand pathCommand;
for (const auto &path : itemPaths) {
std::optional<lottie::PathElement> previousElement;
for (const auto &element : path.elements()) {
if (previousElement.has_value()) {
if (previousElement->vertex.outTangentRelative().isZero() && element.vertex.inTangentRelative().isZero()) {
pathCommand.type = lottieRendering::PathCommandType::LineTo;
pathCommand.points[0] = CGPointMake(element.vertex.point.x, element.vertex.point.y);
iterate(pathCommand);
} else {
pathCommand.type = lottieRendering::PathCommandType::CurveTo;
pathCommand.points[2] = CGPointMake(element.vertex.point.x, element.vertex.point.y);
pathCommand.points[1] = CGPointMake(element.vertex.inTangent.x, element.vertex.inTangent.y);
pathCommand.points[0] = CGPointMake(previousElement->vertex.outTangent.x, previousElement->vertex.outTangent.y);
iterate(pathCommand);
}
} else {
pathCommand.type = lottieRendering::PathCommandType::MoveTo;
pathCommand.points[0] = CGPointMake(element.vertex.point.x, element.vertex.point.y);
iterate(pathCommand);
}
previousElement = element;
}
if (path.closed().value_or(true)) {
pathCommand.type = lottieRendering::PathCommandType::Close;
iterate(pathCommand);
}
}
};
} else {
iteratePaths = [&](std::function<void(lottieRendering::PathCommand const &)> iterate) {
enumeratePaths(item, shading->subItemLimit, lottie::Transform2D::identity(), true, [&](lottie::BezierPath const &sourcePath, lottie::Transform2D const &transform) {
auto path = sourcePath.copyUsingTransform(transform);
@ -299,38 +274,6 @@ static void drawLottieContentItem(std::shared_ptr<lottieRendering::Canvas> const
}
});
};
}
/*auto iteratePaths = [&](std::function<void(lottieRendering::PathCommand const &)> iterate) -> void {
lottieRendering::PathCommand pathCommand;
for (const auto &path : itemPaths) {
std::optional<lottie::PathElement> previousElement;
for (const auto &element : path.elements()) {
if (previousElement.has_value()) {
if (previousElement->vertex.outTangentRelative().isZero() && element.vertex.inTangentRelative().isZero()) {
pathCommand.type = lottieRendering::PathCommandType::LineTo;
pathCommand.points[0] = CGPointMake(element.vertex.point.x, element.vertex.point.y);
iterate(pathCommand);
} else {
pathCommand.type = lottieRendering::PathCommandType::CurveTo;
pathCommand.points[2] = CGPointMake(element.vertex.point.x, element.vertex.point.y);
pathCommand.points[1] = CGPointMake(element.vertex.inTangent.x, element.vertex.inTangent.y);
pathCommand.points[0] = CGPointMake(previousElement->vertex.outTangent.x, previousElement->vertex.outTangent.y);
iterate(pathCommand);
}
} else {
pathCommand.type = lottieRendering::PathCommandType::MoveTo;
pathCommand.points[0] = CGPointMake(element.vertex.point.x, element.vertex.point.y);
iterate(pathCommand);
}
previousElement = element;
}
if (path.closed().value_or(true)) {
pathCommand.type = lottieRendering::PathCommandType::Close;
iterate(pathCommand);
}
}
};*/
if (shading->stroke) {
if (shading->stroke->shading->type() == lottie::RenderTreeNodeContentItem::ShadingType::Solid) {

View File

@ -131,6 +131,31 @@ public:
std::shared_ptr<BezierPathContents> _contents;
};
class PathContents {
public:
struct Element {
Vector2D point;
Vector2D cp1;
Vector2D cp2;
explicit Element(Vector2D const &point_, Vector2D const &cp1_, Vector2D const &cp2_) :
point(point_),
cp1(cp1_),
cp2(cp2_) {
}
};
public:
PathContents(BezierPathContents const &bezierPath);
~PathContents();
std::shared_ptr<BezierPathContents> bezierPath() const;
private:
std::vector<Element> _elements;
bool _isClosed = false;
};
class BezierPathsBoundingBoxContext {
public:
BezierPathsBoundingBoxContext();

View File

@ -368,6 +368,7 @@ public:
float alpha = 0.0;
std::optional<TrimParams> trimParams;
std::shared_ptr<RenderTreeNodeContentPath> path;
std::optional<std::vector<BezierPath>> trimmedPaths;
std::vector<std::shared_ptr<RenderTreeNodeContentShadingVariant>> shadings;
std::vector<std::shared_ptr<RenderTreeNodeContentItem>> subItems;
int drawContentCount = 0;
@ -383,7 +384,6 @@ public:
public:
std::shared_ptr<RenderTreeNodeContentItem::Stroke> stroke;
std::shared_ptr<RenderTreeNodeContentItem::Fill> fill;
std::optional<std::vector<BezierPath>> explicitPath;
size_t subItemLimit = 0;
};

View File

@ -1080,54 +1080,23 @@ public:
_contentItem->transform = containerTransform;
_contentItem->alpha = containerOpacity;
if (trim) {
_contentItem->trimParams = trim->trimParams();
}
for (int i = 0; i < shadings.size(); i++) {
const auto &shadingVariant = shadings[i];
if (!(shadingVariant.fill || shadingVariant.stroke)) {
continue;
}
//std::optional<TrimParams> currentTrim = parentTrim;
//TODO:investigate
/*if (!trims.empty()) {
currentTrim = trims[0];
}*/
if (parentTrim) {
_contentItem->trimParams = parentTrim;
CompoundBezierPath compoundPath;
auto paths = collectPaths(shadingVariant.subItemLimit, Transform2D::identity(), true);
auto paths = collectPaths(INT32_MAX, Transform2D::identity(), true);
for (const auto &path : paths) {
compoundPath.appendPath(path.path.copyUsingTransform(path.transform));
}
compoundPath = trimCompoundPath(compoundPath, parentTrim->start, parentTrim->end, parentTrim->offset, parentTrim->type);
std::vector<BezierPath> resultPaths;
for (const auto &path : compoundPath.paths) {
resultPaths.push_back(path);
}
_contentItem->shadings[i]->explicitPath = resultPaths;
} else {
if (hasTrims()) {
CompoundBezierPath compoundPath;
auto paths = collectPaths(shadingVariant.subItemLimit, Transform2D::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->trimmedPaths = resultPaths;
}
if (isGroup && !subItems.empty()) {

View File

@ -687,4 +687,46 @@ CGRect bezierPathsBoundingBox(std::vector<BezierPath> const &paths) {
return calculateBoundingRectOpt(pointsX, pointsY, pointCount);
}
PathContents::PathContents(BezierPathContents const &bezierPath) {
std::optional<PathElement> previousElement;
for (const auto &element : bezierPath.elements) {
if (previousElement.has_value()) {
_elements.emplace_back(element.vertex.point, previousElement->vertex.outTangent, element.vertex.inTangent);
} else {
_elements.emplace_back(element.vertex.point, Vector2D(0.0, 0.0), Vector2D(0.0, 0.0));
}
previousElement = element;
}
if (bezierPath.closed.value_or(true)) {
_isClosed = true;
} else {
_isClosed = false;
}
}
PathContents::~PathContents() {
}
std::shared_ptr<BezierPathContents> PathContents::bezierPath() const {
auto result = std::make_shared<BezierPathContents>();
bool isFirst = true;
for (const auto &element : _elements) {
if (isFirst) {
isFirst = false;
} else {
result->elements.push_back(PathElement(CurveVertex::absolute(
element.point,
element.cp1,
element.cp2
)));
}
}
result->closed = _isClosed;
return result;
}
}