mirror of
https://github.com/thorvg/thorvg.git
synced 2025-06-08 13:43:43 +00:00
loader/lottie: support trimpath feature
This commit is contained in:
parent
22771b5d8a
commit
ad9d9d0ecd
26 changed files with 281 additions and 48 deletions
|
@ -140,6 +140,18 @@ static inline void mathLog(Matrix* m)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static inline float mathLength(const Point* a, const Point* b)
|
||||||
|
{
|
||||||
|
auto x = b->x - a->x;
|
||||||
|
auto y = b->y - a->y;
|
||||||
|
|
||||||
|
if (x < 0) x = -x;
|
||||||
|
if (y < 0) y = -y;
|
||||||
|
|
||||||
|
return (x > y) ? (x + 0.375f * y) : (y + 0.375f * x);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
static inline Point operator-(const Point& lhs, const Point& rhs)
|
static inline Point operator-(const Point& lhs, const Point& rhs)
|
||||||
{
|
{
|
||||||
return {lhs.x - rhs.x, lhs.y - rhs.y};
|
return {lhs.x - rhs.x, lhs.y - rhs.y};
|
||||||
|
|
|
@ -42,7 +42,7 @@ void tvgDrawCmds(tvg::Canvas* canvas)
|
||||||
auto bg = tvg::Shape::gen();
|
auto bg = tvg::Shape::gen();
|
||||||
bg->appendRect(0, 0, 625, HEIGHT);
|
bg->appendRect(0, 0, 625, HEIGHT);
|
||||||
bg->fill(50, 50, 50);
|
bg->fill(50, 50, 50);
|
||||||
canvas->push(move(bg));
|
canvas->push(std::move(bg));
|
||||||
|
|
||||||
{
|
{
|
||||||
//Shape + Shape Mask Add
|
//Shape + Shape Mask Add
|
||||||
|
|
1
src/examples/images/5344-honey-sack-hud.json
Normal file
1
src/examples/images/5344-honey-sack-hud.json
Normal file
File diff suppressed because one or more lines are too long
1
src/examples/images/batman.json
Normal file
1
src/examples/images/batman.json
Normal file
File diff suppressed because one or more lines are too long
1
src/examples/images/birth_stone_logo.json
Normal file
1
src/examples/images/birth_stone_logo.json
Normal file
|
@ -0,0 +1 @@
|
||||||
|
{"v":"5.2.1","fr":29.9700012207031,"ip":0,"op":120.0000048877,"w":150,"h":120,"nm":"logo 2","ddd":1,"assets":[],"layers":[{"ddd":0,"ind":2,"ty":4,"nm":"Shape Layer 2","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[98,45.5,0],"ix":2},"a":{"a":0,"k":[-23,-14.5,0],"ix":1},"s":{"a":0,"k":[-100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[19.25,-19.25],[0,0],[0,0]],"o":[[0,0],[0,0],[-20.25,20.25],[0,0],[0,0]],"v":[[19.5,-14],[-12.5,-45.25],[-51.25,-42.25],[-55.75,-5.25],[-23,28.75]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"gs","o":{"a":0,"k":100,"ix":9},"w":{"a":0,"k":8,"ix":10},"g":{"p":3,"k":{"a":0,"k":[0,0.584,0.894,0.929,0.5,0.692,0.733,0.965,1,0.8,0.573,1],"ix":8}},"s":{"a":0,"k":[-64.303,-9.492],"ix":4},"e":{"a":0,"k":[23.721,-13.33],"ix":5},"t":1,"lc":2,"lj":2,"nm":"Gradient Stroke 1","mn":"ADBE Vector Graphic - G-Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Shape 1","np":2,"cix":2,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tm","s":{"a":0,"k":100,"ix":1},"e":{"a":1,"k":[{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.333],"y":[0]},"n":["0p667_1_0p333_0"],"t":30,"s":[100],"e":[0]},{"t":60.0000024438501}],"ix":2},"o":{"a":0,"k":0,"ix":3},"m":1,"ix":2,"nm":"Trim Paths 1","mn":"ADBE Vector Filter - Trim","hd":false}],"ip":0,"op":120.0000048877,"st":0,"bm":0},{"ddd":0,"ind":3,"ty":4,"nm":"Shape Layer 1","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[75,60,0],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[22.75,-19.25],[0,0],[0,0]],"o":[[0,0],[0,0],[-19.75,19.75],[0,0],[0,0]],"v":[[19.5,-14],[-12.5,-45.25],[-51.25,-42.25],[-55.75,-5.25],[-23,28.75]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"gs","o":{"a":0,"k":100,"ix":9},"w":{"a":0,"k":8,"ix":10},"g":{"p":3,"k":{"a":0,"k":[0,1,0.788,0.6,0.5,0.902,0.682,0.798,1,0.804,0.576,0.996],"ix":8}},"s":{"a":0,"k":[-64.303,-9.492],"ix":4},"e":{"a":0,"k":[23.721,-13.33],"ix":5},"t":1,"lc":2,"lj":2,"nm":"Gradient Stroke 1","mn":"ADBE Vector Graphic - G-Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Shape 1","np":2,"cix":2,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tm","s":{"a":0,"k":100,"ix":1},"e":{"a":1,"k":[{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.333],"y":[0]},"n":["0p667_1_0p333_0"],"t":0,"s":[100],"e":[0]},{"t":30.0000012219251}],"ix":2},"o":{"a":0,"k":0,"ix":3},"m":1,"ix":2,"nm":"Trim Paths 1","mn":"ADBE Vector Filter - Trim","hd":false}],"ip":0,"op":120.0000048877,"st":0,"bm":0},{"ddd":0,"ind":4,"ty":4,"nm":"Shape Layer 3","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[75.5,103,0],"ix":2},"a":{"a":0,"k":[0,42.5,0],"ix":1},"s":{"a":0,"k":[91.956,91.956,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":0,"k":[18.648,18.648],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"st","c":{"a":0,"k":[0.8,0.572549019608,1,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":8,"ix":5},"lc":2,"lj":2,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[0,42.5],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Ellipse 1","np":2,"cix":2,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tm","s":{"a":0,"k":100,"ix":1},"e":{"a":1,"k":[{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.333],"y":[0]},"n":["0p667_1_0p333_0"],"t":60,"s":[100],"e":[0]},{"t":89.0000036250443}],"ix":2},"o":{"a":0,"k":0,"ix":3},"m":1,"ix":2,"nm":"Trim Paths 1","mn":"ADBE Vector Filter - Trim","hd":false}],"ip":0,"op":120.0000048877,"st":0,"bm":0}],"markers":[]}
|
1
src/examples/images/duck.json
Normal file
1
src/examples/images/duck.json
Normal file
File diff suppressed because one or more lines are too long
1
src/examples/images/foodrating.json
Normal file
1
src/examples/images/foodrating.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
1
src/examples/images/happy_holidays.json
Normal file
1
src/examples/images/happy_holidays.json
Normal file
File diff suppressed because one or more lines are too long
1
src/examples/images/insta_camera.json
Normal file
1
src/examples/images/insta_camera.json
Normal file
File diff suppressed because one or more lines are too long
1
src/examples/images/lolo_walk.json
Normal file
1
src/examples/images/lolo_walk.json
Normal file
File diff suppressed because one or more lines are too long
1
src/examples/images/masking.json
Normal file
1
src/examples/images/masking.json
Normal file
File diff suppressed because one or more lines are too long
1
src/examples/images/skullboy.json
Normal file
1
src/examples/images/skullboy.json
Normal file
File diff suppressed because one or more lines are too long
1
src/examples/images/starts_transparent.json
Normal file
1
src/examples/images/starts_transparent.json
Normal file
File diff suppressed because one or more lines are too long
1
src/examples/images/threads.json
Normal file
1
src/examples/images/threads.json
Normal file
File diff suppressed because one or more lines are too long
1
src/examples/images/world_locations.json
Normal file
1
src/examples/images/world_locations.json
Normal file
File diff suppressed because one or more lines are too long
|
@ -258,7 +258,7 @@ static void _updateStar(LottieGroup* parent, LottiePolyStar* star, Matrix* trans
|
||||||
auto partialPointAmount = ptsCnt - floorf(ptsCnt);
|
auto partialPointAmount = ptsCnt - floorf(ptsCnt);
|
||||||
auto longSegment = false;
|
auto longSegment = false;
|
||||||
auto numPoints = size_t(ceilf(ptsCnt) * 2);
|
auto numPoints = size_t(ceilf(ptsCnt) * 2);
|
||||||
auto direction = star->direction ? 1.0f : -1.0f;
|
auto direction = star->cw ? 1.0f : -1.0f;
|
||||||
auto hasRoundness = false;
|
auto hasRoundness = false;
|
||||||
|
|
||||||
float x, y;
|
float x, y;
|
||||||
|
@ -361,7 +361,7 @@ static void _updatePolygon(LottieGroup* parent, LottiePolyStar* star, Matrix* tr
|
||||||
|
|
||||||
auto angle = -90.0f * K_PI / 180.0f;
|
auto angle = -90.0f * K_PI / 180.0f;
|
||||||
auto anglePerPoint = 2.0f * K_PI / float(ptsCnt);
|
auto anglePerPoint = 2.0f * K_PI / float(ptsCnt);
|
||||||
auto direction = star->direction ? 1.0f : -1.0f;
|
auto direction = star->cw ? 1.0f : -1.0f;
|
||||||
auto hasRoundness = false;
|
auto hasRoundness = false;
|
||||||
auto x = radius * cosf(angle);
|
auto x = radius * cosf(angle);
|
||||||
auto y = radius * sinf(angle);
|
auto y = radius * sinf(angle);
|
||||||
|
@ -481,6 +481,27 @@ static float _updateRoundedCorner(LottieRoundedCorner* roundedCorner, int32_t fr
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static Shape* _updateTrimpath(LottieGroup* parent, LottieTrimpath* trimpath, int32_t frameNo, Shape* baseShape)
|
||||||
|
{
|
||||||
|
float begin, end;
|
||||||
|
trimpath->segment(frameNo, begin, end);
|
||||||
|
|
||||||
|
if (trimpath->type == LottieTrimpath::Simultaneous) {
|
||||||
|
if (P(baseShape)->rs.stroke) {
|
||||||
|
auto pbegin = P(baseShape)->rs.stroke->trim.begin;
|
||||||
|
auto pend = P(baseShape)->rs.stroke->trim.end;
|
||||||
|
auto length = fabsf(pend - pbegin);
|
||||||
|
begin = (length * begin) + pbegin;
|
||||||
|
end = (length * end) + pbegin;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
P(baseShape)->strokeTrim(begin, end);
|
||||||
|
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
static void _updateChildren(LottieGroup* parent, int32_t frameNo, Shape* baseShape, float roundedCorner)
|
static void _updateChildren(LottieGroup* parent, int32_t frameNo, Shape* baseShape, float roundedCorner)
|
||||||
{
|
{
|
||||||
if (parent->children.empty()) return;
|
if (parent->children.empty()) return;
|
||||||
|
@ -538,6 +559,10 @@ static void _updateChildren(LottieGroup* parent, int32_t frameNo, Shape* baseSha
|
||||||
roundedCorner = _updateRoundedCorner(static_cast<LottieRoundedCorner*>(*child), frameNo, roundedCorner);
|
roundedCorner = _updateRoundedCorner(static_cast<LottieRoundedCorner*>(*child), frameNo, roundedCorner);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
case LottieObject::Trimpath: {
|
||||||
|
mergingShape = _updateTrimpath(parent, static_cast<LottieTrimpath*>(*child), frameNo, baseShape);
|
||||||
|
break;
|
||||||
|
}
|
||||||
case LottieObject::Image: {
|
case LottieObject::Image: {
|
||||||
_updateImage(parent, static_cast<LottieImage*>(*child), frameNo, baseShape);
|
_updateImage(parent, static_cast<LottieImage*>(*child), frameNo, baseShape);
|
||||||
break;
|
break;
|
||||||
|
|
|
@ -33,6 +33,49 @@
|
||||||
/* External Class Implementation */
|
/* External Class Implementation */
|
||||||
/************************************************************************/
|
/************************************************************************/
|
||||||
|
|
||||||
|
void LottieTrimpath::segment(int32_t frameNo, float& start, float& end)
|
||||||
|
{
|
||||||
|
auto s = this->start(frameNo) * 0.01f;
|
||||||
|
auto e = this->end(frameNo) * 0.01f;
|
||||||
|
auto o = fmod(this->offset(frameNo), 360.0f) / 360.0f; //0 ~ 1
|
||||||
|
|
||||||
|
auto diff = fabs(s - e);
|
||||||
|
if (mathZero(diff)) {
|
||||||
|
start = 0.0f;
|
||||||
|
end = 0.0f;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (mathEqual(diff, 1.0f) || mathEqual(diff, 2.0f)) {
|
||||||
|
start = 0.0f;
|
||||||
|
end = 1.0f;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
s += o;
|
||||||
|
e += o;
|
||||||
|
|
||||||
|
auto loop = true;
|
||||||
|
|
||||||
|
//no loop
|
||||||
|
if (s > 1.0f && e > 1.0f) loop = false;
|
||||||
|
if (s < 0.0f && e < 0.0f) loop = false;
|
||||||
|
if (s >= 0.0f && s <= 1.0f && e >= 0.0f && e <= 1.0f) loop = false;
|
||||||
|
|
||||||
|
if (s > 1.0f) s -= 1.0f;
|
||||||
|
if (s < 0.0f) s += 1.0f;
|
||||||
|
if (e > 1.0f) e -= 1.0f;
|
||||||
|
if (e < 0.0f) e += 1.0f;
|
||||||
|
|
||||||
|
if (loop) {
|
||||||
|
start = s > e ? s : e;
|
||||||
|
end = s < e ? s : e;
|
||||||
|
} else {
|
||||||
|
start = s < e ? s : e;
|
||||||
|
end = s > e ? s : e;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
Fill* LottieGradient::fill(int32_t frameNo)
|
Fill* LottieGradient::fill(int32_t frameNo)
|
||||||
{
|
{
|
||||||
Fill* fill = nullptr;
|
Fill* fill = nullptr;
|
||||||
|
|
|
@ -78,7 +78,6 @@ struct LottieStroke
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
struct LottieGradient
|
struct LottieGradient
|
||||||
{
|
{
|
||||||
bool dynamic()
|
bool dynamic()
|
||||||
|
@ -130,7 +129,7 @@ struct LottieObject
|
||||||
Ellipse,
|
Ellipse,
|
||||||
Path,
|
Path,
|
||||||
Polystar,
|
Polystar,
|
||||||
Trim,
|
Trimpath,
|
||||||
Repeater,
|
Repeater,
|
||||||
RoundedCorner,
|
RoundedCorner,
|
||||||
Image
|
Image
|
||||||
|
@ -148,10 +147,29 @@ struct LottieObject
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
struct LottieTrimpath : LottieObject
|
||||||
|
{
|
||||||
|
enum Type : uint8_t { Simultaneous = 1, Individual = 2 };
|
||||||
|
|
||||||
|
void prepare()
|
||||||
|
{
|
||||||
|
LottieObject::type = LottieObject::Trimpath;
|
||||||
|
if (start.frames || end.frames || offset.frames) statical = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void segment(int32_t frameNo, float& start, float& end);
|
||||||
|
|
||||||
|
LottieFloat start = 0.0f;
|
||||||
|
LottieFloat end = 0.0f;
|
||||||
|
LottieFloat offset = 0.0f;
|
||||||
|
Type type = Simultaneous;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
struct LottieShape : LottieObject
|
struct LottieShape : LottieObject
|
||||||
{
|
{
|
||||||
virtual ~LottieShape() {}
|
virtual ~LottieShape() {}
|
||||||
bool direction; //path direction (clock wise vs coutner clock wise)
|
bool cw = true; //path direction (clock wise vs coutner clock wise)
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -183,7 +183,8 @@ void LottieParser::getValue(PathSet& path)
|
||||||
outCmds.data = path.cmds;
|
outCmds.data = path.cmds;
|
||||||
outCmds.reserved = path.cmdsCnt;
|
outCmds.reserved = path.cmdsCnt;
|
||||||
|
|
||||||
outPts.reserve(pts.count * 3 + 1);
|
size_t extra = closed ? 3 : 0;
|
||||||
|
outPts.reserve(pts.count * 3 + 1 + extra);
|
||||||
outCmds.reserve(pts.count + 2);
|
outCmds.reserve(pts.count + 2);
|
||||||
|
|
||||||
outCmds.push(PathCommand::MoveTo);
|
outCmds.push(PathCommand::MoveTo);
|
||||||
|
@ -463,7 +464,7 @@ LottieRect* LottieParser::parseRect()
|
||||||
if (!rect) return nullptr;
|
if (!rect) return nullptr;
|
||||||
|
|
||||||
while (auto key = nextObjectKey()) {
|
while (auto key = nextObjectKey()) {
|
||||||
if (!strcmp(key, "d")) rect->direction = getInt();
|
if (!strcmp(key, "d")) rect->cw = getInt();
|
||||||
else if (!strcmp(key, "s")) parseProperty(rect->size);
|
else if (!strcmp(key, "s")) parseProperty(rect->size);
|
||||||
else if (!strcmp(key, "p")) parseProperty(rect->position);
|
else if (!strcmp(key, "p")) parseProperty(rect->position);
|
||||||
else if (!strcmp(key, "r")) parseProperty(rect->radius);
|
else if (!strcmp(key, "r")) parseProperty(rect->radius);
|
||||||
|
@ -485,7 +486,7 @@ LottieEllipse* LottieParser::parseEllipse()
|
||||||
if (!strcmp(key, "nm")) ellipse->name = getStringCopy();
|
if (!strcmp(key, "nm")) ellipse->name = getStringCopy();
|
||||||
else if (!strcmp(key, "p")) parseProperty(ellipse->position);
|
else if (!strcmp(key, "p")) parseProperty(ellipse->position);
|
||||||
else if (!strcmp(key, "s")) parseProperty(ellipse->size);
|
else if (!strcmp(key, "s")) parseProperty(ellipse->size);
|
||||||
else if (!strcmp(key, "d")) ellipse->direction = getInt();
|
else if (!strcmp(key, "d")) ellipse->cw = getInt();
|
||||||
else if (!strcmp(key, "hd")) ellipse->hidden = getBool();
|
else if (!strcmp(key, "hd")) ellipse->hidden = getBool();
|
||||||
else skip(key);
|
else skip(key);
|
||||||
}
|
}
|
||||||
|
@ -565,7 +566,6 @@ void LottieParser::parseStrokeDash(LottieStroke* stroke)
|
||||||
if (!strcmp("o", style)) idx = 0; //offset
|
if (!strcmp("o", style)) idx = 0; //offset
|
||||||
else if (!strcmp("d", style)) idx = 1; //dash
|
else if (!strcmp("d", style)) idx = 1; //dash
|
||||||
else if (!strcmp("g", style)) idx = 2; //gap
|
else if (!strcmp("g", style)) idx = 2; //gap
|
||||||
else TVGERR("LOTTIE", "Unsupported Dash Style");
|
|
||||||
} else if (!strcmp(key, "v")) {
|
} else if (!strcmp(key, "v")) {
|
||||||
parseProperty(stroke->dash(idx));
|
parseProperty(stroke->dash(idx));
|
||||||
} else skip(key);
|
} else skip(key);
|
||||||
|
@ -625,7 +625,7 @@ LottiePath* LottieParser::parsePath()
|
||||||
while (auto key = nextObjectKey()) {
|
while (auto key = nextObjectKey()) {
|
||||||
if (!strcmp(key, "nm")) path->name = getStringCopy();
|
if (!strcmp(key, "nm")) path->name = getStringCopy();
|
||||||
else if (!strcmp(key, "ks")) getPathSet(path->pathset);
|
else if (!strcmp(key, "ks")) getPathSet(path->pathset);
|
||||||
else if (!strcmp(key, "d")) path->direction = getInt();
|
else if (!strcmp(key, "d")) path->cw = getInt();
|
||||||
else if (!strcmp(key, "hd")) path->hidden = getBool();
|
else if (!strcmp(key, "hd")) path->hidden = getBool();
|
||||||
else skip(key);
|
else skip(key);
|
||||||
}
|
}
|
||||||
|
@ -649,7 +649,7 @@ LottiePolyStar* LottieParser::parsePolyStar()
|
||||||
else if (!strcmp(key, "os")) parseProperty(star->outerRoundness);
|
else if (!strcmp(key, "os")) parseProperty(star->outerRoundness);
|
||||||
else if (!strcmp(key, "r")) parseProperty(star->rotation);
|
else if (!strcmp(key, "r")) parseProperty(star->rotation);
|
||||||
else if (!strcmp(key, "sy")) star->type = (LottiePolyStar::Type) getInt();
|
else if (!strcmp(key, "sy")) star->type = (LottiePolyStar::Type) getInt();
|
||||||
else if (!strcmp(key, "d")) star->direction = getInt();
|
else if (!strcmp(key, "d")) star->cw = getInt();
|
||||||
else if (!strcmp(key, "hd")) star->hidden = getBool();
|
else if (!strcmp(key, "hd")) star->hidden = getBool();
|
||||||
else skip(key);
|
else skip(key);
|
||||||
}
|
}
|
||||||
|
@ -740,6 +740,27 @@ LottieGradientStroke* LottieParser::parseGradientStroke()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
LottieTrimpath* LottieParser::parseTrimpath()
|
||||||
|
{
|
||||||
|
auto trim = new LottieTrimpath;
|
||||||
|
if (!trim) return nullptr;
|
||||||
|
|
||||||
|
while (auto key = nextObjectKey()) {
|
||||||
|
if (!strcmp(key, "nm")) trim->name = getStringCopy();
|
||||||
|
else if (!strcmp(key, "s")) parseProperty(trim->start);
|
||||||
|
else if (!strcmp(key, "e")) parseProperty(trim->end);
|
||||||
|
else if (!strcmp(key, "o")) parseProperty(trim->offset);
|
||||||
|
else if (!strcmp(key, "m")) trim->type = static_cast<LottieTrimpath::Type>(getInt());
|
||||||
|
else if (!strcmp(key, "hd")) trim->hidden = getBool();
|
||||||
|
else skip(key);
|
||||||
|
}
|
||||||
|
|
||||||
|
trim->prepare();
|
||||||
|
|
||||||
|
return trim;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
LottieObject* LottieParser::parseObject()
|
LottieObject* LottieParser::parseObject()
|
||||||
{
|
{
|
||||||
auto type = getString();
|
auto type = getString();
|
||||||
|
@ -756,7 +777,7 @@ LottieObject* LottieParser::parseObject()
|
||||||
else if (!strcmp(type, "rd")) return parseRoundedCorner();
|
else if (!strcmp(type, "rd")) return parseRoundedCorner();
|
||||||
else if (!strcmp(type, "gf")) return parseGradientFill();
|
else if (!strcmp(type, "gf")) return parseGradientFill();
|
||||||
else if (!strcmp(type, "gs")) return parseGradientStroke();
|
else if (!strcmp(type, "gs")) return parseGradientStroke();
|
||||||
else if (!strcmp(type, "tm")) TVGERR("LOTTIE", "Trimpath(tm) is not supported");
|
else if (!strcmp(type, "tm")) return parseTrimpath();
|
||||||
else if (!strcmp(type, "rp")) TVGERR("LOTTIE", "Repeater(rp) is not supported yet");
|
else if (!strcmp(type, "rp")) TVGERR("LOTTIE", "Repeater(rp) is not supported yet");
|
||||||
else if (!strcmp(type, "mm")) TVGERR("LOTTIE", "MergePath(mm) is not supported yet");
|
else if (!strcmp(type, "mm")) TVGERR("LOTTIE", "MergePath(mm) is not supported yet");
|
||||||
else TVGERR("LOTTIE", "Unkown object type(%s) is given", type);
|
else TVGERR("LOTTIE", "Unkown object type(%s) is given", type);
|
||||||
|
@ -985,7 +1006,6 @@ LottieLayer* LottieParser::parseLayer()
|
||||||
|
|
||||||
//Not a valid layer
|
//Not a valid layer
|
||||||
if (!layer->transform) {
|
if (!layer->transform) {
|
||||||
TVGERR("LOTTIE", "Invalid Layer data, id(%d), transform(%p)", layer->id, layer->transform);
|
|
||||||
delete(layer);
|
delete(layer);
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
|
@ -83,6 +83,7 @@ private:
|
||||||
LottieGradientFill* parseGradientFill();
|
LottieGradientFill* parseGradientFill();
|
||||||
LottieLayer* parseLayers();
|
LottieLayer* parseLayers();
|
||||||
LottieMask* parseMask();
|
LottieMask* parseMask();
|
||||||
|
LottieTrimpath* parseTrimpath();
|
||||||
|
|
||||||
void parseObject(LottieGroup* parent);
|
void parseObject(LottieGroup* parent);
|
||||||
void parseShapes(LottieLayer* layer);
|
void parseShapes(LottieLayer* layer);
|
||||||
|
|
|
@ -166,13 +166,6 @@ static inline bool _matting(const SwSurface* surface)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
static inline bool _masking(const SwSurface* surface)
|
|
||||||
{
|
|
||||||
if ((int)surface->compositor->method >= (int)CompositeMethod::AddMask) return true;
|
|
||||||
else return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
static inline uint8_t _opMaskAdd(uint8_t s, uint8_t d, uint8_t a)
|
static inline uint8_t _opMaskAdd(uint8_t s, uint8_t d, uint8_t a)
|
||||||
{
|
{
|
||||||
return s + MULTIPLY(d, a);
|
return s + MULTIPLY(d, a);
|
||||||
|
|
|
@ -88,6 +88,7 @@ struct SwShapeTask : SwTask
|
||||||
|
|
||||||
auto width = rshape->stroke->width;
|
auto width = rshape->stroke->width;
|
||||||
if (mathZero(width)) return true;
|
if (mathZero(width)) return true;
|
||||||
|
if (rshape->strokeTrim()) return true;
|
||||||
|
|
||||||
if (transform) {
|
if (transform) {
|
||||||
if (transform->e11 > transform->e22) width *= transform->e11;
|
if (transform->e11 > transform->e22) width *= transform->e11;
|
||||||
|
@ -118,14 +119,15 @@ struct SwShapeTask : SwTask
|
||||||
{
|
{
|
||||||
if (opacity == 0 && !clipper) return; //Invisible
|
if (opacity == 0 && !clipper) return; //Invisible
|
||||||
|
|
||||||
uint8_t strokeAlpha = 0;
|
|
||||||
auto visibleStroke = false;
|
auto visibleStroke = false;
|
||||||
bool visibleFill = false;
|
bool visibleFill = false;
|
||||||
auto clipRegion = bbox;
|
auto clipRegion = bbox;
|
||||||
|
|
||||||
if (HALF_STROKE(rshape->strokeWidth()) > 0) {
|
//confirm valid stroke
|
||||||
rshape->strokeColor(nullptr, nullptr, nullptr, &strokeAlpha);
|
if (auto stroke = rshape->stroke) {
|
||||||
visibleStroke = rshape->strokeFill() || (MULTIPLY(strokeAlpha, opacity) > 0);
|
if (HALF_STROKE(stroke->width) > 0 && !mathZero(stroke->trim.begin - stroke->trim.end)) {
|
||||||
|
visibleStroke = stroke->fill || (MULTIPLY(stroke->color[3], opacity) > 0);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//This checks also for the case, if the invisible shape turned to visible by alpha.
|
//This checks also for the case, if the invisible shape turned to visible by alpha.
|
||||||
|
|
|
@ -21,9 +21,8 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include "tvgSwCommon.h"
|
#include "tvgSwCommon.h"
|
||||||
|
#include "tvgMath.h"
|
||||||
#include "tvgBezier.h"
|
#include "tvgBezier.h"
|
||||||
#include <float.h>
|
|
||||||
#include <math.h>
|
|
||||||
|
|
||||||
/************************************************************************/
|
/************************************************************************/
|
||||||
/* Internal Class Implementation */
|
/* Internal Class Implementation */
|
||||||
|
@ -205,11 +204,10 @@ static void _dashCubicTo(SwDashStroke& dash, const Point* ctrl1, const Point* ct
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
static SwOutline* _genDashOutline(const RenderShape* rshape, const Matrix* transform)
|
static SwOutline* _genDashOutline(const RenderShape* rshape, const Matrix* transform, float length)
|
||||||
{
|
{
|
||||||
const PathCommand* cmds = rshape->path.cmds.data;
|
const PathCommand* cmds = rshape->path.cmds.data;
|
||||||
auto cmdCnt = rshape->path.cmds.count;
|
auto cmdCnt = rshape->path.cmds.count;
|
||||||
|
|
||||||
const Point* pts = rshape->path.pts.data;
|
const Point* pts = rshape->path.pts.data;
|
||||||
auto ptsCnt = rshape->path.pts.count;
|
auto ptsCnt = rshape->path.pts.count;
|
||||||
|
|
||||||
|
@ -217,14 +215,52 @@ static SwOutline* _genDashOutline(const RenderShape* rshape, const Matrix* trans
|
||||||
if (cmdCnt == 0 || ptsCnt == 0) return nullptr;
|
if (cmdCnt == 0 || ptsCnt == 0) return nullptr;
|
||||||
|
|
||||||
SwDashStroke dash;
|
SwDashStroke dash;
|
||||||
|
auto offset = 0.0f;
|
||||||
|
auto trimmed = false;
|
||||||
|
|
||||||
float offset;
|
|
||||||
dash.cnt = rshape->strokeDash((const float**)&dash.pattern, &offset);
|
dash.cnt = rshape->strokeDash((const float**)&dash.pattern, &offset);
|
||||||
if (dash.cnt == 0) return nullptr;
|
|
||||||
|
|
||||||
|
//dash by trimming.
|
||||||
|
if (length > 0.0f && dash.cnt == 0) {
|
||||||
|
auto begin = length * rshape->stroke->trim.begin;
|
||||||
|
auto end = length * rshape->stroke->trim.end;
|
||||||
|
|
||||||
|
//TODO: mix trimming + dash style
|
||||||
|
|
||||||
|
//default
|
||||||
|
if (end > begin) {
|
||||||
|
if (begin > 0) dash.cnt += 4;
|
||||||
|
else dash.cnt += 2;
|
||||||
|
//looping
|
||||||
|
} else dash.cnt += 3;
|
||||||
|
|
||||||
|
dash.pattern = (float*)malloc(sizeof(float) * dash.cnt);
|
||||||
|
|
||||||
|
if (dash.cnt == 2) {
|
||||||
|
dash.pattern[0] = end - begin;
|
||||||
|
dash.pattern[1] = length - (end - begin);
|
||||||
|
} else if (dash.cnt == 3) {
|
||||||
|
dash.pattern[0] = end;
|
||||||
|
dash.pattern[1] = (begin - end);
|
||||||
|
dash.pattern[2] = length - begin;
|
||||||
|
} else {
|
||||||
|
dash.pattern[0] = 0; //zero dash to start with a space.
|
||||||
|
dash.pattern[1] = begin;
|
||||||
|
dash.pattern[2] = end - begin;
|
||||||
|
dash.pattern[3] = length - (end - begin);
|
||||||
|
}
|
||||||
|
|
||||||
|
trimmed = true;
|
||||||
|
//just a dasy style.
|
||||||
|
} else {
|
||||||
|
|
||||||
|
if (dash.cnt == 0) return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
//offset?
|
||||||
auto patternLength = 0.0f;
|
auto patternLength = 0.0f;
|
||||||
uint32_t offIdx = 0;
|
uint32_t offIdx = 0;
|
||||||
if (fabsf(offset) > FLT_EPSILON) {
|
if (!mathZero(offset)) {
|
||||||
for (size_t i = 0; i < dash.cnt; ++i) patternLength += dash.pattern[i];
|
for (size_t i = 0; i < dash.cnt; ++i) patternLength += dash.pattern[i];
|
||||||
bool isOdd = dash.cnt % 2;
|
bool isOdd = dash.cnt % 2;
|
||||||
if (isOdd) patternLength *= 2;
|
if (isOdd) patternLength *= 2;
|
||||||
|
@ -246,10 +282,9 @@ static SwOutline* _genDashOutline(const RenderShape* rshape, const Matrix* trans
|
||||||
auto closeCnt = 0;
|
auto closeCnt = 0;
|
||||||
auto moveCnt = 0;
|
auto moveCnt = 0;
|
||||||
|
|
||||||
for (uint32_t i = 0; i < cmdCnt; ++i) {
|
for (auto cmd = rshape->path.cmds.data; cmd < rshape->path.cmds.end(); ++cmd) {
|
||||||
auto cmd = *(cmds + i);
|
if (*cmd == PathCommand::Close) ++closeCnt;
|
||||||
if (cmd == PathCommand::Close) ++closeCnt;
|
else if (*cmd == PathCommand::MoveTo) ++moveCnt;
|
||||||
else if (cmd == PathCommand::MoveTo) ++moveCnt;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//No idea exact count.... Reserve Approximitely 20x...
|
//No idea exact count.... Reserve Approximitely 20x...
|
||||||
|
@ -289,10 +324,55 @@ static SwOutline* _genDashOutline(const RenderShape* rshape, const Matrix* trans
|
||||||
|
|
||||||
_outlineEnd(*dash.outline);
|
_outlineEnd(*dash.outline);
|
||||||
|
|
||||||
|
if (trimmed) free(dash.pattern);
|
||||||
|
|
||||||
return dash.outline;
|
return dash.outline;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static float _outlineLength(const RenderShape* rshape)
|
||||||
|
{
|
||||||
|
const PathCommand* cmds = rshape->path.cmds.data;
|
||||||
|
auto cmdCnt = rshape->path.cmds.count;
|
||||||
|
const Point* pts = rshape->path.pts.data;
|
||||||
|
auto ptsCnt = rshape->path.pts.count;
|
||||||
|
|
||||||
|
//No actual shape data
|
||||||
|
if (cmdCnt == 0 || ptsCnt == 0) return 0.0f;
|
||||||
|
|
||||||
|
const Point* close = nullptr;
|
||||||
|
auto length = 0.0f;
|
||||||
|
|
||||||
|
//Compute the whole length
|
||||||
|
while (cmdCnt-- > 0) {
|
||||||
|
switch (*cmds) {
|
||||||
|
case PathCommand::Close: {
|
||||||
|
length += mathLength(pts - 1, close);
|
||||||
|
++pts;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case PathCommand::MoveTo: {
|
||||||
|
close = pts;
|
||||||
|
++pts;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case PathCommand::LineTo: {
|
||||||
|
length += mathLength(pts - 1, pts);
|
||||||
|
++pts;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case PathCommand::CubicTo: {
|
||||||
|
length += bezLength({*(pts - 1), *pts, *(pts + 1), *(pts + 2)});
|
||||||
|
pts += 3;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
++cmds;
|
||||||
|
}
|
||||||
|
return length;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
static bool _axisAlignedRect(const SwOutline* outline)
|
static bool _axisAlignedRect(const SwOutline* outline)
|
||||||
{
|
{
|
||||||
//Fast Track: axis-aligned rectangle?
|
//Fast Track: axis-aligned rectangle?
|
||||||
|
@ -316,7 +396,6 @@ static bool _genOutline(SwShape* shape, const RenderShape* rshape, const Matrix*
|
||||||
{
|
{
|
||||||
const PathCommand* cmds = rshape->path.cmds.data;
|
const PathCommand* cmds = rshape->path.cmds.data;
|
||||||
auto cmdCnt = rshape->path.cmds.count;
|
auto cmdCnt = rshape->path.cmds.count;
|
||||||
|
|
||||||
const Point* pts = rshape->path.pts.data;
|
const Point* pts = rshape->path.pts.data;
|
||||||
auto ptsCnt = rshape->path.pts.count;
|
auto ptsCnt = rshape->path.pts.count;
|
||||||
|
|
||||||
|
@ -327,10 +406,9 @@ static bool _genOutline(SwShape* shape, const RenderShape* rshape, const Matrix*
|
||||||
auto moveCnt = 0;
|
auto moveCnt = 0;
|
||||||
auto closeCnt = 0;
|
auto closeCnt = 0;
|
||||||
|
|
||||||
for (uint32_t i = 0; i < cmdCnt; ++i) {
|
for (auto cmd = rshape->path.cmds.data; cmd < rshape->path.cmds.end(); ++cmd) {
|
||||||
auto cmd = *(cmds + i);
|
if (*cmd == PathCommand::Close) ++closeCnt;
|
||||||
if (cmd == PathCommand::Close) ++closeCnt;
|
else if (*cmd == PathCommand::MoveTo) ++moveCnt;
|
||||||
else if (cmd == PathCommand::MoveTo) ++moveCnt;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
shape->outline = mpoolReqOutline(mpool, tid);
|
shape->outline = mpoolReqOutline(mpool, tid);
|
||||||
|
@ -484,12 +562,14 @@ bool shapeGenStrokeRle(SwShape* shape, const RenderShape* rshape, const Matrix*
|
||||||
bool freeOutline = false;
|
bool freeOutline = false;
|
||||||
bool ret = true;
|
bool ret = true;
|
||||||
|
|
||||||
//Dash Style Stroke
|
auto length = rshape->strokeTrim() ? _outlineLength(rshape) : 0.0f;
|
||||||
if (rshape->strokeDash(nullptr, nullptr) > 0) {
|
|
||||||
shapeOutline = _genDashOutline(rshape, transform);
|
//Dash style (+trimming)
|
||||||
|
if (rshape->stroke->dashCnt > 0 || length > 0) {
|
||||||
|
shapeOutline = _genDashOutline(rshape, transform, length);
|
||||||
if (!shapeOutline) return false;
|
if (!shapeOutline) return false;
|
||||||
freeOutline = true;
|
freeOutline = true;
|
||||||
//Normal Style stroke
|
//Normal style
|
||||||
} else {
|
} else {
|
||||||
if (!shape->outline) {
|
if (!shape->outline) {
|
||||||
if (!_genOutline(shape, rshape, transform, mpool, tid, false)) return false;
|
if (!_genOutline(shape, rshape, transform, mpool, tid, false)) return false;
|
||||||
|
|
|
@ -143,6 +143,11 @@ struct RenderStroke
|
||||||
float miterlimit = 4.0f;
|
float miterlimit = 4.0f;
|
||||||
bool strokeFirst = false;
|
bool strokeFirst = false;
|
||||||
|
|
||||||
|
struct {
|
||||||
|
float begin = 0.0f;
|
||||||
|
float end = 1.0f;
|
||||||
|
} trim;
|
||||||
|
|
||||||
~RenderStroke()
|
~RenderStroke()
|
||||||
{
|
{
|
||||||
free(dashPattern);
|
free(dashPattern);
|
||||||
|
@ -183,6 +188,14 @@ struct RenderShape
|
||||||
return stroke->width;
|
return stroke->width;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool strokeTrim() const
|
||||||
|
{
|
||||||
|
if (!stroke) return false;
|
||||||
|
if (stroke->trim.begin == 0.0f && stroke->trim.end == 1.0f) return false;
|
||||||
|
if (stroke->trim.begin == 1.0f && stroke->trim.end == 0.0f) return false;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
bool strokeColor(uint8_t* r, uint8_t* g, uint8_t* b, uint8_t* a) const
|
bool strokeColor(uint8_t* r, uint8_t* g, uint8_t* b, uint8_t* a) const
|
||||||
{
|
{
|
||||||
if (!stroke) return false;
|
if (!stroke) return false;
|
||||||
|
|
|
@ -199,8 +199,6 @@ struct Shape::Impl
|
||||||
|
|
||||||
bool strokeWidth(float width)
|
bool strokeWidth(float width)
|
||||||
{
|
{
|
||||||
//TODO: Size Exception?
|
|
||||||
|
|
||||||
if (!rs.stroke) rs.stroke = new RenderStroke();
|
if (!rs.stroke) rs.stroke = new RenderStroke();
|
||||||
rs.stroke->width = width;
|
rs.stroke->width = width;
|
||||||
flag |= RenderUpdateFlag::Stroke;
|
flag |= RenderUpdateFlag::Stroke;
|
||||||
|
@ -208,6 +206,22 @@ struct Shape::Impl
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool strokeTrim(float begin, float end)
|
||||||
|
{
|
||||||
|
if (!rs.stroke) {
|
||||||
|
if (begin == 0.0f && end == 1.0f) return true;
|
||||||
|
rs.stroke = new RenderStroke();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mathEqual(rs.stroke->trim.begin, begin) && mathEqual(rs.stroke->trim.end, end)) return true;
|
||||||
|
|
||||||
|
rs.stroke->trim.begin = begin;
|
||||||
|
rs.stroke->trim.end = end;
|
||||||
|
flag |= RenderUpdateFlag::Stroke;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
bool strokeCap(StrokeCap cap)
|
bool strokeCap(StrokeCap cap)
|
||||||
{
|
{
|
||||||
if (!rs.stroke) rs.stroke = new RenderStroke();
|
if (!rs.stroke) rs.stroke = new RenderStroke();
|
||||||
|
|
Loading…
Add table
Reference in a new issue