diff options
author | Kim Motoyoshi Kalland <kim.kalland@nokia.com> | 2011-05-09 16:59:37 +0200 |
---|---|---|
committer | Kim Motoyoshi Kalland <kim.kalland@nokia.com> | 2011-05-09 17:31:03 +0200 |
commit | 4fb6ee7d258049237db33da30ee08f106ae490af (patch) | |
tree | 059836951a9a896d054fb91e506ef84c1e0dbca9 | |
parent | 2002baaf680a4dd4637bd400161e29e0c0a0dc47 (diff) |
Fix Rectangle implementation.
-rw-r--r-- | src/declarative/items/qsgrectangle.cpp | 60 | ||||
-rw-r--r-- | src/declarative/items/qsgrectangle_p.h | 17 | ||||
-rw-r--r-- | src/declarative/scenegraph/qsgadaptationlayer_p.h | 1 | ||||
-rw-r--r-- | src/declarative/scenegraph/qsgdefaultrectanglenode.cpp | 146 | ||||
-rw-r--r-- | src/declarative/scenegraph/qsgdefaultrectanglenode_p.h | 4 |
5 files changed, 134 insertions, 94 deletions
diff --git a/src/declarative/items/qsgrectangle.cpp b/src/declarative/items/qsgrectangle.cpp index cba6527759..e97abe3e1c 100644 --- a/src/declarative/items/qsgrectangle.cpp +++ b/src/declarative/items/qsgrectangle.cpp @@ -55,64 +55,82 @@ QT_BEGIN_NAMESPACE // XXX todo - should we change rectangle to draw entirely within its width/height? QSGPen::QSGPen(QObject *parent) -: QObject(parent), _width(1), _color("#000000"), _valid(false) + : QObject(parent) + , m_width(1) + , m_color("#000000") + , m_aligned(true) + , m_valid(false) { } +qreal QSGPen::width() const +{ + return m_width; +} + +void QSGPen::setWidth(qreal w) +{ + if (m_width == w && m_valid) + return; + + m_width = w; + m_valid = m_color.alpha() && (qRound(m_width) >= 1 || (!m_aligned && m_width > 0)); + emit penChanged(); +} + QColor QSGPen::color() const { - return _color; + return m_color; } void QSGPen::setColor(const QColor &c) { - _color = c; - _valid = (_color.alpha() && _width >= 1) ? true : false; + m_color = c; + m_valid = m_color.alpha() && (qRound(m_width) >= 1 || (!m_aligned && m_width > 0)); emit penChanged(); } -int QSGPen::width() const -{ - return _width; +bool QSGPen::aligned() const +{ + return m_aligned; } -void QSGPen::setWidth(int w) +void QSGPen::setAligned(bool aligned) { - if (_width == w && _valid) + if (aligned == m_aligned) return; - - _width = w; - _valid = (_color.alpha() && _width >= 1) ? true : false; + m_aligned = aligned; + m_valid = m_color.alpha() && (qRound(m_width) >= 1 || (!m_aligned && m_width > 0)); emit penChanged(); } bool QSGPen::isValid() const { - return _valid; + return m_valid; } QSGGradientStop::QSGGradientStop(QObject *parent) -: QObject(parent) + : QObject(parent) { } qreal QSGGradientStop::position() const -{ +{ return m_position; } void QSGGradientStop::setPosition(qreal position) -{ +{ m_position = position; updateGradient(); } QColor QSGGradientStop::color() const -{ +{ return m_color; } void QSGGradientStop::setColor(const QColor &color) -{ +{ m_color = color; updateGradient(); } @@ -128,12 +146,12 @@ QSGGradient::QSGGradient(QObject *parent) } QSGGradient::~QSGGradient() -{ +{ delete m_gradient; } QDeclarativeListProperty<QSGGradientStop> QSGGradient::stops() -{ +{ return QDeclarativeListProperty<QSGGradientStop>(this, m_stops); } @@ -257,8 +275,8 @@ QSGNode *QSGRectangle::updatePaintNode(QSGNode *oldNode, UpdatePaintNodeData *da if (d->pen && d->pen->isValid()) { rectangle->setPenColor(d->pen->color()); rectangle->setPenWidth(d->pen->width()); + rectangle->setAligned(d->pen->aligned()); } else { - rectangle->setPenColor(QColor()); rectangle->setPenWidth(0); } diff --git a/src/declarative/items/qsgrectangle_p.h b/src/declarative/items/qsgrectangle_p.h index 6cd5172f35..6157d82442 100644 --- a/src/declarative/items/qsgrectangle_p.h +++ b/src/declarative/items/qsgrectangle_p.h @@ -58,26 +58,31 @@ class Q_DECLARATIVE_PRIVATE_EXPORT QSGPen : public QObject { Q_OBJECT - Q_PROPERTY(int width READ width WRITE setWidth NOTIFY penChanged) + Q_PROPERTY(qreal width READ width WRITE setWidth NOTIFY penChanged) Q_PROPERTY(QColor color READ color WRITE setColor NOTIFY penChanged) + Q_PROPERTY(bool aligned READ aligned WRITE setAligned NOTIFY penChanged) public: QSGPen(QObject *parent=0); - int width() const; - void setWidth(int w); + qreal width() const; + void setWidth(qreal w); QColor color() const; void setColor(const QColor &c); + bool aligned() const; + void setAligned(bool aligned); + bool isValid() const; Q_SIGNALS: void penChanged(); private: - int _width; - QColor _color; - bool _valid; + qreal m_width; + QColor m_color; + bool m_aligned : 1; + bool m_valid : 1; }; class Q_AUTOTEST_EXPORT QSGGradientStop : public QObject diff --git a/src/declarative/scenegraph/qsgadaptationlayer_p.h b/src/declarative/scenegraph/qsgadaptationlayer_p.h index 1e7c794ea9..22d202d404 100644 --- a/src/declarative/scenegraph/qsgadaptationlayer_p.h +++ b/src/declarative/scenegraph/qsgadaptationlayer_p.h @@ -72,6 +72,7 @@ public: virtual void setPenWidth(qreal width) = 0; virtual void setGradientStops(const QGradientStops &stops) = 0; virtual void setRadius(qreal radius) = 0; + virtual void setAligned(bool aligned) = 0; virtual void update() = 0; }; diff --git a/src/declarative/scenegraph/qsgdefaultrectanglenode.cpp b/src/declarative/scenegraph/qsgdefaultrectanglenode.cpp index b82cf283d9..12b26a8d20 100644 --- a/src/declarative/scenegraph/qsgdefaultrectanglenode.cpp +++ b/src/declarative/scenegraph/qsgdefaultrectanglenode.cpp @@ -50,6 +50,7 @@ #include <private/qsgcontext_p.h> #include <QtCore/qmath.h> +#include <QtCore/qvarlengtharray.h> QT_BEGIN_NAMESPACE @@ -57,6 +58,7 @@ QSGDefaultRectangleNode::QSGDefaultRectangleNode(QSGContext *context) : 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) @@ -186,6 +188,14 @@ void QSGDefaultRectangleNode::setRadius(qreal radius) 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) { @@ -225,8 +235,10 @@ struct ColorVertex 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); @@ -234,9 +246,6 @@ void QSGDefaultRectangleNode::updateGeometry() } - // ### 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. @@ -253,9 +262,12 @@ void QSGDefaultRectangleNode::updateGeometry() 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; @@ -264,19 +276,20 @@ void QSGDefaultRectangleNode::updateGeometry() // 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); /* @@ -292,17 +305,15 @@ void QSGDefaultRectangleNode::updateGeometry() */ // 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); @@ -313,18 +324,26 @@ void QSGDefaultRectangleNode::updateGeometry() 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) { @@ -335,20 +354,20 @@ void QSGDefaultRectangleNode::updateGeometry() 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); @@ -361,21 +380,21 @@ void QSGDefaultRectangleNode::updateGeometry() ++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 { @@ -390,7 +409,7 @@ void QSGDefaultRectangleNode::updateGeometry() 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; @@ -405,15 +424,14 @@ void QSGDefaultRectangleNode::updateGeometry() } } - - 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 { @@ -423,27 +441,23 @@ void QSGDefaultRectangleNode::updateGeometry() 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(); @@ -459,7 +473,7 @@ void QSGDefaultRectangleNode::updateGeometry() 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); @@ -469,7 +483,7 @@ void QSGDefaultRectangleNode::updateGeometry() 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); } @@ -479,7 +493,7 @@ void QSGDefaultRectangleNode::updateGeometry() 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 { @@ -494,7 +508,7 @@ void QSGDefaultRectangleNode::updateGeometry() 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; @@ -503,13 +517,13 @@ void QSGDefaultRectangleNode::updateGeometry() ++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()); @@ -531,15 +545,15 @@ void QSGDefaultRectangleNode::updateGeometry() } // 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); } diff --git a/src/declarative/scenegraph/qsgdefaultrectanglenode_p.h b/src/declarative/scenegraph/qsgdefaultrectanglenode_p.h index 3bf14947de..cd337b6748 100644 --- a/src/declarative/scenegraph/qsgdefaultrectanglenode_p.h +++ b/src/declarative/scenegraph/qsgdefaultrectanglenode_p.h @@ -68,6 +68,7 @@ public: virtual void setPenWidth(qreal width); virtual void setGradientStops(const QGradientStops &stops); virtual void setRadius(qreal radius); + virtual void setAligned(bool aligned); virtual void update(); private: @@ -87,8 +88,9 @@ private: QRectF m_rect; QGradientStops m_gradient_stops; qreal m_radius; - int m_pen_width; + qreal m_pen_width; + uint m_aligned : 1; uint m_gradient_is_opaque : 1; uint m_dirty_geometry : 1; |