gl_engine: make stencil task support other advance logical

* Support rendering Gradient in path Stroke mode
* Fix GlStencilCoverTask not support even-odd fill rule
* Make GlStencilCoverTask can discard overlapped area during stroke
  rendering
This commit is contained in:
RuiwenTang 2024-03-17 16:11:17 +08:00 committed by Hermet Park
parent 54acc7c955
commit 23386625fc
6 changed files with 66 additions and 29 deletions

View file

@ -50,6 +50,13 @@
} while(0) } while(0)
enum class GlStencilMode {
None,
FillWinding,
FillEvenOdd,
Stroke,
};
class GlGeometry; class GlGeometry;
struct GlShape struct GlShape

View file

@ -40,7 +40,7 @@ bool GlGeometry::tesselate(const RenderShape& rshape, RenderUpdateFlag flag)
bwTess.tessellate(&rshape); bwTess.tessellate(&rshape);
mStencilFill = true; mFillRule = rshape.rule;
} }
if (flag & (RenderUpdateFlag::Stroke | RenderUpdateFlag::Transform)) { if (flag & (RenderUpdateFlag::Stroke | RenderUpdateFlag::Transform)) {
@ -154,7 +154,7 @@ bool GlGeometry::draw(GlRenderTask* task, GlStageBuffer* gpuBuffer, RenderUpdate
Array<float>* vertexBuffer = nullptr; Array<float>* vertexBuffer = nullptr;
Array<uint32_t>* indexBuffer = nullptr; Array<uint32_t>* indexBuffer = nullptr;
if (flag & RenderUpdateFlag::Stroke) { if ((flag & RenderUpdateFlag::Stroke) || (flag & RenderUpdateFlag::GradientStroke)) {
vertexBuffer = &strokeVertex; vertexBuffer = &strokeVertex;
indexBuffer = &strokeIndex; indexBuffer = &strokeIndex;
} else { } else {
@ -208,11 +208,14 @@ float* GlGeometry::getTransforMatrix()
return mTransform; return mTransform;
} }
bool GlGeometry::needStencilCover(RenderUpdateFlag flag) GlStencilMode GlGeometry::getStencilMode(RenderUpdateFlag flag)
{ {
if (flag & RenderUpdateFlag::Stroke) return false; if (flag & RenderUpdateFlag::Stroke) return GlStencilMode::Stroke;
if (flag & RenderUpdateFlag::GradientStroke) return false; if (flag & RenderUpdateFlag::GradientStroke) return GlStencilMode::Stroke;
if (flag & RenderUpdateFlag::Image) return false; 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;
} }

View file

@ -195,7 +195,7 @@ public:
void updateTransform(const RenderTransform* transform, float w, float h); void updateTransform(const RenderTransform* transform, float w, float h);
void setViewport(const RenderRegion& viewport); void setViewport(const RenderRegion& viewport);
float* getTransforMatrix(); float* getTransforMatrix();
bool needStencilCover(RenderUpdateFlag flag); GlStencilMode getStencilMode(RenderUpdateFlag flag);
private: private:
RenderRegion viewport = {}; RenderRegion viewport = {};
@ -205,7 +205,7 @@ private:
Array<uint32_t> strokeIndex = {}; Array<uint32_t> strokeIndex = {};
float mTransform[16]; float mTransform[16];
bool mStencilFill = false; FillRule mFillRule = FillRule::Winding;
}; };
#endif /* _TVG_GL_GEOMETRY_H_ */ #endif /* _TVG_GL_GEOMETRY_H_ */

View file

@ -99,8 +99,8 @@ void GlRenderTask::setViewport(const RenderRegion &viewport)
mViewport = viewport; mViewport = viewport;
} }
GlStencilCoverTask::GlStencilCoverTask(GlRenderTask* stencil, GlRenderTask* cover) GlStencilCoverTask::GlStencilCoverTask(GlRenderTask* stencil, GlRenderTask* cover, GlStencilMode mode)
:GlRenderTask(nullptr), mStencilTask(stencil), mCoverTask(cover) {} :GlRenderTask(nullptr), mStencilTask(stencil), mCoverTask(cover), mStencilMode(mode) {}
GlStencilCoverTask::~GlStencilCoverTask() GlStencilCoverTask::~GlStencilCoverTask()
{ {
@ -112,23 +112,43 @@ void GlStencilCoverTask::run()
{ {
GL_CHECK(glEnable(GL_STENCIL_TEST)); GL_CHECK(glEnable(GL_STENCIL_TEST));
GL_CHECK(glStencilFuncSeparate(GL_FRONT, GL_ALWAYS, 0x1, 0xFF)); if (mStencilMode == GlStencilMode::Stroke) {
GL_CHECK(glStencilOpSeparate(GL_FRONT, GL_KEEP, GL_KEEP, GL_INCR_WRAP)); GL_CHECK(glStencilFunc(GL_NOTEQUAL, 0x1, 0xFF));
GL_CHECK(glStencilOp(GL_KEEP, GL_KEEP, GL_REPLACE));
GL_CHECK(glStencilFuncSeparate(GL_BACK, GL_ALWAYS, 0x1, 0xFF)); } else {
GL_CHECK(glStencilOpSeparate(GL_BACK, GL_KEEP, GL_KEEP, GL_DECR_WRAP)); 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)); GL_CHECK(glColorMask(0, 0, 0, 0));
mStencilTask->run(); mStencilTask->run();
GL_CHECK(glStencilFunc(GL_NOTEQUAL, 0x0, 0xFF)); if (mStencilMode == GlStencilMode::FillEvenOdd) {
GL_CHECK(glStencilOp(GL_KEEP, GL_KEEP, GL_REPLACE)); 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)); GL_CHECK(glColorMask(1, 1, 1, 1));
mCoverTask->run(); 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)); GL_CHECK(glDisable(GL_STENCIL_TEST));
} }

