common shape: code refactoring & data optimization. 00/289900/1
authorHermet Park <hermetpark@gmail.com>
Sun, 15 Jan 2023 11:20:43 +0000 (20:20 +0900)
committerMichal Szczecinski <m.szczecinsk@partner.samsung.com>
Wed, 15 Mar 2023 07:28:51 +0000 (08:28 +0100)
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.

Change-Id: Iac398dedec39c6bc88fa4c1c3cfdb3f5db4c25e2

13 files changed:
src/lib/gl_engine/tvgGlCommon.h
src/lib/gl_engine/tvgGlGeometry.cpp
src/lib/gl_engine/tvgGlGeometry.h
src/lib/gl_engine/tvgGlRenderer.cpp
src/lib/gl_engine/tvgGlRenderer.h
src/lib/sw_engine/tvgSwCommon.h
src/lib/sw_engine/tvgSwRenderer.cpp
src/lib/sw_engine/tvgSwRenderer.h
src/lib/sw_engine/tvgSwShape.cpp
src/lib/sw_engine/tvgSwStroke.cpp
src/lib/tvgRender.h
src/lib/tvgShape.cpp
src/lib/tvgShapeImpl.h

index 90d6b14..0121900 100644 (file)
@@ -54,7 +54,7 @@ class GlGeometry;
 
 struct GlShape
 {
-  const Shape* shape = nullptr;
+  const RenderShape* rshape = nullptr;
   float viewWd;
   float viewHt;
   RenderUpdateFlag updateFlag = None;
index c3d3297..b8273e2 100644 (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 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;
index ec1b7e8..c5e5b29 100644 (file)
@@ -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);
index 95582fb..3031f46 100644 (file)
@@ -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 RenderData
 }
 
 
-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;
 }
index 2688566..4d1bb87 100644 (file)
@@ -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;
index 1f920eb..e177f61 100644 (file)
@@ -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);
index 93d0d5d..17dff57 100644 (file)
@@ -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);
index 819141f..c3eadbd 100644 (file)
@@ -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;
index 82d812e..7462a7b 100644 (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;
-    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;
     }
index d1803bd..c404bf9 100644 (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) {
         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;
index 88fe502..e9ad821 100644 (file)
@@ -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;
index 23291eb..3d12698 100644 (file)
@@ -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;
 }
index fa2786f..9f39dfa 100644 (file)
 /* Internal Class Implementation                                        */
 /************************************************************************/
 
-struct ShapeStroke
+struct Shape::Impl
 {
-    float width;
-    uint8_t color[4];
-    Fill *fill;
-    float* dashPattern;
-    uint32_t dashCnt;
-    StrokeCap cap;
-    StrokeJoin join;
-
-    void copy(const ShapeStroke* src)
+    RenderShape rs;                     //shape data
+    RenderData rdata = nullptr;         //engine data
+    uint32_t flag = RenderUpdateFlag::None;
+
+    bool dispose(RenderMethod& renderer)
     {
-       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();
+        auto ret = renderer.dispose(rdata);
+        rdata = nullptr;
+        return ret;
     }
 
-    void clear()
+    bool render(RenderMethod& renderer)
     {
-        if (dashPattern) free(dashPattern);
-        if (fill) delete(fill);
+        return renderer.renderShape(rdata);
     }
-};
-
-
-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()
+    void* update(RenderMethod& renderer, const RenderTransform* transform, uint32_t opacity, Array<RenderData>& clips, RenderUpdateFlag pFlag, bool clipper)
     {
-        if (cmds) free(cmds);
-        if (pts) free(pts);
+        rdata = renderer.prepare(rs, rdata, transform, opacity, clips, static_cast<RenderUpdateFlag>(pFlag | flag), clipper);
+        flag = RenderUpdateFlag::None;
+        return rdata;
     }
 
-    ShapePath()
+    RenderRegion bounds(RenderMethod& renderer)
     {
+        return renderer.region(rdata);
     }
 
-    void duplicate(const ShapePath* src)
+    bool bounds(float* x, float* y, float* w, float* h)
     {
-        if (src->cmdCnt == 0 || src->ptsCnt == 0) return;
-
-        cmdCnt = src->cmdCnt;
-        reservedCmdCnt = src->reservedCmdCnt;
-        ptsCnt = src->ptsCnt;
-        reservedPtsCnt = src->reservedPtsCnt;
+        //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;
+            }
 
-        cmds = static_cast<PathCommand*>(malloc(sizeof(PathCommand) * reservedCmdCnt));
-        if (!cmds) return;
-        memcpy(cmds, src->cmds, sizeof(PathCommand) * cmdCnt);
+            if (x) *x = min.x;
+            if (y) *y = min.y;
+            if (w) *w = max.x - min.x;
+            if (h) *h = max.y - min.y;
+        }
 
