loader/lottie: support trimpath feature

This commit is contained in:
Hermet Park 2023-08-31 10:43:21 +09:00 committed by Hermet Park
parent 22771b5d8a
commit ad9d9d0ecd
26 changed files with 281 additions and 48 deletions

View file

@ -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)
{
return {lhs.x - rhs.x, lhs.y - rhs.y};

View file

@ -42,7 +42,7 @@ void tvgDrawCmds(tvg::Canvas* canvas)
auto bg = tvg::Shape::gen();
bg->appendRect(0, 0, 625, HEIGHT);
bg->fill(50, 50, 50);
canvas->push(move(bg));
canvas->push(std::move(bg));
{
//Shape + Shape Mask Add

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View 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":[]}

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View file

@ -258,7 +258,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->direction ? 1.0f : -1.0f;
auto direction = star->cw ? 1.0f : -1.0f;
auto hasRoundness = false;
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 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 x = radius * cosf(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)
{
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);
break;
}
case LottieObject::Trimpath: {
mergingShape = _updateTrimpath(parent, static_cast<LottieTrimpath*>(*child), frameNo, baseShape);
break;
}
case LottieObject::Image: {
_updateImage(parent, static_cast<LottieImage*>(*child), frameNo, baseShape);
break;

View file

@ -33,6 +33,49 @@
/* 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* fill = nullptr;

View file

@ -78,7 +78,6 @@ struct LottieStroke
};
struct LottieGradient
{
bool dynamic()
@ -130,7 +129,7 @@ struct LottieObject
Ellipse,
Path,
Polystar,
Trim,
Trimpath,
Repeater,
RoundedCorner,
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
{
virtual ~LottieShape() {}
bool direction; //path direction (clock wise vs coutner clock wise)
bool cw = true; //path direction (clock wise vs coutner clock wise)
};

View file

@ -183,7 +183,8 @@ void LottieParser::getValue(PathSet& path)
outCmds.data = path.cmds;
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.push(PathCommand::MoveTo);
@ -463,7 +464,7 @@ LottieRect* LottieParser::parseRect()
if (!rect) return nullptr;
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, "p")) parseProperty(rect->position);
else if (!strcmp(key, "r")) parseProperty(rect->radius);
@ -485,7 +486,7 @@ 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->direction = getInt();
else if (!strcmp(key, "d")) ellipse->cw = getInt();
else if (!strcmp(key, "hd")) ellipse->hidden = getBool();
else skip(key);
}
@ -565,7 +566,6 @@ void LottieParser::parseStrokeDash(LottieStroke* stroke)
if (!strcmp("o", style)) idx = 0; //offset
else if (!strcmp("d", style)) idx = 1; //dash
else if (!strcmp("g", style)) idx = 2; //gap
else TVGERR("LOTTIE", "Unsupported Dash Style");
} else if (!strcmp(key, "v")) {
parseProperty(stroke->dash(idx));
} else skip(key);
@ -625,7 +625,7 @@ 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->direction = getInt();
else if (!strcmp(key, "d")) path->cw = getInt();
else if (!strcmp(key, "hd")) path->hidden = getBool();
else skip(key);
}
@ -649,7 +649,7 @@ 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->direction = getInt();
else if (!strcmp(key, "d")) star->cw = getInt();
else if (!strcmp(key, "hd")) star->hidden = getBool();
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()
{
auto type = getString();
@ -756,7 +777,7 @@ LottieObject* LottieParser::parseObject()
else if (!strcmp(type, "rd")) return parseRoundedCorner();
else if (!strcmp(type, "gf")) return parseGradientFill();
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, "mm")) TVGERR("LOTTIE", "MergePath(mm) is not supported yet");
else TVGERR("LOTTIE", "Unkown object type(%s) is given", type);
@ -985,7 +1006,6 @@ LottieLayer* LottieParser::parseLayer()
//Not a valid layer
if (!layer->transform) {
TVGERR("LOTTIE", "Invalid Layer data, id(%d), transform(%p)", layer->id, layer->transform);
delete(layer);
return nullptr;
}

View file

@ -83,6 +83,7 @@ private:
LottieGradientFill* parseGradientFill();
LottieLayer* parseLayers();
LottieMask* parseMask();
LottieTrimpath* parseTrimpath();
void parseObject(LottieGroup* parent);
void parseShapes(LottieLayer* layer);

View file

@ -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)
{
return s + MULTIPLY(d, a);

View file

@ -88,6 +88,7 @@ struct SwShapeTask : SwTask
auto width = rshape->stroke->width;
if (mathZero(width)) return true;
if (rshape->strokeTrim()) return true;
if (transform) {
if (transform->e11 > transform->e22) width *= transform->e11;
@ -118,14 +119,15 @@ struct SwShapeTask : SwTask
{
if (opacity == 0 && !clipper) return; //Invisible
uint8_t strokeAlpha = 0;
auto visibleStroke = false;
bool visibleFill = false;
auto clipRegion = bbox;
if (HALF_STROKE(rshape->strokeWidth()) > 0) {
rshape->strokeColor(nullptr, nullptr, nullptr, &strokeAlpha);
visibleStroke = rshape->strokeFill() || (MULTIPLY(strokeAlpha, opacity) > 0);
//confirm valid stroke
if (auto stroke = rshape->stroke) {
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.

View file

@ -21,9 +21,8 @@
*/
#include "tvgSwCommon.h"
#include "tvgMath.h"
#include "tvgBezier.h"
#include <float.h>
#include <math.h>
/************************************************************************/
/* 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;
auto cmdCnt = rshape->path.cmds.count;
const Point* pts = rshape->path.pts.data;
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;
SwDashStroke dash;
auto offset = 0.0f;
auto trimmed = false;
float 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;
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];
bool isOdd = dash.cnt % 2;
if (isOdd) patternLength *= 2;
@ -246,10 +282,9 @@ static SwOutline* _genDashOutline(const RenderShape* rshape, const Matrix* trans
auto closeCnt = 0;
auto moveCnt = 0;
for (uint32_t i = 0; i < cmdCnt; ++i) {
auto cmd = *(cmds + i);
if (cmd == PathCommand::Close) ++closeCnt;
else if (cmd == PathCommand::MoveTo) ++moveCnt;
for (auto cmd = rshape->path.cmds.data; cmd < rshape->path.cmds.end(); ++cmd) {
if (*cmd == PathCommand::Close) ++closeCnt;
else if (*cmd == PathCommand::MoveTo) ++moveCnt;
}
//No idea exact count.... Reserve Approximitely 20x...
@ -289,10 +324,55 @@ static SwOutline* _genDashOutline(const RenderShape* rshape, const Matrix* trans
_outlineEnd(*dash.outline);
if (trimmed) free(dash.pattern);
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)
{
//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;
auto cmdCnt = rshape->path.cmds.count;
const Point* pts = rshape->path.pts.data;
auto ptsCnt = rshape->path.pts.count;
@ -327,10 +406,9 @@ static bool _genOutline(SwShape* shape, const RenderShape* rshape, const Matrix*
auto moveCnt = 0;
auto closeCnt = 0;
for (uint32_t i = 0; i < cmdCnt; ++i) {
auto cmd = *(cmds + i);
if (cmd == PathCommand::Close) ++closeCnt;
else if (cmd == PathCommand::MoveTo) ++moveCnt;
for (auto cmd = rshape->path.cmds.data; cmd < rshape->path.cmds.end(); ++cmd) {
if (*cmd == PathCommand::Close) ++closeCnt;
else if (*cmd == PathCommand::MoveTo) ++moveCnt;
}
shape->outline = mpoolReqOutline(mpool, tid);
@ -484,12 +562,14 @@ bool shapeGenStrokeRle(SwShape* shape, const RenderShape* rshape, const Matrix*
bool freeOutline = false;
bool ret = true;
//Dash Style Stroke
if (rshape->strokeDash(nullptr, nullptr) > 0) {
shapeOutline = _genDashOutline(rshape, transform);
auto length = rshape->strokeTrim() ? _outlineLength(rshape) : 0.0f;
//Dash style (+trimming)
if (rshape->stroke->dashCnt > 0 || length > 0) {
shapeOutline = _genDashOutline(rshape, transform, length);
if (!shapeOutline) return false;
freeOutline = true;
//Normal Style stroke
//Normal style
} else {
if (!shape->outline) {
if (!_genOutline(shape, rshape, transform, mpool, tid, false)) return false;

View file

@ -143,6 +143,11 @@ struct RenderStroke
float miterlimit = 4.0f;
bool strokeFirst = false;
struct {
float begin = 0.0f;
float end = 1.0f;
} trim;
~RenderStroke()
{
free(dashPattern);
@ -183,6 +188,14 @@ struct RenderShape
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
{
if (!stroke) return false;

View file

@ -199,8 +199,6 @@ struct Shape::Impl
bool strokeWidth(float width)
{
//TODO: Size Exception?
if (!rs.stroke) rs.stroke = new RenderStroke();
rs.stroke->width = width;
flag |= RenderUpdateFlag::Stroke;
@ -208,6 +206,22 @@ struct Shape::Impl
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)
{
if (!rs.stroke) rs.stroke = new RenderStroke();