diff options
Diffstat (limited to 'src/quick/scenegraph/adaptations/software/qsgsoftwareinternalrectanglenode.cpp')
-rw-r--r-- | src/quick/scenegraph/adaptations/software/qsgsoftwareinternalrectanglenode.cpp | 148 |
1 files changed, 143 insertions, 5 deletions
diff --git a/src/quick/scenegraph/adaptations/software/qsgsoftwareinternalrectanglenode.cpp b/src/quick/scenegraph/adaptations/software/qsgsoftwareinternalrectanglenode.cpp index 488f622dce..411c189b3d 100644 --- a/src/quick/scenegraph/adaptations/software/qsgsoftwareinternalrectanglenode.cpp +++ b/src/quick/scenegraph/adaptations/software/qsgsoftwareinternalrectanglenode.cpp @@ -11,6 +11,10 @@ QT_BEGIN_NAMESPACE QSGSoftwareInternalRectangleNode::QSGSoftwareInternalRectangleNode() : m_penWidth(0) , m_radius(0) + , m_topLeftRadius(-1) + , m_topRightRadius(-1) + , m_bottomLeftRadius(-1) + , m_bottomRightRadius(-1) , m_vertical(true) , m_cornerPixmapIsDirty(true) , m_devicePixelRatio(1) @@ -171,6 +175,42 @@ void QSGSoftwareInternalRectangleNode::setRadius(qreal radius) } } +void QSGSoftwareInternalRectangleNode::setTopLeftRadius(qreal radius) +{ + if (m_topLeftRadius != radius) { + m_topLeftRadius = radius; + m_cornerPixmapIsDirty = true; + markDirty(DirtyMaterial); + } +} + +void QSGSoftwareInternalRectangleNode::setTopRightRadius(qreal radius) +{ + if (m_topRightRadius != radius) { + m_topRightRadius = radius; + m_cornerPixmapIsDirty = true; + markDirty(DirtyMaterial); + } +} + +void QSGSoftwareInternalRectangleNode::setBottomLeftRadius(qreal radius) +{ + if (m_bottomLeftRadius != radius) { + m_bottomLeftRadius = radius; + m_cornerPixmapIsDirty = true; + markDirty(DirtyMaterial); + } +} + +void QSGSoftwareInternalRectangleNode::setBottomRightRadius(qreal radius) +{ + if (m_bottomRightRadius != radius) { + m_bottomRightRadius = radius; + m_cornerPixmapIsDirty = true; + markDirty(DirtyMaterial); + } +} + void QSGSoftwareInternalRectangleNode::setAligned(bool /*aligned*/) { } @@ -185,9 +225,8 @@ void QSGSoftwareInternalRectangleNode::update() } if (!m_stops.isEmpty()) { - QLinearGradient gradient(QPoint(0,0), QPoint(m_vertical ? 0 : 1, m_vertical ? 1 : 0)); + QLinearGradient gradient(QPoint(0,0), QPoint(m_vertical ? 0 : m_rect.width(), m_vertical ? m_rect.height() : 0)); gradient.setStops(m_stops); - gradient.setCoordinateMode(QGradient::ObjectBoundingMode); m_brush = QBrush(gradient); } else { m_brush = QBrush(m_color); @@ -212,13 +251,21 @@ void QSGSoftwareInternalRectangleNode::paint(QPainter *painter) //Rotated rectangles lose the benefits of direct rendering, and have poor rendering //quality when using only blits and fills. - if (m_radius == 0 && m_penWidth == 0) { + if (m_radius == 0 + && m_penWidth == 0 + && m_topLeftRadius <= 0 + && m_topRightRadius <= 0 + && m_bottomLeftRadius <= 0 + && m_bottomRightRadius <= 0) { //Non-Rounded Rects without borders (fall back to drawRect) //Most common case painter->setPen(Qt::NoPen); painter->setBrush(m_brush); painter->drawRect(m_rect); - } else { + } else if (m_topLeftRadius < 0 + && m_topRightRadius < 0 + && m_bottomLeftRadius < 0 + && m_bottomRightRadius < 0) { //Rounded Rects and Rects with Borders //Avoids broken behaviors of QPainter::drawRect/roundedRect QPixmap pixmap = QPixmap(qRound(m_rect.width() * m_devicePixelRatio), qRound(m_rect.height() * m_devicePixelRatio)); @@ -231,12 +278,34 @@ void QSGSoftwareInternalRectangleNode::paint(QPainter *painter) painter->setRenderHint(QPainter::SmoothPixmapTransform, true); painter->drawPixmap(m_rect, pixmap); painter->setRenderHints(previousRenderHints); + } else { + // Corners with different radii. Split implementation to avoid + // performance regression of the majority of cases + QPixmap pixmap = QPixmap(qRound(m_rect.width() * m_devicePixelRatio), qRound(m_rect.height() * m_devicePixelRatio)); + pixmap.fill(Qt::transparent); + pixmap.setDevicePixelRatio(m_devicePixelRatio); + QPainter pixmapPainter(&pixmap); + // Slow function relying on paths + paintRectangleIndividualCorners(&pixmapPainter, QRect(0, 0, m_rect.width(), m_rect.height())); + + QPainter::RenderHints previousRenderHints = painter->renderHints(); + painter->setRenderHint(QPainter::SmoothPixmapTransform, true); + painter->drawPixmap(m_rect, pixmap); + painter->setRenderHints(previousRenderHints); + } } else { //Paint directly - paintRectangle(painter, m_rect); + if (m_topLeftRadius < 0 + && m_topRightRadius < 0 + && m_bottomLeftRadius < 0 + && m_bottomRightRadius < 0) { + paintRectangle(painter, m_rect); + } else { + paintRectangleIndividualCorners(painter, m_rect); + } } } @@ -387,6 +456,75 @@ void QSGSoftwareInternalRectangleNode::paintRectangle(QPainter *painter, const Q painter->setRenderHints(previousRenderHints); } +void QSGSoftwareInternalRectangleNode::paintRectangleIndividualCorners(QPainter *painter, const QRect &rect) +{ + QPainterPath path; + + const float w = m_penWidth; + + // Radius should never exceeds half of the width or half of the height + const float radiusTL = qMin(qMin(rect.width(), rect.height()) * 0.5f, float(m_topLeftRadius < 0. ? m_radius : m_topLeftRadius)); + const float radiusTR = qMin(qMin(rect.width(), rect.height()) * 0.5f, float(m_topRightRadius < 0. ? m_radius : m_topRightRadius)); + const float radiusBL = qMin(qMin(rect.width(), rect.height()) * 0.5f, float(m_bottomLeftRadius < 0. ? m_radius : m_bottomLeftRadius)); + const float radiusBR = qMin(qMin(rect.width(), rect.height()) * 0.5f, float(m_bottomRightRadius < 0 ? m_radius : m_bottomRightRadius)); + + const float innerRadiusTL = qMin(qMin(rect.width(), rect.height()) * 0.5f, radiusTL - w); + const float innerRadiusTR = qMin(qMin(rect.width(), rect.height()) * 0.5f, radiusTR - w); + const float innerRadiusBL = qMin(qMin(rect.width(), rect.height()) * 0.5f, radiusBL - w); + const float innerRadiusBR = qMin(qMin(rect.width(), rect.height()) * 0.5f, radiusBR - w); + + QRect rect2 = rect.adjusted(0, 0, 1, 1); + + path.moveTo(rect2.topRight() - QPointF(radiusTR, -w)); + if (innerRadiusTR > 0.) + path.arcTo(QRectF(rect2.topRight() - QPointF(radiusTR + innerRadiusTR, -w), 2. * QSizeF(innerRadiusTR, innerRadiusTR)), 90, -90); + else + path.lineTo(rect2.topRight() - QPointF(w, -w)); + + if (innerRadiusBR > 0.) + path.arcTo(QRectF(rect2.bottomRight() - QPointF(radiusBR + innerRadiusBR, radiusBR + innerRadiusBR), 2. * QSizeF(innerRadiusBR, innerRadiusBR)), 0, -90); + else + path.lineTo(rect2.bottomRight() - QPointF(w, w)); + + if (innerRadiusBL > 0.) + path.arcTo(QRectF(rect2.bottomLeft() - QPointF(-w, radiusBL + innerRadiusBL), 2. * QSizeF(innerRadiusBL, innerRadiusBL)), -90, -90); + else + path.lineTo(rect2.bottomLeft() - QPointF(-w, w)); + if (innerRadiusTL > 0.) + path.arcTo(QRectF(rect2.topLeft() + QPointF(w, w), 2. * QSizeF(innerRadiusTL, innerRadiusTL)), -180, -90); + else + path.lineTo(rect2.topLeft() + QPointF(w, w)); + path.closeSubpath(); + + painter->setPen(Qt::NoPen); + painter->setBrush(m_brush); + painter->drawPath(path); + + if (w > 0) { + path.moveTo(rect2.topRight() - QPointF(radiusTR, 0.)); + if (radiusTR > 0.) + path.arcTo(QRectF(rect2.topRight() - 2. * QPointF(radiusTR, 0.), 2. * QSizeF(radiusTR, radiusTR)), 90, -90); + else + path.lineTo(rect2.topRight()); + if (radiusBR > 0.) + path.arcTo(QRectF(rect2.bottomRight() - 2. * QPointF(radiusBR, radiusBR), 2. * QSizeF(radiusBR, radiusBR)), 0, -90); + else + path.lineTo(rect2.bottomRight()); + if (radiusBL > 0.) + path.arcTo(QRectF(rect2.bottomLeft() - 2. * QPointF(0., radiusBL), 2. * QSizeF(radiusBL, radiusBL)), -90, -90); + else + path.lineTo(rect2.bottomLeft()); + if (radiusTL > 0.) + path.arcTo(QRectF(rect2.topLeft() - 2. * QPointF(0., 0.), 2. * QSizeF(radiusTL, radiusTL)), -180, -90); + else + path.lineTo(rect2.topLeft()); + path.closeSubpath(); + + painter->setBrush(m_penColor); + painter->drawPath(path); + } +} + void QSGSoftwareInternalRectangleNode::generateCornerPixmap() { //Generate new corner Pixmap |