aboutsummaryrefslogtreecommitdiffstats
path: root/src/quick/scenegraph/adaptations/software/qsgsoftwareinternalrectanglenode.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/quick/scenegraph/adaptations/software/qsgsoftwareinternalrectanglenode.cpp')
-rw-r--r--src/quick/scenegraph/adaptations/software/qsgsoftwareinternalrectanglenode.cpp220
1 files changed, 162 insertions, 58 deletions
diff --git a/src/quick/scenegraph/adaptations/software/qsgsoftwareinternalrectanglenode.cpp b/src/quick/scenegraph/adaptations/software/qsgsoftwareinternalrectanglenode.cpp
index e5b4191beb..411c189b3d 100644
--- a/src/quick/scenegraph/adaptations/software/qsgsoftwareinternalrectanglenode.cpp
+++ b/src/quick/scenegraph/adaptations/software/qsgsoftwareinternalrectanglenode.cpp
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtQuick module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#include "qsgsoftwareinternalrectanglenode_p.h"
#include <qmath.h>
@@ -47,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)
@@ -99,17 +67,19 @@ static QGradientStop interpolateStop(const QGradientStop &firstStop, const QGrad
double distance = secondStop.first - firstStop.first;
double distanceDelta = newPos - firstStop.first;
double modifierValue = distanceDelta / distance;
- int redDelta = (secondStop.second.red() - firstStop.second.red()) * modifierValue;
- int greenDelta = (secondStop.second.green() - firstStop.second.green()) * modifierValue;
- int blueDelta = (secondStop.second.blue() - firstStop.second.blue()) * modifierValue;
- int alphaDelta = (secondStop.second.alpha() - firstStop.second.alpha()) * modifierValue;
+ const auto firstStopRgbColor = firstStop.second.toRgb();
+ const auto secondStopRgbColor = secondStop.second.toRgb();
+ int redDelta = (secondStopRgbColor.red() - firstStopRgbColor.red()) * modifierValue;
+ int greenDelta = (secondStopRgbColor.green() - firstStopRgbColor.green()) * modifierValue;
+ int blueDelta = (secondStopRgbColor.blue() - firstStopRgbColor.blue()) * modifierValue;
+ int alphaDelta = (secondStopRgbColor.alpha() - firstStopRgbColor.alpha()) * modifierValue;
QGradientStop newStop;
newStop.first = newPos;
- newStop.second = QColor(firstStop.second.red() + redDelta,
- firstStop.second.green() + greenDelta,
- firstStop.second.blue() + blueDelta,
- firstStop.second.alpha() + alphaDelta);
+ newStop.second = QColor(firstStopRgbColor.red() + redDelta,
+ firstStopRgbColor.green() + greenDelta,
+ firstStopRgbColor.blue() + blueDelta,
+ firstStopRgbColor.alpha() + alphaDelta);
return newStop;
}
@@ -118,7 +88,7 @@ void QSGSoftwareInternalRectangleNode::setGradientStops(const QGradientStops &st
{
//normalize stops
bool needsNormalization = false;
- for (const QGradientStop &stop : qAsConst(stops)) {
+ for (const QGradientStop &stop : std::as_const(stops)) {
if (stop.first < 0.0 || stop.first > 1.0) {
needsNormalization = true;
break;
@@ -127,7 +97,7 @@ void QSGSoftwareInternalRectangleNode::setGradientStops(const QGradientStops &st
if (needsNormalization) {
QGradientStops normalizedStops;
- if (stops.count() == 1) {
+ if (stops.size() == 1) {
//If there is only one stop, then the position does not matter
//It is just treated as a color
QGradientStop stop = stops.at(0);
@@ -138,7 +108,7 @@ void QSGSoftwareInternalRectangleNode::setGradientStops(const QGradientStops &st
int below = -1;
int above = -1;
QVector<int> between;
- for (int i = 0; i < stops.count(); ++i) {
+ for (int i = 0; i < stops.size(); ++i) {
if (stops.at(i).first < 0.0) {
below = i;
} else if (stops.at(i).first > 1.0) {
@@ -152,7 +122,7 @@ void QSGSoftwareInternalRectangleNode::setGradientStops(const QGradientStops &st
//Interpoloate new color values for above and below
if (below != -1 ) {
//If there are more than one stops left, interpolate
- if (below + 1 < stops.count()) {
+ if (below + 1 < stops.size()) {
normalizedStops.append(interpolateStop(stops.at(below), stops.at(below + 1), 0.0));
} else {
QGradientStop singleStop;
@@ -162,7 +132,7 @@ void QSGSoftwareInternalRectangleNode::setGradientStops(const QGradientStops &st
}
}
- for (int i = 0; i < between.count(); ++i)
+ for (int i = 0; i < between.size(); ++i)
normalizedStops.append(stops.at(between.at(i)));
if (above != -1) {
@@ -205,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*/)
{
}
@@ -219,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);
@@ -246,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));
@@ -265,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);
+ }
}
}
@@ -283,8 +318,8 @@ bool QSGSoftwareInternalRectangleNode::isOpaque() const
return false;
if (m_penWidth > 0.0f && m_penColor.alpha() < 255)
return false;
- if (m_stops.count() > 0) {
- for (const QGradientStop &stop : qAsConst(m_stops)) {
+ if (m_stops.size() > 0) {
+ for (const QGradientStop &stop : std::as_const(m_stops)) {
if (stop.second.alpha() < 255)
return false;
}
@@ -421,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