-        pts = static_cast<Point*>(malloc(sizeof(Point) * reservedPtsCnt));
-        if (!pts) {
-            free(cmds);
-            return;
+        //Stroke feathering
+        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;
         }
-        memcpy(pts, src->pts, sizeof(Point) * ptsCnt);
+        return rs.path.ptsCnt > 0 ? true : false;
     }
 
     void reserveCmd(uint32_t cmdCnt)
     {
-        if (cmdCnt <= reservedCmdCnt) return;
-        reservedCmdCnt = cmdCnt;
-        cmds = static_cast<PathCommand*>(realloc(cmds, sizeof(PathCommand) * reservedCmdCnt));
+        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 <= reservedPtsCnt) return;
-        reservedPtsCnt = ptsCnt;
-        pts = static_cast<Point*>(realloc(pts, sizeof(Point) * reservedPtsCnt));
+        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(this->cmdCnt + cmdCnt);
-        reservePts(this->ptsCnt + ptsCnt);
+        reserveCmd(rs.path.cmdCnt + cmdCnt);
+        reservePts(rs.path.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);
+        rs.path.cmdCnt = 0;
+        rs.path.ptsCnt = 0;
 
-        cmds[cmdCnt++] = PathCommand::MoveTo;
-        pts[ptsCnt++] = {x, y};
+        flag = RenderUpdateFlag::Path;
     }
 
-    void lineTo(float x, float y)
+    void append(const PathCommand* cmds, uint32_t cmdCnt, const Point* pts, uint32_t ptsCnt)
     {
-        if (cmdCnt + 1 > reservedCmdCnt) reserveCmd((cmdCnt + 1) * 2);
-        if (ptsCnt + 2 > reservedPtsCnt) reservePts((ptsCnt + 2) * 2);
+        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;
 
-        cmds[cmdCnt++] = PathCommand::LineTo;
-        pts[ptsCnt++] = {x, y};
+        flag |= RenderUpdateFlag::Path;
     }
 
-    void cubicTo(float cx1, float cy1, float cx2, float cy2, float x, float y)
+    void moveTo(float x, float y)
     {
-        if (cmdCnt + 1 > reservedCmdCnt) reserveCmd((cmdCnt + 1) * 2);
-        if (ptsCnt + 3 > reservedPtsCnt) reservePts((ptsCnt + 3) * 2);
+        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);
 
-        cmds[cmdCnt++] = PathCommand::CubicTo;
-        pts[ptsCnt++] = {cx1, cy1};
-        pts[ptsCnt++] = {cx2, cy2};
-        pts[ptsCnt++] = {x, y};
-    }
+        rs.path.cmds[rs.path.cmdCnt++] = PathCommand::MoveTo;
+        rs.path.pts[rs.path.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;
+        flag |= RenderUpdateFlag::Path;
     }
 
-    bool bounds(float* x, float* y, float* w, float* h) const
+    void lineTo(float x, float y)
     {
-        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;
-    RenderData rdata = nullptr;         //engine data
-    Shape *shape = nullptr;
-    uint32_t flag = RenderUpdateFlag::None;
+        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);
 
-    Impl(Shape* s) : shape(s)
-    {
-    }
+        rs.path.cmds[rs.path.cmdCnt++] = PathCommand::LineTo;
+        rs.path.pts[rs.path.ptsCnt++] = {x, y};
 
-    ~Impl()
-    {
-        if (fill) delete(fill);
-        if (stroke) {
-            stroke->clear();
-            free (stroke);
-        }
+        flag |= RenderUpdateFlag::Path;
     }
 
-    bool dispose(RenderMethod& renderer)
+    void cubicTo(float cx1, float cy1, float cx2, float cy2, float x, float y)
     {
-        auto ret = renderer.dispose(rdata);
-        rdata = nullptr;
-        return ret;
-    }
+        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);
 
-    bool render(RenderMethod& renderer)
-    {
-        return renderer.renderShape(rdata);
-    }
+        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};
 
-    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);
-        flag = RenderUpdateFlag::None;
-        return this->rdata;
+        flag |= RenderUpdateFlag::Path;
     }
 
-    RenderRegion bounds(RenderMethod& renderer)
+    void close()
     {
-        return renderer.region(rdata);
-    }
+        if (rs.path.cmdCnt > 0 && rs.path.cmds[rs.path.cmdCnt - 1] == PathCommand::Close) return;
 
-    bool bounds(float* x, float* y, float* w, float* h)
-    {
-        auto ret = path.bounds(x, y, w, h);
+        if (rs.path.cmdCnt + 1 > rs.path.reservedCmdCnt) reserveCmd((rs.path.cmdCnt + 1) * 2);
+        rs.path.cmds[rs.path.cmdCnt++] = PathCommand::Close;
 
-        //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;
-        }
-        return ret;
+        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;
         }