Revision control
++ // Copyright (C) 2016 The Qt Company Ltd.
++ // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
++ // Qt-Security score:significant reason:default
++
++ #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/qsgbasicinternalimagenode_p.h>
++
++ QT_BEGIN_NAMESPACE
++
++ static const char qt_position_attribute_name[] = "qt_Vertex";
++ static const char qt_texcoord_attribute_name[] = "qt_MultiTexCoord0";
++
++ const char *qtPositionAttributeName()
++ {
++ return qt_position_attribute_name;
++ }
++
++ const char *qtTexCoordAttributeName()
++ {
++ return qt_texcoord_attribute_name;
++ }
++
++ QQuickShaderEffectMesh::QQuickShaderEffectMesh(QObject *parent)
++ : QObject(parent)
++ {
++ }
++
++ QQuickShaderEffectMesh::QQuickShaderEffectMesh(QObjectPrivate &dd, QObject *parent)
++ : QObject(dd, parent)
++ {
++ }
++
++ /*!
++ \qmltype GridMesh
++ \nativetype QQuickGridMesh
++ \inqmlmodule QtQuick
++ \since 5.0
++ \ingroup qtquick-effects
++ \brief Defines a mesh with vertices arranged in a grid.
++
++ GridMesh defines a rectangular mesh consisting of vertices arranged in an
++ evenly spaced grid. It is used to generate \l{QSGGeometry}{geometry}.
++ The grid resolution is specified with the \l resolution property.
++ */
++
++ QQuickGridMesh::QQuickGridMesh(QObject *parent)
++ : QQuickShaderEffectMesh(parent)
++ , m_resolution(1, 1)
++ {
++ }
++
++ bool QQuickGridMesh::validateAttributes(const QList<QByteArray> &attributes, int *posIndex)
++ {
++ const int attrCount = attributes.size();
++ int positionIndex = attributes.indexOf(qtPositionAttributeName());
++ int texCoordIndex = attributes.indexOf(qtTexCoordAttributeName());
++
++ switch (attrCount) {
++ case 0:
++ m_log = QLatin1String("Error: No attributes specified.");
++ return false;
++ case 1:
++ if (positionIndex != 0) {
++ m_log = QLatin1String("Error: Missing \'") + QLatin1String(qtPositionAttributeName())
++ + QLatin1String("\' attribute.\n");
++ return false;
++ }
++ break;
++ case 2:
++ if (positionIndex == -1 || texCoordIndex == -1) {
++ m_log.clear();
++ if (positionIndex == -1) {
++ m_log = QLatin1String("Error: Missing \'") + QLatin1String(qtPositionAttributeName())
++ + QLatin1String("\' attribute.\n");
++ }
++ if (texCoordIndex == -1) {
++ m_log += QLatin1String("Error: Missing \'") + QLatin1String(qtTexCoordAttributeName())
++ + QLatin1String("\' attribute.\n");
++ }
++ return false;
++ }
++ break;
++ default:
++ m_log = QLatin1String("Error: Too many attributes specified.");
++ return false;
++ }
++
++ if (posIndex)
++ *posIndex = positionIndex;
++
++ return true;
++ }
++
++ QSGGeometry *QQuickGridMesh::updateGeometry(QSGGeometry *geometry, int attrCount, int posIndex,
++ const QRectF &srcRect, const QRectF &dstRect)
++ {
++ int vmesh = m_resolution.height();
++ int hmesh = m_resolution.width();
++
++ if (!geometry) {
++ Q_ASSERT(attrCount == 1 || attrCount == 2);
++ geometry = new QSGGeometry(attrCount == 1
++ ? QSGGeometry::defaultAttributes_Point2D()
++ : QSGGeometry::defaultAttributes_TexturedPoint2D(),
++ (vmesh + 1) * (hmesh + 1), vmesh * 2 * (hmesh + 2),
++ QSGGeometry::UnsignedShortType);
++
++ } else {
++ geometry->allocate((vmesh + 1) * (hmesh + 1), vmesh * 2 * (hmesh + 2));
++ }
++
++ QSGGeometry::Point2D *vdata = static_cast<QSGGeometry::Point2D *>(geometry->vertexData());
++
++ for (int iy = 0; iy <= vmesh; ++iy) {
++ float fy = iy / float(vmesh);
++ float y = float(dstRect.top()) + fy * float(dstRect.height());
++ float ty = float(srcRect.top()) + fy * float(srcRect.height());
++ for (int ix = 0; ix <= hmesh; ++ix) {
++ float fx = ix / float(hmesh);
++ for (int ia = 0; ia < attrCount; ++ia) {
++ if (ia == posIndex) {
++ vdata->x = float(dstRect.left()) + fx * float(dstRect.width());
++ vdata->y = y;
++ ++vdata;
++ } else {
++ vdata->x = float(srcRect.left()) + fx * float(srcRect.width());
++ vdata->y = ty;
++ ++vdata;
++ }
++ }
++ }
++ }
++
++ quint16 *indices = (quint16 *)geometry->indexDataAsUShort();
++ int i = 0;
++ for (int iy = 0; iy < vmesh; ++iy) {
++ *(indices++) = i + hmesh + 1;
++ for (int ix = 0; ix <= hmesh; ++ix, ++i) {
++ *(indices++) = i + hmesh + 1;
++ *(indices++) = i;
++ }
++ *(indices++) = i - 1;
++ }
++
++ return geometry;
++ }
++
++ /*!
++ \qmlproperty size QtQuick::GridMesh::resolution
++
++ This property holds the grid resolution. The resolution's width and height
++ specify the number of cells or spacings between vertices horizontally and
++ vertically respectively. The minimum and default is 1x1, which corresponds
++ to four vertices in total, one in each corner.
++ For non-linear vertex transformations, you probably want to set the
++ resolution higher.
++
++ \table
++ \header
++ \li Result
++ \li QML code
++ \li gridmesh.vert
++ \row
++ \li \image declarative-gridmesh.png {Qt logo distorted by GridMesh
++ transformation}
++ \li \qml
++ import QtQuick 2.0
++
++ ShaderEffect {
++ width: 200
++ height: 200
++ mesh: GridMesh {
++ resolution: Qt.size(20, 20)
++ }
++ property variant source: Image {
++ source: "qt-logo.png"
++ sourceSize { width: 200; height: 200 }
++ }
++ vertexShader: "gridmesh.vert"
++ }
++ \endqml
++ \li \badcode
++ #version 440
++ layout(location = 0) in vec4 qt_Vertex;
++ layout(location = 1) in vec2 qt_MultiTexCoord0;
++ layout(location = 0) out vec2 qt_TexCoord0;
++ layout(std140, binding = 0) uniform buf {
++ mat4 qt_Matrix;
++ float qt_Opacity;
++ float width;
++ };
++ void main() {
++ vec4 pos = qt_Vertex;
++ float d = 0.5 * smoothstep(0.0, 1.0, qt_MultiTexCoord0.y);
++ pos.x = width * mix(d, 1.0 - d, qt_MultiTexCoord0.x);
++ gl_Position = qt_Matrix * pos;
++ qt_TexCoord0 = qt_MultiTexCoord0;
++ }
++ \endcode
++ \endtable
++ */
++
++ void QQuickGridMesh::setResolution(const QSize &res)
++ {
++ if (res == m_resolution)
++ return;
++ if (res.width() < 1 || res.height() < 1) {
++ return;
++ }
++ m_resolution = res;
++ emit resolutionChanged();
++ emit geometryChanged();
++ }
++
++ QSize QQuickGridMesh::resolution() const
++ {
++ return m_resolution;
++ }
++
++ /*!
++ \qmltype BorderImageMesh
++ \nativetype 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)
++ {
++ }
++
++ bool QQuickBorderImageMesh::validateAttributes(const QList<QByteArray> &attributes, int *posIndex)
++ {
++ Q_UNUSED(attributes);
++ Q_UNUSED(posIndex);
++ return true;
++ }
++
++ QSGGeometry *QQuickBorderImageMesh::updateGeometry(QSGGeometry *geometry, int attrCount, int posIndex,
++ const QRectF &srcRect, const QRectF &rect)
++ {
++ Q_UNUSED(attrCount);
++ Q_UNUSED(posIndex);
++
++ 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 = QSGBasicInternalImageNode::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 {Red rounded rectangle divided into
++ 9 numbered regions by dashed border lines}
++
++ 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() const
++ {
++ 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
++
++ #include "moc_qquickshadereffectmesh_p.cpp"