diff options
Diffstat (limited to 'src/quick/scenegraph/qsgbasicinternalrectanglenode.cpp')
-rw-r--r-- | src/quick/scenegraph/qsgbasicinternalrectanglenode.cpp | 630 |
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); |