diff options
author | Michael Brasser <mbrasser@ford.com> | 2016-03-21 21:31:03 -0500 |
---|---|---|
committer | Michael Brasser <michael.brasser@live.com> | 2016-05-06 18:25:43 +0000 |
commit | 69b7ba225007d68e113ee425ad58ce7e186a92c2 (patch) | |
tree | 72d4723b85085d901c785ae933294c83c6119ccf /src/quick/items | |
parent | 8997e00269d8748a366d84d234dba555fc9548ce (diff) |
Introduce BorderImageMesh.
BorderImageMesh provides an optimized way to use BorderImage-type
images with ShaderEffect.
[ChangeLog][QtQuick][ShaderEffect] Add new mesh type BorderImageMesh.
BorderImageMesh provides an optimized way to use BorderImage-type
images with ShaderEffect.
Change-Id: I73bdc8318a0facba6edc2634b5f4614996b39c1b
Reviewed-by: Shawn Rutledge <shawn.rutledge@theqtcompany.com>
Reviewed-by: Laszlo Agocs <laszlo.agocs@theqtcompany.com>
Diffstat (limited to 'src/quick/items')
-rw-r--r-- | src/quick/items/qquickborderimage.cpp | 88 | ||||
-rw-r--r-- | src/quick/items/qquickborderimage_p_p.h | 11 | ||||
-rw-r--r-- | src/quick/items/qquickitemsmodule.cpp | 1 | ||||
-rw-r--r-- | src/quick/items/qquickshadereffectmesh.cpp | 183 | ||||
-rw-r--r-- | src/quick/items/qquickshadereffectmesh_p.h | 39 |
5 files changed, 291 insertions, 31 deletions
diff --git a/src/quick/items/qquickborderimage.cpp b/src/quick/items/qquickborderimage.cpp index 5f8dd7b989..b3a35e6219 100644 --- a/src/quick/items/qquickborderimage.cpp +++ b/src/quick/items/qquickborderimage.cpp @@ -574,6 +574,52 @@ void QQuickBorderImage::doUpdate() update(); } +void QQuickBorderImagePrivate::calculateRects(const QQuickScaleGrid *border, + const QSize &sourceSize, + const QSizeF &targetSize, + int horizontalTileMode, + int verticalTileMode, + qreal devicePixelRatio, + QRectF *targetRect, + QRectF *innerTargetRect, + QRectF *innerSourceRect, + QRectF *subSourceRect) +{ + *innerSourceRect = QRectF(0, 0, 1, 1); + *targetRect = QRectF(0, 0, targetSize.width(), targetSize.height()); + *innerTargetRect = *targetRect; + + if (border) { + *innerSourceRect = QRectF(border->left() * devicePixelRatio / qreal(sourceSize.width()), + border->top() * devicePixelRatio / qreal(sourceSize.height()), + qMax<qreal>(0, sourceSize.width() - (border->right() + border->left()) * devicePixelRatio) / sourceSize.width(), + qMax<qreal>(0, sourceSize.height() - (border->bottom() + border->top()) * devicePixelRatio) / sourceSize.height()); + *innerTargetRect = QRectF(border->left(), + border->top(), + qMax<qreal>(0, targetSize.width() - (border->right() + border->left())), + qMax<qreal>(0, targetSize.height() - (border->bottom() + border->top()))); + } + + qreal hTiles = 1; + qreal vTiles = 1; + const QSizeF innerTargetSize = innerTargetRect->size() * devicePixelRatio; + if (innerSourceRect->width() != 0 + && horizontalTileMode != QQuickBorderImage::Stretch) { + hTiles = innerTargetSize.width() / qreal(innerSourceRect->width() * sourceSize.width()); + if (horizontalTileMode == QQuickBorderImage::Round) + hTiles = qCeil(hTiles); + } + if (innerSourceRect->height() != 0 + && verticalTileMode != QQuickBorderImage::Stretch) { + vTiles = innerTargetSize.height() / qreal(innerSourceRect->height() * sourceSize.height()); + if (verticalTileMode == QQuickBorderImage::Round) + vTiles = qCeil(vTiles); + } + + *subSourceRect = QRectF(0, 0, hTiles, vTiles); +} + + QSGNode *QQuickBorderImage::updatePaintNode(QSGNode *oldNode, UpdatePaintNodeData *) { Q_D(QQuickBorderImage); @@ -598,45 +644,25 @@ QSGNode *QQuickBorderImage::updatePaintNode(QSGNode *oldNode, UpdatePaintNodeDat 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; - if (d->border) { - const QQuickScaleGrid *border = d->getScaleGrid(); - innerSourceRect = QRectF(border->left() * d->devicePixelRatio / qreal(d->pix.width()), - border->top() * d->devicePixelRatio / qreal(d->pix.height()), - qMax<qreal>(0, d->pix.width() - (border->right() + border->left()) * d->devicePixelRatio) / d->pix.width(), - qMax<qreal>(0, d->pix.height() - (border->bottom() + border->top()) * d->devicePixelRatio) / d->pix.height()); - innerTargetRect = QRectF(border->left(), - border->top(), - qMax<qreal>(0, width() - (border->right() + border->left())), - qMax<qreal>(0, height() - (border->bottom() + border->top()))); - } - qreal hTiles = 1; - qreal vTiles = 1; - const QSizeF innerTargetSize = innerTargetRect.size() * d->devicePixelRatio; - if (innerSourceRect.width() != 0 - && d->horizontalTileMode != QQuickBorderImage::Stretch) { - hTiles = innerTargetSize.width() / qreal(innerSourceRect.width() * d->pix.width()); - if (d->horizontalTileMode == QQuickBorderImage::Round) - hTiles = qCeil(hTiles); - } - if (innerSourceRect.height() != 0 - && d->verticalTileMode != QQuickBorderImage::Stretch) { - vTiles = innerTargetSize.height() / qreal(innerSourceRect.height() * d->pix.height()); - if (d->verticalTileMode == QQuickBorderImage::Round) - vTiles = qCeil(vTiles); - } + QRectF targetRect; + QRectF innerTargetRect; + QRectF innerSourceRect; + QRectF subSourceRect; + d->calculateRects(d->border, + QSize(d->pix.width(), d->pix.height()), QSizeF(width(), height()), + d->horizontalTileMode, d->verticalTileMode, d->devicePixelRatio, + &targetRect, &innerTargetRect, + &innerSourceRect, &subSourceRect); node->setTargetRect(targetRect); node->setInnerSourceRect(innerSourceRect); node->setInnerTargetRect(innerTargetRect); - node->setSubSourceRect(QRectF(0, 0, hTiles, vTiles)); + node->setSubSourceRect(subSourceRect); 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)) { + if (innerSourceRect == QRectF(0, 0, 1, 1) && (subSourceRect.width() > 1 || subSourceRect.height() > 1)) { node->setHorizontalWrapMode(QSGTexture::Repeat); node->setVerticalWrapMode(QSGTexture::Repeat); } else { diff --git a/src/quick/items/qquickborderimage_p_p.h b/src/quick/items/qquickborderimage_p_p.h index 1dc530e34e..56fbbab049 100644 --- a/src/quick/items/qquickborderimage_p_p.h +++ b/src/quick/items/qquickborderimage_p_p.h @@ -91,6 +91,17 @@ public: return border; } + static void calculateRects(const QQuickScaleGrid *border, + const QSize &sourceSize, + const QSizeF &targetSize, + int horizontalTileMode, + int verticalTileMode, + qreal devicePixelRatio, + QRectF *targetRect, + QRectF *innerTargetRect, + QRectF *innerSourceRect, + QRectF *subSourceRect); + QQuickScaleGrid *border; QUrl sciurl; QQuickBorderImage::TileMode horizontalTileMode; diff --git a/src/quick/items/qquickitemsmodule.cpp b/src/quick/items/qquickitemsmodule.cpp index 23245e4a7b..6bcc3248ce 100644 --- a/src/quick/items/qquickitemsmodule.cpp +++ b/src/quick/items/qquickitemsmodule.cpp @@ -211,6 +211,7 @@ static void qt_quickitems_defineModule(const char *uri, int major, int minor) qmlRegisterType<QQuickShaderEffectSource>("QtQuick", 2, 0, "ShaderEffectSource"); qmlRegisterUncreatableType<QQuickShaderEffectMesh>("QtQuick", 2, 0, "ShaderEffectMesh", QQuickShaderEffectMesh::tr("Cannot create instance of abstract class ShaderEffectMesh.")); qmlRegisterType<QQuickGridMesh>("QtQuick", 2, 0, "GridMesh"); + qmlRegisterType<QQuickBorderImageMesh>("QtQuick", 2, 8, "BorderImageMesh"); qmlRegisterUncreatableType<QQuickPaintedItem>("QtQuick", 2, 0, "PaintedItem", QQuickPaintedItem::tr("Cannot create instance of abstract class PaintedItem")); diff --git a/src/quick/items/qquickshadereffectmesh.cpp b/src/quick/items/qquickshadereffectmesh.cpp index 0811025654..842befac37 100644 --- a/src/quick/items/qquickshadereffectmesh.cpp +++ b/src/quick/items/qquickshadereffectmesh.cpp @@ -40,6 +40,9 @@ #include "qquickshadereffectmesh_p.h" #include <QtQuick/qsggeometry.h> #include "qquickshadereffect_p.h" +#include "qquickscalegrid_p_p.h" +#include "qquickborderimage_p_p.h" +#include <QtQuick/private/qsgdefaultimagenode_p.h> QT_BEGIN_NAMESPACE @@ -217,4 +220,184 @@ QSize QQuickGridMesh::resolution() const return m_resolution; } +/*! + \qmltype BorderImageMesh + \instantiates QQuickBorderImageMesh + \inqmlmodule QtQuick + \since 5.8 + \ingroup qtquick-effects + \brief Defines a mesh with vertices arranged like those of a BorderImage. + + BorderImageMesh provides BorderImage-like capabilities to a ShaderEffect + without the need for a potentially costly ShaderEffectSource. + + The following are functionally equivalent: + \qml + BorderImage { + id: borderImage + border { + left: 10 + right: 10 + top: 10 + bottom: 10 + } + source: "myImage.png" + visible: false + } + ShaderEffectSource { + id: effectSource + sourceItem: borderImage + visible: false + } + ShaderEffect { + property var source: effectSource + ... + } + \endqml + + \qml + Image { + id: image + source: "myImage.png" + visible: false + } + ShaderEffect { + property var source: image + mesh: BorderImageMesh { + border { + left: 10 + right: 10 + top: 10 + bottom: 10 + } + size: image.sourceSize + } + ... + } + \endqml + + But the BorderImageMesh version can typically be better optimized. +*/ +QQuickBorderImageMesh::QQuickBorderImageMesh(QObject *parent) + : QQuickShaderEffectMesh(parent), m_border(new QQuickScaleGrid(this)), + m_horizontalTileMode(QQuickBorderImageMesh::Stretch), + m_verticalTileMode(QQuickBorderImageMesh::Stretch) +{ +} + +QSGGeometry *QQuickBorderImageMesh::updateGeometry(QSGGeometry *geometry, const QVector<QByteArray> &/*attributes*/, const QRectF &srcRect, const QRectF &rect) +{ + QRectF innerSourceRect; + QRectF targetRect; + QRectF innerTargetRect; + QRectF subSourceRect; + + QQuickBorderImagePrivate::calculateRects(m_border, m_size, rect.size(), m_horizontalTileMode, m_verticalTileMode, 1, &targetRect, &innerTargetRect, &innerSourceRect, &subSourceRect); + + QRectF sourceRect = srcRect; + QRectF modifiedInnerSourceRect(sourceRect.x() + innerSourceRect.x() * sourceRect.width(), + sourceRect.y() + innerSourceRect.y() * sourceRect.height(), + innerSourceRect.width() * sourceRect.width(), + innerSourceRect.height() * sourceRect.height()); + + geometry = QSGDefaultImageNode::updateGeometry(targetRect, innerTargetRect, sourceRect, modifiedInnerSourceRect, subSourceRect, geometry); + + return geometry; +} + +/*! + \qmlpropertygroup QtQuick::BorderImageMesh::border + \qmlproperty int QtQuick::BorderImageMesh::border.left + \qmlproperty int QtQuick::BorderImageMesh::border.right + \qmlproperty int QtQuick::BorderImageMesh::border.top + \qmlproperty int QtQuick::BorderImageMesh::border.bottom + + The 4 border lines (2 horizontal and 2 vertical) break the image into 9 sections, + as shown below: + + \image declarative-scalegrid.png + + Each border line (left, right, top, and bottom) specifies an offset in pixels + from the respective edge of the mesh. By default, each border line has + a value of 0. + + For example, the following definition sets the bottom line 10 pixels up from + the bottom of the mesh: + + \qml + BorderImageMesh { + border.bottom: 10 + // ... + } + \endqml +*/ +QQuickScaleGrid *QQuickBorderImageMesh::border() +{ + return m_border; +} + +/*! + \qmlproperty size QtQuick::BorderImageMesh::size + + The base size of the mesh. This generally corresponds to the \l {Image::}{sourceSize} + of the image being used by the ShaderEffect. +*/ +QSize QQuickBorderImageMesh::size() const +{ + return m_size; +} + +void QQuickBorderImageMesh::setSize(const QSize &size) +{ + if (size == m_size) + return; + m_size = size; + Q_EMIT sizeChanged(); + Q_EMIT geometryChanged(); +} + +/*! + \qmlproperty enumeration QtQuick::BorderImageMesh::horizontalTileMode + \qmlproperty enumeration QtQuick::BorderImageMesh::verticalTileMode + + This property describes how to repeat or stretch the middle parts of an image. + + \list + \li BorderImage.Stretch - Scales the image to fit to the available area. + \li BorderImage.Repeat - Tile the image until there is no more space. May crop the last image. + \li BorderImage.Round - Like Repeat, but scales the images down to ensure that the last image is not cropped. + \endlist + + The default tile mode for each property is BorderImage.Stretch. +*/ + +QQuickBorderImageMesh::TileMode QQuickBorderImageMesh::horizontalTileMode() const +{ + return m_horizontalTileMode; +} + +void QQuickBorderImageMesh::setHorizontalTileMode(TileMode t) +{ + if (t == m_horizontalTileMode) + return; + m_horizontalTileMode = t; + Q_EMIT horizontalTileModeChanged(); + Q_EMIT geometryChanged(); +} + +QQuickBorderImageMesh::TileMode QQuickBorderImageMesh::verticalTileMode() const +{ + return m_verticalTileMode; +} + +void QQuickBorderImageMesh::setVerticalTileMode(TileMode t) +{ + if (t == m_verticalTileMode) + return; + + m_verticalTileMode = t; + Q_EMIT verticalTileModeChanged(); + Q_EMIT geometryChanged(); +} + QT_END_NAMESPACE diff --git a/src/quick/items/qquickshadereffectmesh_p.h b/src/quick/items/qquickshadereffectmesh_p.h index 7f9c95888c..34d3908140 100644 --- a/src/quick/items/qquickshadereffectmesh_p.h +++ b/src/quick/items/qquickshadereffectmesh_p.h @@ -100,6 +100,45 @@ private: QString m_log; }; +class QQuickScaleGrid; +class QQuickBorderImageMesh : public QQuickShaderEffectMesh +{ + Q_OBJECT + + Q_PROPERTY(QQuickScaleGrid *border READ border CONSTANT) + Q_PROPERTY(QSize size READ size WRITE setSize NOTIFY sizeChanged) + Q_PROPERTY(TileMode horizontalTileMode READ horizontalTileMode WRITE setHorizontalTileMode NOTIFY horizontalTileModeChanged) + Q_PROPERTY(TileMode verticalTileMode READ verticalTileMode WRITE setVerticalTileMode NOTIFY verticalTileModeChanged) +public: + QQuickBorderImageMesh(QObject *parent = 0); + QSGGeometry *updateGeometry(QSGGeometry *geometry, const QVector<QByteArray> &attributes, const QRectF &srcRect, const QRectF &rect) Q_DECL_OVERRIDE; + + QQuickScaleGrid *border(); + + enum TileMode { Stretch = Qt::StretchTile, Repeat = Qt::RepeatTile, Round = Qt::RoundTile }; + Q_ENUM(TileMode) + + QSize size() const; + void setSize(const QSize &size); + + TileMode horizontalTileMode() const; + void setHorizontalTileMode(TileMode); + + TileMode verticalTileMode() const; + void setVerticalTileMode(TileMode); + +Q_SIGNALS: + void sizeChanged(); + void horizontalTileModeChanged(); + void verticalTileModeChanged(); + +private: + QQuickScaleGrid *m_border; + QSize m_size; + TileMode m_horizontalTileMode; + TileMode m_verticalTileMode; +}; + inline QColor qt_premultiply_color(const QColor &c) { return QColor::fromRgbF(c.redF() * c.alphaF(), c.greenF() * c.alphaF(), c.blueF() * c.alphaF(), c.alphaF()); |