Fix pixel bleed in BorderImage
authorEskil Abrahamsen Blomfeldt <eskil.abrahamsen-blomfeldt@digia.com>
Tue, 26 Aug 2014 08:52:22 +0000 (10:52 +0200)
committerEskil Abrahamsen Blomfeldt <eskil.abrahamsen-blomfeldt@digia.com>
Wed, 10 Sep 2014 11:52:07 +0000 (13:52 +0200)
Keeping all patches of the border image in the same texture
with different sample points can cause parts of the border
to bleed over to the center patch. To rectify this, we
create a separate texture for each of the nine patches we
need, and separate image nodes.

To avoid applying antialiasing on the interior edges of the
border image, we introduce new antialiasing flags which can
be used to specify precisely which edges of the image should
be antialiased.

[ChangeLog][BorderImage] Fixed possible pixel bleed between
border patches and center patch in BorderImage.

Change-Id: Icc292b3969217320eecca99e79675316c42eab08
Task-number: QTBUG-35838
Reviewed-by: Gunnar Sletta <gunnar@sletta.org>
19 files changed:
src/quick/items/qquickborderimage.cpp
src/quick/items/qquickborderimage_p.h
src/quick/items/qquickborderimage_p_p.h
src/quick/scenegraph/qsgadaptationlayer_p.h
src/quick/scenegraph/qsgdefaultimagenode.cpp
src/quick/scenegraph/qsgdefaultimagenode_p.h
tests/manual/scenegraph_lancelot/data/borderimages/borderimage_rotated_smoothed.qml [new file with mode: 0644]
tests/manual/scenegraph_lancelot/data/borderimages/borderimage_rotated_unsmoothed.qml [new file with mode: 0644]
tests/manual/scenegraph_lancelot/data/borderimages/borderimage_rotated_unsmoothed_border_overlap.qml [new file with mode: 0644]
tests/manual/scenegraph_lancelot/data/borderimages/borderimage_rotated_unsmoothed_negative_borders.qml [new file with mode: 0644]
tests/manual/scenegraph_lancelot/data/borderimages/borderimage_rotated_unsmoothed_no_borders.qml [new file with mode: 0644]
tests/manual/scenegraph_lancelot/data/borderimages/borderimage_rotated_unsmoothed_no_bottom.qml [new file with mode: 0644]
tests/manual/scenegraph_lancelot/data/borderimages/borderimage_rotated_unsmoothed_no_center.qml [new file with mode: 0644]
tests/manual/scenegraph_lancelot/data/borderimages/borderimage_rotated_unsmoothed_no_left.qml [new file with mode: 0644]
tests/manual/scenegraph_lancelot/data/borderimages/borderimage_rotated_unsmoothed_no_right.qml [new file with mode: 0644]
tests/manual/scenegraph_lancelot/data/borderimages/borderimage_rotated_unsmoothed_no_right_left.qml [new file with mode: 0644]
tests/manual/scenegraph_lancelot/data/borderimages/borderimage_rotated_unsmoothed_no_top.qml [new file with mode: 0644]
tests/manual/scenegraph_lancelot/data/borderimages/borderimage_rotated_unsmoothed_no_top_bottom.qml [new file with mode: 0644]
tests/manual/scenegraph_lancelot/data/shared/uniquepixels.png [new file with mode: 0644]

index 38bbc66..50a0a76 100644 (file)
@@ -540,90 +540,347 @@ void QQuickBorderImage::doUpdate()
     update();
 }
 
