From a9238292145e05d36be7c35bdd50829c6400a3de Mon Sep 17 00:00:00 2001 From: Eskil Abrahamsen Blomfeldt Date: Tue, 26 Aug 2014 10:52:22 +0200 Subject: Fix pixel bleed in BorderImage 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 --- src/quick/items/qquickborderimage.cpp | 381 +++++++++++++++++---- src/quick/items/qquickborderimage_p.h | 2 + src/quick/items/qquickborderimage_p_p.h | 10 + src/quick/scenegraph/qsgadaptationlayer_p.h | 12 + src/quick/scenegraph/qsgdefaultimagenode.cpp | 57 ++- src/quick/scenegraph/qsgdefaultimagenode_p.h | 3 +- .../borderimages/borderimage_rotated_smoothed.qml | 34 ++ .../borderimage_rotated_unsmoothed.qml | 34 ++ ...rderimage_rotated_unsmoothed_border_overlap.qml | 31 ++ ...erimage_rotated_unsmoothed_negative_borders.qml | 34 ++ .../borderimage_rotated_unsmoothed_no_borders.qml | 34 ++ .../borderimage_rotated_unsmoothed_no_bottom.qml | 34 ++ .../borderimage_rotated_unsmoothed_no_center.qml | 34 ++ .../borderimage_rotated_unsmoothed_no_left.qml | 34 ++ .../borderimage_rotated_unsmoothed_no_right.qml | 34 ++ ...orderimage_rotated_unsmoothed_no_right_left.qml | 34 ++ .../borderimage_rotated_unsmoothed_no_top.qml | 34 ++ ...orderimage_rotated_unsmoothed_no_top_bottom.qml | 34 ++ .../data/shared/uniquepixels.png | Bin 0 -> 210 bytes 19 files changed, 798 insertions(+), 72 deletions(-) create mode 100644 tests/manual/scenegraph_lancelot/data/borderimages/borderimage_rotated_smoothed.qml create mode 100644 tests/manual/scenegraph_lancelot/data/borderimages/borderimage_rotated_unsmoothed.qml create mode 100644 tests/manual/scenegraph_lancelot/data/borderimages/borderimage_rotated_unsmoothed_border_overlap.qml create mode 100644 tests/manual/scenegraph_lancelot/data/borderimages/borderimage_rotated_unsmoothed_negative_borders.qml create mode 100644 tests/manual/scenegraph_lancelot/data/borderimages/borderimage_rotated_unsmoothed_no_borders.qml create mode 100644 tests/manual/scenegraph_lancelot/data/borderimages/borderimage_rotated_unsmoothed_no_bottom.qml create mode 100644 tests/manual/scenegraph_lancelot/data/borderimages/borderimage_rotated_unsmoothed_no_center.qml create mode 100644 tests/manual/scenegraph_lancelot/data/borderimages/borderimage_rotated_unsmoothed_no_left.qml create mode 100644 tests/manual/scenegraph_lancelot/data/borderimages/borderimage_rotated_unsmoothed_no_right.qml create mode 100644 tests/manual/scenegraph_lancelot/data/borderimages/borderimage_rotated_unsmoothed_no_right_left.qml create mode 100644 tests/manual/scenegraph_lancelot/data/borderimages/borderimage_rotated_unsmoothed_no_top.qml create mode 100644 tests/manual/scenegraph_lancelot/data/borderimages/borderimage_rotated_unsmoothed_no_top_bottom.qml create mode 100644 tests/manual/scenegraph_lancelot/data/shared/uniquepixels.png diff --git a/src/quick/items/qquickborderimage.cpp b/src/quick/items/qquickborderimage.cpp index 38bbc66896..50a0a76267 100644 --- a/src/quick/items/qquickborderimage.cpp +++ b/src/quick/items/qquickborderimage.cpp @@ -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(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(0, d->pix.width() - border->right() - border->left()) / d->pix.width(), - qMax(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(0, borderRight - borderLeft) / d->pix.width(), + qMax(0, borderBottom - borderTop) / d->pix.height()); + innerTargetRect = QRectF(borderLeft, + borderTop, qMax(0, width() - border->right() - border->left()), qMax(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() diff --git a/src/quick/items/qquickborderimage_p.h b/src/quick/items/qquickborderimage_p.h index 6a2469adc2..1d0898115d 100644 --- a/src/quick/items/qquickborderimage_p.h +++ b/src/quick/items/qquickborderimage_p.h @@ -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) }; diff --git a/src/quick/items/qquickborderimage_p_p.h b/src/quick/items/qquickborderimage_p_p.h index d5b959fd9f..bda2867696 100644 --- a/src/quick/items/qquickborderimage_p_p.h +++ b/src/quick/items/qquickborderimage_p_p.h @@ -89,6 +89,16 @@ public: QQuickBorderImage::TileMode verticalTileMode; int redirectCount; + struct BorderImageRegion + { + BorderImageRegion() : node(0), textureFactory(0) {} + QImage image; + QSGImageNode *node; + QScopedPointer textureFactory; + QRectF targetRect; + }; + BorderImageRegion regions[9]; + bool pixmapChanged : 1; }; diff --git a/src/quick/scenegraph/qsgadaptationlayer_p.h b/src/quick/scenegraph/qsgadaptationlayer_p.h index c96d0a99e3..71033bd6d0 100644 --- a/src/quick/scenegraph/qsgadaptationlayer_p.h +++ b/src/quick/scenegraph/qsgadaptationlayer_p.h @@ -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; diff --git a/src/quick/scenegraph/qsgdefaultimagenode.cpp b/src/quick/scenegraph/qsgdefaultimagenode.cpp index 7f85c31ccb..69df506d2a 100644 --- a/src/quick/scenegraph/qsgdefaultimagenode.cpp +++ b/src/quick/scenegraph/qsgdefaultimagenode.cpp @@ -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); } } diff --git a/src/quick/scenegraph/qsgdefaultimagenode_p.h b/src/quick/scenegraph/qsgdefaultimagenode_p.h index 26b087284b..558e4d8ba4 100644 --- a/src/quick/scenegraph/qsgdefaultimagenode_p.h +++ b/src/quick/scenegraph/qsgdefaultimagenode_p.h @@ -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 index 0000000000..804567cf19 --- /dev/null +++ b/tests/manual/scenegraph_lancelot/data/borderimages/borderimage_rotated_smoothed.qml @@ -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 index 0000000000..b10554ad66 --- /dev/null +++ b/tests/manual/scenegraph_lancelot/data/borderimages/borderimage_rotated_unsmoothed.qml @@ -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 index 0000000000..73cc53ed2b --- /dev/null +++ b/tests/manual/scenegraph_lancelot/data/borderimages/borderimage_rotated_unsmoothed_border_overlap.qml @@ -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 index 0000000000..8356f02614 --- /dev/null +++ b/tests/manual/scenegraph_lancelot/data/borderimages/borderimage_rotated_unsmoothed_negative_borders.qml @@ -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 index 0000000000..9213589648 --- /dev/null +++ b/tests/manual/scenegraph_lancelot/data/borderimages/borderimage_rotated_unsmoothed_no_borders.qml @@ -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 index 0000000000..615abdee20 --- /dev/null +++ b/tests/manual/scenegraph_lancelot/data/borderimages/borderimage_rotated_unsmoothed_no_bottom.qml @@ -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 index 0000000000..58ed4d44dc --- /dev/null +++ b/tests/manual/scenegraph_lancelot/data/borderimages/borderimage_rotated_unsmoothed_no_center.qml @@ -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 index 0000000000..7e0045bf24 --- /dev/null +++ b/tests/manual/scenegraph_lancelot/data/borderimages/borderimage_rotated_unsmoothed_no_left.qml @@ -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 index 0000000000..04c2d021f0 --- /dev/null +++ b/tests/manual/scenegraph_lancelot/data/borderimages/borderimage_rotated_unsmoothed_no_right.qml @@ -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 index 0000000000..5210bab321 --- /dev/null +++ b/tests/manual/scenegraph_lancelot/data/borderimages/borderimage_rotated_unsmoothed_no_right_left.qml @@ -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 index 0000000000..2e89496c92 --- /dev/null +++ b/tests/manual/scenegraph_lancelot/data/borderimages/borderimage_rotated_unsmoothed_no_top.qml @@ -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 index 0000000000..4388601f13 --- /dev/null +++ b/tests/manual/scenegraph_lancelot/data/borderimages/borderimage_rotated_unsmoothed_no_top_bottom.qml @@ -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 index 0000000000..af240b09c3 Binary files /dev/null and b/tests/manual/scenegraph_lancelot/data/shared/uniquepixels.png differ -- cgit v1.2.3