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) 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};

View file

@ -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

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 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;

View file

@ -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;

View file

@ -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)
}; };

View file

@ -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;
} }

View file

@ -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);

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) static inline uint8_t _opMaskAdd(uint8_t s, uint8_t d, uint8_t a)
{ {
return s + MULTIPLY(d, a); return s + MULTIPLY(d, a);

View file

@ -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.

View file

@ -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;

View file

@ -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;

View file

@ -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();