diff --git a/src/renderer/gl_engine/tvgGlCommon.h b/src/renderer/gl_engine/tvgGlCommon.h index fdee0c4f..e9a92471 100644 --- a/src/renderer/gl_engine/tvgGlCommon.h +++ b/src/renderer/gl_engine/tvgGlCommon.h @@ -50,6 +50,13 @@ } while(0) +enum class GlStencilMode { + None, + FillWinding, + FillEvenOdd, + Stroke, +}; + class GlGeometry; struct GlShape diff --git a/src/renderer/gl_engine/tvgGlGeometry.cpp b/src/renderer/gl_engine/tvgGlGeometry.cpp index 1e9cda47..4d7986a1 100644 --- a/src/renderer/gl_engine/tvgGlGeometry.cpp +++ b/src/renderer/gl_engine/tvgGlGeometry.cpp @@ -40,7 +40,7 @@ bool GlGeometry::tesselate(const RenderShape& rshape, RenderUpdateFlag flag) bwTess.tessellate(&rshape); - mStencilFill = true; + mFillRule = rshape.rule; } if (flag & (RenderUpdateFlag::Stroke | RenderUpdateFlag::Transform)) { @@ -154,7 +154,7 @@ bool GlGeometry::draw(GlRenderTask* task, GlStageBuffer* gpuBuffer, RenderUpdate Array* vertexBuffer = nullptr; Array* indexBuffer = nullptr; - if (flag & RenderUpdateFlag::Stroke) { + if ((flag & RenderUpdateFlag::Stroke) || (flag & RenderUpdateFlag::GradientStroke)) { vertexBuffer = &strokeVertex; indexBuffer = &strokeIndex; } else { @@ -208,11 +208,14 @@ float* GlGeometry::getTransforMatrix() return mTransform; } -bool GlGeometry::needStencilCover(RenderUpdateFlag flag) +GlStencilMode GlGeometry::getStencilMode(RenderUpdateFlag flag) { - if (flag & RenderUpdateFlag::Stroke) return false; - if (flag & RenderUpdateFlag::GradientStroke) return false; - if (flag & RenderUpdateFlag::Image) return false; + if (flag & RenderUpdateFlag::Stroke) return GlStencilMode::Stroke; + if (flag & RenderUpdateFlag::GradientStroke) return GlStencilMode::Stroke; + if (flag & RenderUpdateFlag::Image) return GlStencilMode::None; - return mStencilFill; + if (mFillRule == FillRule::Winding) return GlStencilMode::FillWinding; + if (mFillRule == FillRule::EvenOdd) return GlStencilMode::FillEvenOdd; + + return GlStencilMode::None; } diff --git a/src/renderer/gl_engine/tvgGlGeometry.h b/src/renderer/gl_engine/tvgGlGeometry.h index d60b56a6..62c646db 100644 --- a/src/renderer/gl_engine/tvgGlGeometry.h +++ b/src/renderer/gl_engine/tvgGlGeometry.h @@ -195,7 +195,7 @@ public: void updateTransform(const RenderTransform* transform, float w, float h); void setViewport(const RenderRegion& viewport); float* getTransforMatrix(); - bool needStencilCover(RenderUpdateFlag flag); + GlStencilMode getStencilMode(RenderUpdateFlag flag); private: RenderRegion viewport = {}; @@ -205,7 +205,7 @@ private: Array strokeIndex = {}; float mTransform[16]; - bool mStencilFill = false; + FillRule mFillRule = FillRule::Winding; }; #endif /* _TVG_GL_GEOMETRY_H_ */ diff --git a/src/renderer/gl_engine/tvgGlRenderTask.cpp b/src/renderer/gl_engine/tvgGlRenderTask.cpp index 37142d99..753b5c10 100644 --- a/src/renderer/gl_engine/tvgGlRenderTask.cpp +++ b/src/renderer/gl_engine/tvgGlRenderTask.cpp @@ -99,8 +99,8 @@ void GlRenderTask::setViewport(const RenderRegion &viewport) mViewport = viewport; } -GlStencilCoverTask::GlStencilCoverTask(GlRenderTask* stencil, GlRenderTask* cover) - :GlRenderTask(nullptr), mStencilTask(stencil), mCoverTask(cover) {} +GlStencilCoverTask::GlStencilCoverTask(GlRenderTask* stencil, GlRenderTask* cover, GlStencilMode mode) + :GlRenderTask(nullptr), mStencilTask(stencil), mCoverTask(cover), mStencilMode(mode) {} GlStencilCoverTask::~GlStencilCoverTask() { @@ -112,23 +112,43 @@ void GlStencilCoverTask::run() { GL_CHECK(glEnable(GL_STENCIL_TEST)); - GL_CHECK(glStencilFuncSeparate(GL_FRONT, GL_ALWAYS, 0x1, 0xFF)); - GL_CHECK(glStencilOpSeparate(GL_FRONT, GL_KEEP, GL_KEEP, GL_INCR_WRAP)); - - GL_CHECK(glStencilFuncSeparate(GL_BACK, GL_ALWAYS, 0x1, 0xFF)); - GL_CHECK(glStencilOpSeparate(GL_BACK, GL_KEEP, GL_KEEP, GL_DECR_WRAP)); + if (mStencilMode == GlStencilMode::Stroke) { + GL_CHECK(glStencilFunc(GL_NOTEQUAL, 0x1, 0xFF)); + GL_CHECK(glStencilOp(GL_KEEP, GL_KEEP, GL_REPLACE)); + } else { + GL_CHECK(glStencilFuncSeparate(GL_FRONT, GL_ALWAYS, 0x1, 0xFF)); + GL_CHECK(glStencilOpSeparate(GL_FRONT, GL_KEEP, GL_KEEP, GL_INCR_WRAP)); + GL_CHECK(glStencilFuncSeparate(GL_BACK, GL_ALWAYS, 0x1, 0xFF)); + GL_CHECK(glStencilOpSeparate(GL_BACK, GL_KEEP, GL_KEEP, GL_DECR_WRAP)); + } GL_CHECK(glColorMask(0, 0, 0, 0)); mStencilTask->run(); - GL_CHECK(glStencilFunc(GL_NOTEQUAL, 0x0, 0xFF)); - GL_CHECK(glStencilOp(GL_KEEP, GL_KEEP, GL_REPLACE)); + if (mStencilMode == GlStencilMode::FillEvenOdd) { + GL_CHECK(glStencilFunc(GL_EQUAL, 0x01, 0x01)); + GL_CHECK(glStencilOp(GL_KEEP, GL_KEEP, GL_REPLACE)); + } else { + GL_CHECK(glStencilFunc(GL_NOTEQUAL, 0x0, 0xFF)); + GL_CHECK(glStencilOp(GL_KEEP, GL_KEEP, GL_REPLACE)); + } GL_CHECK(glColorMask(1, 1, 1, 1)); mCoverTask->run(); + if (mStencilMode == GlStencilMode::FillEvenOdd) { + GL_CHECK(glStencilFunc(GL_NOTEQUAL, 0x0, 0xFF)); + GL_CHECK(glStencilOp(GL_KEEP, GL_KEEP, GL_REPLACE)); + + GL_CHECK(glColorMask(0, 0, 0, 0)); + + mStencilTask->run(); + + GL_CHECK(glColorMask(1, 1, 1, 1)); + } + GL_CHECK(glDisable(GL_STENCIL_TEST)); } diff --git a/src/renderer/gl_engine/tvgGlRenderTask.h b/src/renderer/gl_engine/tvgGlRenderTask.h index 3256a00a..78fd16cc 100644 --- a/src/renderer/gl_engine/tvgGlRenderTask.h +++ b/src/renderer/gl_engine/tvgGlRenderTask.h @@ -100,7 +100,7 @@ private: class GlStencilCoverTask : public GlRenderTask { public: - GlStencilCoverTask(GlRenderTask* stencil, GlRenderTask* cover); + GlStencilCoverTask(GlRenderTask* stencil, GlRenderTask* cover, GlStencilMode mode); ~GlStencilCoverTask() override; void run() override; @@ -108,6 +108,7 @@ public: private: GlRenderTask* mStencilTask; GlRenderTask* mCoverTask; + GlStencilMode mStencilMode; }; class GlRenderTarget; diff --git a/src/renderer/gl_engine/tvgGlRenderer.cpp b/src/renderer/gl_engine/tvgGlRenderer.cpp index 9583acad..eae0daae 100644 --- a/src/renderer/gl_engine/tvgGlRenderer.cpp +++ b/src/renderer/gl_engine/tvgGlRenderer.cpp @@ -266,12 +266,17 @@ bool GlRenderer::renderShape(RenderData data) } } - if (flags & (RenderUpdateFlag::Stroke | RenderUpdateFlag::Transform)) + if (flags & (RenderUpdateFlag::Stroke | RenderUpdateFlag::GradientStroke | RenderUpdateFlag::Transform)) { - sdata->rshape->strokeColor(&r, &g, &b, &a); - if (a > 0) - { - drawPrimitive(*sdata, r, g, b, a, RenderUpdateFlag::Stroke); + auto gradient = sdata->rshape->strokeFill(); + if (gradient) { + drawPrimitive(*sdata, gradient, RenderUpdateFlag::GradientStroke); + } else { + sdata->rshape->strokeColor(&r, &g, &b, &a); + if (a > 0) + { + drawPrimitive(*sdata, r, g, b, a, RenderUpdateFlag::Stroke); + } } } @@ -491,7 +496,8 @@ void GlRenderer::drawPrimitive(GlShape& sdata, uint8_t r, uint8_t g, uint8_t b, GlRenderTask* stencilTask = nullptr; - if (sdata.geometry->needStencilCover(flag)) stencilTask = new GlRenderTask(mPrograms[RT_Stencil].get(), task); + GlStencilMode stencilMode = sdata.geometry->getStencilMode(flag); + if (stencilMode != GlStencilMode::None) stencilTask = new GlRenderTask(mPrograms[RT_Stencil].get(), task); a = MULTIPLY(a, sdata.opacity); @@ -536,7 +542,7 @@ void GlRenderer::drawPrimitive(GlShape& sdata, uint8_t r, uint8_t g, uint8_t b, } if (stencilTask) { - currentPass()->addRenderTask(new GlStencilCoverTask(stencilTask, task)); + currentPass()->addRenderTask(new GlStencilCoverTask(stencilTask, task, stencilMode)); } else { currentPass()->addRenderTask(task); } @@ -563,8 +569,8 @@ void GlRenderer::drawPrimitive(GlShape& sdata, const Fill* fill, RenderUpdateFla if (!sdata.geometry->draw(task, mGpuBuffer.get(), flag)) return; GlRenderTask* stencilTask = nullptr; - - if (sdata.geometry->needStencilCover(flag)) stencilTask = new GlRenderTask(mPrograms[RT_Stencil].get(), task); + GlStencilMode stencilMode = sdata.geometry->getStencilMode(flag); + if (stencilMode != GlStencilMode::None) stencilTask = new GlRenderTask(mPrograms[RT_Stencil].get(), task); // matrix buffer { @@ -668,7 +674,7 @@ void GlRenderer::drawPrimitive(GlShape& sdata, const Fill* fill, RenderUpdateFla } if (stencilTask) { - currentPass()->addRenderTask(new GlStencilCoverTask(stencilTask, task)); + currentPass()->addRenderTask(new GlStencilCoverTask(stencilTask, task, stencilMode)); } else { currentPass()->addRenderTask(task); }