454 lines
14 KiB
C++
454 lines
14 KiB
C++
/****************************************************************************
|
|
**
|
|
** Copyright (C) 2016 The Qt Company Ltd.
|
|
** Contact: https://www.qt.io/licensing/
|
|
**
|
|
** This file is part of the QtQuick module of the Qt Toolkit.
|
|
**
|
|
** $QT_BEGIN_LICENSE:LGPL$
|
|
** Commercial License Usage
|
|
** Licensees holding valid commercial Qt licenses may use this file in
|
|
** accordance with the commercial license agreement provided with the
|
|
** Software or, alternatively, in accordance with the terms contained in
|
|
** a written agreement between you and The Qt Company. For licensing terms
|
|
** and conditions see https://www.qt.io/terms-conditions. For further
|
|
** information use the contact form at https://www.qt.io/contact-us.
|
|
**
|
|
** GNU Lesser General Public License Usage
|
|
** Alternatively, this file may be used under the terms of the GNU Lesser
|
|
** General Public License version 3 as published by the Free Software
|
|
** Foundation and appearing in the file LICENSE.LGPL3 included in the
|
|
** packaging of this file. Please review the following information to
|
|
** ensure the GNU Lesser General Public License version 3 requirements
|
|
** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
|
|
**
|
|
** GNU General Public License Usage
|
|
** Alternatively, this file may be used under the terms of the GNU
|
|
** General Public License version 2.0 or (at your option) the GNU General
|
|
** Public license version 3 or any later version approved by the KDE Free
|
|
** Qt Foundation. The licenses are as published by the Free Software
|
|
** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
|
|
** included in the packaging of this file. Please review the following
|
|
** information to ensure the GNU General Public License requirements will
|
|
** be met: https://www.gnu.org/licenses/gpl-2.0.html and
|
|
** https://www.gnu.org/licenses/gpl-3.0.html.
|
|
**
|
|
** $QT_END_LICENSE$
|
|
**
|
|
****************************************************************************/
|
|
|
|
#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
|
|
\instantiates 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.count();
|
|
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
|
|
\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
|
|
\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)
|
|
{
|
|
}
|
|
|
|
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
|
|
|
|
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"
|