mirror of
https://github.com/thorvg/thorvg.git
synced 2025-06-08 05:33:36 +00:00
common shape: code refactoring & data optimization.
re-design the shape data structure so that render backends are able to access them directly. This also let us remove tvgShape member data from the Shape::Impl. To achieve this, migrate shape/stroke/path from the canvas interface to the render interface.
This commit is contained in:
parent
110f4a5cc9
commit
919c90a97e
13 changed files with 400 additions and 409 deletions
|
@ -54,7 +54,7 @@ class GlGeometry;
|
|||
|
||||
struct GlShape
|
||||
{
|
||||
const Shape* shape = nullptr;
|
||||
const RenderShape* rshape = nullptr;
|
||||
float viewWd;
|
||||
float viewHt;
|
||||
RenderUpdateFlag updateFlag = None;
|
||||
|
|
|
@ -43,13 +43,12 @@ const GlSize GlGeometry::getPrimitiveSize(const uint32_t primitiveIndex) const
|
|||
}
|
||||
|
||||
|
||||
bool GlGeometry::decomposeOutline(const Shape& shape)
|
||||
bool GlGeometry::decomposeOutline(const RenderShape& rshape)
|
||||
{
|
||||
const PathCommand* cmds = nullptr;
|
||||
auto cmdCnt = shape.pathCommands(&cmds);
|
||||
|
||||
Point* pts = nullptr;
|
||||
auto ptsCnt = shape.pathCoords(const_cast<const Point**>(&pts));
|
||||
auto cmds = rshape.path.cmds;
|
||||
auto cmdCnt = rshape.path.cmdCnt;
|
||||
auto pts = rshape.path.pts;
|
||||
auto ptsCnt = rshape.path.ptsCnt;
|
||||
|
||||
//No actual shape data
|
||||
if (cmdCnt == 0 || ptsCnt == 0) return false;
|
||||
|
@ -101,7 +100,7 @@ bool GlGeometry::decomposeOutline(const Shape& shape)
|
|||
return true;
|
||||
}
|
||||
|
||||
bool GlGeometry::generateAAPoints(TVG_UNUSED const Shape& shape, float strokeWd, RenderUpdateFlag flag)
|
||||
bool GlGeometry::generateAAPoints(TVG_UNUSED const RenderShape& rshape, float strokeWd, RenderUpdateFlag flag)
|
||||
{
|
||||
for (auto& shapeGeometry : mPrimitives) {
|
||||
vector<PointNormals> normalInfo;
|
||||
|
@ -158,7 +157,7 @@ bool GlGeometry::generateAAPoints(TVG_UNUSED const Shape& shape, float strokeWd,
|
|||
return true;
|
||||
}
|
||||
|
||||
bool GlGeometry::tesselate(TVG_UNUSED const Shape& shape, float viewWd, float viewHt, RenderUpdateFlag flag)
|
||||
bool GlGeometry::tesselate(TVG_UNUSED const RenderShape& rshape, float viewWd, float viewHt, RenderUpdateFlag flag)
|
||||
{
|
||||
for (auto& shapeGeometry : mPrimitives) {
|
||||
constexpr float opaque = 1.0f;
|
||||
|
|
|
@ -237,9 +237,9 @@ public:
|
|||
|
||||
uint32_t getPrimitiveCount();
|
||||
const GlSize getPrimitiveSize(const uint32_t primitiveIndex) const;
|
||||
bool decomposeOutline(const Shape& shape);
|
||||
bool generateAAPoints(TVG_UNUSED const Shape& shape, float strokeWd, RenderUpdateFlag flag);
|
||||
bool tesselate(TVG_UNUSED const Shape &shape, float viewWd, float viewHt, RenderUpdateFlag flag);
|
||||
bool decomposeOutline(const RenderShape& rshape);
|
||||
bool generateAAPoints(TVG_UNUSED const RenderShape& rshape, float strokeWd, RenderUpdateFlag flag);
|
||||
bool tesselate(TVG_UNUSED const RenderShape& rshape, float viewWd, float viewHt, RenderUpdateFlag flag);
|
||||
void disableVertex(uint32_t location);
|
||||
void draw(const uint32_t location, const uint32_t primitiveIndex, RenderUpdateFlag flag);
|
||||
void updateTransform(const RenderTransform* transform, float w, float h);
|
||||
|
|
|
@ -150,16 +150,13 @@ bool GlRenderer::renderShape(RenderData data)
|
|||
{
|
||||
if (flags & (RenderUpdateFlag::Gradient | RenderUpdateFlag::Transform))
|
||||
{
|
||||
const Fill* gradient = sdata->shape->fill();
|
||||
if (gradient != nullptr)
|
||||
{
|
||||
drawPrimitive(*sdata, gradient, i, RenderUpdateFlag::Gradient);
|
||||
}
|
||||
auto gradient = sdata->rshape->fill;
|
||||
if (gradient) drawPrimitive(*sdata, gradient, i, RenderUpdateFlag::Gradient);
|
||||
}
|
||||
|
||||
if(flags & (RenderUpdateFlag::Color | RenderUpdateFlag::Transform))
|
||||
{
|
||||
sdata->shape->fillColor(&r, &g, &b, &a);
|
||||
sdata->rshape->fillColor(&r, &g, &b, &a);
|
||||
if (a > 0)
|
||||
{
|
||||
drawPrimitive(*sdata, r, g, b, a, i, RenderUpdateFlag::Color);
|
||||
|
@ -168,7 +165,7 @@ bool GlRenderer::renderShape(RenderData data)
|
|||
|
||||
if (flags & (RenderUpdateFlag::Stroke | RenderUpdateFlag::Transform))
|
||||
{
|
||||
sdata->shape->strokeColor(&r, &g, &b, &a);
|
||||
sdata->rshape->strokeColor(&r, &g, &b, &a);
|
||||
if (a > 0)
|
||||
{
|
||||
drawPrimitive(*sdata, r, g, b, a, i, RenderUpdateFlag::Stroke);
|
||||
|
@ -197,13 +194,13 @@ RenderData GlRenderer::prepare(TVG_UNUSED Surface* image, TVG_UNUSED Polygon* tr
|
|||
}
|
||||
|
||||
|
||||
RenderData GlRenderer::prepare(const Shape& shape, RenderData data, const RenderTransform* transform, TVG_UNUSED uint32_t opacity, Array<RenderData>& clips, RenderUpdateFlag flags, TVG_UNUSED bool clipper)
|
||||
RenderData GlRenderer::prepare(const RenderShape& rshape, RenderData data, const RenderTransform* transform, TVG_UNUSED uint32_t opacity, Array<RenderData>& clips, RenderUpdateFlag flags, TVG_UNUSED bool clipper)
|
||||
{
|
||||
//prepare shape data
|
||||
GlShape* sdata = static_cast<GlShape*>(data);
|
||||
if (!sdata) {
|
||||
sdata = new GlShape;
|
||||
sdata->shape = &shape;
|
||||
sdata->rshape = &rshape;
|
||||
}
|
||||
|
||||
sdata->viewWd = static_cast<float>(surface.w);
|
||||
|
@ -216,9 +213,9 @@ RenderData GlRenderer::prepare(const Shape& shape, RenderData data, const Render
|
|||
|
||||
//invisible?
|
||||
uint8_t alphaF, alphaS;
|
||||
shape.fillColor(nullptr, nullptr, nullptr, &alphaF);
|
||||
shape.strokeColor(nullptr, nullptr, nullptr, &alphaS);
|
||||
auto strokeWd = shape.strokeWidth();
|
||||
rshape.fillColor(nullptr, nullptr, nullptr, &alphaF);
|
||||
rshape.strokeColor(nullptr, nullptr, nullptr, &alphaS);
|
||||
auto strokeWd = rshape.strokeWidth();
|
||||
|
||||
if ( ((sdata->updateFlag & RenderUpdateFlag::Gradient) == 0) &&
|
||||
((sdata->updateFlag & RenderUpdateFlag::Color) && alphaF == 0) &&
|
||||
|
@ -231,9 +228,9 @@ RenderData GlRenderer::prepare(const Shape& shape, RenderData data, const Render
|
|||
|
||||
if (sdata->updateFlag & (RenderUpdateFlag::Color | RenderUpdateFlag::Stroke | RenderUpdateFlag::Gradient | RenderUpdateFlag::Transform) )
|
||||
{
|
||||
if (!sdata->geometry->decomposeOutline(shape)) return sdata;
|
||||
if (!sdata->geometry->generateAAPoints(shape, static_cast<float>(strokeWd), sdata->updateFlag)) return sdata;
|
||||
if (!sdata->geometry->tesselate(shape, sdata->viewWd, sdata->viewHt, sdata->updateFlag)) return sdata;
|
||||
if (!sdata->geometry->decomposeOutline(rshape)) return sdata;
|
||||
if (!sdata->geometry->generateAAPoints(rshape, static_cast<float>(strokeWd), sdata->updateFlag)) return sdata;
|
||||
if (!sdata->geometry->tesselate(rshape, sdata->viewWd, sdata->viewHt, sdata->updateFlag)) return sdata;
|
||||
}
|
||||
return sdata;
|
||||
}
|
||||
|
|
|
@ -30,7 +30,7 @@ class GlRenderer : public RenderMethod
|
|||
public:
|
||||
Surface surface = {nullptr, 0, 0, 0};
|
||||
|
||||
RenderData prepare(const Shape& shape, RenderData data, const RenderTransform* transform, uint32_t opacity, Array<RenderData>& clips, RenderUpdateFlag flags, bool clipper) override;
|
||||
RenderData prepare(const RenderShape& rshape, RenderData data, const RenderTransform* transform, uint32_t opacity, Array<RenderData>& clips, RenderUpdateFlag flags, bool clipper) override;
|
||||
RenderData prepare(Surface* image, Polygon* triangles, uint32_t triangleCnt, RenderData data, const RenderTransform* transform, uint32_t opacity, Array<RenderData>& clips, RenderUpdateFlag flags) override;
|
||||
bool preRender() override;
|
||||
bool renderShape(RenderData data) override;
|
||||
|
|
|
@ -302,12 +302,12 @@ bool mathUpdateOutlineBBox(const SwOutline* outline, const SwBBox& clipRegion, S
|
|||
bool mathClipBBox(const SwBBox& clipper, SwBBox& clipee);
|
||||
|
||||
void shapeReset(SwShape* shape);
|
||||
bool shapePrepare(SwShape* shape, const Shape* sdata, const Matrix* transform, const SwBBox& clipRegion, SwBBox& renderRegion, SwMpool* mpool, unsigned tid, bool hasComposite);
|
||||
bool shapePrepare(SwShape* shape, const RenderShape* rshape, const Matrix* transform, const SwBBox& clipRegion, SwBBox& renderRegion, SwMpool* mpool, unsigned tid, bool hasComposite);
|
||||
bool shapePrepared(const SwShape* shape);
|
||||
bool shapeGenRle(SwShape* shape, const Shape* sdata, bool antiAlias);
|
||||
bool shapeGenRle(SwShape* shape, const RenderShape* rshape, bool antiAlias);
|
||||
void shapeDelOutline(SwShape* shape, SwMpool* mpool, uint32_t tid);
|
||||
void shapeResetStroke(SwShape* shape, const Shape* sdata, const Matrix* transform);
|
||||
bool shapeGenStrokeRle(SwShape* shape, const Shape* sdata, const Matrix* transform, const SwBBox& clipRegion, SwBBox& renderRegion, SwMpool* mpool, unsigned tid);
|
||||
void shapeResetStroke(SwShape* shape, const RenderShape* rshape, const Matrix* transform);
|
||||
bool shapeGenStrokeRle(SwShape* shape, const RenderShape* rshape, const Matrix* transform, const SwBBox& clipRegion, SwBBox& renderRegion, SwMpool* mpool, unsigned tid);
|
||||
void shapeFree(SwShape* shape);
|
||||
void shapeDelStroke(SwShape* shape);
|
||||
bool shapeGenFillColors(SwShape* shape, const Fill* fill, const Matrix* transform, SwSurface* surface, uint32_t opacity, bool ctable);
|
||||
|
@ -317,7 +317,7 @@ void shapeResetStrokeFill(SwShape* shape);
|
|||
void shapeDelFill(SwShape* shape);
|
||||
void shapeDelStrokeFill(SwShape* shape);
|
||||
|
||||
void strokeReset(SwStroke* stroke, const Shape* shape, const Matrix* transform);
|
||||
void strokeReset(SwStroke* stroke, const RenderShape* shape, const Matrix* transform);
|
||||
bool strokeParseOutline(SwStroke* stroke, const SwOutline& outline);
|
||||
SwOutline* strokeExportOutline(SwStroke* stroke, SwMpool* mpool, unsigned tid);
|
||||
void strokeFree(SwStroke* stroke);
|
||||
|
|
|
@ -72,7 +72,7 @@ struct SwTask : Task
|
|||
struct SwShapeTask : SwTask
|
||||
{
|
||||
SwShape shape;
|
||||
const Shape* sdata = nullptr;
|
||||
const RenderShape* rshape = nullptr;
|
||||
bool cmpStroking = false;
|
||||
bool clipper = false;
|
||||
|
||||
|
@ -85,9 +85,9 @@ struct SwShapeTask : SwTask
|
|||
bool visibleFill = false;
|
||||
auto clipRegion = bbox;
|
||||
|
||||
if (HALF_STROKE(sdata->strokeWidth()) > 0) {
|
||||
sdata->strokeColor(nullptr, nullptr, nullptr, &strokeAlpha);
|
||||
visibleStroke = sdata->strokeFill() || (static_cast<uint32_t>(strokeAlpha * opacity / 255) > 0);
|
||||
if (HALF_STROKE(rshape->strokeWidth()) > 0) {
|
||||
rshape->strokeColor(nullptr, nullptr, nullptr, &strokeAlpha);
|
||||
visibleStroke = rshape->strokeFill() || (static_cast<uint32_t>(strokeAlpha * opacity / 255) > 0);
|
||||
}
|
||||
|
||||
//This checks also for the case, if the invisible shape turned to visible by alpha.
|
||||
|
@ -97,12 +97,12 @@ struct SwShapeTask : SwTask
|
|||
//Shape
|
||||
if (flags & (RenderUpdateFlag::Path | RenderUpdateFlag::Transform) || prepareShape) {
|
||||
uint8_t alpha = 0;
|
||||
sdata->fillColor(nullptr, nullptr, nullptr, &alpha);
|
||||
rshape->fillColor(nullptr, nullptr, nullptr, &alpha);
|
||||
alpha = static_cast<uint8_t>(static_cast<uint32_t>(alpha) * opacity / 255);
|
||||
visibleFill = (alpha > 0 || sdata->fill());
|
||||
visibleFill = (alpha > 0 || rshape->fill);
|
||||
if (visibleFill || visibleStroke || clipper) {
|
||||
shapeReset(&shape);
|
||||
if (!shapePrepare(&shape, sdata, transform, clipRegion, bbox, mpool, tid, clips.count > 0 ? true : false)) goto err;
|
||||
if (!shapePrepare(&shape, rshape, transform, clipRegion, bbox, mpool, tid, clips.count > 0 ? true : false)) goto err;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -117,11 +117,11 @@ struct SwShapeTask : SwTask
|
|||
shape outline below stroke could be full covered by stroke drawing.
|
||||
Thus it turns off antialising in that condition.
|
||||
Also, it shouldn't be dash style. */
|
||||
auto antiAlias = (strokeAlpha == 255 && sdata->strokeWidth() > 2 && sdata->strokeDash(nullptr) == 0) ? false : true;
|
||||
auto antiAlias = (strokeAlpha == 255 && rshape->strokeWidth() > 2 && rshape->strokeDash(nullptr) == 0) ? false : true;
|
||||
|
||||
if (!shapeGenRle(&shape, sdata, antiAlias)) goto err;
|
||||
if (!shapeGenRle(&shape, rshape, antiAlias)) goto err;
|
||||
}
|
||||
if (auto fill = sdata->fill()) {
|
||||
if (auto fill = rshape->fill) {
|
||||
auto ctable = (flags & RenderUpdateFlag::Gradient) ? true : false;
|
||||
if (ctable) shapeResetFill(&shape);
|
||||
if (!shapeGenFillColors(&shape, fill, transform, surface, cmpStroking ? 255 : opacity, ctable)) goto err;
|
||||
|
@ -133,10 +133,10 @@ struct SwShapeTask : SwTask
|
|||
//Stroke
|
||||
if (flags & (RenderUpdateFlag::Stroke | RenderUpdateFlag::Transform)) {
|
||||
if (visibleStroke) {
|
||||
shapeResetStroke(&shape, sdata, transform);
|
||||
if (!shapeGenStrokeRle(&shape, sdata, transform, clipRegion, bbox, mpool, tid)) goto err;
|
||||
shapeResetStroke(&shape, rshape, transform);
|
||||
if (!shapeGenStrokeRle(&shape, rshape, transform, clipRegion, bbox, mpool, tid)) goto err;
|
||||
|
||||
if (auto fill = sdata->strokeFill()) {
|
||||
if (auto fill = rshape->strokeFill()) {
|
||||
auto ctable = (flags & RenderUpdateFlag::GradientStroke) ? true : false;
|
||||
if (ctable) shapeResetStrokeFill(&shape);
|
||||
if (!shapeGenStrokeFillColors(&shape, fill, transform, surface, cmpStroking ? 255 : opacity, ctable)) goto err;
|
||||
|
@ -397,18 +397,18 @@ bool SwRenderer::renderShape(RenderData data)
|
|||
//Main raster stage
|
||||
uint8_t r, g, b, a;
|
||||
|
||||
if (auto fill = task->sdata->fill()) {
|
||||
if (auto fill = task->rshape->fill) {
|
||||
rasterGradientShape(surface, &task->shape, fill->identifier());
|
||||
} else {
|
||||
task->sdata->fillColor(&r, &g, &b, &a);
|
||||
task->rshape->fillColor(&r, &g, &b, &a);
|
||||
a = static_cast<uint8_t>((opacity * (uint32_t) a) / 255);
|
||||
if (a > 0) rasterShape(surface, &task->shape, r, g, b, a);
|
||||
}
|
||||
|
||||
if (auto strokeFill = task->sdata->strokeFill()) {
|
||||
if (auto strokeFill = task->rshape->strokeFill()) {
|
||||
rasterGradientStroke(surface, &task->shape, strokeFill->identifier());
|
||||
} else {
|
||||
if (task->sdata->strokeColor(&r, &g, &b, &a) == Result::Success) {
|
||||
if (task->rshape->strokeColor(&r, &g, &b, &a)) {
|
||||
a = static_cast<uint8_t>((opacity * (uint32_t) a) / 255);
|
||||
if (a > 0) rasterStroke(surface, &task->shape, r, g, b, a);
|
||||
}
|
||||
|
@ -643,13 +643,13 @@ RenderData SwRenderer::prepare(Surface* image, Polygon* triangles, uint32_t tria
|
|||
}
|
||||
|
||||
|
||||
RenderData SwRenderer::prepare(const Shape& sdata, RenderData data, const RenderTransform* transform, uint32_t opacity, Array<RenderData>& clips, RenderUpdateFlag flags, bool clipper)
|
||||
RenderData SwRenderer::prepare(const RenderShape& rshape, RenderData data, const RenderTransform* transform, uint32_t opacity, Array<RenderData>& clips, RenderUpdateFlag flags, bool clipper)
|
||||
{
|
||||
//prepare task
|
||||
auto task = static_cast<SwShapeTask*>(data);
|
||||
if (!task) {
|
||||
task = new SwShapeTask;
|
||||
task->sdata = &sdata;
|
||||
task->rshape = &rshape;
|
||||
task->clipper = clipper;
|
||||
}
|
||||
return prepareCommon(task, transform, opacity, clips, flags);
|
||||
|
|
|
@ -36,7 +36,7 @@ namespace tvg
|
|||
class SwRenderer : public RenderMethod
|
||||
{
|
||||
public:
|
||||
RenderData prepare(const Shape& shape, RenderData data, const RenderTransform* transform, uint32_t opacity, Array<RenderData>& clips, RenderUpdateFlag flags, bool clipper) override;
|
||||
RenderData prepare(const RenderShape& rshape, RenderData data, const RenderTransform* transform, uint32_t opacity, Array<RenderData>& clips, RenderUpdateFlag flags, bool clipper) override;
|
||||
RenderData prepare(Surface* image, Polygon* triangles, uint32_t triangleCnt, RenderData data, const RenderTransform* transform, uint32_t opacity, Array<RenderData>& clips, RenderUpdateFlag flags) override;
|
||||
bool preRender() override;
|
||||
bool renderShape(RenderData data) override;
|
||||
|
|
|
@ -267,13 +267,13 @@ static void _dashCubicTo(SwDashStroke& dash, const Point* ctrl1, const Point* ct
|
|||
}
|
||||
|
||||
|
||||
static SwOutline* _genDashOutline(const Shape* sdata, const Matrix* transform)
|
||||
static SwOutline* _genDashOutline(const RenderShape* rshape, const Matrix* transform)
|
||||
{
|
||||
const PathCommand* cmds = nullptr;
|
||||
auto cmdCnt = sdata->pathCommands(&cmds);
|
||||
const PathCommand* cmds = rshape->path.cmds;
|
||||
auto cmdCnt = rshape->path.cmdCnt;
|
||||
|
||||
const Point* pts = nullptr;
|
||||
auto ptsCnt = sdata->pathCoords(&pts);
|
||||
const Point* pts = rshape->path.pts;
|
||||
auto ptsCnt = rshape->path.ptsCnt;
|
||||
|
||||
//No actual shape data
|
||||
if (cmdCnt == 0 || ptsCnt == 0) return nullptr;
|
||||
|
@ -286,7 +286,7 @@ static SwOutline* _genDashOutline(const Shape* sdata, const Matrix* transform)
|
|||
dash.curOpGap = false;
|
||||
|
||||
const float* pattern;
|
||||
dash.cnt = sdata->strokeDash(&pattern);
|
||||
dash.cnt = rshape->strokeDash(&pattern);
|
||||
if (dash.cnt == 0) return nullptr;
|
||||
|
||||
//OPTMIZE ME: Use mempool???
|
||||
|
@ -381,13 +381,13 @@ static bool _axisAlignedRect(const SwOutline* outline)
|
|||
|
||||
|
||||
|
||||
static bool _genOutline(SwShape* shape, const Shape* sdata, const Matrix* transform, SwMpool* mpool, unsigned tid, bool hasComposite)
|
||||
static bool _genOutline(SwShape* shape, const RenderShape* rshape, const Matrix* transform, SwMpool* mpool, unsigned tid, bool hasComposite)
|
||||
{
|
||||
const PathCommand* cmds = nullptr;
|
||||
auto cmdCnt = sdata->pathCommands(&cmds);
|
||||
const PathCommand* cmds = rshape->path.cmds;
|
||||
auto cmdCnt = rshape->path.cmdCnt;
|
||||
|
||||
const Point* pts = nullptr;
|
||||
auto ptsCnt = sdata->pathCoords(&pts);
|
||||
const Point* pts = rshape->path.pts;
|
||||
auto ptsCnt = rshape->path.ptsCnt;
|
||||
|
||||
//No actual shape data
|
||||
if (cmdCnt == 0 || ptsCnt == 0) return false;
|
||||
|
@ -467,7 +467,7 @@ static bool _genOutline(SwShape* shape, const Shape* sdata, const Matrix* transf
|
|||
|
||||
_outlineEnd(*outline);
|
||||
|
||||
outline->fillRule = sdata->fillRule();
|
||||
outline->fillRule = rshape->rule;
|
||||
shape->outline = outline;
|
||||
|
||||
shape->fastTrack = (!hasComposite && _axisAlignedRect(shape->outline));
|
||||
|
@ -479,9 +479,9 @@ static bool _genOutline(SwShape* shape, const Shape* sdata, const Matrix* transf
|
|||
/* External Class Implementation */
|
||||
/************************************************************************/
|
||||
|
||||
bool shapePrepare(SwShape* shape, const Shape* sdata, const Matrix* transform, const SwBBox& clipRegion, SwBBox& renderRegion, SwMpool* mpool, unsigned tid, bool hasComposite)
|
||||
bool shapePrepare(SwShape* shape, const RenderShape* rshape, const Matrix* transform, const SwBBox& clipRegion, SwBBox& renderRegion, SwMpool* mpool, unsigned tid, bool hasComposite)
|
||||
{
|
||||
if (!_genOutline(shape, sdata, transform, mpool, tid, hasComposite)) return false;
|
||||
if (!_genOutline(shape, rshape, transform, mpool, tid, hasComposite)) return false;
|
||||
if (!mathUpdateOutlineBBox(shape->outline, clipRegion, renderRegion, shape->fastTrack)) return false;
|
||||
|
||||
//Keep it for Rasterization Region
|
||||
|
@ -504,7 +504,7 @@ bool shapePrepared(const SwShape* shape)
|
|||
}
|
||||
|
||||
|
||||
bool shapeGenRle(SwShape* shape, TVG_UNUSED const Shape* sdata, bool antiAlias)
|
||||
bool shapeGenRle(SwShape* shape, TVG_UNUSED const RenderShape* rshape, bool antiAlias)
|
||||
{
|
||||
//FIXME: Should we draw it?
|
||||
//Case: Stroke Line
|
||||
|
@ -558,18 +558,18 @@ void shapeDelStroke(SwShape* shape)
|
|||
}
|
||||
|
||||
|
||||
void shapeResetStroke(SwShape* shape, const Shape* sdata, const Matrix* transform)
|
||||
void shapeResetStroke(SwShape* shape, const RenderShape* rshape, const Matrix* transform)
|
||||
{
|
||||
if (!shape->stroke) shape->stroke = static_cast<SwStroke*>(calloc(1, sizeof(SwStroke)));
|
||||
auto stroke = shape->stroke;
|
||||
if (!stroke) return;
|
||||
|
||||
strokeReset(stroke, sdata, transform);
|
||||
strokeReset(stroke, rshape, transform);
|
||||
rleReset(shape->strokeRle);
|
||||
}
|
||||
|
||||
|
||||
bool shapeGenStrokeRle(SwShape* shape, const Shape* sdata, const Matrix* transform, const SwBBox& clipRegion, SwBBox& renderRegion, SwMpool* mpool, unsigned tid)
|
||||
bool shapeGenStrokeRle(SwShape* shape, const RenderShape* rshape, const Matrix* transform, const SwBBox& clipRegion, SwBBox& renderRegion, SwMpool* mpool, unsigned tid)
|
||||
{
|
||||
SwOutline* shapeOutline = nullptr;
|
||||
SwOutline* strokeOutline = nullptr;
|
||||
|
@ -577,14 +577,14 @@ bool shapeGenStrokeRle(SwShape* shape, const Shape* sdata, const Matrix* transfo
|
|||
bool ret = true;
|
||||
|
||||
//Dash Style Stroke
|
||||
if (sdata->strokeDash(nullptr) > 0) {
|
||||
shapeOutline = _genDashOutline(sdata, transform);
|
||||
if (rshape->strokeDash(nullptr) > 0) {
|
||||
shapeOutline = _genDashOutline(rshape, transform);
|
||||
if (!shapeOutline) return false;
|
||||
freeOutline = true;
|
||||
//Normal Style stroke
|
||||
} else {
|
||||
if (!shape->outline) {
|
||||
if (!_genOutline(shape, sdata, transform, mpool, tid, false)) return false;
|
||||
if (!_genOutline(shape, rshape, transform, mpool, tid, false)) return false;
|
||||
}
|
||||
shapeOutline = shape->outline;
|
||||
}
|
||||
|
|
|
@ -826,7 +826,7 @@ void strokeFree(SwStroke* stroke)
|
|||
}
|
||||
|
||||
|
||||
void strokeReset(SwStroke* stroke, const Shape* sdata, const Matrix* transform)
|
||||
void strokeReset(SwStroke* stroke, const RenderShape* rshape, const Matrix* transform)
|
||||
{
|
||||
if (transform) {
|
||||
stroke->sx = sqrtf(powf(transform->e11, 2.0f) + powf(transform->e21, 2.0f));
|
||||
|
@ -835,11 +835,11 @@ void strokeReset(SwStroke* stroke, const Shape* sdata, const Matrix* transform)
|
|||
stroke->sx = stroke->sy = 1.0f;
|
||||
}
|
||||
|
||||
stroke->width = HALF_STROKE(sdata->strokeWidth());
|
||||
stroke->cap = sdata->strokeCap();
|
||||
stroke->width = HALF_STROKE(rshape->strokeWidth());
|
||||
stroke->cap = rshape->strokeCap();
|
||||
|
||||
//Save line join: it can be temporarily changed when stroking curves...
|
||||
stroke->joinSaved = stroke->join = sdata->strokeJoin();
|
||||
stroke->joinSaved = stroke->join = rshape->strokeJoin();
|
||||
|
||||
stroke->borders[0].ptsCnt = 0;
|
||||
stroke->borders[0].start = -1;
|
||||
|
|
|
@ -85,12 +85,107 @@ struct RenderTransform
|
|||
RenderTransform(const RenderTransform* lhs, const RenderTransform* rhs);
|
||||
};
|
||||
|
||||
struct RenderStroke
|
||||
{
|
||||
float width = 0.0f;
|
||||
uint8_t color[4] = {0, 0, 0, 0};
|
||||
Fill *fill = nullptr;
|
||||
float* dashPattern = nullptr;
|
||||
uint32_t dashCnt = 0;
|
||||
StrokeCap cap = StrokeCap::Square;
|
||||
StrokeJoin join = StrokeJoin::Bevel;
|
||||
|
||||
~RenderStroke()
|
||||
{
|
||||
free(dashPattern);
|
||||
if (fill) delete(fill);
|
||||
}
|
||||
};
|
||||
|
||||
struct RenderShape
|
||||
{
|
||||
struct
|
||||
{
|
||||
PathCommand* cmds = nullptr;
|
||||
uint32_t cmdCnt = 0;
|
||||
uint32_t reservedCmdCnt = 0;
|
||||
|
||||
Point *pts = nullptr;
|
||||
uint32_t ptsCnt = 0;
|
||||
uint32_t reservedPtsCnt = 0;
|
||||
} path;
|
||||
|
||||
Fill *fill = nullptr;
|
||||
RenderStroke *stroke = nullptr;
|
||||
uint8_t color[4] = {0, 0, 0, 0}; //r, g, b, a
|
||||
FillRule rule = FillRule::Winding;
|
||||
|
||||
~RenderShape()
|
||||
{
|
||||
free(path.cmds);
|
||||
free(path.pts);
|
||||
|
||||
if (fill) delete(fill);
|
||||
if (stroke) delete(stroke);
|
||||
}
|
||||
|
||||
void fillColor(uint8_t* r, uint8_t* g, uint8_t* b, uint8_t* a) const
|
||||
{
|
||||
if (r) *r = color[0];
|
||||
if (g) *g = color[1];
|
||||
if (b) *b = color[2];
|
||||
if (a) *a = color[3];
|
||||
}
|
||||
|
||||
float strokeWidth() const
|
||||
{
|
||||
if (!stroke) return 0;
|
||||
return stroke->width;
|
||||
}
|
||||
|
||||
bool strokeColor(uint8_t* r, uint8_t* g, uint8_t* b, uint8_t* a) const
|
||||
{
|
||||
if (!stroke) return false;
|
||||
|
||||
if (r) *r = stroke->color[0];
|
||||
if (g) *g = stroke->color[1];
|
||||
if (b) *b = stroke->color[2];
|
||||
if (a) *a = stroke->color[3];
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
const Fill* strokeFill() const
|
||||
{
|
||||
if (!stroke) return nullptr;
|
||||
return stroke->fill;
|
||||
}
|
||||
|
||||
uint32_t strokeDash(const float** dashPattern) const
|
||||
{
|
||||
if (!stroke) return 0;
|
||||
if (dashPattern) *dashPattern = stroke->dashPattern;
|
||||
return stroke->dashCnt;
|
||||
}
|
||||
|
||||
StrokeCap strokeCap() const
|
||||
{
|
||||
if (!stroke) return StrokeCap::Square;
|
||||
return stroke->cap;
|
||||
}
|
||||
|
||||
StrokeJoin strokeJoin() const
|
||||
{
|
||||
if (!stroke) return StrokeJoin::Bevel;
|
||||
return stroke->join;
|
||||
}
|
||||
};
|
||||
|
||||
class RenderMethod
|
||||
{
|
||||
public:
|
||||
virtual ~RenderMethod() {}
|
||||
virtual RenderData prepare(const Shape& shape, RenderData data, const RenderTransform* transform, uint32_t opacity, Array<RenderData>& clips, RenderUpdateFlag flags, bool clipper) = 0;
|
||||
virtual RenderData prepare(const RenderShape& rshape, RenderData data, const RenderTransform* transform, uint32_t opacity, Array<RenderData>& clips, RenderUpdateFlag flags, bool clipper) = 0;
|
||||
virtual RenderData prepare(Surface* image, Polygon* triangles, uint32_t triangleCnt, RenderData data, const RenderTransform* transform, uint32_t opacity, Array<RenderData>& clips, RenderUpdateFlag flags) = 0;
|
||||
virtual bool preRender() = 0;
|
||||
virtual bool renderShape(RenderData data) = 0;
|
||||
|
|
|
@ -32,7 +32,7 @@ constexpr auto PATH_KAPPA = 0.552284f;
|
|||
/* External Class Implementation */
|
||||
/************************************************************************/
|
||||
|
||||
Shape :: Shape() : pImpl(new Impl(this))
|
||||
Shape :: Shape() : pImpl(new Impl())
|
||||
{
|
||||
Paint::pImpl->id = TVG_CLASS_ID_SHAPE;
|
||||
Paint::pImpl->method(new PaintMethod<Shape::Impl>(pImpl));
|
||||
|
@ -59,8 +59,7 @@ uint32_t Shape::identifier() noexcept
|
|||
|
||||
Result Shape::reset() noexcept
|
||||
{
|
||||
pImpl->path.reset();
|
||||
pImpl->flag = RenderUpdateFlag::Path;
|
||||
pImpl->reset();
|
||||
|
||||
return Result::Success;
|
||||
}
|
||||
|
@ -70,9 +69,9 @@ uint32_t Shape::pathCommands(const PathCommand** cmds) const noexcept
|
|||
{
|
||||
if (!cmds) return 0;
|
||||
|
||||
*cmds = pImpl->path.cmds;
|
||||
*cmds = pImpl->rs.path.cmds;
|
||||
|
||||
return pImpl->path.cmdCnt;
|
||||
return pImpl->rs.path.cmdCnt;
|
||||
}
|
||||
|
||||
|
||||
|
@ -80,9 +79,9 @@ uint32_t Shape::pathCoords(const Point** pts) const noexcept
|
|||
{
|
||||
if (!pts) return 0;
|
||||
|
||||
*pts = pImpl->path.pts;
|
||||
*pts = pImpl->rs.path.pts;
|
||||
|
||||
return pImpl->path.ptsCnt;
|
||||
return pImpl->rs.path.ptsCnt;
|
||||
}
|
||||
|
||||
|
||||
|
@ -90,10 +89,8 @@ Result Shape::appendPath(const PathCommand *cmds, uint32_t cmdCnt, const Point*
|
|||
{
|
||||
if (cmdCnt == 0 || ptsCnt == 0 || !cmds || !pts) return Result::InvalidArguments;
|
||||
|
||||
pImpl->path.grow(cmdCnt, ptsCnt);
|
||||
pImpl->path.append(cmds, cmdCnt, pts, ptsCnt);
|
||||
|
||||
pImpl->flag |= RenderUpdateFlag::Path;
|
||||
pImpl->grow(cmdCnt, ptsCnt);
|
||||
pImpl->append(cmds, cmdCnt, pts, ptsCnt);
|
||||
|
||||
return Result::Success;
|
||||
}
|
||||
|
@ -101,9 +98,7 @@ Result Shape::appendPath(const PathCommand *cmds, uint32_t cmdCnt, const Point*
|
|||
|
||||
Result Shape::moveTo(float x, float y) noexcept
|
||||
{
|
||||
pImpl->path.moveTo(x, y);
|
||||
|
||||
pImpl->flag |= RenderUpdateFlag::Path;
|
||||
pImpl->moveTo(x, y);
|
||||
|
||||
return Result::Success;
|
||||
}
|
||||
|
@ -111,9 +106,7 @@ Result Shape::moveTo(float x, float y) noexcept
|
|||
|
||||
Result Shape::lineTo(float x, float y) noexcept
|
||||
{
|
||||
pImpl->path.lineTo(x, y);
|
||||
|
||||
pImpl->flag |= RenderUpdateFlag::Path;
|
||||
pImpl->lineTo(x, y);
|
||||
|
||||
return Result::Success;
|
||||
}
|
||||
|
@ -121,9 +114,7 @@ Result Shape::lineTo(float x, float y) noexcept
|
|||
|
||||
Result Shape::cubicTo(float cx1, float cy1, float cx2, float cy2, float x, float y) noexcept
|
||||
{
|
||||
pImpl->path.cubicTo(cx1, cy1, cx2, cy2, x, y);
|
||||
|
||||
pImpl->flag |= RenderUpdateFlag::Path;
|
||||
pImpl->cubicTo(cx1, cy1, cx2, cy2, x, y);
|
||||
|
||||
return Result::Success;
|
||||
}
|
||||
|
@ -131,9 +122,7 @@ Result Shape::cubicTo(float cx1, float cy1, float cx2, float cy2, float x, float
|
|||
|
||||
Result Shape::close() noexcept
|
||||
{
|
||||
pImpl->path.close();
|
||||
|
||||
pImpl->flag |= RenderUpdateFlag::Path;
|
||||
pImpl->close();
|
||||
|
||||
return Result::Success;
|
||||
}
|
||||
|
@ -144,15 +133,13 @@ Result Shape::appendCircle(float cx, float cy, float rx, float ry) noexcept
|
|||
auto rxKappa = rx * PATH_KAPPA;
|
||||
auto ryKappa = ry * PATH_KAPPA;
|
||||
|
||||
pImpl->path.grow(6, 13);
|
||||
pImpl->path.moveTo(cx, cy - ry);
|
||||
pImpl->path.cubicTo(cx + rxKappa, cy - ry, cx + rx, cy - ryKappa, cx + rx, cy);
|
||||
pImpl->path.cubicTo(cx + rx, cy + ryKappa, cx + rxKappa, cy + ry, cx, cy + ry);
|
||||
pImpl->path.cubicTo(cx - rxKappa, cy + ry, cx - rx, cy + ryKappa, cx - rx, cy);
|
||||
pImpl->path.cubicTo(cx - rx, cy - ryKappa, cx - rxKappa, cy - ry, cx, cy - ry);
|
||||
pImpl->path.close();
|
||||
|
||||
pImpl->flag |= RenderUpdateFlag::Path;
|
||||
pImpl->grow(6, 13);
|
||||
pImpl->moveTo(cx, cy - ry);
|
||||
pImpl->cubicTo(cx + rxKappa, cy - ry, cx + rx, cy - ryKappa, cx + rx, cy);
|
||||
pImpl->cubicTo(cx + rx, cy + ryKappa, cx + rxKappa, cy + ry, cx, cy + ry);
|
||||
pImpl->cubicTo(cx - rxKappa, cy + ry, cx - rx, cy + ryKappa, cx - rx, cy);
|
||||
pImpl->cubicTo(cx - rx, cy - ryKappa, cx - rxKappa, cy - ry, cx, cy - ry);
|
||||
pImpl->close();
|
||||
|
||||
return Result::Success;
|
||||
}
|
||||
|
@ -174,10 +161,10 @@ Result Shape::appendArc(float cx, float cy, float radius, float startAngle, floa
|
|||
Point start = {radius * cosf(startAngle), radius * sinf(startAngle)};
|
||||
|
||||
if (pie) {
|
||||
pImpl->path.moveTo(cx, cy);
|
||||
pImpl->path.lineTo(start.x + cx, start.y + cy);
|
||||
pImpl->moveTo(cx, cy);
|
||||
pImpl->lineTo(start.x + cx, start.y + cy);
|
||||
} else {
|
||||
pImpl->path.moveTo(start.x + cx, start.y + cy);
|
||||
pImpl->moveTo(start.x + cx, start.y + cy);
|
||||
}
|
||||
|
||||
for (int i = 0; i < nCurves; ++i) {
|
||||
|
@ -204,14 +191,12 @@ Result Shape::appendArc(float cx, float cy, float radius, float startAngle, floa
|
|||
Point ctrl1 = {ax - k2 * ay + cx, ay + k2 * ax + cy};
|
||||
Point ctrl2 = {bx + k2 * by + cx, by - k2 * bx + cy};
|
||||
|
||||
pImpl->path.cubicTo(ctrl1.x, ctrl1.y, ctrl2.x, ctrl2.y, end.x, end.y);
|
||||
pImpl->cubicTo(ctrl1.x, ctrl1.y, ctrl2.x, ctrl2.y, end.x, end.y);
|
||||
|
||||
startAngle = endAngle;
|
||||
}
|
||||
|
||||
if (pie) pImpl->path.close();
|
||||
|
||||
pImpl->flag |= RenderUpdateFlag::Path;
|
||||
if (pie) pImpl->close();
|
||||
|
||||
return Result::Success;
|
||||
}
|
||||
|
@ -228,48 +213,46 @@ Result Shape::appendRect(float x, float y, float w, float h, float rx, float ry)
|
|||
|
||||
//rectangle
|
||||
if (rx == 0 && ry == 0) {
|
||||
pImpl->path.grow(5, 4);
|
||||
pImpl->path.moveTo(x, y);
|
||||
pImpl->path.lineTo(x + w, y);
|
||||
pImpl->path.lineTo(x + w, y + h);
|
||||
pImpl->path.lineTo(x, y + h);
|
||||
pImpl->path.close();
|
||||
pImpl->grow(5, 4);
|
||||
pImpl->moveTo(x, y);
|
||||
pImpl->lineTo(x + w, y);
|
||||
pImpl->lineTo(x + w, y + h);
|
||||
pImpl->lineTo(x, y + h);
|
||||
pImpl->close();
|
||||
//circle
|
||||
} else if (mathEqual(rx, halfW) && mathEqual(ry, halfH)) {
|
||||
return appendCircle(x + (w * 0.5f), y + (h * 0.5f), rx, ry);
|
||||
} else {
|
||||
auto hrx = rx * 0.5f;
|
||||
auto hry = ry * 0.5f;
|
||||
pImpl->path.grow(10, 17);
|
||||
pImpl->path.moveTo(x + rx, y);
|
||||
pImpl->path.lineTo(x + w - rx, y);
|
||||
pImpl->path.cubicTo(x + w - rx + hrx, y, x + w, y + ry - hry, x + w, y + ry);
|
||||
pImpl->path.lineTo(x + w, y + h - ry);
|
||||
pImpl->path.cubicTo(x + w, y + h - ry + hry, x + w - rx + hrx, y + h, x + w - rx, y + h);
|
||||
pImpl->path.lineTo(x + rx, y + h);
|
||||
pImpl->path.cubicTo(x + rx - hrx, y + h, x, y + h - ry + hry, x, y + h - ry);
|
||||
pImpl->path.lineTo(x, y + ry);
|
||||
pImpl->path.cubicTo(x, y + ry - hry, x + rx - hrx, y, x + rx, y);
|
||||
pImpl->path.close();
|
||||
pImpl->grow(10, 17);
|
||||
pImpl->moveTo(x + rx, y);
|
||||
pImpl->lineTo(x + w - rx, y);
|
||||
pImpl->cubicTo(x + w - rx + hrx, y, x + w, y + ry - hry, x + w, y + ry);
|
||||
pImpl->lineTo(x + w, y + h - ry);
|
||||
pImpl->cubicTo(x + w, y + h - ry + hry, x + w - rx + hrx, y + h, x + w - rx, y + h);
|
||||
pImpl->lineTo(x + rx, y + h);
|
||||
pImpl->cubicTo(x + rx - hrx, y + h, x, y + h - ry + hry, x, y + h - ry);
|
||||
pImpl->lineTo(x, y + ry);
|
||||
pImpl->cubicTo(x, y + ry - hry, x + rx - hrx, y, x + rx, y);
|
||||
pImpl->close();
|
||||
}
|
||||
|
||||
pImpl->flag |= RenderUpdateFlag::Path;
|
||||
|
||||
return Result::Success;
|
||||
}
|
||||
|
||||
|
||||
Result Shape::fill(uint8_t r, uint8_t g, uint8_t b, uint8_t a) noexcept
|
||||
{
|
||||
pImpl->color[0] = r;
|
||||
pImpl->color[1] = g;
|
||||
pImpl->color[2] = b;
|
||||
pImpl->color[3] = a;
|
||||
pImpl->rs.color[0] = r;
|
||||
pImpl->rs.color[1] = g;
|
||||
pImpl->rs.color[2] = b;
|
||||
pImpl->rs.color[3] = a;
|
||||
pImpl->flag |= RenderUpdateFlag::Color;
|
||||
|
||||
if (pImpl->fill) {
|
||||
delete(pImpl->fill);
|
||||
pImpl->fill = nullptr;
|
||||
if (pImpl->rs.fill) {
|
||||
delete(pImpl->rs.fill);
|
||||
pImpl->rs.fill = nullptr;
|
||||
pImpl->flag |= RenderUpdateFlag::Gradient;
|
||||
}
|
||||
|
||||
|
@ -282,8 +265,8 @@ Result Shape::fill(unique_ptr<Fill> f) noexcept
|
|||
auto p = f.release();
|
||||
if (!p) return Result::MemoryCorruption;
|
||||
|
||||
if (pImpl->fill && pImpl->fill != p) delete(pImpl->fill);
|
||||
pImpl->fill = p;
|
||||
if (pImpl->rs.fill && pImpl->rs.fill != p) delete(pImpl->rs.fill);
|
||||
pImpl->rs.fill = p;
|
||||
pImpl->flag |= RenderUpdateFlag::Gradient;
|
||||
|
||||
return Result::Success;
|
||||
|
@ -292,17 +275,15 @@ Result Shape::fill(unique_ptr<Fill> f) noexcept
|
|||
|
||||
Result Shape::fillColor(uint8_t* r, uint8_t* g, uint8_t* b, uint8_t* a) const noexcept
|
||||
{
|
||||
if (r) *r = pImpl->color[0];
|
||||
if (g) *g = pImpl->color[1];
|
||||
if (b) *b = pImpl->color[2];
|
||||
if (a) *a = pImpl->color[3];
|
||||
pImpl->rs.fillColor(r, g, b, a);
|
||||
|
||||
return Result::Success;
|
||||
}
|
||||
|
||||
|
||||
const Fill* Shape::fill() const noexcept
|
||||
{
|
||||
return pImpl->fill;
|
||||
return pImpl->rs.fill;
|
||||
}
|
||||
|
||||
|
||||
|
@ -316,8 +297,7 @@ Result Shape::stroke(float width) noexcept
|
|||
|
||||
float Shape::strokeWidth() const noexcept
|
||||
{
|
||||
if (!pImpl->stroke) return 0;
|
||||
return pImpl->stroke->width;
|
||||
return pImpl->rs.strokeWidth();
|
||||
}
|
||||
|
||||
|
||||
|
@ -331,12 +311,7 @@ Result Shape::stroke(uint8_t r, uint8_t g, uint8_t b, uint8_t a) noexcept
|
|||
|
||||
Result Shape::strokeColor(uint8_t* r, uint8_t* g, uint8_t* b, uint8_t* a) const noexcept
|
||||
{
|
||||
if (!pImpl->stroke) return Result::InsufficientCondition;
|
||||
|
||||
if (r) *r = pImpl->stroke->color[0];
|
||||
if (g) *g = pImpl->stroke->color[1];
|
||||
if (b) *b = pImpl->stroke->color[2];
|
||||
if (a) *a = pImpl->stroke->color[3];
|
||||
if (!pImpl->rs.strokeColor(r, g, b, a)) return Result::InsufficientCondition;
|
||||
|
||||
return Result::Success;
|
||||
}
|
||||
|
@ -350,9 +325,7 @@ Result Shape::stroke(unique_ptr<Fill> f) noexcept
|
|||
|
||||
const Fill* Shape::strokeFill() const noexcept
|
||||
{
|
||||
if (!pImpl->stroke) return nullptr;
|
||||
|
||||
return pImpl->stroke->fill;
|
||||
return pImpl->rs.strokeFill();
|
||||
}
|
||||
|
||||
|
||||
|
@ -373,11 +346,7 @@ Result Shape::stroke(const float* dashPattern, uint32_t cnt) noexcept
|
|||
|
||||
uint32_t Shape::strokeDash(const float** dashPattern) const noexcept
|
||||
{
|
||||
if (!pImpl->stroke) return 0;
|
||||
|
||||
if (dashPattern) *dashPattern = pImpl->stroke->dashPattern;
|
||||
|
||||
return pImpl->stroke->dashCnt;
|
||||
return pImpl->rs.strokeDash(dashPattern);
|
||||
}
|
||||
|
||||
|
||||
|
@ -399,23 +368,19 @@ Result Shape::stroke(StrokeJoin join) noexcept
|
|||
|
||||
StrokeCap Shape::strokeCap() const noexcept
|
||||
{
|
||||
if (!pImpl->stroke) return StrokeCap::Square;
|
||||
|
||||
return pImpl->stroke->cap;
|
||||
return pImpl->rs.strokeCap();
|
||||
}
|
||||
|
||||
|
||||
StrokeJoin Shape::strokeJoin() const noexcept
|
||||
{
|
||||
if (!pImpl->stroke) return StrokeJoin::Bevel;
|
||||
|
||||
return pImpl->stroke->join;
|
||||
return pImpl->rs.strokeJoin();
|
||||
}
|
||||
|
||||
|
||||
Result Shape::fill(FillRule r) noexcept
|
||||
{
|
||||
pImpl->rule = r;
|
||||
pImpl->rs.rule = r;
|
||||
|
||||
return Result::Success;
|
||||
}
|
||||
|
@ -423,5 +388,5 @@ Result Shape::fill(FillRule r) noexcept
|
|||
|
||||
FillRule Shape::fillRule() const noexcept
|
||||
{
|
||||
return pImpl->rule;
|
||||
return pImpl->rs.rule;
|
||||
}
|
||||
|
|
|
@ -30,199 +30,12 @@
|
|||
/* Internal Class Implementation */
|
||||
/************************************************************************/
|
||||
|
||||
struct ShapeStroke
|
||||
{
|
||||
float width;
|
||||
uint8_t color[4];
|
||||
Fill *fill;
|
||||
float* dashPattern;
|
||||
uint32_t dashCnt;
|
||||
StrokeCap cap;
|
||||
StrokeJoin join;
|
||||
|
||||
void copy(const ShapeStroke* src)
|
||||
{
|
||||
width = src->width;
|
||||
dashCnt = src->dashCnt;
|
||||
cap = src->cap;
|
||||
join = src->join;
|
||||
|
||||
memcpy(color, src->color, sizeof(color));
|
||||
if (dashCnt > 0) {
|
||||
dashPattern = static_cast<float*>(malloc(sizeof(float) * dashCnt));
|
||||
memcpy(dashPattern, src->dashPattern, sizeof(float) * dashCnt);
|
||||
}
|
||||
if (src->fill) fill = src->fill->duplicate();
|
||||
}
|
||||
|
||||
void clear()
|
||||
{
|
||||
if (dashPattern) free(dashPattern);
|
||||
if (fill) delete(fill);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
struct ShapePath
|
||||
{
|
||||
PathCommand* cmds = nullptr;
|
||||
uint32_t cmdCnt = 0;
|
||||
uint32_t reservedCmdCnt = 0;
|
||||
|
||||
Point *pts = nullptr;
|
||||
uint32_t ptsCnt = 0;
|
||||
uint32_t reservedPtsCnt = 0;
|
||||
|
||||
~ShapePath()
|
||||
{
|
||||
if (cmds) free(cmds);
|
||||
if (pts) free(pts);
|
||||
}
|
||||
|
||||
ShapePath()
|
||||
{
|
||||
}
|
||||
|
||||
void duplicate(const ShapePath* src)
|
||||
{
|
||||
if (src->cmdCnt == 0 || src->ptsCnt == 0) return;
|
||||
|
||||
cmdCnt = src->cmdCnt;
|
||||
reservedCmdCnt = src->reservedCmdCnt;
|
||||
ptsCnt = src->ptsCnt;
|
||||
reservedPtsCnt = src->reservedPtsCnt;
|
||||
|
||||
cmds = static_cast<PathCommand*>(malloc(sizeof(PathCommand) * reservedCmdCnt));
|
||||
if (!cmds) return;
|
||||
memcpy(cmds, src->cmds, sizeof(PathCommand) * cmdCnt);
|
||||
|
||||
pts = static_cast<Point*>(malloc(sizeof(Point) * reservedPtsCnt));
|
||||
if (!pts) {
|
||||
free(cmds);
|
||||
return;
|
||||
}
|
||||
memcpy(pts, src->pts, sizeof(Point) * ptsCnt);
|
||||
}
|
||||
|
||||
void reserveCmd(uint32_t cmdCnt)
|
||||
{
|
||||
if (cmdCnt <= reservedCmdCnt) return;
|
||||
reservedCmdCnt = cmdCnt;
|
||||
cmds = static_cast<PathCommand*>(realloc(cmds, sizeof(PathCommand) * reservedCmdCnt));
|
||||
}
|
||||
|
||||
void reservePts(uint32_t ptsCnt)
|
||||
{
|
||||
if (ptsCnt <= reservedPtsCnt) return;
|
||||
reservedPtsCnt = ptsCnt;
|
||||
pts = static_cast<Point*>(realloc(pts, sizeof(Point) * reservedPtsCnt));
|
||||
}
|
||||
|
||||
void grow(uint32_t cmdCnt, uint32_t ptsCnt)
|
||||
{
|
||||
reserveCmd(this->cmdCnt + cmdCnt);
|
||||
reservePts(this->ptsCnt + ptsCnt);
|
||||
}
|
||||
|
||||
void reset()
|
||||
{
|
||||
cmdCnt = 0;
|
||||
ptsCnt = 0;
|
||||
}
|
||||
|
||||
void append(const PathCommand* cmds, uint32_t cmdCnt, const Point* pts, uint32_t ptsCnt)
|
||||
{
|
||||
memcpy(this->cmds + this->cmdCnt, cmds, sizeof(PathCommand) * cmdCnt);
|
||||
memcpy(this->pts + this->ptsCnt, pts, sizeof(Point) * ptsCnt);
|
||||
this->cmdCnt += cmdCnt;
|
||||
this->ptsCnt += ptsCnt;
|
||||
}
|
||||
|
||||
void moveTo(float x, float y)
|
||||
{
|
||||
if (cmdCnt + 1 > reservedCmdCnt) reserveCmd((cmdCnt + 1) * 2);
|
||||
if (ptsCnt + 2 > reservedPtsCnt) reservePts((ptsCnt + 2) * 2);
|
||||
|
||||
cmds[cmdCnt++] = PathCommand::MoveTo;
|
||||
pts[ptsCnt++] = {x, y};
|
||||
}
|
||||
|
||||
void lineTo(float x, float y)
|
||||
{
|
||||
if (cmdCnt + 1 > reservedCmdCnt) reserveCmd((cmdCnt + 1) * 2);
|
||||
if (ptsCnt + 2 > reservedPtsCnt) reservePts((ptsCnt + 2) * 2);
|
||||
|
||||
cmds[cmdCnt++] = PathCommand::LineTo;
|
||||
pts[ptsCnt++] = {x, y};
|
||||
}
|
||||
|
||||
void cubicTo(float cx1, float cy1, float cx2, float cy2, float x, float y)
|
||||
{
|
||||
if (cmdCnt + 1 > reservedCmdCnt) reserveCmd((cmdCnt + 1) * 2);
|
||||
if (ptsCnt + 3 > reservedPtsCnt) reservePts((ptsCnt + 3) * 2);
|
||||
|
||||
cmds[cmdCnt++] = PathCommand::CubicTo;
|
||||
pts[ptsCnt++] = {cx1, cy1};
|
||||
pts[ptsCnt++] = {cx2, cy2};
|
||||
pts[ptsCnt++] = {x, y};
|
||||
}
|
||||
|
||||
void close()
|
||||
{
|
||||
if (cmdCnt > 0 && cmds[cmdCnt - 1] == PathCommand::Close) return;
|
||||
|
||||
if (cmdCnt + 1 > reservedCmdCnt) reserveCmd((cmdCnt + 1) * 2);
|
||||
cmds[cmdCnt++] = PathCommand::Close;
|
||||
}
|
||||
|
||||
bool bounds(float* x, float* y, float* w, float* h) const
|
||||
{
|
||||
if (ptsCnt == 0) return false;
|
||||
|
||||
Point min = { pts[0].x, pts[0].y };
|
||||
Point max = { pts[0].x, pts[0].y };
|
||||
|
||||
for (uint32_t i = 1; i < ptsCnt; ++i) {
|
||||
if (pts[i].x < min.x) min.x = pts[i].x;
|
||||
if (pts[i].y < min.y) min.y = pts[i].y;
|
||||
if (pts[i].x > max.x) max.x = pts[i].x;
|
||||
if (pts[i].y > max.y) max.y = pts[i].y;
|
||||
}
|
||||
|
||||
if (x) *x = min.x;
|
||||
if (y) *y = min.y;
|
||||
if (w) *w = max.x - min.x;
|
||||
if (h) *h = max.y - min.y;
|
||||
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
struct Shape::Impl
|
||||
{
|
||||
ShapePath path;
|
||||
Fill *fill = nullptr;
|
||||
ShapeStroke *stroke = nullptr;
|
||||
uint8_t color[4] = {0, 0, 0, 0}; //r, g, b, a
|
||||
FillRule rule = FillRule::Winding;
|
||||
RenderShape rs; //shape data
|
||||
RenderData rdata = nullptr; //engine data
|
||||
Shape *shape = nullptr;
|
||||
uint32_t flag = RenderUpdateFlag::None;
|
||||
|
||||
Impl(Shape* s) : shape(s)
|
||||
{
|
||||
}
|
||||
|
||||
~Impl()
|
||||
{
|
||||
if (fill) delete(fill);
|
||||
if (stroke) {
|
||||
stroke->clear();
|
||||
free (stroke);
|
||||
}
|
||||
}
|
||||
|
||||
bool dispose(RenderMethod& renderer)
|
||||
{
|
||||
auto ret = renderer.dispose(rdata);
|
||||
|
@ -237,9 +50,9 @@ struct Shape::Impl
|
|||
|
||||
void* update(RenderMethod& renderer, const RenderTransform* transform, uint32_t opacity, Array<RenderData>& clips, RenderUpdateFlag pFlag, bool clipper)
|
||||
{
|
||||
this->rdata = renderer.prepare(*shape, this->rdata, transform, opacity, clips, static_cast<RenderUpdateFlag>(pFlag | flag), clipper);
|
||||
rdata = renderer.prepare(rs, rdata, transform, opacity, clips, static_cast<RenderUpdateFlag>(pFlag | flag), clipper);
|
||||
flag = RenderUpdateFlag::None;
|
||||
return this->rdata;
|
||||
return rdata;
|
||||
}
|
||||
|
||||
RenderRegion bounds(RenderMethod& renderer)
|
||||
|
@ -249,24 +62,123 @@ struct Shape::Impl
|
|||
|
||||
bool bounds(float* x, float* y, float* w, float* h)
|
||||
{
|
||||
auto ret = path.bounds(x, y, w, h);
|
||||
//Path bounding size
|
||||
if (rs.path.ptsCnt > 0 ) {
|
||||
Point min = { rs.path.pts[0].x, rs.path.pts[0].y };
|
||||
Point max = { rs.path.pts[0].x, rs.path.pts[0].y };
|
||||
|
||||
for (uint32_t i = 1; i < rs.path.ptsCnt; ++i) {
|
||||
if (rs.path.pts[i].x < min.x) min.x = rs.path.pts[i].x;
|
||||
if (rs.path.pts[i].y < min.y) min.y = rs.path.pts[i].y;
|
||||
if (rs.path.pts[i].x > max.x) max.x = rs.path.pts[i].x;
|
||||
if (rs.path.pts[i].y > max.y) max.y = rs.path.pts[i].y;
|
||||
}
|
||||
|
||||
if (x) *x = min.x;
|
||||
if (y) *y = min.y;
|
||||
if (w) *w = max.x - min.x;
|
||||
if (h) *h = max.y - min.y;
|
||||
}
|
||||
|
||||
//Stroke feathering
|
||||
if (stroke) {
|
||||
if (x) *x -= stroke->width * 0.5f;
|
||||
if (y) *y -= stroke->width * 0.5f;
|
||||
if (w) *w += stroke->width;
|
||||
if (h) *h += stroke->width;
|
||||
if (rs.stroke) {
|
||||
if (x) *x -= rs.stroke->width * 0.5f;
|
||||
if (y) *y -= rs.stroke->width * 0.5f;
|
||||
if (w) *w += rs.stroke->width;
|
||||
if (h) *h += rs.stroke->width;
|
||||
}
|
||||
return ret;
|
||||
return rs.path.ptsCnt > 0 ? true : false;
|
||||
}
|
||||
|
||||
void reserveCmd(uint32_t cmdCnt)
|
||||
{
|
||||
if (cmdCnt <= rs.path.reservedCmdCnt) return;
|
||||
rs.path.reservedCmdCnt = cmdCnt;
|
||||
rs.path.cmds = static_cast<PathCommand*>(realloc(rs.path.cmds, sizeof(PathCommand) * rs.path.reservedCmdCnt));
|
||||
}
|
||||
|
||||
void reservePts(uint32_t ptsCnt)
|
||||
{
|
||||
if (ptsCnt <= rs.path.reservedPtsCnt) return;
|
||||
rs.path.reservedPtsCnt = ptsCnt;
|
||||
rs.path.pts = static_cast<Point*>(realloc(rs.path.pts, sizeof(Point) * rs.path.reservedPtsCnt));
|
||||
}
|
||||
|
||||
void grow(uint32_t cmdCnt, uint32_t ptsCnt)
|
||||
{
|
||||
reserveCmd(rs.path.cmdCnt + cmdCnt);
|
||||
reservePts(rs.path.ptsCnt + ptsCnt);
|
||||
}
|
||||
|
||||
void reset()
|
||||
{
|
||||
rs.path.cmdCnt = 0;
|
||||
rs.path.ptsCnt = 0;
|
||||
|
||||
flag = RenderUpdateFlag::Path;
|
||||
}
|
||||
|
||||
void append(const PathCommand* cmds, uint32_t cmdCnt, const Point* pts, uint32_t ptsCnt)
|
||||
{
|
||||
memcpy(rs.path.cmds + rs.path.cmdCnt, cmds, sizeof(PathCommand) * cmdCnt);
|
||||
memcpy(rs.path.pts + rs.path.ptsCnt, pts, sizeof(Point) * ptsCnt);
|
||||
rs.path.cmdCnt += cmdCnt;
|
||||
rs.path.ptsCnt += ptsCnt;
|
||||
|
||||
flag |= RenderUpdateFlag::Path;
|
||||
}
|
||||
|
||||
void moveTo(float x, float y)
|
||||
{
|
||||
if (rs.path.cmdCnt + 1 > rs.path.reservedCmdCnt) reserveCmd((rs.path.cmdCnt + 1) * 2);
|
||||
if (rs.path.ptsCnt + 2 > rs.path.reservedPtsCnt) reservePts((rs.path.ptsCnt + 2) * 2);
|
||||
|
||||
rs.path.cmds[rs.path.cmdCnt++] = PathCommand::MoveTo;
|
||||
rs.path.pts[rs.path.ptsCnt++] = {x, y};
|
||||
|
||||
flag |= RenderUpdateFlag::Path;
|
||||
}
|
||||
|
||||
void lineTo(float x, float y)
|
||||
{
|
||||
if (rs.path.cmdCnt + 1 > rs.path.reservedCmdCnt) reserveCmd((rs.path.cmdCnt + 1) * 2);
|
||||
if (rs.path.ptsCnt + 2 > rs.path.reservedPtsCnt) reservePts((rs.path.ptsCnt + 2) * 2);
|
||||
|
||||
rs.path.cmds[rs.path.cmdCnt++] = PathCommand::LineTo;
|
||||
rs.path.pts[rs.path.ptsCnt++] = {x, y};
|
||||
|
||||
flag |= RenderUpdateFlag::Path;
|
||||
}
|
||||
|
||||
void cubicTo(float cx1, float cy1, float cx2, float cy2, float x, float y)
|
||||
{
|
||||
if (rs.path.cmdCnt + 1 > rs.path.reservedCmdCnt) reserveCmd((rs.path.cmdCnt + 1) * 2);
|
||||
if (rs.path.ptsCnt + 3 > rs.path.reservedPtsCnt) reservePts((rs.path.ptsCnt + 3) * 2);
|
||||
|
||||
rs.path.cmds[rs.path.cmdCnt++] = PathCommand::CubicTo;
|
||||
rs.path.pts[rs.path.ptsCnt++] = {cx1, cy1};
|
||||
rs.path.pts[rs.path.ptsCnt++] = {cx2, cy2};
|
||||
rs.path.pts[rs.path.ptsCnt++] = {x, y};
|
||||
|
||||
flag |= RenderUpdateFlag::Path;
|
||||
}
|
||||
|
||||
void close()
|
||||
{
|
||||
if (rs.path.cmdCnt > 0 && rs.path.cmds[rs.path.cmdCnt - 1] == PathCommand::Close) return;
|
||||
|
||||
if (rs.path.cmdCnt + 1 > rs.path.reservedCmdCnt) reserveCmd((rs.path.cmdCnt + 1) * 2);
|
||||
rs.path.cmds[rs.path.cmdCnt++] = PathCommand::Close;
|
||||
|
||||
flag |= RenderUpdateFlag::Path;
|
||||
}
|
||||
|
||||
bool strokeWidth(float width)
|
||||
{
|
||||
//TODO: Size Exception?
|
||||
|
||||
if (!stroke) stroke = static_cast<ShapeStroke*>(calloc(sizeof(ShapeStroke), 1));
|
||||
stroke->width = width;
|
||||
if (!rs.stroke) rs.stroke = new RenderStroke();
|
||||
rs.stroke->width = width;
|
||||
flag |= RenderUpdateFlag::Stroke;
|
||||
|
||||
return true;
|
||||
|
@ -274,8 +186,8 @@ struct Shape::Impl
|
|||
|
||||
bool strokeCap(StrokeCap cap)
|
||||
{
|
||||
if (!stroke) stroke = static_cast<ShapeStroke*>(calloc(sizeof(ShapeStroke), 1));
|
||||
stroke->cap = cap;
|
||||
if (!rs.stroke) rs.stroke = new RenderStroke();
|
||||
rs.stroke->cap = cap;
|
||||
flag |= RenderUpdateFlag::Stroke;
|
||||
|
||||
return true;
|
||||
|
@ -283,8 +195,8 @@ struct Shape::Impl
|
|||
|
||||
bool strokeJoin(StrokeJoin join)
|
||||
{
|
||||
if (!stroke) stroke = static_cast<ShapeStroke*>(calloc(sizeof(ShapeStroke), 1));
|
||||
stroke->join = join;
|
||||
if (!rs.stroke) rs.stroke = new RenderStroke();
|
||||
rs.stroke->join = join;
|
||||
flag |= RenderUpdateFlag::Stroke;
|
||||
|
||||
return true;
|
||||
|
@ -292,17 +204,17 @@ struct Shape::Impl
|
|||
|
||||
bool strokeColor(uint8_t r, uint8_t g, uint8_t b, uint8_t a)
|
||||
{
|
||||
if (!stroke) stroke = static_cast<ShapeStroke*>(calloc(sizeof(ShapeStroke), 1));
|
||||
if (stroke->fill) {
|
||||
delete(stroke->fill);
|
||||
stroke->fill = nullptr;
|
||||
if (!rs.stroke) rs.stroke = new RenderStroke();
|
||||
if (rs.stroke->fill) {
|
||||
delete(rs.stroke->fill);
|
||||
rs.stroke->fill = nullptr;
|
||||
flag |= RenderUpdateFlag::GradientStroke;
|
||||
}
|
||||
|
||||
stroke->color[0] = r;
|
||||
stroke->color[1] = g;
|
||||
stroke->color[2] = b;
|
||||
stroke->color[3] = a;
|
||||
rs.stroke->color[0] = r;
|
||||
rs.stroke->color[1] = g;
|
||||
rs.stroke->color[2] = b;
|
||||
rs.stroke->color[3] = a;
|
||||
|
||||
flag |= RenderUpdateFlag::Stroke;
|
||||
|
||||
|
@ -314,9 +226,9 @@ struct Shape::Impl
|
|||
auto p = f.release();
|
||||
if (!p) return Result::MemoryCorruption;
|
||||
|
||||
if (!stroke) stroke = static_cast<ShapeStroke*>(calloc(sizeof(ShapeStroke), 1));
|
||||
if (stroke->fill && stroke->fill != p) delete(stroke->fill);
|
||||
stroke->fill = p;
|
||||
if (!rs.stroke) rs.stroke = new RenderStroke();
|
||||
if (rs.stroke->fill && rs.stroke->fill != p) delete(rs.stroke->fill);
|
||||
rs.stroke->fill = p;
|
||||
|
||||
flag |= RenderUpdateFlag::Stroke;
|
||||
flag |= RenderUpdateFlag::GradientStroke;
|
||||
|
@ -328,23 +240,23 @@ struct Shape::Impl
|
|||
{
|
||||
//Reset dash
|
||||
if (!pattern && cnt == 0) {
|
||||
free(stroke->dashPattern);
|
||||
stroke->dashPattern = nullptr;
|
||||
free(rs.stroke->dashPattern);
|
||||
rs.stroke->dashPattern = nullptr;
|
||||
} else {
|
||||
if (!stroke) stroke = static_cast<ShapeStroke*>(calloc(sizeof(ShapeStroke), 1));
|
||||
if (stroke->dashCnt != cnt) {
|
||||
free(stroke->dashPattern);
|
||||
stroke->dashPattern = nullptr;
|
||||
if (!rs.stroke) rs.stroke = new RenderStroke();
|
||||
if (rs.stroke->dashCnt != cnt) {
|
||||
free(rs.stroke->dashPattern);
|
||||
rs.stroke->dashPattern = nullptr;
|
||||
}
|
||||
if (!stroke->dashPattern) {
|
||||
stroke->dashPattern = static_cast<float*>(malloc(sizeof(float) * cnt));
|
||||
if (!stroke->dashPattern) return false;
|
||||
if (!rs.stroke->dashPattern) {
|
||||
rs.stroke->dashPattern = static_cast<float*>(malloc(sizeof(float) * cnt));
|
||||
if (!rs.stroke->dashPattern) return false;
|
||||
}
|
||||
for (uint32_t i = 0; i < cnt; ++i) {
|
||||
stroke->dashPattern[i] = pattern[i];
|
||||
rs.stroke->dashPattern[i] = pattern[i];
|
||||
}
|
||||
}
|
||||
stroke->dashCnt = cnt;
|
||||
rs.stroke->dashCnt = cnt;
|
||||
flag |= RenderUpdateFlag::Stroke;
|
||||
|
||||
return true;
|
||||
|
@ -355,29 +267,52 @@ struct Shape::Impl
|
|||
auto ret = Shape::gen();
|
||||
|
||||
auto dup = ret.get()->pImpl;
|
||||
dup->rule = rule;
|
||||
dup->rs.rule = rs.rule;
|
||||
|
||||
//Color
|
||||
memcpy(dup->color, color, sizeof(color));
|
||||
memcpy(dup->rs.color, rs.color, sizeof(rs.color));
|
||||
dup->flag = RenderUpdateFlag::Color;
|
||||
|
||||
//Path
|
||||
dup->path.duplicate(&path);
|
||||
if (rs.path.cmdCnt > 0 && rs.path.ptsCnt > 0) {
|
||||
dup->rs.path.cmdCnt = rs.path.cmdCnt;
|
||||
dup->rs.path.reservedCmdCnt = rs.path.reservedCmdCnt;
|
||||
dup->rs.path.ptsCnt = rs.path.ptsCnt;
|
||||
dup->rs.path.reservedPtsCnt = rs.path.reservedPtsCnt;
|
||||
|
||||
dup->rs.path.cmds = static_cast<PathCommand*>(malloc(sizeof(PathCommand) * dup->rs.path.reservedCmdCnt));
|
||||
if (dup->rs.path.cmds) memcpy(dup->rs.path.cmds, rs.path.cmds, sizeof(PathCommand) * dup->rs.path.cmdCnt);
|
||||
|
||||
dup->rs.path.pts = static_cast<Point*>(malloc(sizeof(Point) * dup->rs.path.reservedPtsCnt));
|
||||
if (dup->rs.path.pts) memcpy(dup->rs.path.pts, rs.path.pts, sizeof(Point) * dup->rs.path.ptsCnt);
|
||||
}
|
||||
dup->flag |= RenderUpdateFlag::Path;
|
||||
|
||||
//Stroke
|
||||
if (stroke) {
|
||||
dup->stroke = static_cast<ShapeStroke*>(calloc(sizeof(ShapeStroke), 1));
|
||||
dup->stroke->copy(stroke);
|
||||
if (rs.stroke) {
|
||||
dup->rs.stroke = new RenderStroke();
|
||||
dup->rs.stroke->width = rs.stroke->width;
|
||||
dup->rs.stroke->dashCnt = rs.stroke->dashCnt;
|
||||
dup->rs.stroke->cap = rs.stroke->cap;
|
||||
dup->rs.stroke->join = rs.stroke->join;
|
||||
memcpy(dup->rs.stroke->color, rs.stroke->color, sizeof(rs.stroke->color));
|
||||
|
||||
if (rs.stroke->dashCnt > 0) {
|
||||
dup->rs.stroke->dashPattern = static_cast<float*>(malloc(sizeof(float) * rs.stroke->dashCnt));
|
||||
memcpy(dup->rs.stroke->dashPattern, rs.stroke->dashPattern, sizeof(float) * rs.stroke->dashCnt);
|
||||
}
|
||||
|
||||
dup->flag |= RenderUpdateFlag::Stroke;
|
||||
|
||||
if (stroke->fill)
|
||||
if (rs.stroke->fill) {
|
||||
dup->rs.stroke->fill = rs.stroke->fill->duplicate();
|
||||
dup->flag |= RenderUpdateFlag::GradientStroke;
|
||||
}
|
||||
}
|
||||
|
||||
//Fill
|
||||
if (fill) {
|
||||
dup->fill = fill->duplicate();
|
||||
if (rs.fill) {
|
||||
dup->rs.fill = rs.fill->duplicate();
|
||||
dup->flag |= RenderUpdateFlag::Gradient;
|
||||
}
|
||||
|
||||
|
|
Loading…
Add table
Reference in a new issue