aboutsummaryrefslogtreecommitdiffstats
path: root/src/quick/scenegraph/qsgbasicinternalrectanglenode.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/quick/scenegraph/qsgbasicinternalrectanglenode.cpp')
-rw-r--r--src/quick/scenegraph/qsgbasicinternalrectanglenode.cpp630
1 files changed, 482 insertions, 148 deletions
diff --git a/src/quick/scenegraph/qsgbasicinternalrectanglenode.cpp b/src/quick/scenegraph/qsgbasicinternalrectanglenode.cpp
index 91317ee2d7..8d1104a407 100644
--- a/src/quick/scenegraph/qsgbasicinternalrectanglenode.cpp
+++ b/src/quick/scenegraph/qsgbasicinternalrectanglenode.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 "qsgbasicinternalrectanglenode_p.h"
@@ -55,10 +19,12 @@ namespace
inline Color4ub colorToColor4ub(const QColor &c)
{
- Color4ub color = { uchar(qRound(c.redF() * c.alphaF() * 255)),
- uchar(qRound(c.greenF() * c.alphaF() * 255)),
- uchar(qRound(c.blueF() * c.alphaF() * 255)),
- uchar(qRound(c.alphaF() * 255))
+ float r, g, b, a;
+ c.getRgbF(&r, &g, &b, &a);
+ Color4ub color = { uchar(qRound(r * a * 255)),
+ uchar(qRound(g * a * 255)),
+ uchar(qRound(b * a * 255)),
+ uchar(qRound(a * 255))
};
return color;
}
@@ -108,8 +74,7 @@ namespace
}
QSGBasicInternalRectangleNode::QSGBasicInternalRectangleNode()
- : m_radius(0)
- , m_pen_width(0)
+ : QSGInternalRectangleNode()
, m_aligned(true)
, m_antialiasing(false)
, m_gradient_is_opaque(true)
@@ -189,6 +154,35 @@ void QSGBasicInternalRectangleNode::setRadius(qreal radius)
m_dirty_geometry = true;
}
+void QSGBasicInternalRectangleNode::setTopLeftRadius(qreal radius)
+{
+ if (radius == m_topLeftRadius)
+ return;
+ m_topLeftRadius = radius;
+ m_dirty_geometry = true;
+}
+void QSGBasicInternalRectangleNode::setTopRightRadius(qreal radius)
+{
+ if (radius == m_topRightRadius)
+ return;
+ m_topRightRadius = radius;
+ m_dirty_geometry = true;
+}
+void QSGBasicInternalRectangleNode::setBottomLeftRadius(qreal radius)
+{
+ if (radius == m_bottomLeftRadius)
+ return;
+ m_bottomLeftRadius = radius;
+ m_dirty_geometry = true;
+}
+void QSGBasicInternalRectangleNode::setBottomRightRadius(qreal radius)
+{
+ if (radius == m_bottomRightRadius)
+ return;
+ m_bottomRightRadius = radius;
+ m_dirty_geometry = true;
+}
+
void QSGBasicInternalRectangleNode::setAntialiasing(bool antialiasing)
{
if (!supportsAntialiasing())
@@ -251,33 +245,93 @@ void QSGBasicInternalRectangleNode::updateGeometry()
Color4ub transparent = { 0, 0, 0, 0 };
const QGradientStops &stops = m_gradient_stops;
- float length = (m_gradient_is_vertical ? height : width);
+ float gradientStart = (m_gradient_is_vertical ? m_rect.top() : m_rect.left());
+ float gradientLength = (m_gradient_is_vertical ? height : width);
float secondaryLength = (m_gradient_is_vertical ? width : height);
int nextGradientStop = 0;
- float gradientPos = penWidth / length;
+ float gradientPos = penWidth / gradientLength;
while (nextGradientStop < stops.size() && stops.at(nextGradientStop).first <= gradientPos)
++nextGradientStop;
int lastGradientStop = stops.size() - 1;
- float lastGradientPos = 1.0f - penWidth / length;
+ float lastGradientPos = 1.0f - penWidth / gradientLength;
while (lastGradientStop >= nextGradientStop && stops.at(lastGradientStop).first >= lastGradientPos)
--lastGradientStop;
int gradientIntersections = (lastGradientStop - nextGradientStop + 1);
- if (m_radius > 0) {
+ if (m_radius > 0
+ || m_topLeftRadius > 0
+ || m_topRightRadius > 0
+ || m_bottomLeftRadius > 0
+ || m_bottomRightRadius > 0) {
// Rounded corners.
- // Radius should never exceeds half of the width or half of the height
- float radius = qMin(qMin(width, height) * 0.5f, float(m_radius));
- QRectF innerRect = m_rect;
- innerRect.adjust(radius, radius, -radius, -radius);
-
- float innerRadius = radius - penWidth * 1.0f;
- float outerRadius = radius;
- float delta = qMin(width, height) * 0.5f;
+ // Radius should never exceed half the width or half the height.
+ float radiusTL = qMin(qMin(width, height) * 0.4999f, float(m_topLeftRadius < 0 ? m_radius : m_topLeftRadius));
+ float radiusTR = qMin(qMin(width, height) * 0.4999f, float(m_topRightRadius < 0 ? m_radius : m_topRightRadius));
+ float radiusBL = qMin(qMin(width, height) * 0.4999f, float(m_bottomLeftRadius < 0 ? m_radius : m_bottomLeftRadius));
+ float radiusBR = qMin(qMin(width, height) * 0.4999f, float(m_bottomRightRadius < 0 ? m_radius : m_bottomRightRadius));
+
+ // The code produces some artefacts when radius <= 0.5. A radius of half a pixel
+ // does not make much sense anyway, so we draw a normal corner in such a case.
+ if (radiusTL <= 0.5)
+ radiusTL = 0;
+ if (radiusTR <= 0.5)
+ radiusTR = 0;
+ if (radiusBL <= 0.5)
+ radiusBL = 0;
+ if (radiusBR <= 0.5)
+ radiusBR = 0;
+
+ // We want to keep a minimal inner radius in order to make the inner
+ // x-coordinates of an arc mathematically unique and identifiable.
+ const float innerRadiusTL = qMax(radiusTL - penWidth * 1.0f, 0.01);
+ const float innerRadiusTR = qMax(radiusTR - penWidth * 1.0f, 0.01);
+ const float innerRadiusBL = qMax(radiusBL - penWidth * 1.0f, 0.01);
+ const float innerRadiusBR = qMax(radiusBR - penWidth * 1.0f, 0.01);
+ const float outerRadiusTL = radiusTL;
+ const float outerRadiusTR = radiusTR;
+ const float outerRadiusBL = radiusBL;
+ const float outerRadiusBR = radiusBR;
+ const float delta = qMin(width, height) * 0.5f;
+
+ int segmentsTL = radiusTL == 0 ? 0 : qBound(3, qCeil(radiusTL * (M_PI / 6)), 18);
+ int segmentsTR = radiusTR == 0 ? 0 : qBound(3, qCeil(radiusTR * (M_PI / 6)), 18);
+ int segmentsBL = radiusBL == 0 ? 0 : qBound(3, qCeil(radiusBL * (M_PI / 6)), 18);
+ int segmentsBR = radiusBR == 0 ? 0 : qBound(3, qCeil(radiusBR * (M_PI / 6)), 18);
+
+ // If the radii on opposite sites in genraration direction are the same,
+ // we will set the segments of one side to 0 as these points would be
+ // calculated twice. Also, this optimizes for the case of similar radii
+ if (m_gradient_is_vertical) {
+ if (innerRadiusTL == innerRadiusTR) {
+ if (segmentsTL <= segmentsTR)
+ segmentsTL = 0;
+ else
+ segmentsTR = 0;
+ }
+ if (innerRadiusBL == innerRadiusBR){
+ if (segmentsBL <= segmentsBR)
+ segmentsBL = 0;
+ else
+ segmentsBR = 0;
+ }
+ } else {
+ if (innerRadiusTL == innerRadiusBL) {
+ if (segmentsTL <= segmentsBL)
+ segmentsTL = 0;
+ else
+ segmentsBL = 0;
+ }
+ if (innerRadiusTR == innerRadiusBR) {
+ if (segmentsTR <= segmentsBR)
+ segmentsTR = 0;
+ else
+ segmentsBR = 0;
+ }
+ }
- // Number of segments per corner, approximately one per 3 pixels.
- int segments = qBound(3, qCeil(outerRadius * (M_PI / 6)), 18);
+ const int sumSegments = segmentsTL + segmentsTR + segmentsBL + segmentsBR;
/*
@@ -299,8 +353,8 @@ void QSGBasicInternalRectangleNode::updateGeometry()
*/
- int innerVertexCount = (segments + 1) * 4 + gradientIntersections * 2;
- int outerVertexCount = (segments + 1) * 4;
+ const int innerVertexCount = (sumSegments + 4) * 2 + gradientIntersections * 2;
+ const int outerVertexCount = (sumSegments + 4) * 2;
int vertexCount = innerVertexCount;
if (m_antialiasing || penWidth)
vertexCount += innerVertexCount;
@@ -309,10 +363,11 @@ void QSGBasicInternalRectangleNode::updateGeometry()
if (m_antialiasing && penWidth)
vertexCount += outerVertexCount;
- int fillIndexCount = innerVertexCount;
- int innerAAIndexCount = innerVertexCount * 2 + 2;
- int borderIndexCount = innerVertexCount * 2 + 2;
- int outerAAIndexCount = outerVertexCount * 2 + 2;
+
+ const int fillIndexCount = innerVertexCount;
+ const int innerAAIndexCount = innerVertexCount * 2 + 2;
+ const int borderIndexCount = innerVertexCount * 2 + 2;
+ const int outerAAIndexCount = outerVertexCount * 2 + 2;
int indexCount = 0;
int fillHead = 0;
int innerAAHead = 0;
@@ -343,48 +398,322 @@ void QSGBasicInternalRectangleNode::updateGeometry()
quint16 *indices = g->indexDataAsUShort();
quint16 index = 0;
- float pp = 0; // previous inner primary coordinate.
- float pss = 0; // previous inner secondary start coordinate.
- float pse = 0; // previous inner secondary end coordinate.
- float angle = 0.5f * float(M_PI) / segments;
- float cosStep = qFastCos(angle);
- float sinStep = qFastSin(angle);
-
- float innerStart = (m_gradient_is_vertical ? innerRect.top() : innerRect.left());
- float innerEnd = (m_gradient_is_vertical ? innerRect.bottom() : innerRect.right());
- float innerLength = (m_gradient_is_vertical ? innerRect.height() : innerRect.width());
- float innerSecondaryStart = (m_gradient_is_vertical ? innerRect.left() : innerRect.top());
- float innerSecondaryEnd = (m_gradient_is_vertical ? innerRect.right() : innerRect.bottom());
+ float innerXPrev = 0.; // previous inner primary coordinate, both sides.
+ float innerYLeftPrev = 0.; // previous inner secondary coordinate, left.
+ float innerYRightPrev = 0.; // previous inner secondary coordinate, right.
+
+ const float angleTL = 0.5f * float(M_PI) / segmentsTL;
+ const float cosStepTL = qFastCos(angleTL);
+ const float sinStepTL = qFastSin(angleTL);
+ const float angleTR = 0.5f * float(M_PI) / segmentsTR;
+ const float cosStepTR = qFastCos(angleTR);
+ const float sinStepTR = qFastSin(angleTR);
+ const float angleBL = 0.5f * float(M_PI) / segmentsBL;
+ const float cosStepBL = qFastCos(angleBL);
+ const float sinStepBL = qFastSin(angleBL);
+ const float angleBR = 0.5f * float(M_PI) / segmentsBR;
+ const float cosStepBR = qFastCos(angleBR);
+ const float sinStepBR = qFastSin(angleBR);
+
+ //The x- and y-Axis are transposed, depending on gradient being vertical or horizontal
+ //Lets define some coordinates and radii. The first index is the part, the second index
+ //is the left or right side, as seen when moving from part0 to part1
+
+ // left vertices | right vertices
+ // |
+ // *************|**************
+ // * | | | *
+ // *--o | o--*
+ // * innerX/Y | innerX/Y *
+ // * | *
+ // * | * part 0
+ // * | *
+ // * | *
+ // * | *
+ // -----------------+--------------------> y
+ // * | *
+ // * | *
+ // * | *
+ // * | * part 1
+ // * | *
+ // * innerX/Y | innerX/Y *
+ // *--o | o--*
+ // * | | | *
+ // *************|**************
+ // |
+ // v x
+ //
+ // direction of vertex generation
+
+ const float outerXCenter[][2] = {{
+ float(m_gradient_is_vertical ? m_rect.top() + radiusTL : m_rect.left() + radiusTL),
+ float(m_gradient_is_vertical ? m_rect.top() + radiusTR : m_rect.left() + radiusBL)
+ }, {
+ float(m_gradient_is_vertical ? m_rect.bottom() - radiusBL : m_rect.right() - radiusTR),
+ float(m_gradient_is_vertical ? m_rect.bottom() - radiusBR : m_rect.right() - radiusBR)
+ }};
+
+ const float outerYCenter[][2] = {{
+ float(!m_gradient_is_vertical ? m_rect.top() + outerRadiusTL : m_rect.left() + outerRadiusTL),
+ float(!m_gradient_is_vertical ? m_rect.bottom() - outerRadiusBL : m_rect.right() - outerRadiusTR)
+ }, {
+ float(!m_gradient_is_vertical ? m_rect.top() + outerRadiusTR : m_rect.left() + outerRadiusBL),
+ float(!m_gradient_is_vertical ? m_rect.bottom() - outerRadiusBR : m_rect.right() - outerRadiusBR)
+ }};
+
+ const float innerXCenter[][2] = { {
+ float(m_gradient_is_vertical ? m_rect.top() + innerRadiusTL + penWidth : m_rect.left() + innerRadiusTL + penWidth),
+ float(m_gradient_is_vertical ? m_rect.top() + innerRadiusTR + penWidth: m_rect.left() + innerRadiusBL + penWidth)
+ }, {
+ float(m_gradient_is_vertical ? m_rect.bottom() - innerRadiusBL - penWidth: m_rect.right() - innerRadiusTR - penWidth),
+ float(m_gradient_is_vertical ? m_rect.bottom() - innerRadiusBR - penWidth: m_rect.right() - innerRadiusBR - penWidth)
+ }};
+
+ const float innerYCenter[][2] = { {
+ float(!m_gradient_is_vertical ? m_rect.top() + innerRadiusTL + penWidth : m_rect.left() + innerRadiusTL + penWidth),
+ float(!m_gradient_is_vertical ? m_rect.bottom() - innerRadiusBL - penWidth : m_rect.right() - innerRadiusTR - penWidth)
+ },{
+ float(!m_gradient_is_vertical ? m_rect.top() + innerRadiusTR + penWidth : m_rect.left() + innerRadiusBL + penWidth),
+ float(!m_gradient_is_vertical ? m_rect.bottom() - innerRadiusBR - penWidth : m_rect.right() - innerRadiusBR - penWidth)
+ }};
+
+ const float innerRadius[][2] = {{
+ innerRadiusTL,
+ !m_gradient_is_vertical ? innerRadiusBL : innerRadiusTR
+ }, {
+ !m_gradient_is_vertical ? innerRadiusTR : innerRadiusBL,
+ innerRadiusBR
+ }};
+
+ const float outerRadius[][2] = {{
+ outerRadiusTL,
+ !m_gradient_is_vertical ? outerRadiusBL : outerRadiusTR
+ }, {
+ !m_gradient_is_vertical ? outerRadiusTR : outerRadiusBL,
+ outerRadiusBR
+ }};
+
+ const int segments[][2] = {{
+ segmentsTL,
+ !m_gradient_is_vertical ? segmentsBL : segmentsTR
+ }, {
+ !m_gradient_is_vertical ? segmentsTR : segmentsBL,
+ segmentsBR
+ }};
+
+ const float cosStep[][2] = {{
+ cosStepTL,
+ !m_gradient_is_vertical ? cosStepBL : cosStepTR
+ }, {
+ !m_gradient_is_vertical ? cosStepTR : cosStepBL,
+ cosStepBR
+ }};
+
+ const float sinStep[][2] = {{
+ sinStepTL,
+ !m_gradient_is_vertical ? sinStepBL : sinStepTR
+ }, {
+ !m_gradient_is_vertical ? sinStepTR : sinStepBL,
+ sinStepBR
+ }};
+
+ auto fillColorFromX = [&](float x) {
+
+ float t = (x - gradientStart) / gradientLength;
+ t = qBound(0.0, t, 1.0);
+
+ int i = 1;
+ if (t < stops.first().first)
+ return colorToColor4ub(stops.first().second);
+ while (i < stops.size()) {
+ const QGradientStop &prev = stops.at(i - 1);
+ const QGradientStop &next = stops.at(i);
+ if (prev.first <= t && next.first > t) {
+ t = (t - prev.first) / (next.first - prev.first);
+ return colorToColor4ub(prev.second) * (1. - t) + colorToColor4ub(next.second) * t; }
+ i++;
+ }
+ return colorToColor4ub(stops.last().second);
+ };
for (int part = 0; part < 2; ++part) {
- float c = 1 - part;
- float s = part;
- for (int i = 0; i <= segments; ++i) {
- float p, ss, se;
- if (innerRadius > 0) {
- p = (part ? innerEnd : innerStart) - innerRadius * c; // current inner primary coordinate.
- ss = innerSecondaryStart - innerRadius * s; // current inner secondary start coordinate.
- se = innerSecondaryEnd + innerRadius * s; // current inner secondary end coordinate.
- gradientPos = ((part ? innerLength : 0) + radius - innerRadius * c) / length;
+ // cosine of the angle of the current segment, starting at 1 for part 0 and 0 for part 1
+ float cosSegmentAngleLeft = 1. - part;
+ // sine of the angle of the current segment
+ float sinSegmentAngleLeft = part;
+
+ float cosSegmentAngleRight = 1. - part;
+ float sinSegmentAngleRight = part;
+
+ bool advanceLeft = true;
+
+ // We draw both the left part and the right part of the rectangle at the same time.
+ // We also draw a vertex on the left side for every vertex on the right side. This
+ // syncronisation is required to make sure that all gradient stops can be inserted.
+ for (int iLeft = 0, iRight = 0; iLeft <= segments[part][0] || iRight <= segments[part][1]; ) {
+
+ float xLeft, yLeft,
+ xRight, yRight;
+
+ float outerXLeft, outerYLeft,
+ outerXRight, outerYRight;
+
+ float sinAngleLeft, cosAngleLeft,
+ sinAngleRight, cosAngleRight;
+
+ // calculate inner x-coordinates
+ xLeft = innerXCenter[part][0] - innerRadius[part][0] * cosSegmentAngleLeft;
+ xRight = innerXCenter[part][1] - innerRadius[part][1] * cosSegmentAngleRight;
+
+ // calcuate inner y-coordinates
+ yLeft = innerYCenter[part][0] - innerRadius[part][0] * sinSegmentAngleLeft;
+ yRight = innerYCenter[part][1] + innerRadius[part][1] * sinSegmentAngleRight;
+
+ // Synchronize left and right hand x-coordinates. This is required to
+ // make sure that we can insert all gradient stops that require exactly two triangles at
+ // every x-coordinate. Take the smaller of both x-coordinates and then find the matching
+ // y-coordinates.
+ if ((iLeft <= segments[part][0] && xLeft <= xRight) || iRight > segments[part][1]) {
+ advanceLeft = true;
} else {
- p = (part ? innerEnd + innerRadius : innerStart - innerRadius); // current inner primary coordinate.
- ss = innerSecondaryStart - innerRadius; // current inner secondary start coordinate.
- se = innerSecondaryEnd + innerRadius; // current inner secondary end coordinate.
- gradientPos = ((part ? innerLength + innerRadius : -innerRadius) + radius) / length;
+ advanceLeft = false;
}
- float outerEdge = (part ? innerEnd : innerStart) - outerRadius * c; // current outer primary coordinate.
- float outerSecondaryStart = innerSecondaryStart - outerRadius * s; // current outer secondary start coordinate.
- float outerSecondaryEnd = innerSecondaryEnd + outerRadius * s; // current outer secondary end coordinate.
+ // Inner: Find the matching y-coordinates for the x-coordinate found above.
+ // Outer: Also set the sine and cosine to make sure that outer vertices are
+ // drawn correctly.
+ if (innerRadius[part][0] == innerRadius[part][1]) {
+ // Special case of equal radii. Optimize to avoid performance regression:
+ // Left and right is always equal and we can just copy the angles and
+ // mirror the coordinates.
+ if (advanceLeft) {
+ if (outerRadius[part][0] == 0) {
+ sinAngleLeft = 1.;
+ cosAngleLeft = part ? -1. : 1.;
+ } else {
+ sinAngleLeft = sinSegmentAngleLeft;
+ cosAngleLeft = cosSegmentAngleLeft;
+ }
+ if (outerRadius[part][1] == 0) {
+ sinAngleRight = 1.;
+ cosAngleRight = part ? -1. : 1.;
+ } else {
+ sinAngleRight = sinSegmentAngleLeft;
+ cosAngleRight = cosSegmentAngleLeft;
+ }
+ xRight = xLeft;
+ yRight = innerYCenter[part][1] + innerRadius[part][1] * sinAngleRight;
+ } else {
+ if (outerRadius[part][0] == 0) {
+ sinAngleLeft = 1.;
+ cosAngleLeft = part ? -1. : 1.;
+ } else {
+ sinAngleLeft = sinSegmentAngleRight;
+ cosAngleLeft = cosSegmentAngleRight;
+ }
+ if (outerRadius[part][1] == 0) {
+ sinAngleRight = 1.;
+ cosAngleRight = part ? -1. : 1.;
+ } else {
+ sinAngleRight = sinSegmentAngleRight;
+ cosAngleRight = cosSegmentAngleRight;
+ }
+ xLeft = xRight;
+ yLeft = innerYCenter[part][0] - innerRadius[part][0] * sinAngleLeft;
+ }
+ } else if (advanceLeft) {
+ if (outerRadius[part][0] == 0) {
+ sinAngleLeft = 1.;
+ cosAngleLeft = part ? -1. : 1.;
+ } else {
+ sinAngleLeft = sinSegmentAngleLeft;
+ cosAngleLeft = cosSegmentAngleLeft;
+ }
+ if (outerRadius[part][1] == 0) {
+ // Outer: If the outer radius is zero we can return both sin and cos = 1
+ // to form a nice corner. Inner: Accept the x-coordinate from the other
+ // side and match the y-coordinate
+ sinAngleRight = 1.;
+ cosAngleRight = part ? -1. : 1.;
+ xRight = xLeft;
+ yRight = innerYCenter[part][1] + innerRadius[part][1] * sinAngleRight;
+ } else if (xLeft >= innerXCenter[0][1] && xLeft <= innerXCenter[1][1]) {
+ // Outer: If we are on the straight line between the inner centers, we can
+ // just return sin = 1 and cos = 0. Inner: Accept the x-coordinate from the
+ // other side and match the y-coordinate
+ sinAngleRight = 1.;
+ cosAngleRight = 0.;
+ xRight = xLeft;
+ yRight = innerYCenter[part][1] + innerRadius[part][1] * sinAngleRight;
+ } else {
+ // Inner: If we are on the rounded part of the oposite side, we have to find a vertex
+ // on that curve that matches the x-coordinate we selected.
+ // We always select the smaller x-coordinate and can therefore use a linear
+ // interpolation between the last point on this side and the point on this side
+ // that was not accepted because it was too large.
+ if (xRight != innerXPrev) {
+ float t = (xLeft - innerXPrev) / (xRight - innerXPrev);
+ yRight = innerYRightPrev * (1. - t) + yRight * t;
+ xRight = xLeft;
+ }
+ // Outer: With the coordinates from the interpolation we can calculate the sine
+ // and cosine of the respective angle quickly.
+ sinAngleRight = (yRight - innerYCenter[part][1]) / innerRadius[part][1];
+ cosAngleRight = -(xRight - innerXCenter[part][1]) / innerRadius[part][1];
+ }
+ } else {
+ // same as above but for the other side.
+ if (outerRadius[part][1] == 0) {
+ sinAngleRight = 1.;
+ cosAngleRight = part ? -1. : 1.;
+ } else {
+ sinAngleRight = sinSegmentAngleRight;
+ cosAngleRight = cosSegmentAngleRight;
+ }
+ if (outerRadius[part][0] == 0) {
+ sinAngleLeft = 1.;
+ cosAngleLeft = part ? -1. : 1.;
+ xLeft = xRight;
+ yLeft = innerYCenter[part][0] - innerRadius[part][0] * sinAngleLeft;
+ } else if (xRight >= innerXCenter[0][0] && xRight <= innerXCenter[1][0]) {
+ sinAngleLeft = 1.;
+ cosAngleLeft = 0.;
+ xLeft = xRight;
+ yLeft = innerYCenter[part][0] - innerRadius[part][0] * sinAngleLeft;
+ } else {
+ if (xLeft != innerXPrev) {
+ float t = (xRight - innerXPrev) / (xLeft - innerXPrev);
+ yLeft = innerYLeftPrev * (1. - t) + yLeft * t;
+ xLeft = xRight;
+ }
+ sinAngleLeft = -(yLeft - innerYCenter[part][0]) / innerRadius[part][0];
+ cosAngleLeft = -(xLeft - innerXCenter[part][0]) / innerRadius[part][0];
+ }
+ }
+
+ gradientPos = (xLeft - gradientStart) / gradientLength;
+
+ // calculate the matching outer coordinates
+ outerXLeft = outerXCenter[part][0] - outerRadius[part][0] * cosAngleLeft;
+ outerYLeft = outerYCenter[part][0] - outerRadius[part][0] * sinAngleLeft;
+ outerXRight = outerXCenter[part][1] - outerRadius[part][1] * cosAngleRight;
+ outerYRight = outerYCenter[part][1] + outerRadius[part][1] * sinAngleRight;
+
+ // insert gradient stops as required
while (nextGradientStop <= lastGradientStop && stops.at(nextGradientStop).first <= gradientPos) {
- // Insert vertices at gradient stops.
- float gp = (innerStart - radius) + stops.at(nextGradientStop).first * length;
- float t = (gp - pp) / (p - pp);
- float gis = pss * (1 - t) + t * ss; // gradient inner start
- float gie = pse * (1 - t) + t * se; // gradient inner end
+ float gradientX;
+ float gradientYLeft;
+ float gradientYRight;
+
+ // Insert vertices at gradient stops
+ gradientX = gradientStart + stops.at(nextGradientStop).first * gradientLength;
+ // bilinear interpolation of known vertices
+ float t = (gradientX - innerXPrev) / (xLeft - innerXPrev);
+ gradientYLeft = innerYLeftPrev * (1. - t) + t * yLeft;
+ gradientYRight = innerYRightPrev * (1. - t) + t * yRight;
- fillColor = colorToColor4ub(stops.at(nextGradientStop).second);
+ fillColor = fillColorFromX(gradientX);
if (hasFill) {
indices[fillHead++] = index;
@@ -407,39 +736,35 @@ void QSGBasicInternalRectangleNode::updateGeometry()
indices[innerAATail++] = index + 3;
bool lower = stops.at(nextGradientStop).first > 0.5f;
- float dp = lower ? qMin(0.0f, length - gp - delta) : qMax(0.0f, delta - gp);
- smoothVertices[index++].set(gp, gie, fillColor, dp, secondaryLength - gie - delta, m_gradient_is_vertical);
- smoothVertices[index++].set(gp, gis, fillColor, dp, delta - gis, m_gradient_is_vertical);
+ float dp = lower ? qMin(0.0f, gradientLength - gradientX - delta) : qMax(0.0f, delta - gradientX);
+ smoothVertices[index++].set(gradientX, gradientYRight, fillColor, dp, secondaryLength - gradientYRight - delta, m_gradient_is_vertical);
+ smoothVertices[index++].set(gradientX, gradientYLeft, fillColor, dp, delta - gradientYLeft, m_gradient_is_vertical);
if (penWidth) {
- smoothVertices[index++].set(gp, gie, borderColor, -0.49f * penWidth * c, 0.49f * penWidth * s, m_gradient_is_vertical);
- smoothVertices[index++].set(gp, gis, borderColor, -0.49f * penWidth * c, -0.49f * penWidth * s, m_gradient_is_vertical);
+ smoothVertices[index++].set(gradientX, gradientYRight, borderColor, -0.49f * penWidth * cosAngleRight, 0.49f * penWidth * sinAngleRight, m_gradient_is_vertical);
+ smoothVertices[index++].set(gradientX, gradientYLeft, borderColor, -0.49f * penWidth * cosAngleLeft, -0.49f * penWidth * sinAngleLeft, m_gradient_is_vertical);
} else {
dp = lower ? delta : -delta;
- smoothVertices[index++].set(gp, gie, transparent, dp, delta, m_gradient_is_vertical);
- smoothVertices[index++].set(gp, gis, transparent, dp, -delta, m_gradient_is_vertical);
+ smoothVertices[index++].set(gradientX, gradientYRight, transparent, dp, delta, m_gradient_is_vertical);
+ smoothVertices[index++].set(gradientX, gradientYLeft, transparent, dp, -delta, m_gradient_is_vertical);
}
} else {
- vertices[index++].set(gp, gie, fillColor, m_gradient_is_vertical);
- vertices[index++].set(gp, gis, fillColor, m_gradient_is_vertical);
+ vertices[index++].set(gradientX, gradientYRight, fillColor, m_gradient_is_vertical);
+ vertices[index++].set(gradientX, gradientYLeft, fillColor, m_gradient_is_vertical);
if (penWidth) {
- vertices[index++].set(gp, gie, borderColor, m_gradient_is_vertical);
- vertices[index++].set(gp, gis, borderColor, m_gradient_is_vertical);
+ vertices[index++].set(gradientX, gradientYRight, borderColor, m_gradient_is_vertical);
+ vertices[index++].set(gradientX, gradientYLeft, borderColor, m_gradient_is_vertical);
}
}
- ++nextGradientStop;
+
+ innerXPrev = gradientX;
+ innerYLeftPrev = gradientYLeft;
+ innerYRightPrev = gradientYRight;
+
+ nextGradientStop++;
}
if (!stops.isEmpty()) {
- if (nextGradientStop == 0) {
- fillColor = colorToColor4ub(stops.at(0).second);
- } else if (nextGradientStop == stops.size()) {
- fillColor = colorToColor4ub(stops.last().second);
- } else {
- const QGradientStop &prev = stops.at(nextGradientStop - 1);
- const QGradientStop &next = stops.at(nextGradientStop);
- float t = (gradientPos - prev.first) / (next.first - prev.first);
- fillColor = colorToColor4ub(prev.second) * (1 - t) + colorToColor4ub(next.second) * t;
- }
+ fillColor = fillColorFromX(xLeft);
}
if (hasFill) {
@@ -460,48 +785,57 @@ void QSGBasicInternalRectangleNode::updateGeometry()
indices[innerAATail++] = index + 1;
indices[innerAATail++] = index + 3;
- float dp = part ? qMin(0.0f, length - p - delta) : qMax(0.0f, delta - p);
- smoothVertices[index++].set(p, se, fillColor, dp, secondaryLength - se - delta, m_gradient_is_vertical);
- smoothVertices[index++].set(p, ss, fillColor, dp, delta - ss, m_gradient_is_vertical);
+ float dp = part ? qMin(0.0f, gradientLength - xRight - delta) : qMax(0.0f, delta - xRight);
+ smoothVertices[index++].set(xRight, yRight, fillColor, dp, secondaryLength - yRight - delta, m_gradient_is_vertical);
+ smoothVertices[index++].set(xLeft, yLeft, fillColor, dp, delta - yLeft, m_gradient_is_vertical);
dp = part ? delta : -delta;
if (penWidth) {
- smoothVertices[index++].set(p, se, borderColor, -0.49f * penWidth * c, 0.49f * penWidth * s, m_gradient_is_vertical);
- smoothVertices[index++].set(p, ss, borderColor, -0.49f * penWidth * c, -0.49f * penWidth * s, m_gradient_is_vertical);
- smoothVertices[index++].set(outerEdge, outerSecondaryEnd, borderColor, 0.49f * penWidth * c, -0.49f * penWidth * s, m_gradient_is_vertical);
- smoothVertices[index++].set(outerEdge, outerSecondaryStart, borderColor, 0.49f * penWidth * c, 0.49f * penWidth * s, m_gradient_is_vertical);
- smoothVertices[index++].set(outerEdge, outerSecondaryEnd, transparent, dp, delta, m_gradient_is_vertical);
- smoothVertices[index++].set(outerEdge, outerSecondaryStart, transparent, dp, -delta, m_gradient_is_vertical);
+ smoothVertices[index++].set(xRight, yRight, borderColor, -0.49f * penWidth * cosAngleRight, 0.49f * penWidth * sinAngleRight, m_gradient_is_vertical);
+ smoothVertices[index++].set(xLeft, yLeft, borderColor, -0.49f * penWidth * cosAngleLeft, -0.49f * penWidth * sinAngleLeft, m_gradient_is_vertical);
+ smoothVertices[index++].set(outerXRight, outerYRight, borderColor, 0.49f * penWidth * cosAngleRight, -0.49f * penWidth * sinAngleRight, m_gradient_is_vertical);
+ smoothVertices[index++].set(outerXLeft, outerYLeft, borderColor, 0.49f * penWidth * cosAngleLeft, 0.49f * penWidth * sinAngleLeft, m_gradient_is_vertical);
+ smoothVertices[index++].set(outerXRight, outerYRight, transparent, dp, delta, m_gradient_is_vertical);
+ smoothVertices[index++].set(outerXLeft, outerYLeft, transparent, dp, -delta, m_gradient_is_vertical);
indices[--outerAAHead] = index - 2;
indices[--outerAAHead] = index - 4;
indices[outerAATail++] = index - 3;
indices[outerAATail++] = index - 1;
} else {
- smoothVertices[index++].set(p, se, transparent, dp, delta, m_gradient_is_vertical);
- smoothVertices[index++].set(p, ss, transparent, dp, -delta, m_gradient_is_vertical);
+ smoothVertices[index++].set(xRight, yRight, transparent, dp, delta, m_gradient_is_vertical);
+ smoothVertices[index++].set(xLeft, yLeft, transparent, dp, -delta, m_gradient_is_vertical);
}
} else {
- vertices[index++].set(p, se, fillColor, m_gradient_is_vertical);
- vertices[index++].set(p, ss, fillColor, m_gradient_is_vertical);
+ vertices[index++].set(xRight, yRight, fillColor, m_gradient_is_vertical);
+ vertices[index++].set(xLeft, yLeft, fillColor, m_gradient_is_vertical);
if (penWidth) {
- vertices[index++].set(p, se, borderColor, m_gradient_is_vertical);
- vertices[index++].set(p, ss, borderColor, m_gradient_is_vertical);
- vertices[index++].set(outerEdge, outerSecondaryEnd, borderColor, m_gradient_is_vertical);
- vertices[index++].set(outerEdge, outerSecondaryStart, borderColor, m_gradient_is_vertical);
+ vertices[index++].set(xRight, yRight, borderColor, m_gradient_is_vertical);
+ vertices[index++].set(xLeft, yLeft, borderColor, m_gradient_is_vertical);
+ vertices[index++].set(outerXRight, outerYRight, borderColor, m_gradient_is_vertical);
+ vertices[index++].set(outerXLeft, outerYLeft, borderColor, m_gradient_is_vertical);
}
}
- pp = p;
- pss = ss;
- pse = se;
+ innerXPrev = xLeft;
+ innerYLeftPrev = yLeft;
+ innerYRightPrev = yRight;
- // Rotate
- qreal tmp = c;
- c = c * cosStep - s * sinStep;
- s = s * cosStep + tmp * sinStep;
+ // Advance the point. This corresponds to a rotation of the respective segment
+ if (advanceLeft) {
+ iLeft++;
+ qreal tmp = cosSegmentAngleLeft;
+ cosSegmentAngleLeft = cosSegmentAngleLeft * cosStep[part][0] - sinSegmentAngleLeft * sinStep[part][0];
+ sinSegmentAngleLeft = sinSegmentAngleLeft * cosStep[part][0] + tmp * sinStep[part][0];
+ } else {
+ iRight++;
+ qreal tmp = cosSegmentAngleRight;
+ cosSegmentAngleRight = cosSegmentAngleRight * cosStep[part][1] - sinSegmentAngleRight * sinStep[part][1];
+ sinSegmentAngleRight = sinSegmentAngleRight * cosStep[part][1] + tmp * sinStep[part][1];
+ }
}
}
+
Q_ASSERT(index == vertexCount);
// Close the triangle strips.
@@ -586,11 +920,11 @@ void QSGBasicInternalRectangleNode::updateGeometry()
for (int part = -1; part <= 1; part += 2) {
float innerEdge = (part == 1 ? innerEnd : innerStart);
float outerEdge = (part == 1 ? outerEnd : outerStart);
- gradientPos = (innerEdge - innerStart + penWidth) / length;
+ gradientPos = (innerEdge - innerStart + penWidth) / gradientLength;
while (nextGradientStop <= lastGradientStop && stops.at(nextGradientStop).first <= gradientPos) {
// Insert vertices at gradient stops.
- float gp = (innerStart - penWidth) + stops.at(nextGradientStop).first * length;
+ float gp = (innerStart - penWidth) + stops.at(nextGradientStop).first * gradientLength;
fillColor = colorToColor4ub(stops.at(nextGradientStop).second);
@@ -615,7 +949,7 @@ void QSGBasicInternalRectangleNode::updateGeometry()
indices[innerAATail++] = index + 3;
bool lower = stops.at(nextGradientStop).first > 0.5f;
- float dp = lower ? qMin(0.0f, length - gp - delta) : qMax(0.0f, delta - gp);
+ float dp = lower ? qMin(0.0f, gradientLength - gp - delta) : qMax(0.0f, delta - gp);
smoothVertices[index++].set(gp, innerSecondaryEnd, fillColor, dp, secondaryLength - innerSecondaryEnd - delta, m_gradient_is_vertical);
smoothVertices[index++].set(gp, innerSecondaryStart, fillColor, dp, delta - innerSecondaryStart, m_gradient_is_vertical);
if (penWidth) {
@@ -667,7 +1001,7 @@ void QSGBasicInternalRectangleNode::updateGeometry()
indices[innerAATail++] = index + 1;
indices[innerAATail++] = index + 3;
- float dp = part == 1 ? qMin(0.0f, length - innerEdge - delta) : qMax(0.0f, delta - innerEdge);
+ float dp = part == 1 ? qMin(0.0f, gradientLength - innerEdge - delta) : qMax(0.0f, delta - innerEdge);
smoothVertices[index++].set(innerEdge, innerSecondaryEnd, fillColor, dp, secondaryLength - innerSecondaryEnd - delta, m_gradient_is_vertical);
smoothVertices[index++].set(innerEdge, innerSecondaryStart, fillColor, dp, delta - innerSecondaryStart, m_gradient_is_vertical);