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:
Hermet Park 2023-01-15 20:20:43 +09:00
parent 110f4a5cc9
commit 919c90a97e
13 changed files with 400 additions and 409 deletions

View file

@ -54,7 +54,7 @@ class GlGeometry;
struct GlShape struct GlShape
{ {
const Shape* shape = nullptr; const RenderShape* rshape = nullptr;
float viewWd; float viewWd;
float viewHt; float viewHt;
RenderUpdateFlag updateFlag = None; RenderUpdateFlag updateFlag = None;

View file

@ -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 cmds = rshape.path.cmds;
auto cmdCnt = shape.pathCommands(&cmds); auto cmdCnt = rshape.path.cmdCnt;
auto pts = rshape.path.pts;
Point* pts = nullptr; auto ptsCnt = rshape.path.ptsCnt;
auto ptsCnt = shape.pathCoords(const_cast<const Point**>(&pts));
//No actual shape data //No actual shape data
if (cmdCnt == 0 || ptsCnt == 0) return false; if (cmdCnt == 0 || ptsCnt == 0) return false;
@ -101,7 +100,7 @@ bool GlGeometry::decomposeOutline(const Shape& shape)
return true; 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) { for (auto& shapeGeometry : mPrimitives) {
vector<PointNormals> normalInfo; vector<PointNormals> normalInfo;
@ -158,7 +157,7 @@ bool GlGeometry::generateAAPoints(TVG_UNUSED const Shape& shape, float strokeWd,
return true; 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) { for (auto& shapeGeometry : mPrimitives) {
constexpr float opaque = 1.0f; constexpr float opaque = 1.0f;

View file

@ -237,9 +237,9 @@ public:
uint32_t getPrimitiveCount(); uint32_t getPrimitiveCount();
const GlSize getPrimitiveSize(const uint32_t primitiveIndex) const; const GlSize getPrimitiveSize(const uint32_t primitiveIndex) const;
bool decomposeOutline(const Shape& shape); bool decomposeOutline(const RenderShape& rshape);
bool generateAAPoints(TVG_UNUSED const Shape& shape, float strokeWd, RenderUpdateFlag flag); bool generateAAPoints(TVG_UNUSED const RenderShape& rshape, float strokeWd, RenderUpdateFlag flag);
bool tesselate(TVG_UNUSED const Shape &shape, float viewWd, float viewHt, RenderUpdateFlag flag); bool tesselate(TVG_UNUSED const RenderShape& rshape, float viewWd, float viewHt, RenderUpdateFlag flag);
void disableVertex(uint32_t location); void disableVertex(uint32_t location);
void draw(const uint32_t location, const uint32_t primitiveIndex, RenderUpdateFlag flag); void draw(const uint32_t location, const uint32_t primitiveIndex, RenderUpdateFlag flag);
void updateTransform(const RenderTransform* transform, float w, float h); void updateTransform(const RenderTransform* transform, float w, float h);

View file

@ -150,16 +150,13 @@ bool GlRenderer::renderShape(RenderData data)
{ {
if (flags & (RenderUpdateFlag::Gradient | RenderUpdateFlag::Transform)) if (flags & (RenderUpdateFlag::Gradient | RenderUpdateFlag::Transform))
{ {
const Fill* gradient = sdata->shape->fill(); auto gradient = sdata->rshape->fill;
if (gradient != nullptr) if (gradient) drawPrimitive(*sdata, gradient, i, RenderUpdateFlag::Gradient);
{
drawPrimitive(*sdata, gradient, i, RenderUpdateFlag::Gradient);
}
} }
if(flags & (RenderUpdateFlag::Color | RenderUpdateFlag::Transform)) if(flags & (RenderUpdateFlag::Color | RenderUpdateFlag::Transform))
{ {
sdata->shape->fillColor(&r, &g, &b, &a); sdata->rshape->fillColor(&r, &g, &b, &a);
if (a > 0) if (a > 0)
{ {
drawPrimitive(*sdata, r, g, b, a, i, RenderUpdateFlag::Color); drawPrimitive(*sdata, r, g, b, a, i, RenderUpdateFlag::Color);
@ -168,7 +165,7 @@ bool GlRenderer::renderShape(RenderData data)
if (flags & (RenderUpdateFlag::Stroke | RenderUpdateFlag::Transform)) if (flags & (RenderUpdateFlag::Stroke | RenderUpdateFlag::Transform))
{ {
sdata->shape->strokeColor(&r, &g, &b, &a); sdata->rshape->strokeColor(&r, &g, &b, &a);
if (a > 0) if (a > 0)
{ {
drawPrimitive(*sdata, r, g, b, a, i, RenderUpdateFlag::Stroke); 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 //prepare shape data
GlShape* sdata = static_cast<GlShape*>(data); GlShape* sdata = static_cast<GlShape*>(data);
if (!sdata) { if (!sdata) {
sdata = new GlShape; sdata = new GlShape;
sdata->shape = &shape; sdata->rshape = &rshape;
} }
sdata->viewWd = static_cast<float>(surface.w); sdata->viewWd = static_cast<float>(surface.w);
@ -216,9 +213,9 @@ RenderData GlRenderer::prepare(const Shape& shape, RenderData data, const Render
//invisible? //invisible?
uint8_t alphaF, alphaS; uint8_t alphaF, alphaS;
shape.fillColor(nullptr, nullptr, nullptr, &alphaF); rshape.fillColor(nullptr, nullptr, nullptr, &alphaF);
shape.strokeColor(nullptr, nullptr, nullptr, &alphaS); rshape.strokeColor(nullptr, nullptr, nullptr, &alphaS);
auto strokeWd = shape.strokeWidth(); auto strokeWd = rshape.strokeWidth();
if ( ((sdata->updateFlag & RenderUpdateFlag::Gradient) == 0) && if ( ((sdata->updateFlag & RenderUpdateFlag::Gradient) == 0) &&
((sdata->updateFlag & RenderUpdateFlag::Color) && alphaF == 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->updateFlag & (RenderUpdateFlag::Color | RenderUpdateFlag::Stroke | RenderUpdateFlag::Gradient | RenderUpdateFlag::Transform) )
{ {
if (!sdata->geometry->decomposeOutline(shape)) return sdata; if (!sdata->geometry->decomposeOutline(rshape)) return sdata;
if (!sdata->geometry->generateAAPoints(shape, static_cast<float>(strokeWd), sdata->updateFlag)) return sdata; if (!sdata->geometry->generateAAPoints(rshape, static_cast<float>(strokeWd), sdata->updateFlag)) return sdata;
if (!sdata->geometry->tesselate(shape, sdata->viewWd, sdata->viewHt, sdata->updateFlag)) return sdata; if (!sdata->geometry->tesselate(rshape, sdata->viewWd, sdata->viewHt, sdata->updateFlag)) return sdata;
} }
return sdata; return sdata;
} }

View file

@ -30,7 +30,7 @@ class GlRenderer : public RenderMethod
public: public:
Surface surface = {nullptr, 0, 0, 0}; 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; 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 preRender() override;
bool renderShape(RenderData data) override; bool renderShape(RenderData data) override;

View file

@ -302,12 +302,12 @@ bool mathUpdateOutlineBBox(const SwOutline* outline, const SwBBox& clipRegion, S
bool mathClipBBox(const SwBBox& clipper, SwBBox& clipee); bool mathClipBBox(const SwBBox& clipper, SwBBox& clipee);
void shapeReset(SwShape* shape); 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 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 shapeDelOutline(SwShape* shape, SwMpool* mpool, uint32_t tid);
void shapeResetStroke(SwShape* shape, const Shape* sdata, const Matrix* transform); void shapeResetStroke(SwShape* shape, const RenderShape* rshape, const Matrix* transform);
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);
void shapeFree(SwShape* shape); void shapeFree(SwShape* shape);
void shapeDelStroke(SwShape* shape); void shapeDelStroke(SwShape* shape);
bool shapeGenFillColors(SwShape* shape, const Fill* fill, const Matrix* transform, SwSurface* surface, uint32_t opacity, bool ctable); 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 shapeDelFill(SwShape* shape);
void shapeDelStrokeFill(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); bool strokeParseOutline(SwStroke* stroke, const SwOutline& outline);
SwOutline* strokeExportOutline(SwStroke* stroke, SwMpool* mpool, unsigned tid); SwOutline* strokeExportOutline(SwStroke* stroke, SwMpool* mpool, unsigned tid);
void strokeFree(SwStroke* stroke); void strokeFree(SwStroke* stroke);

View file

@ -72,7 +72,7 @@ struct SwTask : Task
struct SwShapeTask : SwTask struct SwShapeTask : SwTask
{ {
SwShape shape; SwShape shape;
const Shape* sdata = nullptr; const RenderShape* rshape = nullptr;
bool cmpStroking = false; bool cmpStroking = false;
bool clipper = false; bool clipper = false;
@ -85,9 +85,9 @@ struct SwShapeTask : SwTask
bool visibleFill = false; bool visibleFill = false;
auto clipRegion = bbox; auto clipRegion = bbox;
if (HALF_STROKE(sdata->strokeWidth()) > 0) { if (HALF_STROKE(rshape->strokeWidth()) > 0) {
sdata->strokeColor(nullptr, nullptr, nullptr, &strokeAlpha); rshape->strokeColor(nullptr, nullptr, nullptr, &strokeAlpha);
visibleStroke = sdata->strokeFill() || (static_cast<uint32_t>(strokeAlpha * opacity / 255) > 0); 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. //This checks also for the case, if the invisible shape turned to visible by alpha.
@ -97,12 +97,12 @@ struct SwShapeTask : SwTask
//Shape //Shape
if (flags & (RenderUpdateFlag::Path | RenderUpdateFlag::Transform) || prepareShape) { if (flags & (RenderUpdateFlag::Path | RenderUpdateFlag::Transform) || prepareShape) {
uint8_t alpha = 0; 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); 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) { if (visibleFill || visibleStroke || clipper) {
shapeReset(&shape); 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. shape outline below stroke could be full covered by stroke drawing.
Thus it turns off antialising in that condition. Thus it turns off antialising in that condition.
Also, it shouldn't be dash style. */ 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; auto ctable = (flags & RenderUpdateFlag::Gradient) ? true : false;
if (ctable) shapeResetFill(&shape); if (ctable) shapeResetFill(&shape);
if (!shapeGenFillColors(&shape, fill, transform, surface, cmpStroking ? 255 : opacity, ctable)) goto err; if (!shapeGenFillColors(&shape, fill, transform, surface, cmpStroking ? 255 : opacity, ctable)) goto err;
@ -133,10 +133,10 @@ struct SwShapeTask : SwTask
//Stroke //Stroke
if (flags & (RenderUpdateFlag::Stroke | RenderUpdateFlag::Transform)) { if (flags & (RenderUpdateFlag::Stroke | RenderUpdateFlag::Transform)) {
if (visibleStroke) { if (visibleStroke) {
shapeResetStroke(&shape, sdata, transform); shapeResetStroke(&shape, rshape, transform);
if (!shapeGenStrokeRle(&shape, sdata, transform, clipRegion, bbox, mpool, tid)) goto err; 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; auto ctable = (flags & RenderUpdateFlag::GradientStroke) ? true : false;
if (ctable) shapeResetStrokeFill(&shape); if (ctable) shapeResetStrokeFill(&shape);
if (!shapeGenStrokeFillColors(&shape, fill, transform, surface, cmpStroking ? 255 : opacity, ctable)) goto err; if (!shapeGenStrokeFillColors(&shape, fill, transform, surface, cmpStroking ? 255 : opacity, ctable)) goto err;
@ -397,18 +397,18 @@ bool SwRenderer::renderShape(RenderData data)
//Main raster stage //Main raster stage
uint8_t r, g, b, a; uint8_t r, g, b, a;
if (auto fill = task->sdata->fill()) { if (auto fill = task->rshape->fill) {
rasterGradientShape(surface, &task->shape, fill->identifier()); rasterGradientShape(surface, &task->shape, fill->identifier());
} else { } 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); a = static_cast<uint8_t>((opacity * (uint32_t) a) / 255);
if (a > 0) rasterShape(surface, &task->shape, r, g, b, a); 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()); rasterGradientStroke(surface, &task->shape, strokeFill->identifier());
} else { } 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); a = static_cast<uint8_t>((opacity * (uint32_t) a) / 255);
if (a > 0) rasterStroke(surface, &task->shape, r, g, b, a); 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 //prepare task
auto task = static_cast<SwShapeTask*>(data); auto task = static_cast<SwShapeTask*>(data);
if (!task) { if (!task) {
task = new SwShapeTask; task = new SwShapeTask;
task->sdata = &sdata; task->rshape = &rshape;
task->clipper = clipper; task->clipper = clipper;
} }
return prepareCommon(task, transform, opacity, clips, flags); return prepareCommon(task, transform, opacity, clips, flags);

View file

@ -36,7 +36,7 @@ namespace tvg
class SwRenderer : public RenderMethod class SwRenderer : public RenderMethod
{ {
public: 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; 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 preRender() override;
bool renderShape(RenderData data) override; bool renderShape(RenderData data) override;

View file

@ -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; const PathCommand* cmds = rshape->path.cmds;
auto cmdCnt = sdata->pathCommands(&cmds); auto cmdCnt = rshape->path.cmdCnt;
const Point* pts = nullptr; const Point* pts = rshape->path.pts;
auto ptsCnt = sdata->pathCoords(&pts); auto ptsCnt = rshape->path.ptsCnt;
//No actual shape data //No actual shape data
if (cmdCnt == 0 || ptsCnt == 0) return nullptr; if (cmdCnt == 0 || ptsCnt == 0) return nullptr;
@ -286,7 +286,7 @@ static SwOutline* _genDashOutline(const Shape* sdata, const Matrix* transform)
dash.curOpGap = false; dash.curOpGap = false;
const float* pattern; const float* pattern;
dash.cnt = sdata->strokeDash(&pattern); dash.cnt = rshape->strokeDash(&pattern);
if (dash.cnt == 0) return nullptr; if (dash.cnt == 0) return nullptr;
//OPTMIZE ME: Use mempool??? //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; const PathCommand* cmds = rshape->path.cmds;
auto cmdCnt = sdata->pathCommands(&cmds); auto cmdCnt = rshape->path.cmdCnt;
const Point* pts = nullptr; const Point* pts = rshape->path.pts;
auto ptsCnt = sdata->pathCoords(&pts); auto ptsCnt = rshape->path.ptsCnt;
//No actual shape data //No actual shape data
if (cmdCnt == 0 || ptsCnt == 0) return false; if (cmdCnt == 0 || ptsCnt == 0) return false;
@ -467,7 +467,7 @@ static bool _genOutline(SwShape* shape, const Shape* sdata, const Matrix* transf
_outlineEnd(*outline); _outlineEnd(*outline);
outline->fillRule = sdata->fillRule(); outline->fillRule = rshape->rule;
shape->outline = outline; shape->outline = outline;
shape->fastTrack = (!hasComposite && _axisAlignedRect(shape->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 */ /* 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; if (!mathUpdateOutlineBBox(shape->outline, clipRegion, renderRegion, shape->fastTrack)) return false;
//Keep it for Rasterization Region //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? //FIXME: Should we draw it?
//Case: Stroke Line //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))); if (!shape->stroke) shape->stroke = static_cast<SwStroke*>(calloc(1, sizeof(SwStroke)));
auto stroke = shape->stroke; auto stroke = shape->stroke;
if (!stroke) return; if (!stroke) return;
strokeReset(stroke, sdata, transform); strokeReset(stroke, rshape, transform);
rleReset(shape->strokeRle); 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* shapeOutline = nullptr;
SwOutline* strokeOutline = nullptr; SwOutline* strokeOutline = nullptr;
@ -577,14 +577,14 @@ bool shapeGenStrokeRle(SwShape* shape, const Shape* sdata, const Matrix* transfo
bool ret = true; bool ret = true;
//Dash Style Stroke //Dash Style Stroke
if (sdata->strokeDash(nullptr) > 0) { if (rshape->strokeDash(nullptr) > 0) {
shapeOutline = _genDashOutline(sdata, transform); shapeOutline = _genDashOutline(rshape, transform);
if (!shapeOutline) return false; if (!shapeOutline) return false;
freeOutline = true; freeOutline = true;
//Normal Style stroke //Normal Style stroke
} else { } else {
if (!shape->outline) { 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; shapeOutline = shape->outline;
} }

View file

@ -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) { if (transform) {
stroke->sx = sqrtf(powf(transform->e11, 2.0f) + powf(transform->e21, 2.0f)); 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->sx = stroke->sy = 1.0f;
} }
stroke->width = HALF_STROKE(sdata->strokeWidth()); stroke->width = HALF_STROKE(rshape->strokeWidth());
stroke->cap = sdata->strokeCap(); stroke->cap = rshape->strokeCap();
//Save line join: it can be temporarily changed when stroking curves... //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].ptsCnt = 0;
stroke->borders[0].start = -1; stroke->borders[0].start = -1;

View file

@ -85,12 +85,107 @@ struct RenderTransform
RenderTransform(const RenderTransform* lhs, const RenderTransform* rhs); 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 class RenderMethod
{ {
public: public:
virtual ~RenderMethod() {} 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 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 preRender() = 0;
virtual bool renderShape(RenderData data) = 0; virtual bool renderShape(RenderData data) = 0;

View file

@ -32,7 +32,7 @@ constexpr auto PATH_KAPPA = 0.552284f;
/* External Class Implementation */ /* External Class Implementation */
/************************************************************************/ /************************************************************************/
Shape :: Shape() : pImpl(new Impl(this)) Shape :: Shape() : pImpl(new Impl())
{ {
Paint::pImpl->id = TVG_CLASS_ID_SHAPE; Paint::pImpl->id = TVG_CLASS_ID_SHAPE;
Paint::pImpl->method(new PaintMethod<Shape::Impl>(pImpl)); Paint::pImpl->method(new PaintMethod<Shape::Impl>(pImpl));
@ -59,8 +59,7 @@ uint32_t Shape::identifier() noexcept
Result Shape::reset() noexcept Result Shape::reset() noexcept
{ {
pImpl->path.reset(); pImpl->reset();
pImpl->flag = RenderUpdateFlag::Path;
return Result::Success; return Result::Success;
} }
@ -70,9 +69,9 @@ uint32_t Shape::pathCommands(const PathCommand** cmds) const noexcept
{ {
if (!cmds) return 0; 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; 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; if (cmdCnt == 0 || ptsCnt == 0 || !cmds || !pts) return Result::InvalidArguments;
pImpl->path.grow(cmdCnt, ptsCnt); pImpl->grow(cmdCnt, ptsCnt);
pImpl->path.append(cmds, cmdCnt, pts, ptsCnt); pImpl->append(cmds, cmdCnt, pts, ptsCnt);
pImpl->flag |= RenderUpdateFlag::Path;
return Result::Success; 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 Result Shape::moveTo(float x, float y) noexcept
{ {
pImpl->path.moveTo(x, y); pImpl->moveTo(x, y);
pImpl->flag |= RenderUpdateFlag::Path;
return Result::Success; return Result::Success;
} }
@ -111,9 +106,7 @@ Result Shape::moveTo(float x, float y) noexcept
Result Shape::lineTo(float x, float y) noexcept Result Shape::lineTo(float x, float y) noexcept
{ {
pImpl->path.lineTo(x, y); pImpl->lineTo(x, y);
pImpl->flag |= RenderUpdateFlag::Path;
return Result::Success; 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 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->cubicTo(cx1, cy1, cx2, cy2, x, y);
pImpl->flag |= RenderUpdateFlag::Path;
return Result::Success; 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 Result Shape::close() noexcept
{ {
pImpl->path.close(); pImpl->close();
pImpl->flag |= RenderUpdateFlag::Path;
return Result::Success; 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 rxKappa = rx * PATH_KAPPA;
auto ryKappa = ry * PATH_KAPPA; auto ryKappa = ry * PATH_KAPPA;
pImpl->path.grow(6, 13); pImpl->grow(6, 13);
pImpl->path.moveTo(cx, cy - ry); pImpl->moveTo(cx, cy - ry);
pImpl->path.cubicTo(cx + rxKappa, cy - ry, cx + rx, cy - ryKappa, cx + rx, cy); pImpl->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->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->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->cubicTo(cx - rx, cy - ryKappa, cx - rxKappa, cy - ry, cx, cy - ry);
pImpl->path.close(); pImpl->close();
pImpl->flag |= RenderUpdateFlag::Path;
return Result::Success; 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)}; Point start = {radius * cosf(startAngle), radius * sinf(startAngle)};
if (pie) { if (pie) {
pImpl->path.moveTo(cx, cy); pImpl->moveTo(cx, cy);
pImpl->path.lineTo(start.x + cx, start.y + cy); pImpl->lineTo(start.x + cx, start.y + cy);
} else { } 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) { 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 ctrl1 = {ax - k2 * ay + cx, ay + k2 * ax + cy};
Point ctrl2 = {bx + k2 * by + cx, by - k2 * bx + 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; startAngle = endAngle;
} }
if (pie) pImpl->path.close(); if (pie) pImpl->close();
pImpl->flag |= RenderUpdateFlag::Path;
return Result::Success; return Result::Success;
} }
@ -228,48 +213,46 @@ Result Shape::appendRect(float x, float y, float w, float h, float rx, float ry)
//rectangle //rectangle
if (rx == 0 && ry == 0) { if (rx == 0 && ry == 0) {
pImpl->path.grow(5, 4); pImpl->grow(5, 4);
pImpl->path.moveTo(x, y); pImpl->moveTo(x, y);
pImpl->path.lineTo(x + w, y); pImpl->lineTo(x + w, y);
pImpl->path.lineTo(x + w, y + h); pImpl->lineTo(x + w, y + h);
pImpl->path.lineTo(x, y + h); pImpl->lineTo(x, y + h);
pImpl->path.close(); pImpl->close();
//circle //circle
} else if (mathEqual(rx, halfW) && mathEqual(ry, halfH)) { } else if (mathEqual(rx, halfW) && mathEqual(ry, halfH)) {
return appendCircle(x + (w * 0.5f), y + (h * 0.5f), rx, ry); return appendCircle(x + (w * 0.5f), y + (h * 0.5f), rx, ry);
} else { } else {
auto hrx = rx * 0.5f; auto hrx = rx * 0.5f;
auto hry = ry * 0.5f; auto hry = ry * 0.5f;
pImpl->path.grow(10, 17); pImpl->grow(10, 17);
pImpl->path.moveTo(x + rx, y); pImpl->moveTo(x + rx, y);
pImpl->path.lineTo(x + w - rx, y); pImpl->lineTo(x + w - rx, y);
pImpl->path.cubicTo(x + w - rx + hrx, y, x + w, y + ry - hry, x + w, y + ry); pImpl->cubicTo(x + w - rx + hrx, y, x + w, y + ry - hry, x + w, y + ry);
pImpl->path.lineTo(x + w, y + h - ry); pImpl->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->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->lineTo(x + rx, y + h);
pImpl->path.cubicTo(x + rx - hrx, y + h, x, y + h - ry + hry, x, y + h - ry); pImpl->cubicTo(x + rx - hrx, y + h, x, y + h - ry + hry, x, y + h - ry);
pImpl->path.lineTo(x, y + ry); pImpl->lineTo(x, y + ry);
pImpl->path.cubicTo(x, y + ry - hry, x + rx - hrx, y, x + rx, y); pImpl->cubicTo(x, y + ry - hry, x + rx - hrx, y, x + rx, y);
pImpl->path.close(); pImpl->close();
} }
pImpl->flag |= RenderUpdateFlag::Path;
return Result::Success; return Result::Success;
} }
Result Shape::fill(uint8_t r, uint8_t g, uint8_t b, uint8_t a) noexcept Result Shape::fill(uint8_t r, uint8_t g, uint8_t b, uint8_t a) noexcept
{ {
pImpl->color[0] = r; pImpl->rs.color[0] = r;
pImpl->color[1] = g; pImpl->rs.color[1] = g;
pImpl->color[2] = b; pImpl->rs.color[2] = b;
pImpl->color[3] = a; pImpl->rs.color[3] = a;
pImpl->flag |= RenderUpdateFlag::Color; pImpl->flag |= RenderUpdateFlag::Color;
if (pImpl->fill) { if (pImpl->rs.fill) {
delete(pImpl->fill); delete(pImpl->rs.fill);
pImpl->fill = nullptr; pImpl->rs.fill = nullptr;
pImpl->flag |= RenderUpdateFlag::Gradient; pImpl->flag |= RenderUpdateFlag::Gradient;
} }
@ -282,8 +265,8 @@ Result Shape::fill(unique_ptr<Fill> f) noexcept
auto p = f.release(); auto p = f.release();
if (!p) return Result::MemoryCorruption; if (!p) return Result::MemoryCorruption;
if (pImpl->fill && pImpl->fill != p) delete(pImpl->fill); if (pImpl->rs.fill && pImpl->rs.fill != p) delete(pImpl->rs.fill);
pImpl->fill = p; pImpl->rs.fill = p;
pImpl->flag |= RenderUpdateFlag::Gradient; pImpl->flag |= RenderUpdateFlag::Gradient;
return Result::Success; 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 Result Shape::fillColor(uint8_t* r, uint8_t* g, uint8_t* b, uint8_t* a) const noexcept
{ {
if (r) *r = pImpl->color[0]; pImpl->rs.fillColor(r, g, b, a);
if (g) *g = pImpl->color[1];
if (b) *b = pImpl->color[2];
if (a) *a = pImpl->color[3];
return Result::Success; return Result::Success;
} }
const Fill* Shape::fill() const noexcept 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 float Shape::strokeWidth() const noexcept
{ {
if (!pImpl->stroke) return 0; return pImpl->rs.strokeWidth();
return pImpl->stroke->width;
} }
@ -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 Result Shape::strokeColor(uint8_t* r, uint8_t* g, uint8_t* b, uint8_t* a) const noexcept
{ {
if (!pImpl->stroke) return Result::InsufficientCondition; if (!pImpl->rs.strokeColor(r, g, b, a)) 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];
return Result::Success; return Result::Success;
} }
@ -350,9 +325,7 @@ Result Shape::stroke(unique_ptr<Fill> f) noexcept
const Fill* Shape::strokeFill() const noexcept const Fill* Shape::strokeFill() const noexcept
{ {
if (!pImpl->stroke) return nullptr; return pImpl->rs.strokeFill();
return pImpl->stroke->fill;
} }
@ -373,11 +346,7 @@ Result Shape::stroke(const float* dashPattern, uint32_t cnt) noexcept
uint32_t Shape::strokeDash(const float** dashPattern) const noexcept uint32_t Shape::strokeDash(const float** dashPattern) const noexcept
{ {
if (!pImpl->stroke) return 0; return pImpl->rs.strokeDash(dashPattern);
if (dashPattern) *dashPattern = pImpl->stroke->dashPattern;
return pImpl->stroke->dashCnt;
} }
@ -399,23 +368,19 @@ Result Shape::stroke(StrokeJoin join) noexcept
StrokeCap Shape::strokeCap() const noexcept StrokeCap Shape::strokeCap() const noexcept
{ {
if (!pImpl->stroke) return StrokeCap::Square; return pImpl->rs.strokeCap();
return pImpl->stroke->cap;
} }
StrokeJoin Shape::strokeJoin() const noexcept StrokeJoin Shape::strokeJoin() const noexcept
{ {
if (!pImpl->stroke) return StrokeJoin::Bevel; return pImpl->rs.strokeJoin();
return pImpl->stroke->join;
} }
Result Shape::fill(FillRule r) noexcept Result Shape::fill(FillRule r) noexcept
{ {
pImpl->rule = r; pImpl->rs.rule = r;
return Result::Success; return Result::Success;
} }
@ -423,5 +388,5 @@ Result Shape::fill(FillRule r) noexcept
FillRule Shape::fillRule() const noexcept FillRule Shape::fillRule() const noexcept
{ {
return pImpl->rule; return pImpl->rs.rule;
} }

View file

@ -30,199 +30,12 @@
/* Internal Class Implementation */ /* 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 struct Shape::Impl
{ {
ShapePath path; RenderShape rs; //shape data
Fill *fill = nullptr;
ShapeStroke *stroke = nullptr;
uint8_t color[4] = {0, 0, 0, 0}; //r, g, b, a
FillRule rule = FillRule::Winding;
RenderData rdata = nullptr; //engine data RenderData rdata = nullptr; //engine data
Shape *shape = nullptr;
uint32_t flag = RenderUpdateFlag::None; 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) bool dispose(RenderMethod& renderer)
{ {
auto ret = renderer.dispose(rdata); 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) 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; flag = RenderUpdateFlag::None;
return this->rdata; return rdata;
} }
RenderRegion bounds(RenderMethod& renderer) RenderRegion bounds(RenderMethod& renderer)
@ -249,24 +62,123 @@ struct Shape::Impl
bool bounds(float* x, float* y, float* w, float* h) 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 //Stroke feathering
if (stroke) { if (rs.stroke) {
if (x) *x -= stroke->width * 0.5f; if (x) *x -= rs.stroke->width * 0.5f;
if (y) *y -= stroke->width * 0.5f; if (y) *y -= rs.stroke->width * 0.5f;
if (w) *w += stroke->width; if (w) *w += rs.stroke->width;
if (h) *h += 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) bool strokeWidth(float width)
{ {
//TODO: Size Exception? //TODO: Size Exception?
if (!stroke) stroke = static_cast<ShapeStroke*>(calloc(sizeof(ShapeStroke), 1)); if (!rs.stroke) rs.stroke = new RenderStroke();
stroke->width = width; rs.stroke->width = width;
flag |= RenderUpdateFlag::Stroke; flag |= RenderUpdateFlag::Stroke;
return true; return true;
@ -274,8 +186,8 @@ struct Shape::Impl
bool strokeCap(StrokeCap cap) bool strokeCap(StrokeCap cap)
{ {
if (!stroke) stroke = static_cast<ShapeStroke*>(calloc(sizeof(ShapeStroke), 1)); if (!rs.stroke) rs.stroke = new RenderStroke();
stroke->cap = cap; rs.stroke->cap = cap;
flag |= RenderUpdateFlag::Stroke; flag |= RenderUpdateFlag::Stroke;
return true; return true;
@ -283,8 +195,8 @@ struct Shape::Impl
bool strokeJoin(StrokeJoin join) bool strokeJoin(StrokeJoin join)
{ {
if (!stroke) stroke = static_cast<ShapeStroke*>(calloc(sizeof(ShapeStroke), 1)); if (!rs.stroke) rs.stroke = new RenderStroke();
stroke->join = join; rs.stroke->join = join;
flag |= RenderUpdateFlag::Stroke; flag |= RenderUpdateFlag::Stroke;
return true; return true;
@ -292,17 +204,17 @@ struct Shape::Impl
bool strokeColor(uint8_t r, uint8_t g, uint8_t b, uint8_t a) 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 (!rs.stroke) rs.stroke = new RenderStroke();
if (stroke->fill) { if (rs.stroke->fill) {
delete(stroke->fill); delete(rs.stroke->fill);
stroke->fill = nullptr; rs.stroke->fill = nullptr;
flag |= RenderUpdateFlag::GradientStroke; flag |= RenderUpdateFlag::GradientStroke;
} }
stroke->color[0] = r; rs.stroke->color[0] = r;
stroke->color[1] = g; rs.stroke->color[1] = g;
stroke->color[2] = b; rs.stroke->color[2] = b;
stroke->color[3] = a; rs.stroke->color[3] = a;
flag |= RenderUpdateFlag::Stroke; flag |= RenderUpdateFlag::Stroke;
@ -314,9 +226,9 @@ struct Shape::Impl
auto p = f.release(); auto p = f.release();
if (!p) return Result::MemoryCorruption; if (!p) return Result::MemoryCorruption;
if (!stroke) stroke = static_cast<ShapeStroke*>(calloc(sizeof(ShapeStroke), 1)); if (!rs.stroke) rs.stroke = new RenderStroke();
if (stroke->fill && stroke->fill != p) delete(stroke->fill); if (rs.stroke->fill && rs.stroke->fill != p) delete(rs.stroke->fill);
stroke->fill = p; rs.stroke->fill = p;
flag |= RenderUpdateFlag::Stroke; flag |= RenderUpdateFlag::Stroke;
flag |= RenderUpdateFlag::GradientStroke; flag |= RenderUpdateFlag::GradientStroke;
@ -328,23 +240,23 @@ struct Shape::Impl
{ {
//Reset dash //Reset dash
if (!pattern && cnt == 0) { if (!pattern && cnt == 0) {
free(stroke->dashPattern); free(rs.stroke->dashPattern);
stroke->dashPattern = nullptr; rs.stroke->dashPattern = nullptr;
} else { } else {
if (!stroke) stroke = static_cast<ShapeStroke*>(calloc(sizeof(ShapeStroke), 1)); if (!rs.stroke) rs.stroke = new RenderStroke();
if (stroke->dashCnt != cnt) { if (rs.stroke->dashCnt != cnt) {
free(stroke->dashPattern); free(rs.stroke->dashPattern);
stroke->dashPattern = nullptr; rs.stroke->dashPattern = nullptr;
} }
if (!stroke->dashPattern) { if (!rs.stroke->dashPattern) {
stroke->dashPattern = static_cast<float*>(malloc(sizeof(float) * cnt)); rs.stroke->dashPattern = static_cast<float*>(malloc(sizeof(float) * cnt));
if (!stroke->dashPattern) return false; if (!rs.stroke->dashPattern) return false;
} }
for (uint32_t i = 0; i < cnt; ++i) { 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; flag |= RenderUpdateFlag::Stroke;
return true; return true;
@ -355,29 +267,52 @@ struct Shape::Impl
auto ret = Shape::gen(); auto ret = Shape::gen();
auto dup = ret.get()->pImpl; auto dup = ret.get()->pImpl;
dup->rule = rule; dup->rs.rule = rs.rule;
//Color //Color
memcpy(dup->color, color, sizeof(color)); memcpy(dup->rs.color, rs.color, sizeof(rs.color));
dup->flag = RenderUpdateFlag::Color; dup->flag = RenderUpdateFlag::Color;
//Path //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; dup->flag |= RenderUpdateFlag::Path;
//Stroke //Stroke
if (stroke) { if (rs.stroke) {
dup->stroke = static_cast<ShapeStroke*>(calloc(sizeof(ShapeStroke), 1)); dup->rs.stroke = new RenderStroke();
dup->stroke->copy(stroke); 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; dup->flag |= RenderUpdateFlag::Stroke;
if (stroke->fill) if (rs.stroke->fill) {
dup->rs.stroke->fill = rs.stroke->fill->duplicate();
dup->flag |= RenderUpdateFlag::GradientStroke; dup->flag |= RenderUpdateFlag::GradientStroke;
}
} }
//Fill //Fill
if (fill) { if (rs.fill) {
dup->fill = fill->duplicate(); dup->rs.fill = rs.fill->duplicate();
dup->flag |= RenderUpdateFlag::Gradient; dup->flag |= RenderUpdateFlag::Gradient;
} }