Fix Rectangle implementation.
authorKim Motoyoshi Kalland <kim.kalland@nokia.com>
Mon, 9 May 2011 14:59:37 +0000 (16:59 +0200)
committerKim Motoyoshi Kalland <kim.kalland@nokia.com>
Mon, 9 May 2011 15:31:03 +0000 (17:31 +0200)
src/declarative/items/qsgrectangle.cpp
src/declarative/items/qsgrectangle_p.h
src/declarative/scenegraph/qsgadaptationlayer_p.h
src/declarative/scenegraph/qsgdefaultrectanglenode.cpp
src/declarative/scenegraph/qsgdefaultrectanglenode_p.h

index cba6527..e97abe3 100644 (file)
@@ -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);
     }
 
index 6cd5172..6157d82 100644 (file)
@@ -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
index 1e7c794..22d202d 100644 (file)
@@ -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;
 };
index b82cf28..12b26a8 100644 (file)
@@ -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);
 }
index 3bf1494..cd337b6 100644 (file)
@@ -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;