mirror of
https://github.com/thorvg/thorvg.git
synced 2025-06-08 05:33:36 +00:00
lottie: Newly added support for the text feature.
This enhancement enables embedded glyphs rendering. The 'fonts' and 'chars' properties are now supported.
This commit is contained in:
parent
9bf8bb018d
commit
9109a62819
12 changed files with 399 additions and 17 deletions
|
@ -256,8 +256,7 @@ ThorVG aims to fully support Lottie Animation features. Lottie is a JSON-based v
|
|||
Currently, ThorVG provides experimental support for Lottie Animation, and while most features are supported, a few advanced properties of Lottie may not be available yet:
|
||||
<br />
|
||||
|
||||
- Texts
|
||||
- Shape Modifiers (Pucker/Bloat, Twist, Merge, ZigZag)
|
||||
- Shape Modifiers
|
||||
- Layer Effects
|
||||
- Expressions
|
||||
|
||||
|
|
File diff suppressed because one or more lines are too long
1
src/examples/resources/text.json
Normal file
1
src/examples/resources/text.json
Normal file
File diff suppressed because one or more lines are too long
1
src/examples/resources/text2.json
Normal file
1
src/examples/resources/text2.json
Normal file
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
|
@ -418,7 +418,7 @@ static void _updateEllipse(LottieGroup* parent, LottieObject** child, float fram
|
|||
|
||||
static void _updatePath(LottieGroup* parent, LottieObject** child, float frameNo, TVG_UNUSED Inlist<RenderContext>& contexts, RenderContext* ctx)
|
||||
{
|
||||
auto path= static_cast<LottiePath*>(*child);
|
||||
auto path = static_cast<LottiePath*>(*child);
|
||||
|
||||
if (ctx->repeater) {
|
||||
auto p = Shape::gen();
|
||||
|
@ -439,6 +439,77 @@ 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)
|
||||
{
|
||||
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();
|
||||
|
||||
//text string
|
||||
while (*p != '\0') {
|
||||
//find the glyph
|
||||
for (auto g = text->font->chars.data; 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.data; g < glyph->children.end(); ++g) {
|
||||
auto group = static_cast<LottieGroup*>(*g);
|
||||
for (auto p = group->children.data; p < group->children.end(); ++p) {
|
||||
if (static_cast<LottiePath*>(*p)->pathset(frameNo, P(shape)->rs.path.cmds, P(shape)->rs.path.pts)) {
|
||||
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;
|
||||
|
||||
//end of text, new line of the cursor position
|
||||
if (*p == 13 || *p == 3) {
|
||||
cursor.x = 0.0f;
|
||||
cursor.y += (doc.height / scale);
|
||||
++p;
|
||||
//advance the cursor position horizontally
|
||||
} else {
|
||||
cursor.x += glyph->width + spacing;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//text layout position
|
||||
Point layout = {doc.bbox.pos.x, (doc.bbox.pos.y * 0.5f) - doc.shift};
|
||||
|
||||
//adjust the layout
|
||||
if (doc.justify == 1) layout.x -= (cursor.x * scale); //right aligned
|
||||
else if (doc.justify == 2) layout.x -= (cursor.x * 0.5f * scale); //center aligned
|
||||
|
||||
scene->translate(layout.x, layout.y);
|
||||
scene->scale(scale);
|
||||
|
||||
parent->scene->push(std::move(scene));
|
||||
}
|
||||
|
||||
|
||||
static void _updateStar(LottieGroup* parent, LottiePolyStar* star, Matrix* transform, float frameNo, Shape* merging)
|
||||
{
|
||||
static constexpr auto POLYSTAR_MAGIC_NUMBER = 0.47829f / 0.28f;
|
||||
|
@ -790,6 +861,10 @@ static void _updateChildren(LottieGroup* parent, float frameNo, Inlist<RenderCon
|
|||
_updateTrimpath(parent, child, frameNo, contexts, ctx);
|
||||
break;
|
||||
}
|
||||
case LottieObject::Text: {
|
||||
_updateText(parent, child, frameNo, contexts, ctx);
|
||||
break;
|
||||
}
|
||||
case LottieObject::Repeater: {
|
||||
_updateRepeater(parent, child, frameNo, contexts, ctx);
|
||||
break;
|
||||
|
@ -1028,6 +1103,26 @@ static void _checkFragment(LottieGroup* parent)
|
|||
}
|
||||
|
||||
|
||||
static void _attachFont(LottieComposition* comp, LottieLayer* parent)
|
||||
{
|
||||
//TODO: Consider to migrate this attachment to the frame update time.
|
||||
for (auto c = parent->children.data; c < parent->children.end(); ++c) {
|
||||
auto text = static_cast<LottieText*>(*c);
|
||||
auto& doc = text->doc(0);
|
||||
if (!doc.name) continue;
|
||||
auto len = strlen(doc.name);
|
||||
for (uint32_t i = 0; i < comp->fonts.count; ++i) {
|
||||
auto font = comp->fonts[i];
|
||||
auto len2 = strlen(font->name);
|
||||
if (!strncmp(font->name, doc.name, len < len2 ? len : len2)) {
|
||||
text->font = font;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static bool _buildComposition(LottieComposition* comp, LottieGroup* parent)
|
||||
{
|
||||
if (parent->children.count == 0) return false;
|
||||
|
@ -1049,6 +1144,9 @@ static bool _buildComposition(LottieComposition* comp, LottieGroup* parent)
|
|||
|
||||
_checkFragment(static_cast<LottieGroup*>(*c));
|
||||
|
||||
//attach the necessary font data
|
||||
if (child->type == LottieLayer::Text) _attachFont(comp, child);
|
||||
|
||||
child->statical &= parent->statical;
|
||||
parent->statical &= child->statical;
|
||||
}
|
||||
|
|
|
@ -203,4 +203,9 @@ LottieComposition::~LottieComposition()
|
|||
for (auto a = assets.data; a < assets.end(); ++a) {
|
||||
delete(*a);
|
||||
}
|
||||
|
||||
//delete fonts
|
||||
for (auto f = fonts.data; f < fonts.end(); ++f) {
|
||||
delete(*f);
|
||||
}
|
||||
}
|
|
@ -219,6 +219,7 @@ struct LottieObject
|
|||
Polystar,
|
||||
Image,
|
||||
Trimpath,
|
||||
Text,
|
||||
Repeater,
|
||||
RoundedCorner
|
||||
};
|
||||
|
@ -235,6 +236,61 @@ struct LottieObject
|
|||
};
|
||||
|
||||
|
||||
struct LottieGlyph
|
||||
{
|
||||
Array<LottieObject*> children; //glyph shapes.
|
||||
float width;
|
||||
char* code;
|
||||
uint16_t size;
|
||||
uint8_t len;
|
||||
|
||||
void prepare()
|
||||
{
|
||||
len = strlen(code);
|
||||
}
|
||||
|
||||
~LottieGlyph()
|
||||
{
|
||||
for (auto p = children.data; p < children.end(); ++p) delete(*p);
|
||||
free(code);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
struct LottieFont
|
||||
{
|
||||
enum Origin : uint8_t { Local = 0, CssURL, ScriptURL, FontURL, Embedded };
|
||||
|
||||
~LottieFont()
|
||||
{
|
||||
for (auto c = chars.data; c < chars.end(); ++c) delete(*c);
|
||||
free(style);
|
||||
free(family);
|
||||
free(name);
|
||||
}
|
||||
|
||||
Array<LottieGlyph*> chars;
|
||||
char* name = nullptr;
|
||||
char* family = nullptr;
|
||||
char* style = nullptr;
|
||||
float ascent = 0.0f;
|
||||
Origin origin = Embedded;
|
||||
};
|
||||
|
||||
|
||||
struct LottieText : LottieObject
|
||||
{
|
||||
void prepare()
|
||||
{
|
||||
LottieObject::type = LottieObject::Text;
|
||||
}
|
||||
|
||||
LottieTextDoc doc;
|
||||
LottieFont* font;
|
||||
LottieFloat spacing = 0.0f; //letter spacing
|
||||
};
|
||||
|
||||
|
||||
struct LottieTrimpath : LottieObject
|
||||
{
|
||||
enum Type : uint8_t { Individual = 1, Simultaneous = 2 };
|
||||
|
@ -561,6 +617,7 @@ struct LottieComposition
|
|||
float frameRate;
|
||||
Array<LottieObject*> assets;
|
||||
Array<LottieInterpolator*> interpolators;
|
||||
Array<LottieFont*> fonts;
|
||||
bool initiated = false;
|
||||
};
|
||||
|
||||
|
|
|
@ -153,6 +153,28 @@ StrokeJoin LottieParser::getStrokeJoin()
|
|||
}
|
||||
|
||||
|
||||
void LottieParser::getValue(TextDocument& doc)
|
||||
{
|
||||
enterObject();
|
||||
while (auto key = nextObjectKey()) {
|
||||
if (!strcmp(key, "s")) doc.size = getFloat();
|
||||
else if (!strcmp(key, "f")) doc.name = getStringCopy();
|
||||
else if (!strcmp(key, "t")) doc.text = getStringCopy();
|
||||
else if (!strcmp(key, "j")) doc.justify = getInt();
|
||||
else if (!strcmp(key, "tr")) doc.tracking = getInt();
|
||||
else if (!strcmp(key, "lh")) doc.height = getFloat();
|
||||
else if (!strcmp(key, "ls")) doc.shift = getFloat();
|
||||
else if (!strcmp(key, "fc")) getValue(doc.color);
|
||||
else if (!strcmp(key, "ps")) getValue(doc.bbox.pos);
|
||||
else if (!strcmp(key, "sz")) getValue(doc.bbox.size);
|
||||
else if (!strcmp(key, "sc")) getValue(doc.stroke.color);
|
||||
else if (!strcmp(key, "sw")) doc.stroke.width = getFloat();
|
||||
else if (!strcmp(key, "of")) doc.stroke.render = getBool();
|
||||
else skip(key);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void LottieParser::getValue(PathSet& path)
|
||||
{
|
||||
Array<Point> outs, ins, pts;
|
||||
|
@ -427,6 +449,8 @@ void LottieParser::parsePropertyInternal(T& prop)
|
|||
getValue(prop.value);
|
||||
//multi value property
|
||||
} else {
|
||||
//TODO: Here might be a single frame.
|
||||
//Can we figure out the frame number in advance?
|
||||
enterArray();
|
||||
while (nextArrayValue()) {
|
||||
//keyframes value
|
||||
|
@ -816,14 +840,14 @@ LottieObject* LottieParser::parseObject()
|
|||
}
|
||||
|
||||
|
||||
void LottieParser::parseObject(LottieGroup* parent)
|
||||
void LottieParser::parseObject(Array<LottieObject*>& parent)
|
||||
{
|
||||
enterObject();
|
||||
while (auto key = nextObjectKey()) {
|
||||
if (!strcmp(key, "ty")) {
|
||||
if (auto child = parseObject()) {
|
||||
if (child->hidden) delete(child);
|
||||
else parent->children.push(child);
|
||||
else parent.push(child);
|
||||
}
|
||||
} else skip(key);
|
||||
}
|
||||
|
@ -907,6 +931,24 @@ LottieObject* LottieParser::parseAsset()
|
|||
}
|
||||
|
||||
|
||||
LottieFont* LottieParser::parseFont()
|
||||
{
|
||||
enterObject();
|
||||
|
||||
auto font = new LottieFont;
|
||||
|
||||
while (auto key = nextObjectKey()) {
|
||||
if (!strcmp(key, "fName")) font->name = getStringCopy();
|
||||
else if (!strcmp(key, "fFamily")) font->family = getStringCopy();
|
||||
else if (!strcmp(key, "fStyle")) font->style = getStringCopy();
|
||||
else if (!strcmp(key, "ascent")) font->ascent = getFloat();
|
||||
else if (!strcmp(key, "origin")) font->origin = (LottieFont::Origin) getInt();
|
||||
else skip(key);
|
||||
}
|
||||
return font;
|
||||
}
|
||||
|
||||
|
||||
void LottieParser::parseAssets()
|
||||
{
|
||||
enterArray();
|
||||
|
@ -918,6 +960,58 @@ void LottieParser::parseAssets()
|
|||
}
|
||||
|
||||
|
||||
void LottieParser::parseChars()
|
||||
{
|
||||
if (comp->fonts.count == 0) {
|
||||
TVGERR("LOTTIE", "No font data?");
|
||||
return;
|
||||
}
|
||||
|
||||
enterArray();
|
||||
while (nextArrayValue()) {
|
||||
enterObject();
|
||||
//a new glyph
|
||||
auto glyph = new LottieGlyph;
|
||||
char* style = nullptr;
|
||||
char* family = nullptr;
|
||||
while (auto key = nextObjectKey()) {
|
||||
if (!strcmp("ch", key)) glyph->code = getStringCopy();
|
||||
else if (!strcmp("size", key)) glyph->size = static_cast<uint16_t>(getFloat());
|
||||
else if (!strcmp("style", key)) style = const_cast<char*>(getString());
|
||||
else if (!strcmp("w", key)) glyph->width = getFloat();
|
||||
else if (!strcmp("fFamily", key)) family = const_cast<char*>(getString());
|
||||
else if (!strcmp("data", key))
|
||||
{ //glyph shapes
|
||||
enterObject();
|
||||
while (auto key = nextObjectKey()) {
|
||||
if (!strcmp(key, "shapes")) parseShapes(glyph->children);
|
||||
}
|
||||
} else skip(key);
|
||||
}
|
||||
//aggregate the font characters
|
||||
for (uint32_t i = 0; i < comp->fonts.count; ++i) {
|
||||
auto& font = comp->fonts[i];
|
||||
if (!strcmp(font->family, family) && !strcmp(font->style, style)) font->chars.push(glyph);
|
||||
else TVGERR("LOTTIE", "No font data?");
|
||||
}
|
||||
glyph->prepare();
|
||||
}
|
||||
}
|
||||
|
||||
void LottieParser::parseFonts()
|
||||
{
|
||||
enterObject();
|
||||
while (auto key = nextObjectKey()) {
|
||||
if (!strcmp("list", key)) {
|
||||
enterArray();
|
||||
while (nextArrayValue()) {
|
||||
comp->fonts.push(parseFont());
|
||||
}
|
||||
} else skip(key);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
LottieObject* LottieParser::parseGroup()
|
||||
{
|
||||
auto group = new LottieGroup;
|
||||
|
@ -928,7 +1022,7 @@ LottieObject* LottieParser::parseGroup()
|
|||
group->name = getStringCopy();
|
||||
} else if (!strcmp(key, "it")) {
|
||||
enterArray();
|
||||
while (nextArrayValue()) parseObject(group);
|
||||
while (nextArrayValue()) parseObject(group->children);
|
||||
} else skip(key);
|
||||
}
|
||||
if (group->children.empty()) {
|
||||
|
@ -947,7 +1041,7 @@ void LottieParser::parseTimeRemap(LottieLayer* layer)
|
|||
}
|
||||
|
||||
|
||||
void LottieParser::parseShapes(LottieLayer* layer)
|
||||
void LottieParser::parseShapes(Array<LottieObject*>& parent)
|
||||
{
|
||||
enterArray();
|
||||
while (nextArrayValue()) {
|
||||
|
@ -955,11 +1049,11 @@ void LottieParser::parseShapes(LottieLayer* layer)
|
|||
while (auto key = nextObjectKey()) {
|
||||
if (!strcmp(key, "it")) {
|
||||
enterArray();
|
||||
while (nextArrayValue()) parseObject(layer);
|
||||
while (nextArrayValue()) parseObject(parent);
|
||||
} else if (!strcmp(key, "ty")) {
|
||||
if (auto child = parseObject()) {
|
||||
if (child->hidden) delete(child);
|
||||
else layer->children.push(child);
|
||||
else parent.push(child);
|
||||
}
|
||||
} else skip(key);
|
||||
}
|
||||
|
@ -967,6 +1061,43 @@ void LottieParser::parseShapes(LottieLayer* layer)
|
|||
}
|
||||
|
||||
|
||||
void LottieParser::parseTextRange(LottieText* text)
|
||||
{
|
||||
enterArray();
|
||||
while (nextArrayValue()) {
|
||||
enterObject();
|
||||
while (auto key2 = nextObjectKey()) {
|
||||
if (!strcmp(key2, "a")) { //text style
|
||||
enterObject();
|
||||
while (auto key3 = nextObjectKey()) {
|
||||
if (!strcmp(key3, "t")) parseProperty(text->spacing);
|
||||
else skip(key3);
|
||||
}
|
||||
} else skip(key2);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void LottieParser::parseText(Array<LottieObject*>& parent)
|
||||
{
|
||||
enterObject();
|
||||
|
||||
auto text = new LottieText;
|
||||
|
||||
while (auto key = nextObjectKey()) {
|
||||
if (!strcmp(key, "d")) parseProperty(text->doc);
|
||||
else if (!strcmp(key, "a")) parseTextRange(text);
|
||||
//else if (!strcmp(key, "p")) TVGLOG("LOTTIE", "Text Follow Path (p) is not supported");
|
||||
//else if (!strcmp(key, "m")) TVGLOG("LOTTIE", "Text Alignment Option (m) is not supported");
|
||||
else skip(key);
|
||||
}
|
||||
|
||||
text->prepare();
|
||||
parent.push(text);
|
||||
}
|
||||
|
||||
|
||||
void LottieParser::getLayerSize(uint32_t& val)
|
||||
{
|
||||
if (val == 0) {
|
||||
|
@ -1032,7 +1163,7 @@ LottieLayer* LottieParser::parseLayer()
|
|||
layer->transform = parseTransform(ddd);
|
||||
}
|
||||
else if (!strcmp(key, "ao")) layer->autoOrient = getInt();
|
||||
else if (!strcmp(key, "shapes")) parseShapes(layer);
|
||||
else if (!strcmp(key, "shapes")) parseShapes(layer->children);
|
||||
else if (!strcmp(key, "ip")) layer->inFrame = getFloat();
|
||||
else if (!strcmp(key, "op")) layer->outFrame = getFloat();
|
||||
else if (!strcmp(key, "st")) layer->startFrame = getFloat();
|
||||
|
@ -1047,6 +1178,7 @@ LottieLayer* LottieParser::parseLayer()
|
|||
else if (!strcmp(key, "hd")) layer->hidden = getBool();
|
||||
else if (!strcmp(key, "refId")) layer->refId = getStringCopy();
|
||||
else if (!strcmp(key, "td")) layer->matteSrc = getInt(); //used for matte layer
|
||||
else if (!strcmp(key, "t")) parseText(layer->children);
|
||||
else if (!strcmp(key, "ef"))
|
||||
{
|
||||
TVGERR("LOTTIE", "layer effect(ef) is not supported!");
|
||||
|
@ -1126,6 +1258,8 @@ bool LottieParser::parse()
|
|||
else if (!strcmp(key, "nm")) comp->name = getStringCopy();
|
||||
else if (!strcmp(key, "assets")) parseAssets();
|
||||
else if (!strcmp(key, "layers")) comp->root = parseLayers();
|
||||
else if (!strcmp(key, "fonts")) parseFonts();
|
||||
else if (!strcmp(key, "chars")) parseChars();
|
||||
else skip(key);
|
||||
}
|
||||
|
||||
|
|
|
@ -51,6 +51,8 @@ private:
|
|||
|
||||
void getInperpolatorPoint(Point& pt);
|
||||
void getPathSet(LottiePathSet& path);
|
||||
void getLayerSize(uint32_t& val);
|
||||
void getValue(TextDocument& doc);
|
||||
void getValue(PathSet& path);
|
||||
void getValue(Array<Point>& pts);
|
||||
void getValue(ColorStop& color);
|
||||
|
@ -58,7 +60,6 @@ private:
|
|||
void getValue(uint8_t& val);
|
||||
void getValue(Point& pt);
|
||||
void getValue(RGB24& color);
|
||||
void getLayerSize(uint32_t& val);
|
||||
|
||||
template<typename T> bool parseTangent(const char *key, LottieVectorFrame<T>& value);
|
||||
template<typename T> bool parseTangent(const char *key, LottieScalarFrame<T>& value);
|
||||
|
@ -85,14 +86,19 @@ private:
|
|||
LottieMask* parseMask();
|
||||
LottieTrimpath* parseTrimpath();
|
||||
LottieRepeater* parseRepeater();
|
||||
LottieFont* parseFont();
|
||||
|
||||
void parseObject(LottieGroup* parent);
|
||||
void parseShapes(LottieLayer* layer);
|
||||
void parseObject(Array<LottieObject*>& parent);
|
||||
void parseShapes(Array<LottieObject*>& parent);
|
||||
void parseText(Array<LottieObject*>& parent);
|
||||
void parseMasks(LottieLayer* layer);
|
||||
void parseTimeRemap(LottieLayer* layer);
|
||||
void parseStrokeDash(LottieStroke* stroke);
|
||||
void parseGradient(LottieGradient* gradient, const char* key);
|
||||
void parseTextRange(LottieText* text);
|
||||
void parseAssets();
|
||||
void parseFonts();
|
||||
void parseChars();
|
||||
|
||||
//Current parsing context
|
||||
struct Context {
|
||||
|
|
|
@ -51,6 +51,30 @@ struct ColorStop
|
|||
};
|
||||
|
||||
|
||||
struct LottieFont;
|
||||
|
||||
struct TextDocument
|
||||
{
|
||||
char* text = nullptr;
|
||||
float height;
|
||||
float shift;
|
||||
RGB24 color;
|
||||
struct {
|
||||
Point pos;
|
||||
Point size;
|
||||
} bbox;
|
||||
struct {
|
||||
RGB24 color;
|
||||
float width;
|
||||
bool render = false;
|
||||
} stroke;
|
||||
char* name = nullptr;
|
||||
uint16_t size;
|
||||
uint8_t justify;
|
||||
uint8_t tracking;
|
||||
};
|
||||
|
||||
|
||||
static inline RGB24 operator-(const RGB24& lhs, const RGB24& rhs)
|
||||
{
|
||||
return {lhs.rgb[0] - rhs.rgb[0], lhs.rgb[1] - rhs.rgb[1], lhs.rgb[2] - rhs.rgb[2]};
|
||||
|
@ -505,6 +529,66 @@ struct LottiePosition
|
|||
};
|
||||
|
||||
|
||||
struct LottieTextDoc
|
||||
{
|
||||
Array<LottieScalarFrame<TextDocument>>* frames = nullptr;
|
||||
TextDocument value;
|
||||
|
||||
~LottieTextDoc()
|
||||
{
|
||||
free(value.text);
|
||||
free(value.name);
|
||||
|
||||
if (!frames) return;
|
||||
for (auto p = frames->data; p < frames->end(); ++p) {
|
||||
free((*p).value.text);
|
||||
free((*p).value.name);
|
||||
}
|
||||
delete(frames);
|
||||
}
|
||||
|
||||
LottieScalarFrame<TextDocument>& newFrame()
|
||||
{
|
||||
if (!frames) frames = new Array<LottieScalarFrame<TextDocument>>;
|
||||
if (frames->count + 1 >= frames->reserved) {
|
||||
auto old = frames->reserved;
|
||||
frames->grow(frames->count + 2);
|
||||
memset((void*)(frames->data + old), 0x00, sizeof(LottieScalarFrame<TextDocument>) * (frames->reserved - old));
|
||||
}
|
||||
++frames->count;
|
||||
return frames->last();
|
||||
}
|
||||
|
||||
LottieScalarFrame<TextDocument>& nextFrame()
|
||||
{
|
||||
return (*frames)[frames->count];
|
||||
}
|
||||
|
||||
TextDocument& operator()(float frameNo)
|
||||
{
|
||||
if (!frames) return value;
|
||||
if (frames->count == 1 || frameNo <= frames->first().no) return frames->first().value;
|
||||
if (frameNo >= frames->last().no) return frames->last().value;
|
||||
|
||||
uint32_t low = 0;
|
||||
uint32_t high = frames->count - 1;
|
||||
|
||||
while (low <= high) {
|
||||
auto mid = low + (high - low) / 2;
|
||||
auto frame = frames->data + mid;
|
||||
if (mathEqual(frameNo, frame->no)) return frame->value;
|
||||
else if (frameNo < frame->no) high = mid - 1;
|
||||
else low = mid + 1;
|
||||
}
|
||||
if (high < low) low = high;
|
||||
|
||||
return (*frames)[low].value;
|
||||
}
|
||||
|
||||
void prepare() {}
|
||||
};
|
||||
|
||||
|
||||
using LottiePoint = LottieProperty<Point>;
|
||||
using LottieFloat = LottieProperty<float>;
|
||||
using LottieOpacity = LottieProperty<uint8_t>;
|
||||
|
|
|
@ -1108,8 +1108,7 @@ static bool _rasterTexmapPolygon(SwSurface* surface, const SwImage* image, const
|
|||
|
||||
float ys = FLT_MAX, ye = -1.0f;
|
||||
for (int i = 0; i < 4; i++) {
|
||||
mathMultiply(&vertices[i].pt, transform);
|
||||
|
||||
if (transform) mathMultiply(&vertices[i].pt, transform);
|
||||
if (vertices[i].pt.y < ys) ys = vertices[i].pt.y;
|
||||
if (vertices[i].pt.y > ye) ye = vertices[i].pt.y;
|
||||
}
|
||||
|
|
Loading…
Add table
Reference in a new issue