fix: reduce shader usage
This commit is contained in:
@@ -0,0 +1,131 @@
|
||||
#include "roundedclip.hpp"
|
||||
|
||||
#include <private/qquickclipnode_p.h>
|
||||
#include <private/qquickitem_p.h>
|
||||
|
||||
#include <qsggeometry.h>
|
||||
|
||||
#include <algorithm>
|
||||
#include <cmath>
|
||||
|
||||
namespace ZShell::components {
|
||||
|
||||
RoundedClip::RoundedClip(QQuickItem* parent) : QQuickItem(parent) {
|
||||
setFlag(ItemHasContents);
|
||||
setClip(true);
|
||||
}
|
||||
|
||||
bool RoundedClip::updateRadius(qreal& member, qreal radius) {
|
||||
if (qFuzzyCompare(member + 1.0, radius + 1.0)) return false;
|
||||
|
||||
member = radius;
|
||||
update();
|
||||
return true;
|
||||
}
|
||||
|
||||
void RoundedClip::geometryChange(
|
||||
const QRectF& newGeometry, const QRectF& oldGeometry) {
|
||||
QQuickItem::geometryChange(newGeometry, oldGeometry);
|
||||
if (newGeometry.size() != oldGeometry.size()) update();
|
||||
}
|
||||
|
||||
QSGNode* RoundedClip::updatePaintNode(
|
||||
QSGNode* oldNode, UpdatePaintNodeData* data) {
|
||||
Q_UNUSED(oldNode)
|
||||
Q_UNUSED(data)
|
||||
|
||||
QQuickItemPrivate* priv = QQuickItemPrivate::get(this);
|
||||
QQuickDefaultClipNode* clip = priv->clipNode();
|
||||
|
||||
if (!clip) return nullptr;
|
||||
|
||||
// Clip node geometry is in item-local coordinates. The clip node is a
|
||||
// child of the item's transform node, so the clip rect is
|
||||
// QRectF(0, 0, width, height), i.e. boundingRect()/clipRect().
|
||||
const QRectF r(0, 0, width(), height());
|
||||
|
||||
// Per-corner radii fall back to the shared `radius` shorthand.
|
||||
const qreal tl = m_topLeftRadius > 0 ? m_topLeftRadius : m_radius;
|
||||
const qreal tr = m_topRightRadius > 0 ? m_topRightRadius : m_radius;
|
||||
const qreal br = m_bottomRightRadius > 0 ? m_bottomRightRadius : m_radius;
|
||||
const qreal bl = m_bottomLeftRadius > 0 ? m_bottomLeftRadius : m_radius;
|
||||
|
||||
// Nothing rounded: plain rectangular clip.
|
||||
if (tl <= 0 && tr <= 0 && br <= 0 && bl <= 0) {
|
||||
clip->setRect(r);
|
||||
clip->update();
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// All corners equal: let QQuickDefaultClipNode build its own known-correct
|
||||
// scanline geometry. This is the working path.
|
||||
if (qFuzzyCompare(tl, tr) && qFuzzyCompare(tr, br) &&
|
||||
qFuzzyCompare(br, bl)) {
|
||||
clip->setRect(r);
|
||||
clip->setRadius(tl);
|
||||
clip->update();
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// Per-corner radii. setRadius() supports only a single value, so we
|
||||
// must supply the geometry ourselves. Build it as a triangle fan from an
|
||||
// interior point so the polygon interior fills for the stencil clip.
|
||||
const qreal half = std::min(r.width() / 2, r.height() / 2);
|
||||
const qreal rtl = std::min(tl, half);
|
||||
const qreal rtr = std::min(tr, half);
|
||||
const qreal rbr = std::min(br, half);
|
||||
const qreal rbl = std::min(bl, half);
|
||||
|
||||
auto segments = [](qreal radius) {
|
||||
return qMin(30, int(std::ceil(radius)));
|
||||
};
|
||||
|
||||
const int s_tl = segments(rtl);
|
||||
const int s_tr = segments(rtr);
|
||||
const int s_br = segments(rbr);
|
||||
const int s_bl = segments(rbl);
|
||||
|
||||
clip->setRect(r);
|
||||
clip->setIsRectangular(false);
|
||||
|
||||
QSGGeometry* g = clip->geometry();
|
||||
g->setDrawingMode(QSGGeometry::DrawTriangleFan);
|
||||
|
||||
// One apex + one point per arc step + one point per zero-radius corner,
|
||||
// plus a closing apex to complete the fan.
|
||||
g->allocate(1 + s_tl + s_tr + s_br + s_bl + 5);
|
||||
|
||||
QSGGeometry::Point2D* vertices = g->vertexDataAsPoint2D();
|
||||
int vi = 0;
|
||||
|
||||
// Apex at an interior point.
|
||||
vertices[vi++] =
|
||||
QSGGeometry::Point2D(float(r.center().x()), float(r.center().y()));
|
||||
|
||||
// Each corner contributes its arc points (i = 0..segments) from its
|
||||
// edge-entry tangent to its edge-exit tangent. Consecutive corners are
|
||||
// joined by the implicit straight edges, forming the rounded rectangle.
|
||||
auto addArc =
|
||||
[&](int segs, qreal cx, qreal cy, qreal rad, qreal a0, qreal a1) {
|
||||
for (int i = 0; i <= segs; ++i) {
|
||||
const qreal angle = a0 + (a1 - a0) * qreal(i) / qreal(segs);
|
||||
vertices[vi++] = QSGGeometry::Point2D(
|
||||
float(cx + rad * std::cos(angle)),
|
||||
float(cy + rad * std::sin(angle)));
|
||||
}
|
||||
};
|
||||
|
||||
addArc(s_tl, rtl, rtl, rtl, M_PI, 3.0 * M_PI / 2.0);
|
||||
addArc(s_tr, r.width() - rtr, rtr, rtr, -M_PI / 2.0, 0.0);
|
||||
addArc(s_br, r.width() - rbr, r.height() - rbr, rbr, 0.0, M_PI / 2.0);
|
||||
addArc(s_bl, rbl, r.height() - rbl, rbl, M_PI / 2.0, M_PI);
|
||||
|
||||
// Close the fan back to the first perimeter vertex.
|
||||
vertices[vi] = vertices[1];
|
||||
|
||||
clip->markDirty(QSGNode::DirtyGeometry);
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
} // namespace ZShell::components
|
||||
Reference in New Issue
Block a user