mirror of
https://github.com/thorvg/thorvg.git
synced 2025-06-08 05:33:36 +00:00
lottie/builder: revise the layer build logic
Promote the text/image updates to the layer level.
This commit is contained in:
parent
309f7b6763
commit
9300de2d06
1 changed files with 118 additions and 126 deletions
|
@ -592,85 +592,6 @@ static void _updatePath(LottieGroup* parent, LottieObject** child, float frameNo
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
static void _updateText(LottieGroup* parent, LottieObject** child, float frameNo, TVG_UNUSED Inlist<RenderContext>& contexts, TVG_UNUSED RenderContext* ctx, LottieExpressions* exps)
|
|
||||||
{
|
|
||||||
auto text = static_cast<LottieText*>(*child);
|
|
||||||
auto& doc = text->doc(frameNo);
|
|
||||||
auto p = doc.text;
|
|
||||||
|
|
||||||
if (!p || !text->font) return;
|
|
||||||
|
|
||||||
auto scale = doc.size * 0.01f;
|
|
||||||
float spacing = text->spacing(frameNo) / scale;
|
|
||||||
Point cursor = {0.0f, 0.0f};
|
|
||||||
auto scene = Scene::gen();
|
|
||||||
int line = 0;
|
|
||||||
|
|
||||||
//text string
|
|
||||||
while (true) {
|
|
||||||
//TODO: remove nested scenes.
|
|
||||||
//end of text, new line of the cursor position
|
|
||||||
if (*p == 13 || *p == 3 || *p == '\0') {
|
|
||||||
//text layout position
|
|
||||||
auto ascent = text->font->ascent * scale;
|
|
||||||
if (ascent > doc.bbox.size.y) ascent = doc.bbox.size.y;
|
|
||||||
Point layout = {doc.bbox.pos.x, doc.bbox.pos.y + ascent - doc.shift};
|
|
||||||
|
|
||||||
//adjust the layout
|
|
||||||
if (doc.justify == 1) layout.x += doc.bbox.size.x - (cursor.x * scale); //right aligned
|
|
||||||
else if (doc.justify == 2) layout.x += (doc.bbox.size.x * 0.5f) - (cursor.x * 0.5f * scale); //center aligned
|
|
||||||
|
|
||||||
scene->translate(layout.x, layout.y);
|
|
||||||
scene->scale(scale);
|
|
||||||
|
|
||||||
parent->scene->push(std::move(scene));
|
|
||||||
|
|
||||||
if (*p == '\0') break;
|
|
||||||
++p;
|
|
||||||
|
|
||||||
//new text group, single scene for each line
|
|
||||||
scene = Scene::gen();
|
|
||||||
cursor.x = 0.0f;
|
|
||||||
cursor.y = ++line * (doc.height / scale);
|
|
||||||
}
|
|
||||||
|
|
||||||
//find the glyph
|
|
||||||
for (auto g = text->font->chars.begin(); g < text->font->chars.end(); ++g) {
|
|
||||||
auto glyph = *g;
|
|
||||||
//draw matched glyphs
|
|
||||||
if (!strncmp(glyph->code, p, glyph->len)) {
|
|
||||||
//TODO: caching?
|
|
||||||
auto shape = Shape::gen();
|
|
||||||
for (auto g = glyph->children.begin(); g < glyph->children.end(); ++g) {
|
|
||||||
auto group = static_cast<LottieGroup*>(*g);
|
|
||||||
for (auto p = group->children.begin(); p < group->children.end(); ++p) {
|
|
||||||
if (static_cast<LottiePath*>(*p)->pathset(frameNo, P(shape)->rs.path.cmds, P(shape)->rs.path.pts, nullptr, 0.0f, exps)) {
|
|
||||||
P(shape)->update(RenderUpdateFlag::Path);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
shape->fill(doc.color.rgb[0], doc.color.rgb[1], doc.color.rgb[2]);
|
|
||||||
shape->translate(cursor.x, cursor.y);
|
|
||||||
|
|
||||||
if (doc.stroke.render) {
|
|
||||||
shape->strokeJoin(StrokeJoin::Round);
|
|
||||||
shape->strokeWidth(doc.stroke.width / scale);
|
|
||||||
shape->strokeFill(doc.stroke.color.rgb[0], doc.stroke.color.rgb[1], doc.stroke.color.rgb[2]);
|
|
||||||
}
|
|
||||||
|
|
||||||
scene->push(std::move(shape));
|
|
||||||
|
|
||||||
p += glyph->len;
|
|
||||||
|
|
||||||
//advance the cursor position horizontally
|
|
||||||
cursor.x += glyph->width + spacing + doc.tracking;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
static void _applyRoundedCorner(Shape* star, Shape* merging, float outerRoundness, float roundness, bool hasRoundness)
|
static void _applyRoundedCorner(Shape* star, Shape* merging, float outerRoundness, float roundness, bool hasRoundness)
|
||||||
{
|
{
|
||||||
static constexpr auto ROUNDED_POLYSTAR_MAGIC_NUMBER = 0.47829f;
|
static constexpr auto ROUNDED_POLYSTAR_MAGIC_NUMBER = 0.47829f;
|
||||||
|
@ -949,45 +870,6 @@ static void _updatePolystar(LottieGroup* parent, LottieObject** child, float fra
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
static void _updateImage(LottieGroup* parent, LottieObject** child, float frameNo, TVG_UNUSED Inlist<RenderContext>& contexts, RenderContext* ctx)
|
|
||||||
{
|
|
||||||
auto image = static_cast<LottieImage*>(*child);
|
|
||||||
auto picture = image->picture;
|
|
||||||
|
|
||||||
if (!picture) {
|
|
||||||
picture = Picture::gen().release();
|
|
||||||
|
|
||||||
//force to load a picture on the same thread
|
|
||||||
TaskScheduler::async(false);
|
|
||||||
|
|
||||||
if (image->size > 0) {
|
|
||||||
if (picture->load((const char*)image->b64Data, image->size, image->mimeType) != Result::Success) {
|
|
||||||
delete(picture);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if (picture->load(image->path) != Result::Success) {
|
|
||||||
delete(picture);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
TaskScheduler::async(true);
|
|
||||||
|
|
||||||
image->picture = picture;
|
|
||||||
PP(picture)->ref();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (ctx->propagator) {
|
|
||||||
if (auto matrix = PP(ctx->propagator)->transform()) {
|
|
||||||
picture->transform(*matrix);
|
|
||||||
}
|
|
||||||
picture->opacity(PP(ctx->propagator)->opacity);
|
|
||||||
}
|
|
||||||
parent->scene->push(cast<Picture>(picture));
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
static void _updateRoundedCorner(TVG_UNUSED LottieGroup* parent, LottieObject** child, float frameNo, TVG_UNUSED Inlist<RenderContext>& contexts, RenderContext* ctx, LottieExpressions* exps)
|
static void _updateRoundedCorner(TVG_UNUSED LottieGroup* parent, LottieObject** child, float frameNo, TVG_UNUSED Inlist<RenderContext>& contexts, RenderContext* ctx, LottieExpressions* exps)
|
||||||
{
|
{
|
||||||
auto roundedCorner= static_cast<LottieRoundedCorner*>(*child);
|
auto roundedCorner= static_cast<LottieRoundedCorner*>(*child);
|
||||||
|
@ -1088,18 +970,10 @@ static void _updateChildren(LottieGroup* parent, float frameNo, Inlist<RenderCon
|
||||||
_updatePolystar(parent, child, frameNo, contexts, ctx, exps);
|
_updatePolystar(parent, child, frameNo, contexts, ctx, exps);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case LottieObject::Image: {
|
|
||||||
_updateImage(parent, child, frameNo, contexts, ctx);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case LottieObject::Trimpath: {
|
case LottieObject::Trimpath: {
|
||||||
_updateTrimpath(parent, child, frameNo, contexts, ctx, exps);
|
_updateTrimpath(parent, child, frameNo, contexts, ctx, exps);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case LottieObject::Text: {
|
|
||||||
_updateText(parent, child, frameNo, contexts, ctx, exps);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case LottieObject::Repeater: {
|
case LottieObject::Repeater: {
|
||||||
_updateRepeater(parent, child, frameNo, contexts, ctx, exps);
|
_updateRepeater(parent, child, frameNo, contexts, ctx, exps);
|
||||||
break;
|
break;
|
||||||
|
@ -1151,6 +1025,116 @@ static void _updateSolid(LottieLayer* layer)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static void _updateImage(LottieGroup* layer)
|
||||||
|
{
|
||||||
|
auto image = static_cast<LottieImage*>(layer->children.first());
|
||||||
|
auto picture = image->picture;
|
||||||
|
|
||||||
|
if (!picture) {
|
||||||
|
picture = Picture::gen().release();
|
||||||
|
|
||||||
|
//force to load a picture on the same thread
|
||||||
|
TaskScheduler::async(false);
|
||||||
|
|
||||||
|
if (image->size > 0) {
|
||||||
|
if (picture->load((const char*)image->b64Data, image->size, image->mimeType) != Result::Success) {
|
||||||
|
delete(picture);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (picture->load(image->path) != Result::Success) {
|
||||||
|
delete(picture);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
TaskScheduler::async(true);
|
||||||
|
|
||||||
|
image->picture = picture;
|
||||||
|
PP(picture)->ref();
|
||||||
|
}
|
||||||
|
layer->scene->push(cast<Picture>(picture));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static void _updateText(LottieLayer* layer, float frameNo)
|
||||||
|
{
|
||||||
|
auto text = static_cast<LottieText*>(layer->children.first());
|
||||||
|
auto& doc = text->doc(frameNo);
|
||||||
|
auto p = doc.text;
|
||||||
|
|
||||||
|
if (!p || !text->font) return;
|
||||||
|
|
||||||
|
auto scale = doc.size * 0.01f;
|
||||||
|
float spacing = text->spacing(frameNo) / scale;
|
||||||
|
Point cursor = {0.0f, 0.0f};
|
||||||
|
auto scene = Scene::gen();
|
||||||
|
int line = 0;
|
||||||
|
|
||||||
|
//text string
|
||||||
|
while (true) {
|
||||||
|
//TODO: remove nested scenes.
|
||||||
|
//end of text, new line of the cursor position
|
||||||
|
if (*p == 13 || *p == 3 || *p == '\0') {
|
||||||
|
//text layout position
|
||||||
|
auto ascent = text->font->ascent * scale;
|
||||||
|
if (ascent > doc.bbox.size.y) ascent = doc.bbox.size.y;
|
||||||
|
Point layout = {doc.bbox.pos.x, doc.bbox.pos.y + ascent - doc.shift};
|
||||||
|
|
||||||
|
//adjust the layout
|
||||||
|
if (doc.justify == 1) layout.x += doc.bbox.size.x - (cursor.x * scale); //right aligned
|
||||||
|
else if (doc.justify == 2) layout.x += (doc.bbox.size.x * 0.5f) - (cursor.x * 0.5f * scale); //center aligned
|
||||||
|
|
||||||
|
scene->translate(layout.x, layout.y);
|
||||||
|
scene->scale(scale);
|
||||||
|
|
||||||
|
layer->scene->push(std::move(scene));
|
||||||
|
|
||||||
|
if (*p == '\0') break;
|
||||||
|
++p;
|
||||||
|
|
||||||
|
//new text group, single scene for each line
|
||||||
|
scene = Scene::gen();
|
||||||
|
cursor.x = 0.0f;
|
||||||
|
cursor.y = ++line * (doc.height / scale);
|
||||||
|
}
|
||||||
|
//find the glyph
|
||||||
|
for (auto g = text->font->chars.begin(); g < text->font->chars.end(); ++g) {
|
||||||
|
auto glyph = *g;
|
||||||
|
//draw matched glyphs
|
||||||
|
if (!strncmp(glyph->code, p, glyph->len)) {
|
||||||
|
//TODO: caching?
|
||||||
|
auto shape = Shape::gen();
|
||||||
|
for (auto g = glyph->children.begin(); g < glyph->children.end(); ++g) {
|
||||||
|
auto group = static_cast<LottieGroup*>(*g);
|
||||||
|
for (auto p = group->children.begin(); p < group->children.end(); ++p) {
|
||||||
|
if (static_cast<LottiePath*>(*p)->pathset(frameNo, P(shape)->rs.path.cmds, P(shape)->rs.path.pts, nullptr, 0.0f)) {
|
||||||
|
P(shape)->update(RenderUpdateFlag::Path);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
shape->fill(doc.color.rgb[0], doc.color.rgb[1], doc.color.rgb[2]);
|
||||||
|
shape->translate(cursor.x, cursor.y);
|
||||||
|
|
||||||
|
if (doc.stroke.render) {
|
||||||
|
shape->strokeJoin(StrokeJoin::Round);
|
||||||
|
shape->strokeWidth(doc.stroke.width / scale);
|
||||||
|
shape->strokeFill(doc.stroke.color.rgb[0], doc.stroke.color.rgb[1], doc.stroke.color.rgb[2]);
|
||||||
|
}
|
||||||
|
|
||||||
|
scene->push(std::move(shape));
|
||||||
|
|
||||||
|
p += glyph->len;
|
||||||
|
|
||||||
|
//advance the cursor position horizontally
|
||||||
|
cursor.x += glyph->width + spacing + doc.tracking;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
static void _updateMaskings(LottieLayer* layer, float frameNo, LottieExpressions* exps)
|
static void _updateMaskings(LottieLayer* layer, float frameNo, LottieExpressions* exps)
|
||||||
{
|
{
|
||||||
if (layer->masks.count == 0) return;
|
if (layer->masks.count == 0) return;
|
||||||
|
@ -1243,6 +1227,14 @@ static void _updateLayer(LottieLayer* root, LottieLayer* layer, float frameNo, L
|
||||||
_updateSolid(layer);
|
_updateSolid(layer);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
case LottieLayer::Image: {
|
||||||
|
_updateImage(layer);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case LottieLayer::Text: {
|
||||||
|
_updateText(layer, frameNo);
|
||||||
|
break;
|
||||||
|
}
|
||||||
default: {
|
default: {
|
||||||
if (!layer->children.empty()) {
|
if (!layer->children.empty()) {
|
||||||
Inlist<RenderContext> contexts;
|
Inlist<RenderContext> contexts;
|
||||||
|
|
Loading…
Add table
Reference in a new issue