mirror of
https://github.com/thorvg/thorvg.git
synced 2025-06-07 21:23:32 +00:00
renderer: revise the Shape rect/circle features
The path direction of shapes is now functional for path trimming. Replace the main logic with Lottie's by default to align the spec, migrate the original logic to svg loader side. This revision helps to reduce the binary size by 2–3 KB for lottie loader. API Modifications: - Result Shape::appendRect(float x, float y, float w, float h, float rx = 0, float ry = 0) -> Result Shape::appendRect(float x, float y, float w, float h, float rx = 0, float ry = 0, bool cw = true) - Result Shape::appendCircle(float cx, float cy, float rx, float ry) -> Result Shape::appendCircle(float cx, float cy, float rx, float ry, bool cw = true) - TVG_API Tvg_Result tvg_shape_append_circle(Tvg_Paint* paint, float cx, float cy, float rx, float ry) -> TVG_API Tvg_Result tvg_shape_append_circle(Tvg_Paint* paint, float cx, float cy, float rx, float ry, bool cw) - Tvg_Result tvg_shape_append_rect(Tvg_Paint* paint, float x, float y, float w, float h, float rx, float ry) -> Tvg_Result tvg_shape_append_rect(Tvg_Paint* paint, float x, float y, float w, float h, float rx, float ry, bool cw) issue: https://github.com/thorvg/thorvg/issues/3179
This commit is contained in:
parent
5c950b783c
commit
cc4c18d6c6
10 changed files with 196 additions and 163 deletions
|
@ -94,9 +94,9 @@ void contents()
|
|||
//////3. Radial gradient shape with a radial dashed stroke
|
||||
//Set a shape
|
||||
Tvg_Paint* shape3 = tvg_shape_new();
|
||||
tvg_shape_append_rect(shape3, 550.0f, 20.0f, 100.0f, 50.0f, 0.0f, 0.0f);
|
||||
tvg_shape_append_circle(shape3, 600.0f, 150.0f, 100.0f, 50.0f);
|
||||
tvg_shape_append_rect(shape3, 550.0f, 230.0f, 100.0f, 100.0f, 20.0f, 40.0f);
|
||||
tvg_shape_append_rect(shape3, 550.0f, 20.0f, 100.0f, 50.0f, 0.0f, 0.0f, true);
|
||||
tvg_shape_append_circle(shape3, 600.0f, 150.0f, 100.0f, 50.0f, true);
|
||||
tvg_shape_append_rect(shape3, 550.0f, 230.0f, 100.0f, 100.0f, 20.0f, 40.0f, true);
|
||||
|
||||
//Prepare a radial gradient for the fill
|
||||
Tvg_Gradient* grad2 = tvg_radial_gradient_new();
|
||||
|
@ -142,8 +142,8 @@ void contents()
|
|||
|
||||
//Set circles
|
||||
Tvg_Paint* scene_shape1 = tvg_shape_new();
|
||||
tvg_shape_append_circle(scene_shape1, 80.0f, 650.f, 40.0f, 140.0f);
|
||||
tvg_shape_append_circle(scene_shape1, 180.0f, 600.f, 40.0f, 60.0f);
|
||||
tvg_shape_append_circle(scene_shape1, 80.0f, 650.f, 40.0f, 140.0f, true);
|
||||
tvg_shape_append_circle(scene_shape1, 180.0f, 600.f, 40.0f, 60.0f, true);
|
||||
tvg_shape_set_fill_color(scene_shape1, 0, 0, 255, 150);
|
||||
tvg_shape_set_stroke_color(scene_shape1, 75, 25, 155, 255);
|
||||
tvg_shape_set_stroke_width(scene_shape1, 10.0f);
|
||||
|
@ -189,7 +189,7 @@ void contents()
|
|||
|
||||
// Set a composite shape
|
||||
Tvg_Paint* comp = tvg_shape_new();
|
||||
tvg_shape_append_circle(comp, 600.0f, 600.0f, 100.0f, 100.0f);
|
||||
tvg_shape_append_circle(comp, 600.0f, 600.0f, 100.0f, 100.0f, true);
|
||||
tvg_shape_set_fill_color(comp, 0, 0, 0, 200);
|
||||
tvg_paint_set_mask_method(pict, comp, TVG_MASK_METHOD_INVERSE_ALPHA);
|
||||
|
||||
|
|
|
@ -43,13 +43,13 @@ struct UserExample : tvgexam::Example
|
|||
shape1->strokeJoin(tvg::StrokeJoin::Round);
|
||||
shape1->strokeCap(tvg::StrokeCap::Round);
|
||||
shape1->strokeWidth(12);
|
||||
shape1->strokeTrim(0.0f, 0.5f, false);
|
||||
shape1->strokeTrim(0.25f, 0.75f, false);
|
||||
|
||||
auto shape2 = static_cast<tvg::Shape*>(shape1->duplicate());
|
||||
shape2->translate(300, 300);
|
||||
shape2->fill(0, 155, 50, 100);
|
||||
shape2->strokeFill(0, 255, 0);
|
||||
shape2->strokeTrim(0.0f, 0.5f, true);
|
||||
shape2->strokeTrim(0.25f, 0.75f, true);
|
||||
|
||||
canvas->push(shape1);
|
||||
canvas->push(shape2);
|
||||
|
|
|
@ -983,10 +983,11 @@ public:
|
|||
* @param[in] h The height of the rectangle.
|
||||
* @param[in] rx The x-axis radius of the ellipse defining the rounded corners of the rectangle.
|
||||
* @param[in] ry The y-axis radius of the ellipse defining the rounded corners of the rectangle.
|
||||
* @param[in] cw Specifies the path direction: @c true for clockwise, @c false for counterclockwise.
|
||||
*
|
||||
* @note For @p rx and @p ry greater than or equal to the half of @p w and the half of @p h, respectively, the shape become an ellipse.
|
||||
*/
|
||||
Result appendRect(float x, float y, float w, float h, float rx = 0, float ry = 0) noexcept;
|
||||
Result appendRect(float x, float y, float w, float h, float rx = 0, float ry = 0, bool cw = true) noexcept;
|
||||
|
||||
/**
|
||||
* @brief Appends an ellipse to the path.
|
||||
|
@ -1001,9 +1002,10 @@ public:
|
|||
* @param[in] cy The vertical coordinate of the center of the ellipse.
|
||||
* @param[in] rx The x-axis radius of the ellipse.
|
||||
* @param[in] ry The y-axis radius of the ellipse.
|
||||
* @param[in] cw Specifies the path direction: @c true for clockwise, @c false for counterclockwise.
|
||||
*
|
||||
*/
|
||||
Result appendCircle(float cx, float cy, float rx, float ry) noexcept;
|
||||
Result appendCircle(float cx, float cy, float rx, float ry, bool cw = true) noexcept;
|
||||
|
||||
/**
|
||||
* @brief Appends a given sub-path to the path.
|
||||
|
|
|
@ -1175,13 +1175,14 @@ TVG_API Tvg_Result tvg_shape_close(Tvg_Paint* paint);
|
|||
* @param[in] h The height of the rectangle.
|
||||
* @param[in] rx The x-axis radius of the ellipse defining the rounded corners of the rectangle.
|
||||
* @param[in] ry The y-axis radius of the ellipse defining the rounded corners of the rectangle.
|
||||
* @param[in] cw Specifies the path direction: @c true for clockwise, @c false for counterclockwise.
|
||||
*
|
||||
* @return Tvg_Result enumeration.
|
||||
* @retval TVG_RESULT_INVALID_ARGUMENT An invalid Tvg_Paint pointer.
|
||||
*
|
||||
& @note For @p rx and @p ry greater than or equal to the half of @p w and the half of @p h, respectively, the shape become an ellipse.
|
||||
*/
|
||||
TVG_API Tvg_Result tvg_shape_append_rect(Tvg_Paint* paint, float x, float y, float w, float h, float rx, float ry);
|
||||
TVG_API Tvg_Result tvg_shape_append_rect(Tvg_Paint* paint, float x, float y, float w, float h, float rx, float ry, bool cw);
|
||||
|
||||
|
||||
/*!
|
||||
|
@ -1198,11 +1199,12 @@ TVG_API Tvg_Result tvg_shape_append_rect(Tvg_Paint* paint, float x, float y, flo
|
|||
* @param[in] cy The vertical coordinate of the center of the ellipse.
|
||||
* @param[in] rx The x-axis radius of the ellipse.
|
||||
* @param[in] ry The y-axis radius of the ellipse.
|
||||
* @param[in] cw Specifies the path direction: @c true for clockwise, @c false for counterclockwise.
|
||||
*
|
||||
* @return Tvg_Result enumeration.
|
||||
* @retval TVG_RESULT_INVALID_ARGUMENT An invalid Tvg_Paint pointer.
|
||||
*/
|
||||
TVG_API Tvg_Result tvg_shape_append_circle(Tvg_Paint* paint, float cx, float cy, float rx, float ry);
|
||||
TVG_API Tvg_Result tvg_shape_append_circle(Tvg_Paint* paint, float cx, float cy, float rx, float ry, bool cw);
|
||||
|
||||
|
||||
/*!
|
||||
|
|
|
@ -352,17 +352,17 @@ TVG_API Tvg_Result tvg_shape_close(Tvg_Paint* paint)
|
|||
}
|
||||
|
||||
|
||||
TVG_API Tvg_Result tvg_shape_append_rect(Tvg_Paint* paint, float x, float y, float w, float h, float rx, float ry)
|
||||
TVG_API Tvg_Result tvg_shape_append_rect(Tvg_Paint* paint, float x, float y, float w, float h, float rx, float ry, bool cw)
|
||||
{
|
||||
if (!paint) return TVG_RESULT_INVALID_ARGUMENT;
|
||||
return (Tvg_Result) reinterpret_cast<Shape*>(paint)->appendRect(x, y, w, h, rx, ry);
|
||||
return (Tvg_Result) reinterpret_cast<Shape*>(paint)->appendRect(x, y, w, h, rx, ry, cw);
|
||||
}
|
||||
|
||||
|
||||
TVG_API Tvg_Result tvg_shape_append_circle(Tvg_Paint* paint, float cx, float cy, float rx, float ry)
|
||||
TVG_API Tvg_Result tvg_shape_append_circle(Tvg_Paint* paint, float cx, float cy, float rx, float ry, bool cw)
|
||||
{
|
||||
if (!paint) return TVG_RESULT_INVALID_ARGUMENT;
|
||||
return (Tvg_Result) reinterpret_cast<Shape*>(paint)->appendCircle(cx, cy, rx, ry);
|
||||
return (Tvg_Result) reinterpret_cast<Shape*>(paint)->appendCircle(cx, cy, rx, ry, cw);
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -371,93 +371,26 @@ static void _repeat(LottieGroup* parent, Shape* path, RenderContext* ctx)
|
|||
}
|
||||
|
||||
|
||||
static void _sharpRect(RenderPath& out, Point& pos, Point& size, float r, bool clockwise, RenderContext* ctx)
|
||||
{
|
||||
auto& pts = out.pts;
|
||||
auto& cmds = out.cmds;
|
||||
|
||||
pts.reserve(4);
|
||||
pts.count = 4;
|
||||
cmds.reserve(5);
|
||||
cmds.count = 5;
|
||||
|
||||
cmds[0] = PathCommand::MoveTo;
|
||||
cmds[1] = cmds[2] = cmds[3] = PathCommand::LineTo;
|
||||
cmds[4] = PathCommand::Close;
|
||||
|
||||
pts[0] = {pos.x + size.x, pos.y};
|
||||
pts[2] = {pos.x, pos.y + size.y};
|
||||
|
||||
if (clockwise) {
|
||||
pts[1] = pos + size;
|
||||
pts[3] = pos;
|
||||
} else {
|
||||
pts[1] = pos;
|
||||
pts[3] = pos + size;
|
||||
}
|
||||
}
|
||||
|
||||
static void _roundRect(RenderPath& out, Point& pos, Point& size, float r, bool clockwise, RenderContext* ctx)
|
||||
{
|
||||
auto& pts = out.pts;
|
||||
auto& cmds = out.cmds;
|
||||
|
||||
pts.reserve(17);
|
||||
pts.count = 17;
|
||||
cmds.reserve(10);
|
||||
cmds.count = 10;
|
||||
|
||||
auto hsize = size * 0.5f;
|
||||
auto rsize = Point{r > hsize.x ? hsize.x : r, r > hsize.y ? hsize.y : r};
|
||||
auto hr = rsize * PATH_KAPPA;
|
||||
|
||||
cmds[0] = PathCommand::MoveTo;
|
||||
cmds[9] = PathCommand::Close;
|
||||
pts[0] = {pos.x + size.x, pos.y + rsize.y}; //move
|
||||
|
||||
if (clockwise) {
|
||||
cmds[1] = cmds[3] = cmds[5] = cmds[7] = PathCommand::LineTo;
|
||||
cmds[2] = cmds[4] = cmds[6] = cmds[8] = PathCommand::CubicTo;
|
||||
|
||||
pts[1] = {pos.x + size.x, pos.y + size.y - rsize.y}; //line
|
||||
pts[2] = {pos.x + size.x, pos.y + size.y - rsize.y + hr.y}; pts[3] = {pos.x + size.x - rsize.x + hr.x, pos.y + size.y}; pts[4] = {pos.x + size.x - rsize.x, pos.y + size.y}; //cubic
|
||||
pts[5] = {pos.x + rsize.x, pos.y + size.y}, //line
|
||||
pts[6] = {pos.x + rsize.x - hr.x, pos.y + size.y}; pts[7] = {pos.x, pos.y + size.y - rsize.y + hr.y}; pts[8] = {pos.x, pos.y + size.y - rsize.y}; //cubic
|
||||
pts[9] = {pos.x, pos.y + rsize.y}, //line
|
||||
pts[10] = {pos.x, pos.y + rsize.y - hr.y}; pts[11] = {pos.x + rsize.x - hr.x, pos.y}; pts[12] = {pos.x + rsize.x, pos.y}; //cubic
|
||||
pts[13] = {pos.x + size.x - rsize.x, pos.y}; //line
|
||||
pts[14] = {pos.x + size.x - rsize.x + hr.x, pos.y}; pts[15] = {pos.x + size.x, pos.y + rsize.y - hr.y}; pts[16] = {pos.x + size.x, pos.y + rsize.y}; //cubic
|
||||
} else {
|
||||
cmds[1] = cmds[3] = cmds[5] = cmds[7] = PathCommand::CubicTo;
|
||||
cmds[2] = cmds[4] = cmds[6] = cmds[8] = PathCommand::LineTo;
|
||||
|
||||
pts[1] = {pos.x + size.x, pos.y + rsize.y - hr.y}; pts[2] = {pos.x + size.x - rsize.x + hr.x, pos.y}; pts[3] = {pos.x + size.x - rsize.x, pos.y}; //cubic
|
||||
pts[4] = {pos.x + rsize.x, pos.y}; //line
|
||||
pts[5] = {pos.x + rsize.x - hr.x, pos.y}; pts[6] = {pos.x, pos.y + rsize.y - hr.y}; pts[7] = {pos.x, pos.y + rsize.y}; //cubic
|
||||
pts[8] = {pos.x, pos.y + size.y - rsize.y}; //line
|
||||
pts[9] = {pos.x, pos.y + size.y - rsize.y + hr.y}; pts[10] = {pos.x + rsize.x - hr.x, pos.y + size.y}; pts[11] = {pos.x + rsize.x, pos.y + size.y}; //cubic
|
||||
pts[12] = {pos.x + size.x - rsize.x, pos.y + size.y}; //line
|
||||
pts[13] = {pos.x + size.x - rsize.x + hr.x, pos.y + size.y}; pts[14] = {pos.x + size.x, pos.y + size.y - rsize.y + hr.y}; pts[15] = {pos.x + size.x, pos.y + size.y - rsize.y}; //cubic
|
||||
pts[16] = {pos.x + size.x, pos.y + rsize.y}; //line
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void LottieBuilder::appendRect(Shape* shape, Point& pos, Point& size, float r, bool clockwise, RenderContext* ctx)
|
||||
{
|
||||
buffer.clear();
|
||||
auto temp = (ctx->offset) ? Shape::gen() : shape;
|
||||
|
||||
if (tvg::zero(r)) _sharpRect(buffer, pos, size, r, clockwise, ctx);
|
||||
else _roundRect(buffer, pos, size, r, clockwise, ctx);
|
||||
auto before = SHAPE(temp)->rs.path.pts.count;
|
||||
|
||||
temp->appendRect(pos.x, pos.y, size.x, size.y, r, r, clockwise);
|
||||
|
||||
auto after = SHAPE(temp)->rs.path.pts.count;
|
||||
|
||||
if (ctx->transform) {
|
||||
ARRAY_FOREACH(pt, buffer.pts) {
|
||||
*pt *= *ctx->transform;
|
||||
for (uint32_t i = before; i < after; ++i) {
|
||||
SHAPE(temp)->rs.path.pts[i] *= *ctx->transform;
|
||||
}
|
||||
}
|
||||
|
||||
if (ctx->offset) ctx->offset->modifyRect(buffer, SHAPE(shape)->rs.path);
|
||||
else shape->appendPath(buffer.cmds.data, buffer.cmds.count, buffer.pts.data, buffer.pts.count);
|
||||
if (ctx->offset) {
|
||||
ctx->offset->modifyRect(SHAPE(temp)->rs.path, SHAPE(shape)->rs.path);
|
||||
delete(temp);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
@ -490,32 +423,18 @@ static void _appendCircle(Shape* shape, Point& center, Point& radius, bool clock
|
|||
{
|
||||
if (ctx->offset) ctx->offset->modifyEllipse(radius);
|
||||
|
||||
if (tvg::zero(radius)) return;
|
||||
auto before = SHAPE(shape)->rs.path.pts.count;
|
||||
|
||||
auto rKappa = radius * PATH_KAPPA;
|
||||
shape->appendCircle(center.x, center.y, radius.x, radius.y, clockwise);
|
||||
|
||||
constexpr int cmdsCnt = 6;
|
||||
PathCommand cmds[cmdsCnt] = {PathCommand::MoveTo, PathCommand::CubicTo, PathCommand::CubicTo, PathCommand::CubicTo, PathCommand::CubicTo, PathCommand::Close};
|
||||
auto after = SHAPE(shape)->rs.path.pts.count;
|
||||
|
||||
constexpr int ptsCnt = 13;
|
||||
Point pts[ptsCnt];
|
||||
|
||||
int table[2][ptsCnt] = {{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12}, {0, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 12}};
|
||||
int* idx = clockwise ? table[0] : table[1];
|
||||
|
||||
pts[idx[0]] = {center.x, center.y - radius.y}; //moveTo
|
||||
pts[idx[1]] = {center.x + rKappa.x, center.y - radius.y}; pts[idx[2]] = {center.x + radius.x, center.y - rKappa.y}; pts[idx[3]] = {center.x + radius.x, center.y}; //cubicTo
|
||||
pts[idx[4]] = {center.x + radius.x, center.y + rKappa.y}; pts[idx[5]] = {center.x + rKappa.x, center.y + radius.y}; pts[idx[6]] = {center.x, center.y + radius.y}; //cubicTo
|
||||
pts[idx[7]] = {center.x - rKappa.x, center.y + radius.y}; pts[idx[8]] = {center.x - radius.x, center.y + rKappa.y}; pts[idx[9]] = {center.x - radius.x, center.y}; //cubicTo
|
||||
pts[idx[10]] = {center.x - radius.x, center.y - rKappa.y}; pts[idx[11]] = {center.x - rKappa.x, center.y - radius.y}; pts[idx[12]] = {center.x, center.y - radius.y}; //cubicTo
|
||||
|
||||
if (ctx->transform) {
|
||||
for (int i = 0; i < ptsCnt; ++i) {
|
||||
pts[i] *= *ctx->transform;
|
||||
for (uint32_t i = before; i < after; ++i) {
|
||||
SHAPE(shape)->rs.path.pts[i] *= *ctx->transform;
|
||||
}
|
||||
}
|
||||
|
||||
shape->appendPath(cmds, cmdsCnt, pts, ptsCnt);
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -180,6 +180,56 @@ static RadialGradient* _applyRadialGradientProperty(SvgStyleGradient* g, const B
|
|||
}
|
||||
|
||||
|
||||
static void _appendRect(Shape* shape, float x, float y, float w, float h, float rx, float ry)
|
||||
{
|
||||
auto halfW = w * 0.5f;
|
||||
auto halfH = h * 0.5f;
|
||||
|
||||
//clamping cornerRadius by minimum size
|
||||
if (rx > halfW) rx = halfW;
|
||||
if (ry > halfH) ry = halfH;
|
||||
|
||||
if (rx == 0 && ry == 0) {
|
||||
SHAPE(shape)->grow(5, 4);
|
||||
shape->moveTo(x, y);
|
||||
shape->lineTo(x + w, y);
|
||||
shape->lineTo(x + w, y + h);
|
||||
shape->lineTo(x, y + h);
|
||||
shape->close();
|
||||
} else {
|
||||
auto hrx = rx * PATH_KAPPA;
|
||||
auto hry = ry * PATH_KAPPA;
|
||||
|
||||
SHAPE(shape)->grow(10, 17);
|
||||
shape->moveTo(x + rx, y);
|
||||
shape->lineTo(x + w - rx, y);
|
||||
shape->cubicTo(x + w - rx + hrx, y, x + w, y + ry - hry, x + w, y + ry);
|
||||
shape->lineTo(x + w, y + h - ry);
|
||||
shape->cubicTo(x + w, y + h - ry + hry, x + w - rx + hrx, y + h, x + w - rx, y + h);
|
||||
shape->lineTo(x + rx, y + h);
|
||||
shape->cubicTo(x + rx - hrx, y + h, x, y + h - ry + hry, x, y + h - ry);
|
||||
shape->lineTo(x, y + ry);
|
||||
shape->cubicTo(x, y + ry - hry, x + rx - hrx, y, x + rx, y);
|
||||
shape->close();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static void _appendCircle(Shape* shape, float cx, float cy, float rx, float ry)
|
||||
{
|
||||
auto rxKappa = rx * PATH_KAPPA;
|
||||
auto ryKappa = ry * PATH_KAPPA;
|
||||
|
||||
SHAPE(shape)->grow(6, 13);
|
||||
shape->moveTo(cx + rx, cy);
|
||||
shape->cubicTo(cx + rx, cy + ryKappa, cx + rxKappa, cy + ry, cx, cy + ry);
|
||||
shape->cubicTo(cx - rxKappa, cy + ry, cx - rx, cy + ryKappa, cx - rx, cy);
|
||||
shape->cubicTo(cx - rx, cy - ryKappa, cx - rxKappa, cy - ry, cx, cy - ry);
|
||||
shape->cubicTo(cx + rxKappa, cy - ry, cx + rx, cy - ryKappa, cx + rx, cy);
|
||||
shape->close();
|
||||
}
|
||||
|
||||
|
||||
static bool _appendClipChild(SvgLoaderData& loaderData, SvgNode* node, Shape* shape, const Box& vBox, const string& svgPath)
|
||||
{
|
||||
//The SVG standard allows only for 'use' nodes that point directly to a basic shape.
|
||||
|
@ -430,7 +480,7 @@ static bool _recognizeShape(SvgNode* node, Shape* shape)
|
|||
break;
|
||||
}
|
||||
case SvgNodeType::Ellipse: {
|
||||
shape->appendCircle(node->node.ellipse.cx, node->node.ellipse.cy, node->node.ellipse.rx, node->node.ellipse.ry);
|
||||
_appendCircle(shape, node->node.ellipse.cx, node->node.ellipse.cy, node->node.ellipse.rx, node->node.ellipse.ry);
|
||||
break;
|
||||
}
|
||||
case SvgNodeType::Polygon: {
|
||||
|
@ -453,11 +503,11 @@ static bool _recognizeShape(SvgNode* node, Shape* shape)
|
|||
break;
|
||||
}
|
||||
case SvgNodeType::Circle: {
|
||||
shape->appendCircle(node->node.circle.cx, node->node.circle.cy, node->node.circle.r, node->node.circle.r);
|
||||
_appendCircle(shape, node->node.circle.cx, node->node.circle.cy, node->node.circle.r, node->node.circle.r);
|
||||
break;
|
||||
}
|
||||
case SvgNodeType::Rect: {
|
||||
shape->appendRect(node->node.rect.x, node->node.rect.y, node->node.rect.w, node->node.rect.h, node->node.rect.rx, node->node.rect.ry);
|
||||
_appendRect(shape, node->node.rect.x, node->node.rect.y, node->node.rect.w, node->node.rect.h, node->node.rect.rx, node->node.rect.ry);
|
||||
break;
|
||||
}
|
||||
case SvgNodeType::Line: {
|
||||
|
@ -774,7 +824,7 @@ static Scene* _useBuildHelper(SvgLoaderData& loaderData, const SvgNode* node, co
|
|||
|
||||
if (!node->node.use.symbol->node.symbol.overflowVisible) {
|
||||
auto viewBoxClip = Shape::gen();
|
||||
viewBoxClip->appendRect(0, 0, width, height, 0, 0);
|
||||
viewBoxClip->appendRect(0, 0, width, height);
|
||||
|
||||
// mClipTransform = mUseTransform * mSymbolTransform
|
||||
Matrix mClipTransform = mUseTransform;
|
||||
|
|
|
@ -95,16 +95,16 @@ Result Shape::close() noexcept
|
|||
}
|
||||
|
||||
|
||||
Result Shape::appendCircle(float cx, float cy, float rx, float ry) noexcept
|
||||
Result Shape::appendCircle(float cx, float cy, float rx, float ry, bool cw) noexcept
|
||||
{
|
||||
SHAPE(this)->appendCircle(cx, cy, rx, ry);
|
||||
SHAPE(this)->appendCircle(cx, cy, rx, ry, cw);
|
||||
return Result::Success;
|
||||
}
|
||||
|
||||
|
||||
Result Shape::appendRect(float x, float y, float w, float h, float rx, float ry) noexcept
|
||||
Result Shape::appendRect(float x, float y, float w, float h, float rx, float ry, bool cw) noexcept
|
||||
{
|
||||
SHAPE(this)->appendRect(x, y, w, h, rx, ry);
|
||||
SHAPE(this)->appendRect(x, y, w, h, rx, ry, cw);
|
||||
return Result::Success;
|
||||
}
|
||||
|
||||
|
|
|
@ -386,52 +386,111 @@ struct Shape::Impl : Paint::Impl
|
|||
return Result::Success;
|
||||
}
|
||||
|
||||
void appendCircle(float cx, float cy, float rx, float ry)
|
||||
void appendCircle(float cx, float cy, float rx, float ry, bool cw)
|
||||
{
|
||||
auto rxKappa = rx * PATH_KAPPA;
|
||||
auto ryKappa = ry * PATH_KAPPA;
|
||||
|
||||
grow(6, 13);
|
||||
moveTo(cx + rx, cy);
|
||||
cubicTo(cx + rx, cy + ryKappa, cx + rxKappa, cy + ry, cx, cy + ry);
|
||||
cubicTo(cx - rxKappa, cy + ry, cx - rx, cy + ryKappa, cx - rx, cy);
|
||||
cubicTo(cx - rx, cy - ryKappa, cx - rxKappa, cy - ry, cx, cy - ry);
|
||||
cubicTo(cx + rxKappa, cy - ry, cx + rx, cy - ryKappa, cx + rx, cy);
|
||||
close();
|
||||
rs.path.cmds.grow(6);
|
||||
auto cmds = rs.path.cmds.end();
|
||||
|
||||
cmds[0] = PathCommand::MoveTo;
|
||||
cmds[1] = PathCommand::CubicTo;
|
||||
cmds[2] = PathCommand::CubicTo;
|
||||
cmds[3] = PathCommand::CubicTo;
|
||||
cmds[4] = PathCommand::CubicTo;
|
||||
cmds[5] = PathCommand::Close;
|
||||
|
||||
rs.path.cmds.count += 6;
|
||||
|
||||
int table[2][13] = {{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12}, {0, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 12}};
|
||||
int* idx = cw ? table[0] : table[1];
|
||||
|
||||
rs.path.pts.grow(13);
|
||||
auto pts = rs.path.pts.end();
|
||||
|
||||
pts[idx[0]] = {cx, cy - ry}; //moveTo
|
||||
pts[idx[1]] = {cx + rxKappa, cy - ry}; pts[idx[2]] = {cx + rx, cy - ryKappa}; pts[idx[3]] = {cx + rx, cy}; //cubicTo
|
||||
pts[idx[4]] = {cx + rx, cy + ryKappa}; pts[idx[5]] = {cx + rxKappa, cy + ry}; pts[idx[6]] = {cx, cy + ry}; //cubicTo
|
||||
pts[idx[7]] = {cx - rxKappa, cy + ry}; pts[idx[8]] = {cx - rx, cy + ryKappa}; pts[idx[9]] = {cx - rx, cy}; //cubicTo
|
||||
pts[idx[10]] = {cx - rx, cy - ryKappa}; pts[idx[11]] = {cx - rxKappa, cy - ry}; pts[idx[12]] = {cx, cy - ry}; //cubicTo
|
||||
|
||||
rs.path.pts.count += 13;
|
||||
|
||||
renderFlag |= RenderUpdateFlag::Path;
|
||||
}
|
||||
|
||||
void appendRect(float x, float y, float w, float h, float rx, float ry)
|
||||
void appendRect(float x, float y, float w, float h, float rx, float ry, bool cw)
|
||||
{
|
||||
auto halfW = w * 0.5f;
|
||||
auto halfH = h * 0.5f;
|
||||
//sharp rect
|
||||
if (tvg::zero(rx) && tvg::zero(ry)) {
|
||||
rs.path.cmds.grow(5);
|
||||
rs.path.pts.grow(4);
|
||||
|
||||
//clamping cornerRadius by minimum size
|
||||
if (rx > halfW) rx = halfW;
|
||||
if (ry > halfH) ry = halfH;
|
||||
auto cmds = rs.path.cmds.end();
|
||||
auto pts = rs.path.pts.end();
|
||||
|
||||
//rectangle
|
||||
if (rx == 0 && ry == 0) {
|
||||
grow(5, 4);
|
||||
moveTo(x, y);
|
||||
lineTo(x + w, y);
|
||||
lineTo(x + w, y + h);
|
||||
lineTo(x, y + h);
|
||||
close();
|
||||
//rounded rectangle or circle
|
||||
cmds[0] = PathCommand::MoveTo;
|
||||
cmds[1] = cmds[2] = cmds[3] = PathCommand::LineTo;
|
||||
cmds[4] = PathCommand::Close;
|
||||
|
||||
pts[0] = {x + w, y};
|
||||
pts[2] = {x, y + h};
|
||||
if (cw) {
|
||||
pts[1] = {x + w, y + h};
|
||||
pts[3] = {x, y};
|
||||
} else {
|
||||
auto hrx = rx * PATH_KAPPA;
|
||||
auto hry = ry * PATH_KAPPA;
|
||||
grow(10, 17);
|
||||
moveTo(x + rx, y);
|
||||
lineTo(x + w - rx, y);
|
||||
cubicTo(x + w - rx + hrx, y, x + w, y + ry - hry, x + w, y + ry);
|
||||
lineTo(x + w, y + h - ry);
|
||||
cubicTo(x + w, y + h - ry + hry, x + w - rx + hrx, y + h, x + w - rx, y + h);
|
||||
lineTo(x + rx, y + h);
|
||||
cubicTo(x + rx - hrx, y + h, x, y + h - ry + hry, x, y + h - ry);
|
||||
lineTo(x, y + ry);
|
||||
cubicTo(x, y + ry - hry, x + rx - hrx, y, x + rx, y);
|
||||
close();
|
||||
pts[1] = {x, y};
|
||||
pts[3] = {x + w, y + h};
|
||||
}
|
||||
|
||||
rs.path.cmds.count += 5;
|
||||
rs.path.pts.count += 4;
|
||||
//round rect
|
||||
} else {
|
||||
auto hsize = Point{w * 0.5f, h * 0.5f};
|
||||
rx = (rx > hsize.x) ? hsize.x : rx;
|
||||
ry = (ry > hsize.y) ? hsize.y : ry;
|
||||
auto hr = Point{rx * PATH_KAPPA, ry * PATH_KAPPA};
|
||||
|
||||
rs.path.cmds.grow(10);
|
||||
rs.path.pts.grow(17);
|
||||
|
||||
auto cmds = rs.path.cmds.end();
|
||||
auto pts = rs.path.pts.end();
|
||||
|
||||
cmds[0] = PathCommand::MoveTo;
|
||||
cmds[9] = PathCommand::Close;
|
||||
pts[0] = {x + w, y + ry}; //move
|
||||
|
||||
if (cw) {
|
||||
cmds[1] = cmds[3] = cmds[5] = cmds[7] = PathCommand::LineTo;
|
||||
cmds[2] = cmds[4] = cmds[6] = cmds[8] = PathCommand::CubicTo;
|
||||
|
||||
pts[1] = {x + w, y + h - ry}; //line
|
||||
pts[2] = {x + w, y + h - ry + hr.y}; pts[3] = {x + w - rx + hr.x, y + h}; pts[4] = {x + w - rx, y + h}; //cubic
|
||||
pts[5] = {x + rx, y + h}, //line
|
||||
pts[6] = {x + rx - hr.x, y + h}; pts[7] = {x, y + h - ry + hr.y}; pts[8] = {x, y + h - ry}; //cubic
|
||||
pts[9] = {x, y + ry}, //line
|
||||
pts[10] = {x, y + ry - hr.y}; pts[11] = {x + rx - hr.x, y}; pts[12] = {x + rx, y}; //cubic
|
||||
pts[13] = {x + w - rx, y}; //line
|
||||
pts[14] = {x + w - rx + hr.x, y}; pts[15] = {x + w, y + ry - hr.y}; pts[16] = {x + w, y + ry}; //cubic
|
||||
} else {
|
||||
cmds[1] = cmds[3] = cmds[5] = cmds[7] = PathCommand::CubicTo;
|
||||
cmds[2] = cmds[4] = cmds[6] = cmds[8] = PathCommand::LineTo;
|
||||
|
||||
pts[1] = {x + w, y + ry - hr.y}; pts[2] = {x + w - rx + hr.x, y}; pts[3] = {x + w - rx, y}; //cubic
|
||||
pts[4] = {x + rx, y}; //line
|
||||
pts[5] = {x + rx - hr.x, y}; pts[6] = {x, y + ry - hr.y}; pts[7] = {x, y + ry}; //cubic
|
||||
pts[8] = {x, y + h - ry}; //line
|
||||
pts[9] = {x, y + h - ry + hr.y}; pts[10] = {x + rx - hr.x, y + h}; pts[11] = {x + rx, y + h}; //cubic
|
||||
pts[12] = {x + w - rx, y + h}; //line
|
||||
pts[13] = {x + w - rx + hr.x, y + h}; pts[14] = {x + w, y + h - ry + hr.y}; pts[15] = {x + w, y + h - ry}; //cubic
|
||||
pts[16] = {x + w, y + ry}; //line
|
||||
}
|
||||
|
||||
rs.path.cmds.count += 10;
|
||||
rs.path.pts.count += 17;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -70,13 +70,14 @@ TEST_CASE("Appending Shapes", "[tvgShape]")
|
|||
REQUIRE(shape->lineTo(120, 140) == Result::Success);
|
||||
|
||||
REQUIRE(shape->appendRect(0, 0, 0, 0, 0, 0) == Result::Success);
|
||||
REQUIRE(shape->appendRect(0, 0,99999999.0f, -99999999.0f, 0, 0) == Result::Success);
|
||||
REQUIRE(shape->appendRect(0, 0, 0, 0, -99999999.0f, 99999999.0f) == Result::Success);
|
||||
REQUIRE(shape->appendRect(99999999.0f, -99999999.0f, 99999999.0f, -99999999.0f, 99999999.0f, -99999999.0f) == Result::Success);
|
||||
REQUIRE(shape->appendRect(0, 0,99999999.0f, -99999999.0f, 0, 0, true) == Result::Success);
|
||||
REQUIRE(shape->appendRect(0, 0, 0, 0, -99999999.0f, 99999999.0f, false) == Result::Success);
|
||||
REQUIRE(shape->appendRect(99999999.0f, -99999999.0f, 99999999.0f, -99999999.0f, 99999999.0f, -99999999.0f, true) == Result::Success);
|
||||
REQUIRE(shape->appendRect(99999999.0f, -99999999.0f, 99999999.0f, -99999999.0f, 99999999.0f, -99999999.0f, false) == Result::Success);
|
||||
|
||||
REQUIRE(shape->appendCircle(0, 0, 0, 0) == Result::Success);
|
||||
REQUIRE(shape->appendCircle(-99999999.0f, 99999999.0f, 0, 0) == Result::Success);
|
||||
REQUIRE(shape->appendCircle(-99999999.0f, 99999999.0f, -99999999.0f, 99999999.0f) == Result::Success);
|
||||
REQUIRE(shape->appendCircle(-99999999.0f, 99999999.0f, 0, 0, true) == Result::Success);
|
||||
REQUIRE(shape->appendCircle(-99999999.0f, 99999999.0f, -99999999.0f, 99999999.0f, false) == Result::Success);
|
||||
}
|
||||
|
||||
TEST_CASE("Appending Paths", "[tvgShape]")
|
||||
|
|
Loading…
Add table
Reference in a new issue