#include <private/qsgcontext_p.h>
#include <QtCore/qmath.h>
+#include <QtCore/qvarlengtharray.h>
QT_BEGIN_NAMESPACE
: m_border(0)
, m_radius(0)
, m_pen_width(0)
+ , m_aligned(true)
, m_gradient_is_opaque(true)
, m_dirty_geometry(false)
, m_default_geometry(QSGGeometry::defaultAttributes_Point2D(), 4)
m_dirty_geometry = true;
}
+void QSGDefaultRectangleNode::setAligned(bool aligned)
+{
+ if (aligned == m_aligned)
+ return;
+ m_aligned = aligned;
+ m_dirty_geometry = true;
+}
+
void QSGDefaultRectangleNode::update()
{
if (m_dirty_geometry) {
void QSGDefaultRectangleNode::updateGeometry()
{
+ qreal penWidth = m_aligned ? qreal(qRound(m_pen_width)) : m_pen_width;
+
// fast path for the simple case...
- if ((m_pen_width == 0 || m_border_material.color().alpha() == 0)
+ if ((penWidth == 0 || m_border_material.color().alpha() == 0)
&& m_radius == 0
&& m_material_type == TypeFlat) {
QSGGeometry::updateRectGeometry(&m_default_geometry, m_rect);
}
- // ### This part down below is not optimal, using QVectors and reallocation for
- // every change, but its all going to be fixed up in rewrite...
-
QSGGeometry *fill = geometry();
// Check that the vertex type matches the material.
int borderVertexCount = 0;
int borderIndexCount = 0;
- QVector<uchar> fillVertexData;
- QVector<Vertex> borderVertexData;
- QVector<ushort> borderIndexData;
+ // Preallocate arrays for a rectangle with 18 segments per corner and 3 gradient stops.
+ QVarLengthArray<uchar, sizeof(ColorVertex) * (19 * 4 + 3 * 2)> fillVertices;
+ QVarLengthArray<Vertex, 19 * 8 + 3 * 2> borderVertices;
+ QVarLengthArray<ushort, 19 * 8 + 3 * 4 + 2> borderIndices;
+ int borderIndexHead = 0;
+ int borderIndexTail = 0;
Color4ub fillColor = colorToColor4ub(m_fill_material.color());
const QGradientStops &stops = m_gradient_stops;
// Rounded corners.
// Radius should never exceeds half of the width or half of the height
- qreal radius = qMin(qMin(m_rect.width() / 2, m_rect.height() / 2), m_radius);
+ qreal radius = qMin(qMin(m_rect.width() * qreal(0.5), m_rect.height() * qreal(0.5)), m_radius);
QRectF innerRect = m_rect;
innerRect.adjust(radius, radius, -radius, -radius);
- if (m_pen_width & 1) {
+ if (m_aligned && (int(penWidth) & 1)) {
// Pen width is odd, so add the offset as documented.
innerRect.moveLeft(innerRect.left() + qreal(0.5));
innerRect.moveTop(innerRect.top() + qreal(0.5));
}
- qreal innerRadius = radius - m_pen_width * qreal(0.5);
- qreal outerRadius = radius + m_pen_width * qreal(0.5);
+ qreal innerRadius = radius - penWidth * qreal(0.5);
+ qreal outerRadius = radius + penWidth * qreal(0.5);
- int segments = qMin(30, qCeil(outerRadius)); // Number of segments per corner.
+ // Number of segments per corner, approximately one per 3 pixels.
+ int segments = qBound(3, qCeil(outerRadius * (M_PI / 6)), 18);
/*
*/
// Overestimate the number of vertices and indices, reduce afterwards when the actual numbers are known.
- if (m_pen_width) {
+ if (penWidth) {
// The reason I add extra vertices where the gradient lines intersect the border is
// to avoid pixel sized gaps between the fill and the border caused by floating point
// inaccuracies.
- borderVertexData.resize((segments + 1) * 2 * 4 + m_gradient_stops.size() * 2);
+ borderVertices.resize((segments + 1) * 2 * 4 + m_gradient_stops.size() * 2);
+ borderIndices.resize((segments + 1) * 2 * 4 + m_gradient_stops.size() * 4 + 2);
+ borderIndexHead = borderIndexTail = (borderIndices.count() >> 1) - 1;
}
- fillVertexData.resize(((segments + 1) * 4 + m_gradient_stops.size() * 2) * fill->stride());
-
- Vertex *borderVertices = borderVertexData.data();
- void *fillVertices = fillVertexData.data(); // Can be Vertex, ColorVertex or TextureVertex.
-
+ fillVertices.resize(((segments + 1) * 4 + m_gradient_stops.size() * 2) * fill->stride());
int nextGradientStop = 0;
qreal gradientPos = (radius - innerRadius) / (innerRect.height() + 2 * radius);
qreal plx = 0; // previous inner left x-coordinate.
qreal prx = 0; // previous inner right x-coordinate.
+ qreal angle = qreal(0.5) * M_PI / qreal(segments);
+ qreal cosStep = qFastCos(angle);
+ qreal sinStep = qFastSin(angle);
+
for (int part = 0; part < 2; ++part) {
+ qreal c = 1 - part;
+ qreal s = part;
for (int i = 0; i <= segments; ++i) {
- //### Should change to calculate sin/cos only once.
- qreal angle = qreal(0.5 * M_PI) * (part + i / qreal(segments));
- qreal s = qFastSin(angle);
- qreal c = qFastCos(angle);
qreal y = (part ? innerRect.bottom() : innerRect.top()) - innerRadius * c; // current inner y-coordinate.
qreal lx = innerRect.left() - innerRadius * s; // current inner left x-coordinate.
qreal rx = innerRect.right() + innerRadius * s; // current inner right x-coordinate.
qreal Y = (part ? innerRect.bottom() : innerRect.top()) - outerRadius * c; // current outer y-coordinate.
qreal lX = innerRect.left() - outerRadius * s; // current outer left x-coordinate.
qreal rX = innerRect.right() + outerRadius * s; // current outer right x-coordinate.
+ {
+ // Rotate
+ qreal tmp = c;
+ c = c * cosStep - s * sinStep;
+ s = s * cosStep + tmp * sinStep;
+ }
gradientPos = ((part ? innerRect.height() : 0) + radius - innerRadius * c) / (innerRect.height() + 2 * radius);
while (nextGradientStop < stops.size() && stops.at(nextGradientStop).first <= gradientPos) {
qreal glx = plx * (1 - t) + t * lx;
qreal grx = prx * (1 - t) + t * rx;
- if (m_pen_width) {
+ if (penWidth) {
borderVertices[borderVertexCount++].position = QVector2D(grx, gy);
borderVertices[borderVertexCount++].position = QVector2D(glx, gy);
- int first = borderIndexData.first();
- borderIndexData.prepend(borderVertexCount - 1);
- borderIndexData.prepend(first);
+ int first = borderIndices[borderIndexHead];
+ borderIndices[--borderIndexHead] = borderVertexCount - 1;
+ borderIndices[--borderIndexHead] = first;
- int last = borderIndexData.at(borderIndexData.size() - 2);
- borderIndexData.append(last);
- borderIndexData.append(borderVertexCount - 2);
+ int last = borderIndices[borderIndexTail - 2];
+ borderIndices[borderIndexTail++] = last;
+ borderIndices[borderIndexTail++] = borderVertexCount - 2;
}
- ColorVertex *vertices = (ColorVertex *)fillVertices;
+ ColorVertex *vertices = (ColorVertex *)fillVertices.data();
fillColor = colorToColor4ub(stops.at(nextGradientStop).second);
vertices[fillVertexCount].position = QVector2D(grx, gy);
++nextGradientStop;
}
- if (m_pen_width) {
+ if (penWidth) {
borderVertices[borderVertexCount++].position = QVector2D(rX, Y);
borderVertices[borderVertexCount++].position = QVector2D(lX, Y);
borderVertices[borderVertexCount++].position = QVector2D(rx, y);
borderVertices[borderVertexCount++].position = QVector2D(lx, y);
- borderIndexData.prepend(borderVertexCount - 1);
- borderIndexData.prepend(borderVertexCount - 3);
- borderIndexData.append(borderVertexCount - 4);
- borderIndexData.append(borderVertexCount - 2);
+ borderIndices[--borderIndexHead] = borderVertexCount - 1;
+ borderIndices[--borderIndexHead] = borderVertexCount - 3;
+ borderIndices[borderIndexTail++] = borderVertexCount - 4;
+ borderIndices[borderIndexTail++] = borderVertexCount - 2;
}
if (stops.isEmpty()) {
Q_ASSERT(m_material_type == TypeFlat);
- Vertex *vertices = (Vertex *)fillVertices;
+ Vertex *vertices = (Vertex *)fillVertices.data();
vertices[fillVertexCount++].position = QVector2D(rx, y);
vertices[fillVertexCount++].position = QVector2D(lx, y);
} else {
fillColor = (colorToColor4ub(prev.second) * (1 - t) + colorToColor4ub(next.second) * t);
}
- ColorVertex *vertices = (ColorVertex *)fillVertices;
+ ColorVertex *vertices = (ColorVertex *)fillVertices.data();
vertices[fillVertexCount].position = QVector2D(rx, y);
vertices[fillVertexCount].color = fillColor;
++fillVertexCount;
}
}
-
- if (m_pen_width) {
+ if (penWidth) {
// Close border.
- ushort first = borderIndexData.at(0);
- ushort second = borderIndexData.at(1);
- borderIndexData.append(first);
- borderIndexData.append(second);
+ ushort first = borderIndices[borderIndexHead];
+ ushort second = borderIndices[borderIndexHead + 1];
+ borderIndices[borderIndexTail++] = first;
+ borderIndices[borderIndexTail++] = second;
- borderIndexCount = borderIndexData.size();
+ borderIndexCount = borderIndexTail - borderIndexHead;
}
} else {
QRectF outerRect = m_rect;
qreal halfPenWidth = 0;
- if (m_pen_width) {
- if (m_pen_width & 1) {
+ if (penWidth) {
+ if (m_aligned && (int(penWidth) & 1)) {
// Pen width is odd, so add the offset as documented.
innerRect.moveLeft(innerRect.left() + qreal(0.5));
innerRect.moveTop(innerRect.top() + qreal(0.5));
outerRect = innerRect;
}
- halfPenWidth = m_pen_width * qreal(0.5);
+ halfPenWidth = penWidth * qreal(0.5);
innerRect.adjust(halfPenWidth, halfPenWidth, -halfPenWidth, -halfPenWidth);
outerRect.adjust(-halfPenWidth, -halfPenWidth, halfPenWidth, halfPenWidth);
}
- if (m_pen_width) {
- borderVertexData.resize((2 + stops.size()) * 2 + 4);
- borderIndexData.resize((2 + stops.size()) * 2 * 2 + 4);
+ if (penWidth) {
+ borderVertices.resize((2 + stops.size()) * 2 + 4);
+ borderIndices.resize((2 + stops.size()) * 2 * 2 + 4);
}
- fillVertexData.resize((2 + stops.size()) * 2 * fill->stride());
-
- void *fillVertices = fillVertexData.data();
- Vertex *borderVertices = (Vertex *) borderVertexData.data();
- ushort *borderIndices = borderIndexData.data();
+ fillVertices.resize((2 + stops.size()) * 2 * fill->stride());
int nextGradientStop = 0;
qreal gradientPos = halfPenWidth / m_rect.height();
qreal gy = (innerRect.top() - halfPenWidth) + stops.at(nextGradientStop).first * m_rect.height();
Q_ASSERT(fillVertexCount >= 2);
- ColorVertex *vertices = (ColorVertex *)fillVertices;
+ ColorVertex *vertices = (ColorVertex *)fillVertices.data();
fillColor = colorToColor4ub(stops.at(nextGradientStop).second);
vertices[fillVertexCount].position = QVector2D(innerRect.right(), gy);
vertices[fillVertexCount].color = fillColor;
++fillVertexCount;
- if (m_pen_width) {
+ if (penWidth) {
borderVertices[borderVertexCount++].position = QVector2D(innerRect.right(), gy);
borderVertices[borderVertexCount++].position = QVector2D(innerRect.left(), gy);
}
if (stops.isEmpty()) {
Q_ASSERT(m_material_type == TypeFlat);
- Vertex *vertices = (Vertex *)fillVertices;
+ Vertex *vertices = (Vertex *)fillVertices.data();
vertices[fillVertexCount++].position = QVector2D(innerRect.right(), y);
vertices[fillVertexCount++].position = QVector2D(innerRect.left(), y);
} else {
fillColor = (colorToColor4ub(prev.second) * (1 - t) + colorToColor4ub(next.second) * t);
}
- ColorVertex *vertices = (ColorVertex *)fillVertices;
+ ColorVertex *vertices = (ColorVertex *)fillVertices.data();
vertices[fillVertexCount].position = QVector2D(innerRect.right(), y);
vertices[fillVertexCount].color = fillColor;
++fillVertexCount;
++fillVertexCount;
}
- if (m_pen_width) {
+ if (penWidth) {
borderVertices[borderVertexCount++].position = QVector2D(innerRect.right(), y);
borderVertices[borderVertexCount++].position = QVector2D(innerRect.left(), y);
}
}
- if (m_pen_width) {
+ if (penWidth) {
// Add four corners.
borderVertices[borderVertexCount++].position = QVector2D(outerRect.right(), outerRect.top());
borderVertices[borderVertexCount++].position = QVector2D(outerRect.left(), outerRect.top());
}
// Copy from temporary datastructures to geometry...
- if (m_pen_width) {
+ if (penWidth) {
borderGeometry->allocate(borderVertexCount, borderIndexCount);
- memcpy(borderGeometry->indexData(), borderIndexData.constData(), borderIndexCount * sizeof(quint16));
- memcpy(borderGeometry->vertexData(), borderVertexData.constData(), borderVertexCount * sizeof(Vertex));
+ memcpy(borderGeometry->indexData(), borderIndices.constData() + borderIndexHead, borderIndexCount * sizeof(quint16));
+ memcpy(borderGeometry->vertexData(), borderVertices.constData(), borderVertexCount * sizeof(Vertex));
m_border->markDirty(DirtyGeometry);
}
fill->allocate(fillVertexCount);
- memcpy(fill->vertexData(), fillVertexData.constData(), fillVertexCount * fill->stride());
+ memcpy(fill->vertexData(), fillVertices.constData(), fillVertexCount * fill->stride());
markDirty(DirtyGeometry);
}