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 a4d169d4af
commit ed146ed162
6 changed files with 66 additions and 29 deletions

View file

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

View file

@ -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<float>* vertexBuffer = nullptr;
Array<uint32_t>* 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;
}

View file

@ -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<uint32_t> strokeIndex = {};
float mTransform[16];
bool mStencilFill = false;
FillRule mFillRule = FillRule::Winding;
};
#endif /* _TVG_GL_GEOMETRY_H_ */

View file

@ -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,22 +112,42 @@ void GlStencilCoverTask::run()
{
GL_CHECK(glEnable(GL_STENCIL_TEST));
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();
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(glStencilFunc(GL_NOTEQUAL, 0x0, 0xFF));
GL_CHECK(glStencilOp(GL_KEEP, GL_KEEP, GL_REPLACE));
GL_CHECK(glColorMask(1, 1, 1, 1));
mCoverTask->run();
}
GL_CHECK(glDisable(GL_STENCIL_TEST));
}

View file

@ -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;

View file

@ -266,14 +266,19 @@ bool GlRenderer::renderShape(RenderData data)
}
}
if (flags & (RenderUpdateFlag::Stroke | RenderUpdateFlag::Transform))
if (flags & (RenderUpdateFlag::Stroke | RenderUpdateFlag::GradientStroke | RenderUpdateFlag::Transform))
{
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);
}
}
}
return true;
}
@ -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);
}