mirror of
https://github.com/thorvg/thorvg.git
synced 2025-06-14 12:04:29 +00:00
lottie: ++scene rendering optimization
Apply LottieRenderPooler to path/rect/ellipse/polystar. This enhances the animation performance: ~10%
This commit is contained in:
parent
d0210a1a72
commit
cf253dd873
10 changed files with 138 additions and 83 deletions
|
@ -99,7 +99,7 @@ struct RenderContext
|
||||||
static void _updateChildren(LottieGroup* parent, float frameNo, Inlist<RenderContext>& contexts, LottieExpressions* exps);
|
static void _updateChildren(LottieGroup* parent, float frameNo, Inlist<RenderContext>& contexts, LottieExpressions* exps);
|
||||||
static void _updateLayer(LottieLayer* root, LottieLayer* layer, float frameNo, LottieExpressions* exps);
|
static void _updateLayer(LottieLayer* root, LottieLayer* layer, float frameNo, LottieExpressions* exps);
|
||||||
static bool _buildComposition(LottieComposition* comp, LottieLayer* parent);
|
static bool _buildComposition(LottieComposition* comp, LottieLayer* parent);
|
||||||
static bool _draw(LottieGroup* parent, RenderContext* ctx);
|
static bool _draw(LottieGroup* parent, LottieShape* shape, RenderContext* ctx);
|
||||||
|
|
||||||
|
|
||||||
static void _rotationXYZ(Matrix* m, float degreeX, float degreeY, float degreeZ)
|
static void _rotationXYZ(Matrix* m, float degreeX, float degreeY, float degreeZ)
|
||||||
|
@ -275,7 +275,7 @@ static void _updateGroup(LottieGroup* parent, LottieObject** child, float frameN
|
||||||
group->reqFragment |= ctx->reqFragment;
|
group->reqFragment |= ctx->reqFragment;
|
||||||
|
|
||||||
//generate a merging shape to consolidate partial shapes into a single entity
|
//generate a merging shape to consolidate partial shapes into a single entity
|
||||||
if (group->mergeable()) _draw(parent, ctx);
|
if (group->mergeable()) _draw(parent, nullptr, ctx);
|
||||||
|
|
||||||
Inlist<RenderContext> contexts;
|
Inlist<RenderContext> contexts;
|
||||||
contexts.back(new RenderContext(*ctx, group->mergeable()));
|
contexts.back(new RenderContext(*ctx, group->mergeable()));
|
||||||
|
@ -374,20 +374,24 @@ static void _updateGradientFill(TVG_UNUSED LottieGroup* parent, LottieObject** c
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
static bool _draw(LottieGroup* parent, RenderContext* ctx)
|
static bool _draw(LottieGroup* parent, LottieShape* shape, RenderContext* ctx)
|
||||||
{
|
{
|
||||||
if (ctx->merging) return false;
|
if (ctx->merging) return false;
|
||||||
|
|
||||||
auto shape = cast<Shape>(ctx->propagator->duplicate());
|
if (shape) {
|
||||||
ctx->merging = shape.get();
|
ctx->merging = shape->pooling();
|
||||||
parent->scene->push(std::move(shape));
|
PP(ctx->propagator)->duplicate(ctx->merging);
|
||||||
|
} else {
|
||||||
|
ctx->merging = static_cast<Shape*>(ctx->propagator->duplicate());
|
||||||
|
}
|
||||||
|
|
||||||
|
parent->scene->push(cast(ctx->merging));
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
//OPTIMIZE: path?
|
static void _repeat(LottieGroup* parent, Shape* path, RenderContext* ctx)
|
||||||
static void _repeat(LottieGroup* parent, unique_ptr<Shape> path, RenderContext* ctx)
|
|
||||||
{
|
{
|
||||||
Array<Shape*> propagators;
|
Array<Shape*> propagators;
|
||||||
propagators.push(ctx->propagator);
|
propagators.push(ctx->propagator);
|
||||||
|
@ -401,7 +405,7 @@ static void _repeat(LottieGroup* parent, unique_ptr<Shape> path, RenderContext*
|
||||||
|
|
||||||
for (auto propagator = propagators.begin(); propagator < propagators.end(); ++propagator) {
|
for (auto propagator = propagators.begin(); propagator < propagators.end(); ++propagator) {
|
||||||
auto shape = static_cast<Shape*>((*propagator)->duplicate());
|
auto shape = static_cast<Shape*>((*propagator)->duplicate());
|
||||||
P(shape)->rs.path = P(path.get())->rs.path;
|
P(shape)->rs.path = P(path)->rs.path;
|
||||||
|
|
||||||
auto opacity = repeater->interpOpacity ? mathLerp<uint8_t>(repeater->startOpacity, repeater->endOpacity, static_cast<float>(i + 1) / repeater->cnt) : repeater->startOpacity;
|
auto opacity = repeater->interpOpacity ? mathLerp<uint8_t>(repeater->startOpacity, repeater->endOpacity, static_cast<float>(i + 1) / repeater->cnt) : repeater->startOpacity;
|
||||||
shape->opacity(opacity);
|
shape->opacity(opacity);
|
||||||
|
@ -542,11 +546,12 @@ static void _updateRect(LottieGroup* parent, LottieObject** child, float frameNo
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!ctx->repeaters.empty()) {
|
if (!ctx->repeaters.empty()) {
|
||||||
auto path = Shape::gen();
|
auto shape = rect->pooling();
|
||||||
_appendRect(path.get(), position.x - size.x * 0.5f, position.y - size.y * 0.5f, size.x, size.y, roundness, ctx->transform, rect->clockwise);
|
shape->reset();
|
||||||
_repeat(parent, std::move(path), ctx);
|
_appendRect(shape, position.x - size.x * 0.5f, position.y - size.y * 0.5f, size.x, size.y, roundness, ctx->transform, rect->clockwise);
|
||||||
|
_repeat(parent, shape, ctx);
|
||||||
} else {
|
} else {
|
||||||
_draw(parent, ctx);
|
_draw(parent, rect, ctx);
|
||||||
_appendRect(ctx->merging, position.x - size.x * 0.5f, position.y - size.y * 0.5f, size.x, size.y, roundness, ctx->transform, rect->clockwise);
|
_appendRect(ctx->merging, position.x - size.x * 0.5f, position.y - size.y * 0.5f, size.x, size.y, roundness, ctx->transform, rect->clockwise);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -598,11 +603,12 @@ static void _updateEllipse(LottieGroup* parent, LottieObject** child, float fram
|
||||||
auto size = ellipse->size(frameNo, exps);
|
auto size = ellipse->size(frameNo, exps);
|
||||||
|
|
||||||
if (!ctx->repeaters.empty()) {
|
if (!ctx->repeaters.empty()) {
|
||||||
auto path = Shape::gen();
|
auto shape = ellipse->pooling();
|
||||||
_appendCircle(path.get(), position.x, position.y, size.x * 0.5f, size.y * 0.5f, ctx->transform, ellipse->clockwise);
|
shape->reset();
|
||||||
_repeat(parent, std::move(path), ctx);
|
_appendCircle(shape, position.x, position.y, size.x * 0.5f, size.y * 0.5f, ctx->transform, ellipse->clockwise);
|
||||||
|
_repeat(parent, shape, ctx);
|
||||||
} else {
|
} else {
|
||||||
_draw(parent, ctx);
|
_draw(parent, ellipse, ctx);
|
||||||
_appendCircle(ctx->merging, position.x, position.y, size.x * 0.5f, size.y * 0.5f, ctx->transform, ellipse->clockwise);
|
_appendCircle(ctx->merging, position.x, position.y, size.x * 0.5f, size.y * 0.5f, ctx->transform, ellipse->clockwise);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -613,11 +619,12 @@ static void _updatePath(LottieGroup* parent, LottieObject** child, float frameNo
|
||||||
auto path = static_cast<LottiePath*>(*child);
|
auto path = static_cast<LottiePath*>(*child);
|
||||||
|
|
||||||
if (!ctx->repeaters.empty()) {
|
if (!ctx->repeaters.empty()) {
|
||||||
auto p = Shape::gen();
|
auto shape = path->pooling();
|
||||||
path->pathset(frameNo, P(p)->rs.path.cmds, P(p)->rs.path.pts, ctx->transform, ctx->roundness, exps);
|
shape->reset();
|
||||||
_repeat(parent, std::move(p), ctx);
|
path->pathset(frameNo, P(shape)->rs.path.cmds, P(shape)->rs.path.pts, ctx->transform, ctx->roundness, exps);
|
||||||
|
_repeat(parent, shape, ctx);
|
||||||
} else {
|
} else {
|
||||||
_draw(parent, ctx);
|
_draw(parent, path, ctx);
|
||||||
if (path->pathset(frameNo, P(ctx->merging)->rs.path.cmds, P(ctx->merging)->rs.path.pts, ctx->transform, ctx->roundness, exps)) {
|
if (path->pathset(frameNo, P(ctx->merging)->rs.path.cmds, P(ctx->merging)->rs.path.pts, ctx->transform, ctx->roundness, exps)) {
|
||||||
P(ctx->merging)->update(RenderUpdateFlag::Path);
|
P(ctx->merging)->update(RenderUpdateFlag::Path);
|
||||||
}
|
}
|
||||||
|
@ -708,8 +715,14 @@ static void _updateStar(LottieGroup* parent, LottiePolyStar* star, Matrix* trans
|
||||||
auto direction = star->clockwise ? 1.0f : -1.0f;
|
auto direction = star->clockwise ? 1.0f : -1.0f;
|
||||||
auto hasRoundness = false;
|
auto hasRoundness = false;
|
||||||
bool roundedCorner = (roundness > ROUNDNESS_EPSILON) && (mathZero(innerRoundness) || mathZero(outerRoundness));
|
bool roundedCorner = (roundness > ROUNDNESS_EPSILON) && (mathZero(innerRoundness) || mathZero(outerRoundness));
|
||||||
//TODO: we can use PathCommand / PathCoord directly.
|
|
||||||
auto shape = roundedCorner ? Shape::gen().release() : merging;
|
Shape* shape;
|
||||||
|
if (roundedCorner) {
|
||||||
|
shape = star->pooling();
|
||||||
|
shape->reset();
|
||||||
|
} else {
|
||||||
|
shape = merging;
|
||||||
|
}
|
||||||
|
|
||||||
float x, y;
|
float x, y;
|
||||||
|
|
||||||
|
@ -798,10 +811,7 @@ static void _updateStar(LottieGroup* parent, LottiePolyStar* star, Matrix* trans
|
||||||
}
|
}
|
||||||
shape->close();
|
shape->close();
|
||||||
|
|
||||||
if (roundedCorner) {
|
if (roundedCorner) _applyRoundedCorner(shape, merging, outerRoundness, roundness, hasRoundness);
|
||||||
_applyRoundedCorner(shape, merging, outerRoundness, roundness, hasRoundness);
|
|
||||||
delete(shape);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -876,7 +886,7 @@ static void _updatePolygon(LottieGroup* parent, LottiePolyStar* star, Matrix* tr
|
||||||
|
|
||||||
static void _updatePolystar(LottieGroup* parent, LottieObject** child, float frameNo, TVG_UNUSED Inlist<RenderContext>& contexts, RenderContext* ctx, LottieExpressions* exps)
|
static void _updatePolystar(LottieGroup* parent, LottieObject** child, float frameNo, TVG_UNUSED Inlist<RenderContext>& contexts, RenderContext* ctx, LottieExpressions* exps)
|
||||||
{
|
{
|
||||||
auto star= static_cast<LottiePolyStar*>(*child);
|
auto star = static_cast<LottiePolyStar*>(*child);
|
||||||
|
|
||||||
//Optimize: Can we skip the individual coords transform?
|
//Optimize: Can we skip the individual coords transform?
|
||||||
Matrix matrix;
|
Matrix matrix;
|
||||||
|
@ -890,12 +900,13 @@ static void _updatePolystar(LottieGroup* parent, LottieObject** child, float fra
|
||||||
auto identity = mathIdentity((const Matrix*)&matrix);
|
auto identity = mathIdentity((const Matrix*)&matrix);
|
||||||
|
|
||||||
if (!ctx->repeaters.empty()) {
|
if (!ctx->repeaters.empty()) {
|
||||||
auto p = Shape::gen();
|
auto shape = star->pooling();
|
||||||
if (star->type == LottiePolyStar::Star) _updateStar(parent, star, identity ? nullptr : &matrix, ctx->roundness, frameNo, p.get(), exps);
|
shape->reset();
|
||||||
else _updatePolygon(parent, star, identity ? nullptr : &matrix, frameNo, p.get(), exps);
|
if (star->type == LottiePolyStar::Star) _updateStar(parent, star, identity ? nullptr : &matrix, ctx->roundness, frameNo, shape, exps);
|
||||||
_repeat(parent, std::move(p), ctx);
|
else _updatePolygon(parent, star, identity ? nullptr : &matrix, frameNo, shape, exps);
|
||||||
|
_repeat(parent, shape, ctx);
|
||||||
} else {
|
} else {
|
||||||
_draw(parent, ctx);
|
_draw(parent, star, ctx);
|
||||||
if (star->type == LottiePolyStar::Star) _updateStar(parent, star, identity ? nullptr : &matrix, ctx->roundness, frameNo, ctx->merging, exps);
|
if (star->type == LottiePolyStar::Star) _updateStar(parent, star, identity ? nullptr : &matrix, ctx->roundness, frameNo, ctx->merging, exps);
|
||||||
else _updatePolygon(parent, star, identity ? nullptr : &matrix, frameNo, ctx->merging, exps);
|
else _updatePolygon(parent, star, identity ? nullptr : &matrix, frameNo, ctx->merging, exps);
|
||||||
P(ctx->merging)->update(RenderUpdateFlag::Path);
|
P(ctx->merging)->update(RenderUpdateFlag::Path);
|
||||||
|
|
|
@ -37,6 +37,16 @@
|
||||||
/* External Class Implementation */
|
/* External Class Implementation */
|
||||||
/************************************************************************/
|
/************************************************************************/
|
||||||
|
|
||||||
|
void LottieShape::prepare(LottieObject::Type type)
|
||||||
|
{
|
||||||
|
LottieObject::type = type;
|
||||||
|
|
||||||
|
auto shape = tvg::Shape::gen().release();
|
||||||
|
PP(shape)->ref();
|
||||||
|
pooler.push(shape);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
void LottieSlot::reset()
|
void LottieSlot::reset()
|
||||||
{
|
{
|
||||||
if (!overriden) return;
|
if (!overriden) return;
|
||||||
|
|
|
@ -266,15 +266,18 @@ struct LottieTrimpath : LottieObject
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
struct LottieShape : LottieObject
|
struct LottieShape : LottieObject, LottieRenderPooler<tvg::Shape>
|
||||||
{
|
{
|
||||||
virtual ~LottieShape() {}
|
|
||||||
bool clockwise = true; //clockwise or counter-clockwise
|
bool clockwise = true; //clockwise or counter-clockwise
|
||||||
|
|
||||||
|
virtual ~LottieShape() {}
|
||||||
|
|
||||||
bool mergeable() override
|
bool mergeable() override
|
||||||
{
|
{
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void prepare(LottieObject::Type type);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
@ -292,7 +295,7 @@ struct LottiePath : LottieShape
|
||||||
{
|
{
|
||||||
void prepare()
|
void prepare()
|
||||||
{
|
{
|
||||||
LottieObject::type = LottieObject::Path;
|
LottieShape::prepare(LottieObject::Path);
|
||||||
}
|
}
|
||||||
|
|
||||||
LottiePathSet pathset;
|
LottiePathSet pathset;
|
||||||
|
@ -303,7 +306,7 @@ struct LottieRect : LottieShape
|
||||||
{
|
{
|
||||||
void prepare()
|
void prepare()
|
||||||
{
|
{
|
||||||
LottieObject::type = LottieObject::Rect;
|
LottieShape::prepare(LottieObject::Rect);
|
||||||
}
|
}
|
||||||
|
|
||||||
LottiePosition position = Point{0.0f, 0.0f};
|
LottiePosition position = Point{0.0f, 0.0f};
|
||||||
|
@ -318,7 +321,7 @@ struct LottiePolyStar : LottieShape
|
||||||
|
|
||||||
void prepare()
|
void prepare()
|
||||||
{
|
{
|
||||||
LottieObject::type = LottieObject::Polystar;
|
LottieShape::prepare(LottieObject::Polystar);
|
||||||
}
|
}
|
||||||
|
|
||||||
LottiePosition position = Point{0.0f, 0.0f};
|
LottiePosition position = Point{0.0f, 0.0f};
|
||||||
|
@ -336,7 +339,7 @@ struct LottieEllipse : LottieShape
|
||||||
{
|
{
|
||||||
void prepare()
|
void prepare()
|
||||||
{
|
{
|
||||||
LottieObject::type = LottieObject::Ellipse;
|
LottieShape::prepare(LottieObject::Ellipse);
|
||||||
}
|
}
|
||||||
|
|
||||||
LottiePosition position = Point{0.0f, 0.0f};
|
LottiePosition position = Point{0.0f, 0.0f};
|
||||||
|
@ -552,7 +555,7 @@ struct LottieGroup : LottieObject
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
Scene* scene = nullptr; //tvg render data
|
Scene* scene = nullptr;
|
||||||
Array<LottieObject*> children;
|
Array<LottieObject*> children;
|
||||||
|
|
||||||
bool reqFragment = false; //requirment to fragment the render context
|
bool reqFragment = false; //requirment to fragment the render context
|
||||||
|
|
|
@ -158,16 +158,22 @@ Iterator* Paint::Impl::iterator()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
Paint* Paint::Impl::duplicate()
|
Paint* Paint::Impl::duplicate(Paint* ret)
|
||||||
{
|
{
|
||||||
Paint* ret;
|
if (ret) ret->composite(nullptr, CompositeMethod::None);
|
||||||
PAINT_METHOD(ret, duplicate());
|
|
||||||
|
PAINT_METHOD(ret, duplicate(ret));
|
||||||
|
|
||||||
//duplicate Transform
|
//duplicate Transform
|
||||||
if (rTransform) {
|
if (rTransform) {
|
||||||
ret->pImpl->rTransform = new RenderTransform();
|
if (!ret->pImpl->rTransform) {
|
||||||
|
ret->pImpl->rTransform = new RenderTransform();
|
||||||
|
}
|
||||||
*ret->pImpl->rTransform = *rTransform;
|
*ret->pImpl->rTransform = *rTransform;
|
||||||
ret->pImpl->renderFlag |= RenderUpdateFlag::Transform;
|
ret->pImpl->renderFlag |= RenderUpdateFlag::Transform;
|
||||||
|
} else {
|
||||||
|
delete(ret->pImpl->rTransform);
|
||||||
|
ret->pImpl->rTransform = nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
ret->pImpl->opacity = opacity;
|
ret->pImpl->opacity = opacity;
|
||||||
|
|
|
@ -138,7 +138,7 @@ namespace tvg
|
||||||
bool bounds(float* x, float* y, float* w, float* h, bool transformed, bool stroking);
|
bool bounds(float* x, float* y, float* w, float* h, bool transformed, bool stroking);
|
||||||
RenderData update(RenderMethod* renderer, const RenderTransform* pTransform, Array<RenderData>& clips, uint8_t opacity, RenderUpdateFlag pFlag, bool clipper = false);
|
RenderData update(RenderMethod* renderer, const RenderTransform* pTransform, Array<RenderData>& clips, uint8_t opacity, RenderUpdateFlag pFlag, bool clipper = false);
|
||||||
bool render(RenderMethod* renderer);
|
bool render(RenderMethod* renderer);
|
||||||
Paint* duplicate();
|
Paint* duplicate(Paint* ret = nullptr);
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -189,19 +189,21 @@ struct Picture::Impl
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Paint* duplicate()
|
Paint* duplicate(Paint* ret)
|
||||||
{
|
{
|
||||||
|
if (ret) TVGERR("RENDERER", "TODO: duplicate()");
|
||||||
|
|
||||||
load();
|
load();
|
||||||
|
|
||||||
auto ret = Picture::gen().release();
|
auto picture = Picture::gen().release();
|
||||||
auto dup = ret->pImpl;
|
auto dup = picture->pImpl;
|
||||||
|
|
||||||
if (paint) dup->paint = paint->duplicate();
|
if (paint) dup->paint = paint->duplicate();
|
||||||
|
|
||||||
if (loader) {
|
if (loader) {
|
||||||
dup->loader = loader;
|
dup->loader = loader;
|
||||||
++dup->loader->sharing;
|
++dup->loader->sharing;
|
||||||
PP(ret)->renderFlag |= RenderUpdateFlag::Image;
|
PP(picture)->renderFlag |= RenderUpdateFlag::Image;
|
||||||
}
|
}
|
||||||
|
|
||||||
dup->surface = surface;
|
dup->surface = surface;
|
||||||
|
@ -215,7 +217,7 @@ struct Picture::Impl
|
||||||
memcpy(dup->rm.triangles, rm.triangles, sizeof(Polygon) * rm.triangleCnt);
|
memcpy(dup->rm.triangles, rm.triangles, sizeof(Polygon) * rm.triangleCnt);
|
||||||
}
|
}
|
||||||
|
|
||||||
return ret;
|
return picture;
|
||||||
}
|
}
|
||||||
|
|
||||||
Iterator* iterator()
|
Iterator* iterator()
|
||||||
|
|
|
@ -147,6 +147,31 @@ struct RenderStroke
|
||||||
bool simultaneous = true;
|
bool simultaneous = true;
|
||||||
} trim;
|
} trim;
|
||||||
|
|
||||||
|
void operator=(const RenderStroke& rhs)
|
||||||
|
{
|
||||||
|
width = rhs.width;
|
||||||
|
|
||||||
|
memcpy(color, rhs.color, sizeof(color));
|
||||||
|
|
||||||
|
delete(fill);
|
||||||
|
if (rhs.fill) fill = rhs.fill->duplicate();
|
||||||
|
else fill = nullptr;
|
||||||
|
|
||||||
|
free(dashPattern);
|
||||||
|
if (rhs.dashCnt > 0) {
|
||||||
|
dashPattern = static_cast<float*>(malloc(sizeof(float) * rhs.dashCnt));
|
||||||
|
memcpy(dashPattern, rhs.dashPattern, sizeof(float) * rhs.dashCnt);
|
||||||
|
} else {
|
||||||
|
dashPattern = nullptr;
|
||||||
|
}
|
||||||
|
dashCnt = rhs.dashCnt;
|
||||||
|
miterlimit = rhs.miterlimit;
|
||||||
|
cap = rhs.cap;
|
||||||
|
join = rhs.join;
|
||||||
|
strokeFirst = rhs.strokeFirst;
|
||||||
|
trim = rhs.trim;
|
||||||
|
}
|
||||||
|
|
||||||
~RenderStroke()
|
~RenderStroke()
|
||||||
{
|
{
|
||||||
free(dashPattern);
|
free(dashPattern);
|
||||||
|
|
|
@ -198,10 +198,12 @@ struct Scene::Impl
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
Paint* duplicate()
|
Paint* duplicate(Paint* ret)
|
||||||
{
|
{
|
||||||
auto ret = Scene::gen().release();
|
if (ret) TVGERR("RENDERER", "TODO: duplicate()");
|
||||||
auto dup = ret->pImpl;
|
|
||||||
|
auto scene = Scene::gen().release();
|
||||||
|
auto dup = scene->pImpl;
|
||||||
|
|
||||||
for (auto paint : paints) {
|
for (auto paint : paints) {
|
||||||
auto cdup = paint->duplicate();
|
auto cdup = paint->duplicate();
|
||||||
|
@ -209,7 +211,7 @@ struct Scene::Impl
|
||||||
dup->paints.push_back(cdup);
|
dup->paints.push_back(cdup);
|
||||||
}
|
}
|
||||||
|
|
||||||
return ret;
|
return scene;
|
||||||
}
|
}
|
||||||
|
|
||||||
void clear(bool free)
|
void clear(bool free)
|
||||||
|
|
|
@ -34,6 +34,7 @@ struct Shape::Impl
|
||||||
RenderData rd = nullptr; //engine data
|
RenderData rd = nullptr; //engine data
|
||||||
Shape* shape;
|
Shape* shape;
|
||||||
uint8_t flag = RenderUpdateFlag::None;
|
uint8_t flag = RenderUpdateFlag::None;
|
||||||
|
|
||||||
uint8_t opacity; //for composition
|
uint8_t opacity; //for composition
|
||||||
bool needComp = false; //composite or not
|
bool needComp = false; //composite or not
|
||||||
|
|
||||||
|
@ -359,47 +360,40 @@ struct Shape::Impl
|
||||||
this->flag |= flag;
|
this->flag |= flag;
|
||||||
}
|
}
|
||||||
|
|
||||||
Paint* duplicate()
|
Paint* duplicate(Paint* ret)
|
||||||
{
|
{
|
||||||
auto ret = Shape::gen().release();
|
auto shape = static_cast<Shape*>(ret);
|
||||||
auto dup = ret->pImpl;
|
if (shape) shape->reset();
|
||||||
|
else shape = Shape::gen().release();
|
||||||
|
|
||||||
|
auto dup = shape->pImpl;
|
||||||
|
delete(dup->rs.fill);
|
||||||
|
|
||||||
|
//Default Properties
|
||||||
|
dup->flag = RenderUpdateFlag::All;
|
||||||
dup->rs.rule = rs.rule;
|
dup->rs.rule = rs.rule;
|
||||||
|
|
||||||
//Color
|
//Color
|
||||||
memcpy(dup->rs.color, rs.color, sizeof(rs.color));
|
memcpy(dup->rs.color, rs.color, sizeof(rs.color));
|
||||||
dup->flag = RenderUpdateFlag::Color;
|
|
||||||
|
|
||||||
//Path
|
//Path
|
||||||
if (rs.path.cmds.count > 0 && rs.path.pts.count > 0) {
|
dup->rs.path.cmds.push(rs.path.cmds);
|
||||||
dup->rs.path.cmds = rs.path.cmds;
|
dup->rs.path.pts.push(rs.path.pts);
|
||||||
dup->rs.path.pts = rs.path.pts;
|
|
||||||
dup->flag |= RenderUpdateFlag::Path;
|
|
||||||
}
|
|
||||||
|
|
||||||
//Stroke
|
//Stroke
|
||||||
if (rs.stroke) {
|
if (rs.stroke) {
|
||||||
dup->rs.stroke = new RenderStroke();
|
if (!dup->rs.stroke) dup->rs.stroke = new RenderStroke;
|
||||||
*dup->rs.stroke = *rs.stroke;
|
*dup->rs.stroke = *rs.stroke;
|
||||||
memcpy(dup->rs.stroke->color, rs.stroke->color, sizeof(rs.stroke->color));
|
} else {
|
||||||
if (rs.stroke->dashCnt > 0) {
|
delete(dup->rs.stroke);
|
||||||
dup->rs.stroke->dashPattern = static_cast<float*>(malloc(sizeof(float) * rs.stroke->dashCnt));
|
dup->rs.stroke = nullptr;
|
||||||
memcpy(dup->rs.stroke->dashPattern, rs.stroke->dashPattern, sizeof(float) * rs.stroke->dashCnt);
|
|
||||||
}
|
|
||||||
if (rs.stroke->fill) {
|
|
||||||
dup->rs.stroke->fill = rs.stroke->fill->duplicate();
|
|
||||||
dup->flag |= RenderUpdateFlag::GradientStroke;
|
|
||||||
}
|
|
||||||
dup->flag |= RenderUpdateFlag::Stroke;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//Fill
|
//Fill
|
||||||
if (rs.fill) {
|
if (rs.fill) dup->rs.fill = rs.fill->duplicate();
|
||||||
dup->rs.fill = rs.fill->duplicate();
|
else dup->rs.fill = nullptr;
|
||||||
dup->flag |= RenderUpdateFlag::Gradient;
|
|
||||||
}
|
|
||||||
|
|
||||||
return ret;
|
return shape;
|
||||||
}
|
}
|
||||||
|
|
||||||
Iterator* iterator()
|
Iterator* iterator()
|
||||||
|
|
|
@ -152,12 +152,14 @@ struct Text::Impl
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
Paint* duplicate()
|
Paint* duplicate(Paint* ret)
|
||||||
{
|
{
|
||||||
|
if (ret) TVGERR("RENDERER", "TODO: duplicate()");
|
||||||
|
|
||||||
load();
|
load();
|
||||||
|
|
||||||
auto ret = Text::gen().release();
|
auto text = Text::gen().release();
|
||||||
auto dup = ret->pImpl;
|
auto dup = text->pImpl;
|
||||||
if (paint) dup->paint = static_cast<Shape*>(paint->duplicate());
|
if (paint) dup->paint = static_cast<Shape*>(paint->duplicate());
|
||||||
|
|
||||||
if (loader) {
|
if (loader) {
|
||||||
|
@ -169,7 +171,7 @@ struct Text::Impl
|
||||||
dup->italic = italic;
|
dup->italic = italic;
|
||||||
dup->fontSize = fontSize;
|
dup->fontSize = fontSize;
|
||||||
|
|
||||||
return ret;
|
return text;
|
||||||
}
|
}
|
||||||
|
|
||||||
Iterator* iterator()
|
Iterator* iterator()
|
||||||
|
|
Loading…
Add table
Reference in a new issue