mirror of
https://github.com/thorvg/thorvg.git
synced 2025-06-15 12:34:30 +00:00
gl_engine: optimize tessellation performance
* restrict the scissor box of composite task * do not tessellate stroke or fill geometry if there is no Fill or Stroke color * use actually transformed curve to calculate polyline count when doing curve flatten
This commit is contained in:
parent
b1912e2ef9
commit
eb6067f87b
7 changed files with 187 additions and 27 deletions
|
@ -93,5 +93,11 @@ struct GlRadialGradientBlock
|
||||||
alignas(16) float stopColors[4 * MAX_GRADIENT_STOPS] = {};
|
alignas(16) float stopColors[4 * MAX_GRADIENT_STOPS] = {};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct GlCompositor : public Compositor
|
||||||
|
{
|
||||||
|
RenderRegion bbox = {};
|
||||||
|
|
||||||
|
GlCompositor(const RenderRegion& box) : bbox(box) {}
|
||||||
|
};
|
||||||
|
|
||||||
#endif /* _TVG_GL_COMMON_H_ */
|
#endif /* _TVG_GL_COMMON_H_ */
|
||||||
|
|
|
@ -38,17 +38,21 @@ bool GlGeometry::tesselate(const RenderShape& rshape, RenderUpdateFlag flag)
|
||||||
|
|
||||||
BWTessellator bwTess{&fillVertex, &fillIndex};
|
BWTessellator bwTess{&fillVertex, &fillIndex};
|
||||||
|
|
||||||
bwTess.tessellate(&rshape);
|
bwTess.tessellate(&rshape, mMatrix);
|
||||||
|
|
||||||
mFillRule = rshape.rule;
|
mFillRule = rshape.rule;
|
||||||
|
|
||||||
|
mBounds = bwTess.bounds();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (flag & (RenderUpdateFlag::Stroke | RenderUpdateFlag::Transform)) {
|
if (flag & (RenderUpdateFlag::Stroke | RenderUpdateFlag::Transform)) {
|
||||||
strokeVertex.clear();
|
strokeVertex.clear();
|
||||||
strokeIndex.clear();
|
strokeIndex.clear();
|
||||||
|
|
||||||
Stroker stroke{&strokeVertex, &strokeIndex};
|
Stroker stroke{&strokeVertex, &strokeIndex, mMatrix};
|
||||||
stroke.stroke(&rshape);
|
stroke.stroke(&rshape);
|
||||||
|
|
||||||
|
mBounds = stroke.bounds();
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
|
@ -90,6 +94,32 @@ bool GlGeometry::tesselate(const Surface* image, const RenderMesh* mesh, RenderU
|
||||||
index += 3;
|
index += 3;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
float left = mesh->triangles[0].vertex[0].pt.x;
|
||||||
|
float top = mesh->triangles[0].vertex[0].pt.y;
|
||||||
|
float right = mesh->triangles[0].vertex[0].pt.x;
|
||||||
|
float bottom = mesh->triangles[0].vertex[0].pt.y;
|
||||||
|
|
||||||
|
for (uint32_t i = 0; i < mesh->triangleCnt; i++) {
|
||||||
|
left = min(left, mesh->triangles[i].vertex[0].pt.x);
|
||||||
|
left = min(left, mesh->triangles[i].vertex[1].pt.x);
|
||||||
|
left = min(left, mesh->triangles[i].vertex[2].pt.x);
|
||||||
|
top = min(top, mesh->triangles[i].vertex[0].pt.y);
|
||||||
|
top = min(top, mesh->triangles[i].vertex[1].pt.y);
|
||||||
|
top = min(top, mesh->triangles[i].vertex[2].pt.y);
|
||||||
|
|
||||||
|
right = max(right, mesh->triangles[i].vertex[0].pt.x);
|
||||||
|
right = max(right, mesh->triangles[i].vertex[1].pt.x);
|
||||||
|
right = max(right, mesh->triangles[i].vertex[2].pt.x);
|
||||||
|
bottom = max(bottom, mesh->triangles[i].vertex[0].pt.y);
|
||||||
|
bottom = max(bottom, mesh->triangles[i].vertex[1].pt.y);
|
||||||
|
bottom = max(bottom, mesh->triangles[i].vertex[2].pt.y);
|
||||||
|
}
|
||||||
|
|
||||||
|
mBounds.x = static_cast<int32_t>(left);
|
||||||
|
mBounds.y = static_cast<int32_t>(top);
|
||||||
|
mBounds.w = static_cast<int32_t>(right - left);
|
||||||
|
mBounds.h = static_cast<int32_t>(bottom - top);
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
fillVertex.reserve(5 * 4);
|
fillVertex.reserve(5 * 4);
|
||||||
fillIndex.reserve(6);
|
fillIndex.reserve(6);
|
||||||
|
@ -131,6 +161,11 @@ bool GlGeometry::tesselate(const Surface* image, const RenderMesh* mesh, RenderU
|
||||||
fillIndex.push(2);
|
fillIndex.push(2);
|
||||||
fillIndex.push(1);
|
fillIndex.push(1);
|
||||||
fillIndex.push(3);
|
fillIndex.push(3);
|
||||||
|
|
||||||
|
mBounds.x = 0;
|
||||||
|
mBounds.y = 0;
|
||||||
|
mBounds.w = image->w;
|
||||||
|
mBounds.h = image->h;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -186,12 +221,15 @@ void GlGeometry::updateTransform(const RenderTransform* transform, float w, floa
|
||||||
float modelMatrix[16];
|
float modelMatrix[16];
|
||||||
if (transform) {
|
if (transform) {
|
||||||
GET_MATRIX44(transform->m, modelMatrix);
|
GET_MATRIX44(transform->m, modelMatrix);
|
||||||
|
mMatrix = transform->m;
|
||||||
} else {
|
} else {
|
||||||
memset(modelMatrix, 0, 16 * sizeof(float));
|
memset(modelMatrix, 0, 16 * sizeof(float));
|
||||||
modelMatrix[0] = 1.f;
|
modelMatrix[0] = 1.f;
|
||||||
modelMatrix[5] = 1.f;
|
modelMatrix[5] = 1.f;
|
||||||
modelMatrix[10] = 1.f;
|
modelMatrix[10] = 1.f;
|
||||||
modelMatrix[15] = 1.f;
|
modelMatrix[15] = 1.f;
|
||||||
|
|
||||||
|
mMatrix = Matrix{1, 0, 0, 0, 1, 0, 0, 0, 1};
|
||||||
}
|
}
|
||||||
|
|
||||||
MVP_MATRIX();
|
MVP_MATRIX();
|
||||||
|
@ -219,3 +257,37 @@ GlStencilMode GlGeometry::getStencilMode(RenderUpdateFlag flag)
|
||||||
|
|
||||||
return GlStencilMode::None;
|
return GlStencilMode::None;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
RenderRegion GlGeometry::getBounds() const
|
||||||
|
{
|
||||||
|
if (mathIdentity(&mMatrix)) {
|
||||||
|
return mBounds;
|
||||||
|
} else {
|
||||||
|
Point lt{static_cast<float>(mBounds.x), static_cast<float>(mBounds.y)};
|
||||||
|
Point lb{static_cast<float>(mBounds.x), static_cast<float>(mBounds.y + mBounds.h)};
|
||||||
|
Point rt{static_cast<float>(mBounds.x + mBounds.w), static_cast<float>(mBounds.y)};
|
||||||
|
Point rb{static_cast<float>(mBounds.x + mBounds.w), static_cast<float>(mBounds.y + mBounds.h)};
|
||||||
|
|
||||||
|
mathMultiply(<, &mMatrix);
|
||||||
|
mathMultiply(&lb, &mMatrix);
|
||||||
|
mathMultiply(&rt, &mMatrix);
|
||||||
|
mathMultiply(&rb, &mMatrix);
|
||||||
|
|
||||||
|
float left = min(min(lt.x, lb.x), min(rt.x, rb.x));
|
||||||
|
float top = min(min(lt.y, lb.y), min(rt.y, rb.y));
|
||||||
|
float right = max(max(lt.x, lb.x), max(rt.x, rb.x));
|
||||||
|
float bottom = max(max(lt.y, lb.y), max(rt.y, rb.y));
|
||||||
|
|
||||||
|
auto bounds = RenderRegion {
|
||||||
|
static_cast<int32_t>(left),
|
||||||
|
static_cast<int32_t>(top),
|
||||||
|
static_cast<int32_t>(right - left),
|
||||||
|
static_cast<int32_t>(bottom - top),
|
||||||
|
};
|
||||||
|
if (bounds.x < 0 || bounds.y < 0 || bounds.w < 0 || bounds.h < 0) {
|
||||||
|
return mBounds;
|
||||||
|
} else {
|
||||||
|
return bounds;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -196,6 +196,7 @@ public:
|
||||||
void setViewport(const RenderRegion& viewport);
|
void setViewport(const RenderRegion& viewport);
|
||||||
float* getTransforMatrix();
|
float* getTransforMatrix();
|
||||||
GlStencilMode getStencilMode(RenderUpdateFlag flag);
|
GlStencilMode getStencilMode(RenderUpdateFlag flag);
|
||||||
|
RenderRegion getBounds() const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
RenderRegion viewport = {};
|
RenderRegion viewport = {};
|
||||||
|
@ -204,8 +205,10 @@ private:
|
||||||
Array<uint32_t> fillIndex = {};
|
Array<uint32_t> fillIndex = {};
|
||||||
Array<uint32_t> strokeIndex = {};
|
Array<uint32_t> strokeIndex = {};
|
||||||
float mTransform[16];
|
float mTransform[16];
|
||||||
|
Matrix mMatrix = {};
|
||||||
|
|
||||||
FillRule mFillRule = FillRule::Winding;
|
FillRule mFillRule = FillRule::Winding;
|
||||||
|
RenderRegion mBounds = {};
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif /* _TVG_GL_GEOMETRY_H_ */
|
#endif /* _TVG_GL_GEOMETRY_H_ */
|
||||||
|
|
|
@ -133,9 +133,10 @@ bool GlRenderer::sync()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
RenderRegion GlRenderer::region(TVG_UNUSED RenderData data)
|
RenderRegion GlRenderer::region(RenderData data)
|
||||||
{
|
{
|
||||||
return {0, 0, static_cast<int32_t>(surface.w), static_cast<int32_t>(surface.h)};
|
auto shape = reinterpret_cast<GlShape*>(data);
|
||||||
|
return shape->geometry->getBounds();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -158,9 +159,9 @@ bool GlRenderer::postRender()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
Compositor* GlRenderer::target(TVG_UNUSED const RenderRegion& region, TVG_UNUSED ColorSpace cs)
|
Compositor* GlRenderer::target(const RenderRegion& region, TVG_UNUSED ColorSpace cs)
|
||||||
{
|
{
|
||||||
mComposeStack.emplace_back(make_unique<tvg::Compositor>());
|
mComposeStack.emplace_back(make_unique<GlCompositor>(region));
|
||||||
return mComposeStack.back().get();
|
return mComposeStack.back().get();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -209,6 +210,9 @@ ColorSpace GlRenderer::colorSpace()
|
||||||
|
|
||||||
bool GlRenderer::blend(TVG_UNUSED BlendMethod method)
|
bool GlRenderer::blend(TVG_UNUSED BlendMethod method)
|
||||||
{
|
{
|
||||||
|
if (method != BlendMethod::Normal) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
//TODO:
|
//TODO:
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -273,6 +277,8 @@ bool GlRenderer::renderShape(RenderData data)
|
||||||
auto sdata = static_cast<GlShape*>(data);
|
auto sdata = static_cast<GlShape*>(data);
|
||||||
if (!sdata) return false;
|
if (!sdata) return false;
|
||||||
|
|
||||||
|
if (sdata->updateFlag == RenderUpdateFlag::None) return false;
|
||||||
|
|
||||||
if (!sdata->clips.empty()) drawClip(sdata->clips);
|
if (!sdata->clips.empty()) drawClip(sdata->clips);
|
||||||
|
|
||||||
uint8_t r = 0, g = 0, b = 0, a = 0;
|
uint8_t r = 0, g = 0, b = 0, a = 0;
|
||||||
|
@ -351,7 +357,7 @@ RenderData GlRenderer::prepare(Surface* image, const RenderMesh* mesh, RenderDat
|
||||||
|
|
||||||
sdata->viewWd = static_cast<float>(surface.w);
|
sdata->viewWd = static_cast<float>(surface.w);
|
||||||
sdata->viewHt = static_cast<float>(surface.h);
|
sdata->viewHt = static_cast<float>(surface.h);
|
||||||
sdata->updateFlag = flags;
|
sdata->updateFlag = RenderUpdateFlag::Image;
|
||||||
|
|
||||||
if (sdata->texId == 0) {
|
if (sdata->texId == 0) {
|
||||||
sdata->texId = _genTexture(image);
|
sdata->texId = _genTexture(image);
|
||||||
|
@ -398,9 +404,7 @@ RenderData GlRenderer::prepare(const RenderShape& rshape, RenderData data, const
|
||||||
|
|
||||||
sdata->viewWd = static_cast<float>(surface.w);
|
sdata->viewWd = static_cast<float>(surface.w);
|
||||||
sdata->viewHt = static_cast<float>(surface.h);
|
sdata->viewHt = static_cast<float>(surface.h);
|
||||||
sdata->updateFlag = flags;
|
sdata->updateFlag = RenderUpdateFlag::None;
|
||||||
|
|
||||||
if (sdata->updateFlag == RenderUpdateFlag::None) return sdata;
|
|
||||||
|
|
||||||
sdata->geometry = make_unique<GlGeometry>();
|
sdata->geometry = make_unique<GlGeometry>();
|
||||||
sdata->opacity = opacity;
|
sdata->opacity = opacity;
|
||||||
|
@ -417,6 +421,17 @@ RenderData GlRenderer::prepare(const RenderShape& rshape, RenderData data, const
|
||||||
return sdata;
|
return sdata;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (clipper) {
|
||||||
|
sdata->updateFlag = RenderUpdateFlag::Path;
|
||||||
|
} else {
|
||||||
|
if (alphaF) sdata->updateFlag = static_cast<RenderUpdateFlag>(RenderUpdateFlag::Color | sdata->updateFlag);
|
||||||
|
if (rshape.fill) sdata->updateFlag = static_cast<RenderUpdateFlag>(RenderUpdateFlag::Gradient | sdata->updateFlag);
|
||||||
|
if (alphaS) sdata->updateFlag = static_cast<RenderUpdateFlag>(RenderUpdateFlag::Stroke | sdata->updateFlag);
|
||||||
|
if (rshape.strokeFill()) sdata->updateFlag = static_cast<RenderUpdateFlag>(RenderUpdateFlag::GradientStroke | sdata->updateFlag);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (sdata->updateFlag == RenderUpdateFlag::None) return sdata;
|
||||||
|
|
||||||
sdata->geometry->updateTransform(transform, sdata->viewWd, sdata->viewHt);
|
sdata->geometry->updateTransform(transform, sdata->viewWd, sdata->viewHt);
|
||||||
sdata->geometry->setViewport(RenderRegion{
|
sdata->geometry->setViewport(RenderRegion{
|
||||||
mViewport.x,
|
mViewport.x,
|
||||||
|
@ -835,7 +850,7 @@ GlRenderPass* GlRenderer::currentPass()
|
||||||
|
|
||||||
void GlRenderer::prepareBlitTask(GlBlitTask* task)
|
void GlRenderer::prepareBlitTask(GlBlitTask* task)
|
||||||
{
|
{
|
||||||
prepareCmpTask(task);
|
prepareCmpTask(task, mViewport);
|
||||||
|
|
||||||
{
|
{
|
||||||
uint32_t loc = task->getProgram()->getUniformLocation("uSrcTexture");
|
uint32_t loc = task->getProgram()->getUniformLocation("uSrcTexture");
|
||||||
|
@ -843,7 +858,7 @@ void GlRenderer::prepareBlitTask(GlBlitTask* task)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void GlRenderer::prepareCmpTask(GlRenderTask* task)
|
void GlRenderer::prepareCmpTask(GlRenderTask* task, const RenderRegion& vp)
|
||||||
{
|
{
|
||||||
// we use 1:1 blit mapping since compositor fbo is same size as root fbo
|
// we use 1:1 blit mapping since compositor fbo is same size as root fbo
|
||||||
Array<float> vertices(4 * 4);
|
Array<float> vertices(4 * 4);
|
||||||
|
@ -891,15 +906,16 @@ void GlRenderer::prepareCmpTask(GlRenderTask* task)
|
||||||
|
|
||||||
task->setDrawRange(indexOffset, indices.count);
|
task->setDrawRange(indexOffset, indices.count);
|
||||||
task->setViewport(RenderRegion{
|
task->setViewport(RenderRegion{
|
||||||
mViewport.x,
|
vp.x,
|
||||||
static_cast<int32_t>((surface.h - mViewport.y - mViewport.h)),
|
static_cast<int32_t>((surface.h - vp.y - vp.h)),
|
||||||
mViewport.w,
|
vp.w,
|
||||||
mViewport.h,
|
vp.h,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
void GlRenderer::endRenderPass(Compositor* cmp)
|
void GlRenderer::endRenderPass(Compositor* cmp)
|
||||||
{
|
{
|
||||||
|
auto gl_cmp = static_cast<GlCompositor*>(cmp);
|
||||||
if (cmp->method != CompositeMethod::None) {
|
if (cmp->method != CompositeMethod::None) {
|
||||||
auto self_pass = std::move(mRenderPassStack.back());
|
auto self_pass = std::move(mRenderPassStack.back());
|
||||||
mRenderPassStack.pop_back();
|
mRenderPassStack.pop_back();
|
||||||
|
@ -949,7 +965,7 @@ void GlRenderer::endRenderPass(Compositor* cmp)
|
||||||
|
|
||||||
auto compose_task = self_pass.endRenderPass<GlDrawBlitTask>(program, currentPass()->getFboId());
|
auto compose_task = self_pass.endRenderPass<GlDrawBlitTask>(program, currentPass()->getFboId());
|
||||||
|
|
||||||
prepareCmpTask(compose_task);
|
prepareCmpTask(compose_task, gl_cmp->bbox);
|
||||||
|
|
||||||
{
|
{
|
||||||
uint32_t loc = program->getUniformLocation("uSrcTexture");
|
uint32_t loc = program->getUniformLocation("uSrcTexture");
|
||||||
|
@ -971,7 +987,7 @@ void GlRenderer::endRenderPass(Compositor* cmp)
|
||||||
auto task = renderPass.endRenderPass<GlDrawBlitTask>(
|
auto task = renderPass.endRenderPass<GlDrawBlitTask>(
|
||||||
mPrograms[RT_Image].get(), currentPass()->getFboId());
|
mPrograms[RT_Image].get(), currentPass()->getFboId());
|
||||||
|
|
||||||
prepareCmpTask(task);
|
prepareCmpTask(task, gl_cmp->bbox);
|
||||||
|
|
||||||
// matrix buffer
|
// matrix buffer
|
||||||
{
|
{
|
||||||
|
|
|
@ -93,7 +93,7 @@ private:
|
||||||
GlRenderPass* currentPass();
|
GlRenderPass* currentPass();
|
||||||
|
|
||||||
void prepareBlitTask(GlBlitTask* task);
|
void prepareBlitTask(GlBlitTask* task);
|
||||||
void prepareCmpTask(GlRenderTask* task);
|
void prepareCmpTask(GlRenderTask* task, const RenderRegion& vp);
|
||||||
void endRenderPass(Compositor* cmp);
|
void endRenderPass(Compositor* cmp);
|
||||||
|
|
||||||
GLint mTargetFboId = 0;
|
GLint mTargetFboId = 0;
|
||||||
|
@ -105,7 +105,7 @@ private:
|
||||||
unique_ptr<GlRenderTarget> mRootTarget = {};
|
unique_ptr<GlRenderTarget> mRootTarget = {};
|
||||||
Array<GlRenderTarget*> mComposePool = {};
|
Array<GlRenderTarget*> mComposePool = {};
|
||||||
vector<GlRenderPass> mRenderPassStack = {};
|
vector<GlRenderPass> mRenderPassStack = {};
|
||||||
vector<unique_ptr<Compositor>> mComposeStack = {};
|
vector<unique_ptr<GlCompositor>> mComposeStack = {};
|
||||||
|
|
||||||
bool mClearBuffer = true;
|
bool mClearBuffer = true;
|
||||||
};
|
};
|
||||||
|
|
|
@ -1638,7 +1638,7 @@ void Tessellator::emitTriangle(detail::Vertex *p1, detail::Vertex *p2, detail::V
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
Stroker::Stroker(Array<float> *points, Array<uint32_t> *indices) : mResGlPoints(points), mResIndices(indices)
|
Stroker::Stroker(Array<float> *points, Array<uint32_t> *indices, const Matrix& matrix) : mResGlPoints(points), mResIndices(indices), mMatrix(matrix)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1665,6 +1665,16 @@ void Stroker::stroke(const RenderShape *rshape)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
RenderRegion Stroker::bounds() const
|
||||||
|
{
|
||||||
|
return RenderRegion {
|
||||||
|
static_cast<int32_t>(mLeftTop.x),
|
||||||
|
static_cast<int32_t>(mLeftTop.y),
|
||||||
|
static_cast<int32_t>(mRightBottom.x - mLeftTop.x),
|
||||||
|
static_cast<int32_t>(mRightBottom.y - mLeftTop.y),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
void Stroker::doStroke(const PathCommand *cmds, uint32_t cmd_count, const Point *pts, uint32_t pts_count)
|
void Stroker::doStroke(const PathCommand *cmds, uint32_t cmd_count, const Point *pts, uint32_t pts_count)
|
||||||
{
|
{
|
||||||
mResGlPoints->reserve(pts_count * 4 + 16);
|
mResGlPoints->reserve(pts_count * 4 + 16);
|
||||||
|
@ -1773,6 +1783,16 @@ void Stroker::strokeLineTo(const GlPoint &curr)
|
||||||
mStrokeState.prevPtDir = dir;
|
mStrokeState.prevPtDir = dir;
|
||||||
mStrokeState.prevPt = curr;
|
mStrokeState.prevPt = curr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (ia == 0) {
|
||||||
|
mRightBottom.x = mLeftTop.x = curr.x;
|
||||||
|
mRightBottom.y = mLeftTop.y = curr.y;
|
||||||
|
} else {
|
||||||
|
mLeftTop.x = min(mLeftTop.x, curr.x);
|
||||||
|
mLeftTop.y = min(mLeftTop.y, curr.y);
|
||||||
|
mRightBottom.x = max(mRightBottom.x, curr.x);
|
||||||
|
mRightBottom.y = max(mRightBottom.y , curr.y);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Stroker::strokeCubicTo(const GlPoint &cnt1, const GlPoint &cnt2, const GlPoint &end)
|
void Stroker::strokeCubicTo(const GlPoint &cnt1, const GlPoint &cnt2, const GlPoint &end)
|
||||||
|
@ -1783,7 +1803,13 @@ void Stroker::strokeCubicTo(const GlPoint &cnt1, const GlPoint &cnt2, const GlPo
|
||||||
curve.ctrl2 = Point{cnt2.x, cnt2.y};
|
curve.ctrl2 = Point{cnt2.x, cnt2.y};
|
||||||
curve.end = Point{end.x, end.y};
|
curve.end = Point{end.x, end.y};
|
||||||
|
|
||||||
auto count = detail::_bezierCurveCount(curve);
|
Bezier relCurve {curve.start, curve.ctrl1, curve.ctrl2, curve.end};
|
||||||
|
mathMultiply(&relCurve.start, &mMatrix);
|
||||||
|
mathMultiply(&relCurve.ctrl1, &mMatrix);
|
||||||
|
mathMultiply(&relCurve.ctrl2, &mMatrix);
|
||||||
|
mathMultiply(&relCurve.end, &mMatrix);
|
||||||
|
|
||||||
|
auto count = detail::_bezierCurveCount(relCurve);
|
||||||
|
|
||||||
float step = 1.f / count;
|
float step = 1.f / count;
|
||||||
|
|
||||||
|
@ -2110,7 +2136,7 @@ BWTessellator::BWTessellator(Array<float>* points, Array<uint32_t>* indices): mR
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
void BWTessellator::tessellate(const RenderShape *rshape)
|
void BWTessellator::tessellate(const RenderShape *rshape, const Matrix& matrix)
|
||||||
{
|
{
|
||||||
auto cmds = rshape->path.cmds.data;
|
auto cmds = rshape->path.cmds.data;
|
||||||
auto cmdCnt = rshape->path.cmds.count;
|
auto cmdCnt = rshape->path.cmds.count;
|
||||||
|
@ -2148,7 +2174,13 @@ void BWTessellator::tessellate(const RenderShape *rshape)
|
||||||
case PathCommand::CubicTo: {
|
case PathCommand::CubicTo: {
|
||||||
Bezier curve{pts[-1], pts[0], pts[1], pts[2]};
|
Bezier curve{pts[-1], pts[0], pts[1], pts[2]};
|
||||||
|
|
||||||
auto stepCount = detail::_bezierCurveCount(curve);
|
Bezier relCurve {pts[-1], pts[0], pts[1], pts[2]};
|
||||||
|
mathMultiply(&relCurve.start, &matrix);
|
||||||
|
mathMultiply(&relCurve.ctrl1, &matrix);
|
||||||
|
mathMultiply(&relCurve.ctrl2, &matrix);
|
||||||
|
mathMultiply(&relCurve.end, &matrix);
|
||||||
|
|
||||||
|
auto stepCount = detail::_bezierCurveCount(relCurve);
|
||||||
|
|
||||||
if (stepCount <= 1) stepCount = 2;
|
if (stepCount <= 1) stepCount = 2;
|
||||||
|
|
||||||
|
@ -2176,9 +2208,31 @@ void BWTessellator::tessellate(const RenderShape *rshape)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
RenderRegion BWTessellator::bounds() const
|
||||||
|
{
|
||||||
|
return RenderRegion {
|
||||||
|
static_cast<int32_t>(mLeftTop.x),
|
||||||
|
static_cast<int32_t>(mLeftTop.y),
|
||||||
|
static_cast<int32_t>(mRightBottom.x - mLeftTop.x),
|
||||||
|
static_cast<int32_t>(mRightBottom.y - mLeftTop.y),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
uint32_t BWTessellator::pushVertex(float x, float y)
|
uint32_t BWTessellator::pushVertex(float x, float y)
|
||||||
{
|
{
|
||||||
return detail::_pushVertex(mResPoints, x, y);
|
auto index = detail::_pushVertex(mResPoints, x, y);
|
||||||
|
|
||||||
|
if (index == 0) {
|
||||||
|
mRightBottom.x = mLeftTop.x = x;
|
||||||
|
mRightBottom.y = mLeftTop.y = y;
|
||||||
|
} else {
|
||||||
|
mLeftTop.x = min(mLeftTop.x, x);
|
||||||
|
mLeftTop.y = min(mLeftTop.y, y);
|
||||||
|
mRightBottom.x = max(mRightBottom.x, x);
|
||||||
|
mRightBottom.y = max(mRightBottom.y , y);
|
||||||
|
}
|
||||||
|
|
||||||
|
return index;
|
||||||
}
|
}
|
||||||
|
|
||||||
void BWTessellator::pushTriangle(uint32_t a, uint32_t b, uint32_t c)
|
void BWTessellator::pushTriangle(uint32_t a, uint32_t b, uint32_t c)
|
||||||
|
|
|
@ -108,11 +108,13 @@ class Stroker final
|
||||||
bool hasMove = false;
|
bool hasMove = false;
|
||||||
};
|
};
|
||||||
public:
|
public:
|
||||||
Stroker(Array<float>* points, Array<uint32_t>* indices);
|
Stroker(Array<float>* points, Array<uint32_t>* indices, const Matrix& matrix);
|
||||||
~Stroker() = default;
|
~Stroker() = default;
|
||||||
|
|
||||||
void stroke(const RenderShape *rshape);
|
void stroke(const RenderShape *rshape);
|
||||||
|
|
||||||
|
RenderRegion bounds() const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void doStroke(const PathCommand* cmds, uint32_t cmd_count, const Point* pts, uint32_t pts_count);
|
void doStroke(const PathCommand* cmds, uint32_t cmd_count, const Point* pts, uint32_t pts_count);
|
||||||
void doDashStroke(const PathCommand* cmds, uint32_t cmd_count, const Point* pts, uint32_t pts_count,
|
void doDashStroke(const PathCommand* cmds, uint32_t cmd_count, const Point* pts, uint32_t pts_count,
|
||||||
|
@ -141,11 +143,14 @@ private:
|
||||||
private:
|
private:
|
||||||
Array<float>* mResGlPoints;
|
Array<float>* mResGlPoints;
|
||||||
Array<uint32_t>* mResIndices;
|
Array<uint32_t>* mResIndices;
|
||||||
|
Matrix mMatrix;
|
||||||
float mStrokeWidth = 1.f;
|
float mStrokeWidth = 1.f;
|
||||||
float mMiterLimit = 4.f;
|
float mMiterLimit = 4.f;
|
||||||
StrokeCap mStrokeCap = StrokeCap::Square;
|
StrokeCap mStrokeCap = StrokeCap::Square;
|
||||||
StrokeJoin mStrokeJoin = StrokeJoin::Bevel;
|
StrokeJoin mStrokeJoin = StrokeJoin::Bevel;
|
||||||
State mStrokeState = {};
|
State mStrokeState = {};
|
||||||
|
GlPoint mLeftTop = {};
|
||||||
|
GlPoint mRightBottom = {};
|
||||||
};
|
};
|
||||||
|
|
||||||
class DashStroke
|
class DashStroke
|
||||||
|
@ -183,7 +188,9 @@ public:
|
||||||
BWTessellator(Array<float>* points, Array<uint32_t>* indices);
|
BWTessellator(Array<float>* points, Array<uint32_t>* indices);
|
||||||
~BWTessellator() = default;
|
~BWTessellator() = default;
|
||||||
|
|
||||||
void tessellate(const RenderShape *rshape);
|
void tessellate(const RenderShape *rshape, const Matrix& matrix);
|
||||||
|
|
||||||
|
RenderRegion bounds() const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
uint32_t pushVertex(float x, float y);
|
uint32_t pushVertex(float x, float y);
|
||||||
|
@ -192,6 +199,8 @@ private:
|
||||||
private:
|
private:
|
||||||
Array<float>* mResPoints;
|
Array<float>* mResPoints;
|
||||||
Array<uint32_t>* mResIndices;
|
Array<uint32_t>* mResIndices;
|
||||||
|
GlPoint mLeftTop = {};
|
||||||
|
GlPoint mRightBottom = {};
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace tvg
|
} // namespace tvg
|
||||||
|
|
Loading…
Add table
Reference in a new issue