lottie: Improve feature coverage by correctly handling XOR shapes

XOR when the shape's direction property is set to a value of 2.
Currently, the direction property is expected to have
either 1 for clockwise or 3 for counterclockwise orientation.

Just found out the number 2 use-case...
This commit is contained in:
Hermet Park 2024-01-17 22:07:29 +09:00 committed by Hermet Park
parent 27b49dcf45
commit 6944633f41
4 changed files with 26 additions and 14 deletions

View file

@ -250,8 +250,6 @@ static bool _fragmented(LottieObject** child, Inlist<RenderContext>& contexts, R
fragment->begin = child - 1;
ctx->fragmenting = true;
TVGERR("LOTTIE", "Rendering is fragmented.");
return false;
}
@ -394,6 +392,7 @@ static void _updateRect(LottieGroup* parent, LottieObject** child, float frameNo
} else {
auto merging = _draw(parent, ctx);
merging->appendRect(position.x - size.x * 0.5f, position.y - size.y * 0.5f, size.x, size.y, roundness, roundness);
if (rect->direction == 2) merging->fill(FillRule::EvenOdd);
}
}
@ -412,6 +411,7 @@ static void _updateEllipse(LottieGroup* parent, LottieObject** child, float fram
} else {
auto merging = _draw(parent, ctx);
merging->appendCircle(position.x, position.y, size.x * 0.5f, size.y * 0.5f);
if (ellipse->direction == 2) merging->fill(FillRule::EvenOdd);
}
}
@ -426,15 +426,14 @@ static void _updatePath(LottieGroup* parent, LottieObject** child, float frameNo
_repeat(parent, std::move(p), ctx);
} else {
auto merging = _draw(parent, ctx);
if (path->pathset(frameNo, P(merging)->rs.path.cmds, P(merging)->rs.path.pts)) {
P(merging)->update(RenderUpdateFlag::Path);
}
if (ctx->roundness > 1.0f && P(merging)->rs.stroke) {
TVGERR("LOTTIE", "FIXME: Path roundesss should be applied properly!");
P(merging)->rs.stroke->join = StrokeJoin::Round;
}
if (path->direction == 2) merging->fill(FillRule::EvenOdd);
}
}
@ -527,7 +526,7 @@ static void _updateStar(LottieGroup* parent, LottiePolyStar* star, Matrix* trans
auto partialPointAmount = ptsCnt - floorf(ptsCnt);
auto longSegment = false;
auto numPoints = size_t(ceilf(ptsCnt) * 2);
auto direction = star->cw ? 1.0f : -1.0f;
auto direction = (star->direction == 0) ? 1.0f : -1.0f;
auto hasRoundness = false;
float x, y;
@ -629,7 +628,7 @@ static void _updatePolygon(LottieGroup* parent, LottiePolyStar* star, Matrix* tr
auto angle = -90.0f * MATH_PI / 180.0f;
auto anglePerPoint = 2.0f * MATH_PI / float(ptsCnt);
auto direction = star->cw ? 1.0f : -1.0f;
auto direction = (star->direction == 0) ? 1.0f : -1.0f;
auto hasRoundness = false;
auto x = radius * cosf(angle);
auto y = radius * sinf(angle);
@ -711,6 +710,7 @@ static void _updatePolystar(LottieGroup* parent, LottieObject** child, float fra
if (star->type == LottiePolyStar::Star) _updateStar(parent, star, identity ? nullptr : &matrix, frameNo, merging);
else _updatePolygon(parent, star, identity ? nullptr : &matrix, frameNo, merging);
P(merging)->update(RenderUpdateFlag::Path);
if (star->direction == 2) merging->fill(FillRule::EvenOdd);
}
}

View file

@ -313,7 +313,7 @@ struct LottieTrimpath : LottieObject
struct LottieShape : LottieObject
{
virtual ~LottieShape() {}
bool cw = true; //path direction (clock wise vs coutner clock wise)
uint8_t direction = 0; //0: clockwise, 2: counter-clockwise, 3: xor(?)
};

View file

@ -115,8 +115,8 @@ RGB24 LottieParser::getColor(const char *str)
FillRule LottieParser::getFillRule()
{
switch (getInt()) {
case 2: return FillRule::EvenOdd;
default: return FillRule::Winding;
case 1: return FillRule::Winding;
default: return FillRule::EvenOdd;
}
}
@ -484,8 +484,7 @@ LottieRect* LottieParser::parseRect()
if (!rect) return nullptr;
while (auto key = nextObjectKey()) {
if (!strcmp(key, "d")) rect->cw = getInt();
else if (!strcmp(key, "s")) parseProperty(rect->size);
if (!strcmp(key, "s")) parseProperty(rect->size);
else if (!strcmp(key, "p")) parseProperty(rect->position);
else if (!strcmp(key, "r")) parseProperty(rect->radius);
else if (!strcmp(key, "nm")) rect->name = getStringCopy();
@ -506,7 +505,6 @@ LottieEllipse* LottieParser::parseEllipse()
if (!strcmp(key, "nm")) ellipse->name = getStringCopy();
else if (!strcmp(key, "p")) parseProperty(ellipse->position);
else if (!strcmp(key, "s")) parseProperty(ellipse->size);
else if (!strcmp(key, "d")) ellipse->cw = getInt();
else if (!strcmp(key, "hd")) ellipse->hidden = getBool();
else skip(key);
}
@ -648,7 +646,6 @@ LottiePath* LottieParser::parsePath()
while (auto key = nextObjectKey()) {
if (!strcmp(key, "nm")) path->name = getStringCopy();
else if (!strcmp(key, "ks")) getPathSet(path->pathset);
else if (!strcmp(key, "d")) path->cw = getInt();
else if (!strcmp(key, "hd")) path->hidden = getBool();
else skip(key);
}
@ -672,7 +669,6 @@ LottiePolyStar* LottieParser::parsePolyStar()
else if (!strcmp(key, "os")) parseProperty(star->outerRoundness);
else if (!strcmp(key, "r")) parseProperty(star->rotation);
else if (!strcmp(key, "sy")) star->type = (LottiePolyStar::Type) getInt();
else if (!strcmp(key, "d")) star->cw = getInt();
else if (!strcmp(key, "hd")) star->hidden = getBool();
else skip(key);
}
@ -1041,19 +1037,34 @@ void LottieParser::parseTimeRemap(LottieLayer* layer)
}
uint8_t LottieParser::getDirection()
{
auto v = getInt();
if (v == 1) return 0;
if (v == 2) return 3;
if (v == 3) return 2;
return 0;
}
void LottieParser::parseShapes(Array<LottieObject*>& parent)
{
uint8_t direction;
enterArray();
while (nextArrayValue()) {
direction = 0;
enterObject();
while (auto key = nextObjectKey()) {
if (!strcmp(key, "it")) {
enterArray();
while (nextArrayValue()) parseObject(parent);
} else if (!strcmp(key, "d")) {
direction = getDirection();
} else if (!strcmp(key, "ty")) {
if (auto child = parseObject()) {
if (child->hidden) delete(child);
else parent.push(child);
if (direction > 0) static_cast<LottieShape*>(child)->direction = direction;
}
} else skip(key);
}

View file

@ -48,6 +48,7 @@ private:
StrokeJoin getStrokeJoin();
CompositeMethod getMaskMethod(bool inversed);
LottieInterpolator* getInterpolator(const char* key, Point& in, Point& out);
uint8_t getDirection();
void getInperpolatorPoint(Point& pt);
void getPathSet(LottiePathSet& path);