View file

@ -100,7 +100,7 @@ private:
class GlStencilCoverTask : public GlRenderTask class GlStencilCoverTask : public GlRenderTask
{ {
public: public:
GlStencilCoverTask(GlRenderTask* stencil, GlRenderTask* cover); GlStencilCoverTask(GlRenderTask* stencil, GlRenderTask* cover, GlStencilMode mode);
~GlStencilCoverTask() override; ~GlStencilCoverTask() override;
void run() override; void run() override;
@ -108,6 +108,7 @@ public:
private: private:
GlRenderTask* mStencilTask; GlRenderTask* mStencilTask;
GlRenderTask* mCoverTask; GlRenderTask* mCoverTask;
GlStencilMode mStencilMode;
}; };
class GlRenderTarget; class GlRenderTarget;

View file

@ -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->strokeFill(&r, &g, &b, &a); auto gradient = sdata->rshape->strokeFill();
if (a > 0) if (gradient) {
{ drawPrimitive(*sdata, gradient, RenderUpdateFlag::GradientStroke);
drawPrimitive(*sdata, r, g, b, a, RenderUpdateFlag::Stroke); } else {
sdata->rshape->strokeFill(&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; 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); 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) { if (stencilTask) {
currentPass()->addRenderTask(new GlStencilCoverTask(stencilTask, task)); currentPass()->addRenderTask(new GlStencilCoverTask(stencilTask, task, stencilMode));
} else { } else {
currentPass()->addRenderTask(task); 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; if (!sdata.geometry->draw(task, mGpuBuffer.get(), flag)) return;
GlRenderTask* stencilTask = nullptr; GlRenderTask* stencilTask = nullptr;
GlStencilMode stencilMode = sdata.geometry->getStencilMode(flag);
if (sdata.geometry->needStencilCover(flag)) stencilTask = new GlRenderTask(mPrograms[RT_Stencil].get(), task); if (stencilMode != GlStencilMode::None) stencilTask = new GlRenderTask(mPrograms[RT_Stencil].get(), task);
// matrix buffer // matrix buffer
{ {
@ -668,7 +674,7 @@ void GlRenderer::drawPrimitive(GlShape& sdata, const Fill* fill, RenderUpdateFla
} }
if (stencilTask) { if (stencilTask) {
currentPass()->addRenderTask(new GlStencilCoverTask(stencilTask, task)); currentPass()->addRenderTask(new GlStencilCoverTask(stencilTask, task, stencilMode));
} else { } else {
currentPass()->addRenderTask(task); currentPass()->addRenderTask(task);
} }