+QImage QQuickBorderImage::shallowCopy(const QImage &image, const QRect &rect)
+{
+    if (image.depth() == 1) {
+        return image.copy(rect);
+    } else {
+        const uchar *bits = image.constBits() + image.bytesPerLine() * rect.y()  + (image.depth() / 8) * rect.x();
+        return QImage(bits, rect.width(), rect.height(), image.bytesPerLine(), image.format());
+    }
+}
+
 QSGNode *QQuickBorderImage::updatePaintNode(QSGNode *oldNode, UpdatePaintNodeData *)
 {
     Q_D(QQuickBorderImage);
 
-    QSGTexture *texture = d->sceneGraphRenderContext()->textureForFactory(d->pix.textureFactory(), window());
-
-    if (!texture || width() <= 0 || height() <= 0) {
+    if (!d->pix.isReady() || width() <= 0 || height() <= 0) {
         delete oldNode;
         return 0;
     }
 
-    QSGImageNode *node = static_cast<QSGImageNode *>(oldNode);
-
-    bool updatePixmap = d->pixmapChanged;
-    d->pixmapChanged = false;
-    if (!node) {
-        node = d->sceneGraphContext()->createImageNode();
-        updatePixmap = true;
-    }
-
-    if (updatePixmap)
-        node->setTexture(texture);
-
     // Don't implicitly create the scalegrid in the rendering thread...
     QRectF innerSourceRect(0, 0, 1, 1);
-    QRectF targetRect(0, 0, width(), height());
-    QRectF innerTargetRect = targetRect;
+    QRectF innerTargetRect(0, 0, width(), height());
+    int borderLeft, borderTop, borderRight, borderBottom;
     if (d->border) {
         const QQuickScaleGrid *border = d->getScaleGrid();
-        innerSourceRect = QRectF(border->left() / qreal(d->pix.width()),
-                                 border->top() / qreal(d->pix.height()),
-                                 qMax<qreal>(0, d->pix.width() - border->right() - border->left()) / d->pix.width(),
-                                 qMax<qreal>(0, d->pix.height() - border->bottom() - border->top()) / d->pix.height());
-        innerTargetRect = QRectF(border->left(),
-                                 border->top(),
+
+        borderLeft = qBound(0, border->left(), d->pix.width());
+        borderTop = qBound(0, border->top(), d->pix.height());
+        borderRight = d->pix.rect().width() - qBound(0, border->right(), d->pix.rect().width() - borderLeft);
+        borderBottom = d->pix.rect().height() - qBound(0, border->bottom(), d->pix.rect().height() - borderTop);
+
+        innerSourceRect = QRectF(borderLeft / qreal(d->pix.width()),
+                                 borderTop / qreal(d->pix.height()),
+                                 qMax<qreal>(0, borderRight - borderLeft) / d->pix.width(),
+                                 qMax<qreal>(0, borderBottom - borderTop) / d->pix.height());
+        innerTargetRect = QRectF(borderLeft,
+                                 borderTop,
                                  qMax<qreal>(0, width() - border->right() - border->left()),
                                  qMax<qreal>(0, height() - border->bottom() - border->top()));
     }
-    qreal hTiles = 1;
-    qreal vTiles = 1;
-    if (innerSourceRect.width() != 0) {
-        switch (d->horizontalTileMode) {
-        case QQuickBorderImage::Repeat:
-            hTiles = innerTargetRect.width() / qreal(innerSourceRect.width() * d->pix.width());
-            break;
-        case QQuickBorderImage::Round:
-            hTiles = qCeil(innerTargetRect.width() / qreal(innerSourceRect.width() * d->pix.width()));
-            break;
-        default:
-            break;
+
+    bool updatePixmap = d->pixmapChanged;
+    d->pixmapChanged = false;
+    if (!oldNode) {
+        oldNode = new QSGNode;
+        updatePixmap = true;
+
+        for (int i=0; i<9; ++i)
+            d->regions[i].node = 0;
+
+        if (innerSourceRect.left() > 0) {
+            if (innerSourceRect.top() > 0)
+                d->regions[0].node = d->sceneGraphContext()->createImageNode();
+            if (innerSourceRect.bottom() < 1)
+                d->regions[6].node = d->sceneGraphContext()->createImageNode();
+
+            if (innerSourceRect.top() < innerSourceRect.bottom())
+                d->regions[3].node = d->sceneGraphContext()->createImageNode();
+        }
+
+        if (innerSourceRect.right() < 1) {
+            if (innerSourceRect.top() > 0)
+                d->regions[2].node = d->sceneGraphContext()->createImageNode();
+            if (innerSourceRect.bottom() < 1)
+                d->regions[8].node = d->sceneGraphContext()->createImageNode();
+
+            if (innerSourceRect.top() < innerSourceRect.bottom())
+                d->regions[5].node = d->sceneGraphContext()->createImageNode();
+        }
+
+        if (innerSourceRect.top() > 0 && innerSourceRect.left() < innerSourceRect.right())
+            d->regions[1].node = d->sceneGraphContext()->createImageNode();
+
+        if (innerSourceRect.bottom() < 1 && innerSourceRect.left() < innerSourceRect.right())
+            d->regions[7].node = d->sceneGraphContext()->createImageNode();
+
+        if (innerSourceRect.left() < innerSourceRect.right() && innerSourceRect.top() < innerSourceRect.bottom())
+            d->regions[4].node = d->sceneGraphContext()->createImageNode();
+
+        for (int i=0; i<9; ++i) {
+            if (d->regions[i].node != 0)
+                oldNode->appendChildNode(d->regions[i].node);
         }
     }
-    if (innerSourceRect.height() != 0) {
-        switch (d->verticalTileMode) {
-        case QQuickBorderImage::Repeat:
-            vTiles = innerTargetRect.height() / qreal(innerSourceRect.height() * d->pix.height());
-            break;
-        case QQuickBorderImage::Round:
-            vTiles = qCeil(innerTargetRect.height() / qreal(innerSourceRect.height() * d->pix.height()));
-            break;
-        default:
-            break;
+
+
+    QImage image = d->pix.image();
+
+    if (d->regions[0].node != 0) {
+        if (updatePixmap)
+            d->regions[0].image = shallowCopy(image, QRect(QPoint(0, 0), QSize(borderLeft, borderTop)));
+
+        QSGImageNode::AntialiasingFlags antialiasing = QSGImageNode::AntialiasingFlags(QSGImageNode::AntialiasingLeft | QSGImageNode::AntialiasingTop);
+        if (d->regions[1].node == 0 && d->regions[2].node == 0)
+            antialiasing |= QSGImageNode::AntialiasingRight;
+        if (d->regions[3].node == 0 && d->regions[6].node == 0)
+            antialiasing |= QSGImageNode::AntialiasingBottom;
+        d->regions[0].node->setAntialiasing(antialiasing);
+
+        QRectF rect(0,
+                    0,
+                    innerTargetRect.left(),
+                    innerTargetRect.top());
+        d->regions[0].node->setTargetRect(rect);
+        d->regions[0].node->setInnerTargetRect(rect);
+        d->regions[0].targetRect = rect;
+    }
+
+    if (d->regions[1].node != 0) {
+        if (updatePixmap)
+            d->regions[1].image = shallowCopy(image, QRect(QPoint(borderLeft, 0), QSize(borderRight - borderLeft, borderTop)));
+
+        QSGImageNode::AntialiasingFlags antialiasing = QSGImageNode::AntialiasingTop;
+        if (d->regions[0].node == 0)
+            antialiasing |= QSGImageNode::AntialiasingLeft;
+        if (d->regions[2].node == 0)
+            antialiasing |= QSGImageNode::AntialiasingRight;
+        if (d->regions[4].node == 0 && d->regions[7].node == 0)
+            antialiasing |= QSGImageNode::AntialiasingBottom;
+        d->regions[1].node->setAntialiasing(antialiasing);
+
+        QRectF rect(innerTargetRect.left(),
+                    0,
+                    innerTargetRect.width(),
+                    innerTargetRect.top());
+        d->regions[1].node->setTargetRect(rect);
+        d->regions[1].node->setInnerTargetRect(rect);
+        d->regions[1].targetRect = rect;
+    }
+
+    if (d->regions[2].node != 0) {
+        if (updatePixmap)
+            d->regions[2].image = shallowCopy(image, QRect(QPoint(borderRight, 0), QSize(d->pix.rect().width() - borderRight, borderTop)));
+
+        QSGImageNode::AntialiasingFlags antialiasing = QSGImageNode::AntialiasingFlags(QSGImageNode::AntialiasingTop | QSGImageNode::AntialiasingRight);
+        if (d->regions[0].node == 0 && d->regions[1].node == 0)
+            antialiasing |= QSGImageNode::AntialiasingLeft;
+        if (d->regions[5].node == 0 && d->regions[8].node == 0)
+            antialiasing |= QSGImageNode::AntialiasingBottom;
+        d->regions[2].node->setAntialiasing(antialiasing);
+
+        QRectF rect(innerTargetRect.right(),
+                    0,
+                    width() - innerTargetRect.width() - innerTargetRect.left(),
+                    innerTargetRect.top());
+        d->regions[2].node->setTargetRect(rect);
+        d->regions[2].node->setInnerTargetRect(rect);
+        d->regions[2].targetRect = rect;
+    }
+
+    if (d->regions[3].node != 0) {
+        if (updatePixmap)
+            d->regions[3].image = shallowCopy(image, QRect(QPoint(0, borderTop), QSize(borderLeft, borderBottom - borderTop)));
+
+        QSGImageNode::AntialiasingFlags antialiasing = QSGImageNode::AntialiasingLeft;
+        if (d->regions[4].node == 0 && d->regions[5].node == 0)
+            antialiasing |= QSGImageNode::AntialiasingRight;
+        if (d->regions[6].node == 0)
+            antialiasing |= QSGImageNode::AntialiasingBottom;
+        if (d->regions[0].node == 0)
+            antialiasing |= QSGImageNode::AntialiasingTop;
+        d->regions[3].node->setAntialiasing(antialiasing);
+
+        QRectF rect(0,
+                    innerTargetRect.top(),
+                    innerTargetRect.left(),
+                    innerTargetRect.height());
+        d->regions[3].node->setTargetRect(rect);
+        d->regions[3].node->setInnerTargetRect(rect);
+        d->regions[3].targetRect = rect;
+    }
+
+    if (d->regions[4].node != 0) {
+        if (updatePixmap) {
+            if (innerSourceRect == QRectF(0, 0, 1, 1)) {
+                d->regions[4].image = image;
+            } else {
+                d->regions[4].image = shallowCopy(image, QRect(QPoint(borderLeft, borderTop), QSize(borderRight - borderLeft, borderBottom - borderTop)));
+            }
         }
+
+        QSGImageNode::AntialiasingFlags antialiasing = QSGImageNode::AntialiasingNone;
+        if (d->regions[3].node == 0)
+            antialiasing |= QSGImageNode::AntialiasingLeft;
+        if (d->regions[5].node == 0)
+            antialiasing |= QSGImageNode::AntialiasingRight;
+        if (d->regions[1].node == 0)
+            antialiasing |= QSGImageNode::AntialiasingTop;
+        if (d->regions[7].node == 0)
+            antialiasing |= QSGImageNode::AntialiasingBottom;
+        d->regions[4].node->setAntialiasing(antialiasing);
+
+        d->regions[4].node->setInnerTargetRect(innerTargetRect);
+        d->regions[4].node->setTargetRect(innerTargetRect);
+        d->regions[4].targetRect = innerTargetRect;
     }
 
-    node->setTargetRect(targetRect);
-    node->setInnerSourceRect(innerSourceRect);
-    node->setInnerTargetRect(innerTargetRect);
-    node->setSubSourceRect(QRectF(0, 0, hTiles, vTiles));
-    node->setMirror(d->mirror);
-
-    node->setMipmapFiltering(QSGTexture::None);
-    node->setFiltering(d->smooth ? QSGTexture::Linear : QSGTexture::Nearest);
-    if (innerSourceRect == QRectF(0, 0, 1, 1) && (vTiles > 1 || hTiles > 1)) {
-        node->setHorizontalWrapMode(QSGTexture::Repeat);
-        node->setVerticalWrapMode(QSGTexture::Repeat);
-    } else {
-        node->setHorizontalWrapMode(QSGTexture::ClampToEdge);
-        node->setVerticalWrapMode(QSGTexture::ClampToEdge);
+    if (d->regions[5].node != 0) {
+        if (updatePixmap)
+            d->regions[5].image = shallowCopy(image, QRect(QPoint(borderRight, borderTop), QSize(d->pix.rect().width() - borderRight, borderBottom - borderTop)));
+
+        QSGImageNode::AntialiasingFlags antialiasing = QSGImageNode::AntialiasingRight;
+        if (d->regions[4].node == 0 && d->regions[3].node == 0)
+            antialiasing |= QSGImageNode::AntialiasingLeft;
+        if (d->regions[2].node == 0)
+            antialiasing |= QSGImageNode::AntialiasingTop;
+        if (d->regions[8].node == 0)
+            antialiasing |= QSGImageNode::AntialiasingBottom;
+        d->regions[5].node->setAntialiasing(antialiasing);
+
+        QRectF rect(innerTargetRect.right(),
+                    innerTargetRect.top(),
+                    width() - innerTargetRect.width() - innerTargetRect.left(),
+                    innerTargetRect.height());
+        d->regions[5].node->setTargetRect(rect);
+        d->regions[5].node->setInnerTargetRect(rect);
+        d->regions[5].targetRect = rect;
+    }
+
+    if (d->regions[6].node != 0) {
+        if (updatePixmap)
+            d->regions[6].image = shallowCopy(image, QRect(QPoint(0, borderBottom), QSize(borderLeft, d->pix.rect().height() - borderBottom)));
+
+        QSGImageNode::AntialiasingFlags antialiasing = QSGImageNode::AntialiasingFlags(QSGImageNode::AntialiasingBottom | QSGImageNode::AntialiasingLeft);
+        if (d->regions[7].node == 0 && d->regions[8].node == 0)
+            antialiasing |= QSGImageNode::AntialiasingRight;
+        if (d->regions[3].node == 0 && d->regions[0].node == 0)
+            antialiasing |= QSGImageNode::AntialiasingTop;
+        d->regions[6].node->setAntialiasing(antialiasing);
+
+        QRectF rect(0,
+                    innerTargetRect.bottom(),
+                    innerTargetRect.left(),
+                    height() - innerTargetRect.height() - innerTargetRect.top());
+        d->regions[6].node->setTargetRect(rect);
+        d->regions[6].node->setInnerTargetRect(rect);
+        d->regions[6].targetRect = rect;
+    }
+
+    if (d->regions[7].node != 0) {
+        if (updatePixmap)
+            d->regions[7].image = shallowCopy(image, QRect(QPoint(borderLeft, borderBottom), QSize(borderRight - borderLeft, d->pix.rect().height() - borderBottom)));
+
+        QSGImageNode::AntialiasingFlags antialiasing = QSGImageNode::AntialiasingBottom;
+        if (d->regions[6].node == 0)
+            antialiasing |= QSGImageNode::AntialiasingLeft;
+        if (d->regions[8].node == 0)
+            antialiasing |= QSGImageNode::AntialiasingRight;
+        if (d->regions[4].node == 0 && d->regions[1].node == 0)
+            antialiasing |= QSGImageNode::AntialiasingTop;
+        d->regions[7].node->setAntialiasing(antialiasing);
+
+        QRectF rect(innerTargetRect.left(),
+                    innerTargetRect.bottom(),
+                    innerTargetRect.width(),
+                    height() - innerTargetRect.height() - innerTargetRect.top());
+        d->regions[7].node->setTargetRect(rect);
+        d->regions[7].node->setInnerTargetRect(rect);
+        d->regions[7].targetRect = rect;
+    }
+
+    if (d->regions[8].node != 0) {
+        if (updatePixmap)
+            d->regions[8].image = shallowCopy(image, QRect(QPoint(borderRight, borderBottom), QSize(d->pix.rect().width() - borderRight, d->pix.rect().height() - borderBottom)));
+
+        QSGImageNode::AntialiasingFlags antialiasing = QSGImageNode::AntialiasingFlags(QSGImageNode::AntialiasingBottom | QSGImageNode::AntialiasingRight);
+        if (d->regions[7].node == 0 && d->regions[6].node == 0)
+            antialiasing |= QSGImageNode::AntialiasingLeft;
+        if (d->regions[5].node == 0 && d->regions[2].node == 0)
+            antialiasing |= QSGImageNode::AntialiasingTop;
+        d->regions[8].node->setAntialiasing(antialiasing);
+
+        QRectF rect(innerTargetRect.right(),
+                    innerTargetRect.bottom(),
+                    width() - innerTargetRect.width() - innerTargetRect.left(),
+                    height() - innerTargetRect.height() - innerTargetRect.top());
+        d->regions[8].node->setTargetRect(rect);
+        d->regions[8].node->setInnerTargetRect(rect);
+        d->regions[8].targetRect = rect;
+    }
+
+    for (int i=0; i<9; ++i) {
+        if (d->regions[i].node != 0) {
+            if (updatePixmap) {
+                QQuickTextureFactory *textureFactory = QSGContext::createTextureFactoryFromImage(d->regions[i].image);
+                if (textureFactory == 0)
+                    textureFactory = new QQuickDefaultTextureFactory(d->regions[i].image);
+                d->regions[i].textureFactory.reset(textureFactory);
+                d->regions[i].node->setTexture(d->sceneGraphRenderContext()->textureForFactory(d->regions[i].textureFactory.data(),
+                                                                                               window()));
+            }
+
+            d->regions[i].node->setInnerSourceRect(QRectF(0, 0, 1, 1));
+            d->regions[i].node->setMipmapFiltering(QSGTexture::None);
+            d->regions[i].node->setFiltering(d->smooth ? QSGTexture::Linear : QSGTexture::Nearest);
+            d->regions[i].node->setMirror(d->mirror);
+
+
+            qreal hTiles = 1;
+            qreal vTiles = 1;
+
+            if (innerSourceRect.width() != 0) {
+                switch (d->horizontalTileMode) {
+                case QQuickBorderImage::Repeat:
+                    hTiles = d->regions[i].targetRect.width() / qreal(d->regions[i].image.width());
+                    break;
+                case QQuickBorderImage::Round:
+                    hTiles = qCeil(d->regions[i].targetRect.width() / qreal(d->regions[i].image.width()));
+                    break;
+                default:
+                    break;
+                }
+            }
+
+            if (innerSourceRect.height() != 0) {
+                switch (d->verticalTileMode) {
+                case QQuickBorderImage::Repeat:
+                    vTiles = d->regions[i].targetRect.height() / qreal(d->regions[i].image.height());
+                    break;
+                case QQuickBorderImage::Round:
+                    vTiles = qCeil(d->regions[i].targetRect.height() / qreal(d->regions[i].image.height()));
+                    break;
+                default:
+                    break;
+                }
+            }
+
+            if (vTiles > 1 || hTiles > 1) {
+                d->regions[i].node->setHorizontalWrapMode(QSGTexture::Repeat);
+                d->regions[i].node->setVerticalWrapMode(QSGTexture::Repeat);
+            } else {
+                d->regions[i].node->setHorizontalWrapMode(QSGTexture::ClampToEdge);
+                d->regions[i].node->setVerticalWrapMode(QSGTexture::ClampToEdge);
+            }
+
+            d->regions[i].node->setSubSourceRect(QRectF(0, 0, hTiles, vTiles));
+            d->regions[i].node->update();
+        }
     }
-    node->setAntialiasing(d->antialiasing);
-    node->update();
 
-    return node;
+    return oldNode;
 }
 
 void QQuickBorderImage::pixmapChange()
index 6a2469a..1d08981 100644 (file)
@@ -87,6 +87,8 @@ private Q_SLOTS:
     void sciRequestFinished();
 
 private:
+    static QImage shallowCopy(const QImage &image, const QRect &rect);
+
     Q_DISABLE_COPY(QQuickBorderImage)
     Q_DECLARE_PRIVATE(QQuickBorderImage)
 };
index d5b959f..bda2867 100644 (file)
@@ -89,6 +89,16 @@ public:
     QQuickBorderImage::TileMode verticalTileMode;
     int redirectCount;
 
+    struct BorderImageRegion
+    {
+        BorderImageRegion() : node(0), textureFactory(0) {}
+        QImage image;
+        QSGImageNode *node;
+        QScopedPointer<QQuickTextureFactory> textureFactory;
+        QRectF targetRect;
+    };
+    BorderImageRegion regions[9];
+
     bool pixmapChanged : 1;
 };
 
index c96d0a9..71033bd 100644 (file)
@@ -126,6 +126,17 @@ public:
 class Q_QUICK_PRIVATE_EXPORT QSGImageNode : public QSGVisitableNode
 {
 public:
+    enum AntialiasingFlag
+    {
+        AntialiasingNone = 0,
+        AntialiasingLeft = 1,
+        AntialiasingRight = 2,
+        AntialiasingTop = 4,
+        AntialiasingBottom = 8,
+        AntialiasingAll = AntialiasingLeft | AntialiasingRight | AntialiasingBottom | AntialiasingTop
+    };
+    Q_DECLARE_FLAGS(AntialiasingFlags, AntialiasingFlag)
+
     virtual void setTargetRect(const QRectF &rect) = 0;
     virtual void setInnerTargetRect(const QRectF &rect) = 0;
     virtual void setInnerSourceRect(const QRectF &rect) = 0;
@@ -140,6 +151,7 @@ public:
     virtual void setFiltering(QSGTexture::Filtering filtering) = 0;
     virtual void setHorizontalWrapMode(QSGTexture::WrapMode wrapMode) = 0;
     virtual void setVerticalWrapMode(QSGTexture::WrapMode wrapMode) = 0;
+    virtual void setAntialiasing(AntialiasingFlags flags) { Q_UNUSED(flags); }
 
     virtual void update() = 0;
 
index 7f85c31..69df506 100644 (file)
@@ -140,10 +140,11 @@ void SmoothTextureMaterialShader::initialize()
 QSGDefaultImageNode::QSGDefaultImageNode()
     : m_innerSourceRect(0, 0, 1, 1)
     , m_subSourceRect(0, 0, 1, 1)
-    , m_antialiasing(false)
     , m_mirror(false)
     , m_dirtyGeometry(false)
     , m_geometry(QSGGeometry::defaultAttributes_TexturedPoint2D(), 4)
+    , m_antialiasing(AntialiasingNone)
+
 {
     setMaterial(&m_materialO);
     setOpaqueMaterial(&m_material);
@@ -249,10 +250,20 @@ void QSGDefaultImageNode::setTexture(QSGTexture *texture)
 
 void QSGDefaultImageNode::setAntialiasing(bool antialiasing)
 {
-    if (antialiasing == m_antialiasing)
+    AntialiasingFlags antialiasingFlags = antialiasing
+                                          ? AntialiasingAll
+                                          : AntialiasingNone;
+
+    setAntialiasing(antialiasingFlags);
+}
+
+void QSGDefaultImageNode::setAntialiasing(AntialiasingFlags antialiasingFlags)
+{
+    if (antialiasingFlags == m_antialiasing)
         return;
-    m_antialiasing = antialiasing;
-    if (m_antialiasing) {
+
+    m_antialiasing = antialiasingFlags;
+    if (m_antialiasing != AntialiasingNone) {
         setMaterial(&m_smoothMaterial);
         setOpaqueMaterial(0);
         setGeometry(new QSGGeometry(smoothAttributeSet(), 0));
@@ -364,11 +375,14 @@ void QSGDefaultImageNode::updateGeometry()
         }
 
         // An image can be rendered as a single quad if:
+        // - There is antialiasing on all or no edges
         // - There are no margins, and either:
         //   - the image isn't repeated
         //   - the source rectangle fills the entire texture so that texture wrapping can be used,
         //     and NPOT is supported
-        if (!hasMargins && (!hasTiles || (fullTexture && wrapSupported))) {
+        if (!hasMargins
+                && (m_antialiasing == AntialiasingAll || m_antialiasing == AntialiasingNone)
+                && (!hasTiles || (fullTexture && wrapSupported))) {
             QRectF sr;
             if (!fullTexture) {
                 sr = QRectF(innerSourceRect.x() + (m_subSourceRect.left() - floorLeft) * innerSourceRect.width(),
@@ -547,10 +561,35 @@ void QSGDefaultImageNode::updateGeometry()
                     topDv = bottomDv *= 0.5f;
                 }
 
+                if (!m_antialiasing.testFlag(AntialiasingTop)) {
+                    topDy = 0.0f;
+                    topDv = 0.0f;
+                }
+
+                if (!m_antialiasing.testFlag(AntialiasingBottom)) {
+                    bottomDy = 0.0f;
+                    bottomDv = 0.0f;
+                }
+
+                if (!m_antialiasing.testFlag(AntialiasingLeft)) {
+                    leftDx = 0.0f;
+                    leftDu = 0.0f;
+                }
+
+                if (!m_antialiasing.testFlag(AntialiasingRight)) {
+                    rightDx = 0.0f;
+                    rightDu = 0.0f;
+                }
+
                 // This delta is how much the fuzziness can reach out from the image.
                 float delta = float(qAbs(m_targetRect.width()) < qAbs(m_targetRect.height())
                                     ? m_targetRect.width() : m_targetRect.height()) * 0.5f;
 
+                float deltaTop = m_antialiasing.testFlag(AntialiasingTop) ? delta : 0.0f;
+                float deltaBottom = m_antialiasing.testFlag(AntialiasingBottom) ? delta : 0.0f;
+                float deltaLeft = m_antialiasing.testFlag(AntialiasingLeft) ? delta : 0.0f;
+                float deltaRight = m_antialiasing.testFlag(AntialiasingRight) ? delta : 0.0f;
+
                 quint16 index = 0;
                 ys = yData.data();
                 for (int j = 0; j < vCells; ++j, ys += 2) {
@@ -600,28 +639,28 @@ void QSGDefaultImageNode::updateGeometry()
                         if (isTop) {
                             vertices[topLeft].dy = vertices[topRight].dy = topDy;
                             vertices[topLeft].dv = vertices[topRight].dv = topDv;
-                            vertices[topLeft + 1].dy = vertices[topRight + 1].dy = -delta;
+                            vertices[topLeft + 1].dy = vertices[topRight + 1].dy = -deltaTop;
                             appendQuad(&indices, topLeft + 1, topRight + 1, topLeft, topRight);
                         }
 
                         if (isBottom) {
                             vertices[bottomLeft].dy = vertices[bottomRight].dy = -bottomDy;
                             vertices[bottomLeft].dv = vertices[bottomRight].dv = -bottomDv;
-                            vertices[bottomLeft + 1].dy = vertices[bottomRight + 1].dy = delta;
+                            vertices[bottomLeft + 1].dy = vertices[bottomRight + 1].dy = deltaBottom;
                             appendQuad(&indices, bottomLeft, bottomRight, bottomLeft + 1, bottomRight + 1);
                         }
 
                         if (isLeft) {
                             vertices[topLeft].dx = vertices[bottomLeft].dx = leftDx;
                             vertices[topLeft].du = vertices[bottomLeft].du = leftDu;
-                            vertices[topLeft + 1].dx = vertices[bottomLeft + 1].dx = -delta;
+                            vertices[topLeft + 1].dx = vertices[bottomLeft + 1].dx = -deltaLeft;
                             appendQuad(&indices, topLeft + 1, topLeft, bottomLeft + 1, bottomLeft);
                         }
 
                         if (isRight) {
                             vertices[topRight].dx = vertices[bottomRight].dx = -rightDx;
                             vertices[topRight].du = vertices[bottomRight].du = -rightDu;
-                            vertices[topRight + 1].dx = vertices[bottomRight + 1].dx = delta;
+                            vertices[topRight + 1].dx = vertices[bottomRight + 1].dx = deltaRight;
                             appendQuad(&indices, topRight, topRight + 1, bottomRight, bottomRight + 1);
                         }
                     }
index 26b0872..558e4d8 100644 (file)
@@ -62,6 +62,7 @@ public:
     virtual void setSubSourceRect(const QRectF &rect);
     virtual void setTexture(QSGTexture *t);
     virtual void setAntialiasing(bool antialiasing);
+    virtual void setAntialiasing(AntialiasingFlags antialiasing);
     virtual void setMirror(bool mirror);
     virtual void update();
 
@@ -84,11 +85,11 @@ private:
     QSGTextureMaterial m_materialO;
     QSGSmoothTextureMaterial m_smoothMaterial;
 
-    uint m_antialiasing : 1;
     uint m_mirror : 1;
     uint m_dirtyGeometry : 1;
 
     QSGGeometry m_geometry;
+    AntialiasingFlags m_antialiasing;
 };
 
 QT_END_NAMESPACE
diff --git a/tests/manual/scenegraph_lancelot/data/borderimages/borderimage_rotated_smoothed.qml b/tests/manual/scenegraph_lancelot/data/borderimages/borderimage_rotated_smoothed.qml
new file mode 100644 (file)
index 0000000..804567c
--- /dev/null
@@ -0,0 +1,34 @@
+import QtQuick 2.2
+
+Rectangle {
+    width: 320
+    height: 480
+
+    color: "red"
+
+    Item {
+        x: 80
+        y: 80
+
+        BorderImage {
+            source: "../shared/uniquepixels.png"
+
+            width: 8
+            height: 8
+
+            antialiasing: true
+
+            horizontalTileMode: BorderImage.Repeat
+            verticalTileMode: BorderImage.Repeat
+
+            smooth: true
+            rotation: 10
+            scale: 10
+
+            border.bottom: 1
+            border.left: 1
+            border.right: 1
+            border.top: 1
+        }
+    }
+}
diff --git a/tests/manual/scenegraph_lancelot/data/borderimages/borderimage_rotated_unsmoothed.qml b/tests/manual/scenegraph_lancelot/data/borderimages/borderimage_rotated_unsmoothed.qml
new file mode 100644 (file)
index 0000000..b10554a
--- /dev/null
@@ -0,0 +1,34 @@
+import QtQuick 2.2
+
+Rectangle {
+    width: 320
+    height: 480
+
+    color: "red"
+
+    Item {
+        x: 80
+        y: 80
+
+        BorderImage {
+            source: "../shared/uniquepixels.png"
+
+            width: 8
+            height: 8
+
+            antialiasing: true
+
+            horizontalTileMode: BorderImage.Repeat
+            verticalTileMode: BorderImage.Repeat
+
+            smooth: false
+            rotation: 10
+            scale: 10
+
+            border.bottom: 1
+            border.left: 1
+            border.right: 1
+            border.top: 1
+        }
+    }
+}
diff --git a/tests/manual/scenegraph_lancelot/data/borderimages/borderimage_rotated_unsmoothed_border_overlap.qml b/tests/manual/scenegraph_lancelot/data/borderimages/borderimage_rotated_unsmoothed_border_overlap.qml
new file mode 100644 (file)
index 0000000..73cc53e
--- /dev/null
@@ -0,0 +1,31 @@
+import QtQuick 2.2
+
+Rectangle {
+    width: 320
+    height: 480
+
+    color: "red"
+
+    Item {
+        x: 80
+        y: 80
+
+        BorderImage {
+            source: "../shared/uniquepixels.png"
+
+            antialiasing: true
+
+            horizontalTileMode: BorderImage.Repeat
+            verticalTileMode: BorderImage.Repeat
+
+            smooth: false
+            rotation: 10
+            scale: 10
+
+            border.bottom: 0
+            border.left: 3
+            border.right: 3
+            border.top: 0
+        }
+    }
+}
diff --git a/tests/manual/scenegraph_lancelot/data/borderimages/borderimage_rotated_unsmoothed_negative_borders.qml b/tests/manual/scenegraph_lancelot/data/borderimages/borderimage_rotated_unsmoothed_negative_borders.qml
new file mode 100644 (file)
index 0000000..8356f02
--- /dev/null
@@ -0,0 +1,34 @@
+import QtQuick 2.2
+
+Rectangle {
+    width: 320
+    height: 480
+
+    color: "red"
+
+    Item {
+        x: 80
+        y: 80
+
+        BorderImage {
+            source: "../shared/uniquepixels.png"
+
+            width: 8
+            height: 8
+
+            antialiasing: true
+
+            horizontalTileMode: BorderImage.Repeat
+            verticalTileMode: BorderImage.Repeat
+
+            smooth: false
+            rotation: 10
+            scale: 10
+
+            border.bottom: -1
+            border.left: -1
+            border.right: -1
+            border.top: -1
+        }
+    }
+}
diff --git a/tests/manual/scenegraph_lancelot/data/borderimages/borderimage_rotated_unsmoothed_no_borders.qml b/tests/manual/scenegraph_lancelot/data/borderimages/borderimage_rotated_unsmoothed_no_borders.qml
new file mode 100644 (file)
index 0000000..9213589
--- /dev/null
@@ -0,0 +1,34 @@
+import QtQuick 2.2
+
+Rectangle {
+    width: 320
+    height: 480
+
+    color: "red"
+
+    Item {
+        x: 80
+        y: 80
+
+        BorderImage {
+            source: "../shared/uniquepixels.png"
+
+            width: 8
+            height: 8
+
+            antialiasing: true
+
+            horizontalTileMode: BorderImage.Repeat
+            verticalTileMode: BorderImage.Repeat
+
+            smooth: false
+            rotation: 10
+            scale: 10
+
+            border.bottom: 0
+            border.left: 0
+            border.right: 0
+            border.top: 0
+        }
+    }
+}
diff --git a/tests/manual/scenegraph_lancelot/data/borderimages/borderimage_rotated_unsmoothed_no_bottom.qml b/tests/manual/scenegraph_lancelot/data/borderimages/borderimage_rotated_unsmoothed_no_bottom.qml
new file mode 100644 (file)
index 0000000..615abde
--- /dev/null
@@ -0,0 +1,34 @@
+import QtQuick 2.2
+
+Rectangle {
+    width: 320
+    height: 480
+
+    color: "red"
+
+    Item {
+        x: 80
+        y: 80
+
+        BorderImage {
+            source: "../shared/uniquepixels.png"
+
+            width: 8
+            height: 8
+
+            antialiasing: true
+
+            horizontalTileMode: BorderImage.Repeat
+            verticalTileMode: BorderImage.Repeat
+
+            smooth: false
+            rotation: 10
+            scale: 10
+
+            border.bottom: 0
+            border.left: 1
+            border.right: 1
+            border.top: 1
+        }
+    }
+}
diff --git a/tests/manual/scenegraph_lancelot/data/borderimages/borderimage_rotated_unsmoothed_no_center.qml b/tests/manual/scenegraph_lancelot/data/borderimages/borderimage_rotated_unsmoothed_no_center.qml
new file mode 100644 (file)
index 0000000..58ed4d4
--- /dev/null
@@ -0,0 +1,34 @@
+import QtQuick 2.2
+
+Rectangle {
+    width: 320
+    height: 480
+
+    color: "red"
+
+    Item {
+        x: 80
+        y: 80
+
+        BorderImage {
+            source: "../shared/uniquepixels.png"
+
+            width: 8
+            height: 8
+
+            antialiasing: true
+
+            horizontalTileMode: BorderImage.Repeat
+            verticalTileMode: BorderImage.Repeat
+
+            smooth: false
+            rotation: 10
+            scale: 10
+
+            border.bottom: 4
+            border.left: 4
+            border.right: 4
+            border.top: 4
+        }
+    }
+}
diff --git a/tests/manual/scenegraph_lancelot/data/borderimages/borderimage_rotated_unsmoothed_no_left.qml b/tests/manual/scenegraph_lancelot/data/borderimages/borderimage_rotated_unsmoothed_no_left.qml
new file mode 100644 (file)
index 0000000..7e0045b
--- /dev/null
@@ -0,0 +1,34 @@
+import QtQuick 2.2
+
+Rectangle {
+    width: 320
+    height: 480
+
+    color: "red"
+
+    Item {
+        x: 80
+        y: 80
+
+        BorderImage {
+            source: "../shared/uniquepixels.png"
+
+            width: 8
+            height: 8
+
+            antialiasing: true
+
+            horizontalTileMode: BorderImage.Repeat
+            verticalTileMode: BorderImage.Repeat
+
+            smooth: false
+            rotation: 10
+            scale: 10
+
+            border.bottom: 1
+            border.left: 0
+            border.right: 1
+            border.top: 1
+        }
+    }
+}
diff --git a/tests/manual/scenegraph_lancelot/data/borderimages/borderimage_rotated_unsmoothed_no_right.qml b/tests/manual/scenegraph_lancelot/data/borderimages/borderimage_rotated_unsmoothed_no_right.qml
new file mode 100644 (file)
index 0000000..04c2d02
--- /dev/null
@@ -0,0 +1,34 @@
+import QtQuick 2.2
+
+Rectangle {
+    width: 320
+    height: 480
+
+    color: "red"
+
+    Item {
+        x: 80
+        y: 80
+
+        BorderImage {
+            source: "../shared/uniquepixels.png"
+
+            width: 8
+            height: 8
+
+            antialiasing: true
+
+            horizontalTileMode: BorderImage.Repeat
+            verticalTileMode: BorderImage.Repeat
+
+            smooth: false
+            rotation: 10
+            scale: 10
+
+            border.bottom: 1
+            border.left: 1
+            border.right: 0
+            border.top: 1
+        }
+    }
+}
diff --git a/tests/manual/scenegraph_lancelot/data/borderimages/borderimage_rotated_unsmoothed_no_right_left.qml b/tests/manual/scenegraph_lancelot/data/borderimages/borderimage_rotated_unsmoothed_no_right_left.qml
new file mode 100644 (file)
index 0000000..5210bab
--- /dev/null
@@ -0,0 +1,34 @@
+import QtQuick 2.2
+
+Rectangle {
+    width: 320
+    height: 480
+
+    color: "red"
+
+    Item {
+        x: 80
+        y: 80
+
+        BorderImage {
+            source: "../shared/uniquepixels.png"
+
+            width: 8
+            height: 8
+
+            antialiasing: true
+
+            horizontalTileMode: BorderImage.Repeat
+            verticalTileMode: BorderImage.Repeat
+
+            smooth: false
+            rotation: 10
+            scale: 10
+
+            border.bottom: 1
+            border.left: 0
+            border.right: 0
+            border.top: 1
+        }
+    }
+}
diff --git a/tests/manual/scenegraph_lancelot/data/borderimages/borderimage_rotated_unsmoothed_no_top.qml b/tests/manual/scenegraph_lancelot/data/borderimages/borderimage_rotated_unsmoothed_no_top.qml
new file mode 100644 (file)
index 0000000..2e89496
--- /dev/null
@@ -0,0 +1,34 @@
+import QtQuick 2.2
+
+Rectangle {
+    width: 320
+    height: 480
+
+    color: "red"
+
+    Item {
+        x: 80
+        y: 80
+
+        BorderImage {
+            source: "../shared/uniquepixels.png"
+
+            width: 8
+            height: 8
+
+            antialiasing: true
+
+            horizontalTileMode: BorderImage.Repeat
+            verticalTileMode: BorderImage.Repeat
+
+            smooth: false
+            rotation: 10
+            scale: 10
+
+            border.bottom: 1
+            border.left: 1
+            border.right: 1
+            border.top: 0
+        }
+    }
+}
diff --git a/tests/manual/scenegraph_lancelot/data/borderimages/borderimage_rotated_unsmoothed_no_top_bottom.qml b/tests/manual/scenegraph_lancelot/data/borderimages/borderimage_rotated_unsmoothed_no_top_bottom.qml
new file mode 100644 (file)
index 0000000..4388601
--- /dev/null
@@ -0,0 +1,34 @@
+import QtQuick 2.2
+
+Rectangle {
+    width: 320
+    height: 480
+
+    color: "red"
+
+    Item {
+        x: 80
+        y: 80
+
+        BorderImage {
+            source: "../shared/uniquepixels.png"
+
+            width: 8
+            height: 8
+
+            antialiasing: true
+
+            horizontalTileMode: BorderImage.Repeat
+            verticalTileMode: BorderImage.Repeat
+
+            smooth: false
+            rotation: 10
+            scale: 10
+
+            border.bottom: 0
+            border.left: 1
+            border.right: 1
+            border.top: 0
+        }
+    }
+}
diff --git a/tests/manual/scenegraph_lancelot/data/shared/uniquepixels.png b/tests/manual/scenegraph_lancelot/data/shared/uniquepixels.png
new file mode 100644 (file)
index 0000000..af240b0
Binary files /dev/null and b/tests/manual/scenegraph_lancelot/data/shared/uniquepixels.png differ