1// Copyright (C) 2016 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
3
4#include "qquickshadereffectmesh_p.h"
5#include <QtQuick/qsggeometry.h>
6#include "qquickshadereffect_p.h"
7#include "qquickscalegrid_p_p.h"
8#include "qquickborderimage_p_p.h"
9#include <QtQuick/private/qsgbasicinternalimagenode_p.h>
10
11QT_BEGIN_NAMESPACE
12
13static const char qt_position_attribute_name[] = "qt_Vertex";
14static const char qt_texcoord_attribute_name[] = "qt_MultiTexCoord0";
15
16const char *qtPositionAttributeName()
17{
18 return qt_position_attribute_name;
19}
20
21const char *qtTexCoordAttributeName()
22{
23 return qt_texcoord_attribute_name;
24}
25
26QQuickShaderEffectMesh::QQuickShaderEffectMesh(QObject *parent)
27 : QObject(parent)
28{
29}
30
31QQuickShaderEffectMesh::QQuickShaderEffectMesh(QObjectPrivate &dd, QObject *parent)
32 : QObject(dd, parent)
33{
34}
35
36/*!
37 \qmltype GridMesh
38 \instantiates QQuickGridMesh
39 \inqmlmodule QtQuick
40 \since 5.0
41 \ingroup qtquick-effects
42 \brief Defines a mesh with vertices arranged in a grid.
43
44 GridMesh defines a rectangular mesh consisting of vertices arranged in an
45 evenly spaced grid. It is used to generate \l{QSGGeometry}{geometry}.
46 The grid resolution is specified with the \l resolution property.
47*/
48
49QQuickGridMesh::QQuickGridMesh(QObject *parent)
50 : QQuickShaderEffectMesh(parent)
51 , m_resolution(1, 1)
52{
53}
54
55bool QQuickGridMesh::validateAttributes(const QList<QByteArray> &attributes, int *posIndex)
56{
57 const int attrCount = attributes.size();
58 int positionIndex = attributes.indexOf(t: qtPositionAttributeName());
59 int texCoordIndex = attributes.indexOf(t: qtTexCoordAttributeName());
60
61 switch (attrCount) {
62 case 0:
63 m_log = QLatin1String("Error: No attributes specified.");
64 return false;
65 case 1:
66 if (positionIndex != 0) {
67 m_log = QLatin1String("Error: Missing \'") + QLatin1String(qtPositionAttributeName())
68 + QLatin1String("\' attribute.\n");
69 return false;
70 }
71 break;
72 case 2:
73 if (positionIndex == -1 || texCoordIndex == -1) {
74 m_log.clear();
75 if (positionIndex == -1) {
76 m_log = QLatin1String("Error: Missing \'") + QLatin1String(qtPositionAttributeName())
77 + QLatin1String("\' attribute.\n");
78 }
79 if (texCoordIndex == -1) {
80 m_log += QLatin1String("Error: Missing \'") + QLatin1String(qtTexCoordAttributeName())
81 + QLatin1String("\' attribute.\n");
82 }
83 return false;
84 }
85 break;
86 default:
87 m_log = QLatin1String("Error: Too many attributes specified.");
88 return false;
89 }
90
91 if (posIndex)
92 *posIndex = positionIndex;
93
94 return true;
95}
96
97QSGGeometry *QQuickGridMesh::updateGeometry(QSGGeometry *geometry, int attrCount, int posIndex,
98 const QRectF &srcRect, const QRectF &dstRect)
99{
100 int vmesh = m_resolution.height();
101 int hmesh = m_resolution.width();
102
103 if (!geometry) {
104 Q_ASSERT(attrCount == 1 || attrCount == 2);
105 geometry = new QSGGeometry(attrCount == 1
106 ? QSGGeometry::defaultAttributes_Point2D()
107 : QSGGeometry::defaultAttributes_TexturedPoint2D(),
108 (vmesh + 1) * (hmesh + 1), vmesh * 2 * (hmesh + 2),
109 QSGGeometry::UnsignedShortType);
110
111 } else {
112 geometry->allocate(vertexCount: (vmesh + 1) * (hmesh + 1), indexCount: vmesh * 2 * (hmesh + 2));
113 }
114
115 QSGGeometry::Point2D *vdata = static_cast<QSGGeometry::Point2D *>(geometry->vertexData());
116
117 for (int iy = 0; iy <= vmesh; ++iy) {
118 float fy = iy / float(vmesh);
119 float y = float(dstRect.top()) + fy * float(dstRect.height());
120 float ty = float(srcRect.top()) + fy * float(srcRect.height());
121 for (int ix = 0; ix <= hmesh; ++ix) {
122 float fx = ix / float(hmesh);
123 for (int ia = 0; ia < attrCount; ++ia) {
124 if (ia == posIndex) {
125 vdata->x = float(dstRect.left()) + fx * float(dstRect.width());
126 vdata->y = y;
127 ++vdata;
128 } else {
129 vdata->x = float(srcRect.left()) + fx * float(srcRect.width());
130 vdata->y = ty;
131 ++vdata;
132 }
133 }
134 }
135 }
136
137 quint16 *indices = (quint16 *)geometry->indexDataAsUShort();
138 int i = 0;
139 for (int iy = 0; iy < vmesh; ++iy) {
140 *(indices++) = i + hmesh + 1;
141 for (int ix = 0; ix <= hmesh; ++ix, ++i) {
142 *(indices++) = i + hmesh + 1;
143 *(indices++) = i;
144 }
145 *(indices++) = i - 1;
146 }
147
148 return geometry;
149}
150
151/*!
152 \qmlproperty size QtQuick::GridMesh::resolution
153
154 This property holds the grid resolution. The resolution's width and height
155 specify the number of cells or spacings between vertices horizontally and
156 vertically respectively. The minimum and default is 1x1, which corresponds
157 to four vertices in total, one in each corner.
158 For non-linear vertex transformations, you probably want to set the
159 resolution higher.
160
161 \table
162 \header
163 \li Result
164 \li QML code
165 \li gridmesh.vert
166 \row
167 \li \image declarative-gridmesh.png
168 \li \qml
169 import QtQuick 2.0
170
171 ShaderEffect {
172 width: 200
173 height: 200
174 mesh: GridMesh {
175 resolution: Qt.size(20, 20)
176 }
177 property variant source: Image {
178 source: "qt-logo.png"
179 sourceSize { width: 200; height: 200 }
180 }
181 vertexShader: "gridmesh.vert"
182 }
183 \endqml
184 \li \badcode
185 #version 440
186 layout(location = 0) in vec4 qt_Vertex;
187 layout(location = 1) in vec2 qt_MultiTexCoord0;
188 layout(location = 0) out vec2 qt_TexCoord0;
189 layout(std140, binding = 0) uniform buf {
190 mat4 qt_Matrix;
191 float qt_Opacity;
192 float width;
193 };
194 void main() {
195 vec4 pos = qt_Vertex;
196 float d = 0.5 * smoothstep(0.0, 1.0, qt_MultiTexCoord0.y);
197 pos.x = width * mix(d, 1.0 - d, qt_MultiTexCoord0.x);
198 gl_Position = qt_Matrix * pos;
199 qt_TexCoord0 = qt_MultiTexCoord0;
200 }
201 \endcode
202 \endtable
203*/
204
205void QQuickGridMesh::setResolution(const QSize &res)
206{
207 if (res == m_resolution)
208 return;
209 if (res.width() < 1 || res.height() < 1) {
210 return;
211 }
212 m_resolution = res;
213 emit resolutionChanged();
214 emit geometryChanged();
215}
216
217QSize QQuickGridMesh::resolution() const
218{
219 return m_resolution;
220}
221
222/*!
223 \qmltype BorderImageMesh
224 \instantiates QQuickBorderImageMesh
225 \inqmlmodule QtQuick
226 \since 5.8
227 \ingroup qtquick-effects
228 \brief Defines a mesh with vertices arranged like those of a BorderImage.
229
230 BorderImageMesh provides BorderImage-like capabilities to a ShaderEffect
231 without the need for a potentially costly ShaderEffectSource.
232
233 The following are functionally equivalent:
234 \qml
235 BorderImage {
236 id: borderImage
237 border {
238 left: 10
239 right: 10
240 top: 10
241 bottom: 10
242 }
243 source: "myImage.png"
244 visible: false
245 }
246 ShaderEffectSource {
247 id: effectSource
248 sourceItem: borderImage
249 visible: false
250 }
251 ShaderEffect {
252 property var source: effectSource
253 ...
254 }
255 \endqml
256
257 \qml
258 Image {
259 id: image
260 source: "myImage.png"
261 visible: false
262 }
263 ShaderEffect {
264 property var source: image
265 mesh: BorderImageMesh {
266 border {
267 left: 10
268 right: 10
269 top: 10
270 bottom: 10
271 }
272 size: image.sourceSize
273 }
274 ...
275 }
276 \endqml
277
278 But the BorderImageMesh version can typically be better optimized.
279*/
280QQuickBorderImageMesh::QQuickBorderImageMesh(QObject *parent)
281 : QQuickShaderEffectMesh(parent), m_border(new QQuickScaleGrid(this)),
282 m_horizontalTileMode(QQuickBorderImageMesh::Stretch),
283 m_verticalTileMode(QQuickBorderImageMesh::Stretch)
284{
285}
286
287bool QQuickBorderImageMesh::validateAttributes(const QList<QByteArray> &attributes, int *posIndex)
288{
289 Q_UNUSED(attributes);
290 Q_UNUSED(posIndex);
291 return true;
292}
293
294QSGGeometry *QQuickBorderImageMesh::updateGeometry(QSGGeometry *geometry, int attrCount, int posIndex,
295 const QRectF &srcRect, const QRectF &rect)
296{
297 Q_UNUSED(attrCount);
298 Q_UNUSED(posIndex);
299
300 QRectF innerSourceRect;
301 QRectF targetRect;
302 QRectF innerTargetRect;
303 QRectF subSourceRect;
304
305 QQuickBorderImagePrivate::calculateRects(border: m_border, sourceSize: m_size, targetSize: rect.size(), horizontalTileMode: m_horizontalTileMode, verticalTileMode: m_verticalTileMode,
306 devicePixelRatio: 1, targetRect: &targetRect, innerTargetRect: &innerTargetRect, innerSourceRect: &innerSourceRect, subSourceRect: &subSourceRect);
307
308 QRectF sourceRect = srcRect;
309 QRectF modifiedInnerSourceRect(sourceRect.x() + innerSourceRect.x() * sourceRect.width(),
310 sourceRect.y() + innerSourceRect.y() * sourceRect.height(),
311 innerSourceRect.width() * sourceRect.width(),
312 innerSourceRect.height() * sourceRect.height());
313
314 geometry = QSGBasicInternalImageNode::updateGeometry(targetRect, innerTargetRect, sourceRect,
315 innerSourceRect: modifiedInnerSourceRect, subSourceRect, geometry);
316
317 return geometry;
318}
319
320/*!
321 \qmlpropertygroup QtQuick::BorderImageMesh::border
322 \qmlproperty int QtQuick::BorderImageMesh::border.left
323 \qmlproperty int QtQuick::BorderImageMesh::border.right
324 \qmlproperty int QtQuick::BorderImageMesh::border.top
325 \qmlproperty int QtQuick::BorderImageMesh::border.bottom
326
327 The 4 border lines (2 horizontal and 2 vertical) break the image into 9 sections,
328 as shown below:
329
330 \image declarative-scalegrid.png
331
332 Each border line (left, right, top, and bottom) specifies an offset in pixels
333 from the respective edge of the mesh. By default, each border line has
334 a value of 0.
335
336 For example, the following definition sets the bottom line 10 pixels up from
337 the bottom of the mesh:
338
339 \qml
340 BorderImageMesh {
341 border.bottom: 10
342 // ...
343 }
344 \endqml
345*/
346QQuickScaleGrid *QQuickBorderImageMesh::border() const
347{
348 return m_border;
349}
350
351/*!
352 \qmlproperty size QtQuick::BorderImageMesh::size
353
354 The base size of the mesh. This generally corresponds to the \l {Image::}{sourceSize}
355 of the image being used by the ShaderEffect.
356*/
357QSize QQuickBorderImageMesh::size() const
358{
359 return m_size;
360}
361
362void QQuickBorderImageMesh::setSize(const QSize &size)
363{
364 if (size == m_size)
365 return;
366 m_size = size;
367 Q_EMIT sizeChanged();
368 Q_EMIT geometryChanged();
369}
370
371/*!
372 \qmlproperty enumeration QtQuick::BorderImageMesh::horizontalTileMode
373 \qmlproperty enumeration QtQuick::BorderImageMesh::verticalTileMode
374
375 This property describes how to repeat or stretch the middle parts of an image.
376
377 \list
378 \li BorderImage.Stretch - Scales the image to fit to the available area.
379 \li BorderImage.Repeat - Tile the image until there is no more space. May crop the last image.
380 \li BorderImage.Round - Like Repeat, but scales the images down to ensure that the last image is not cropped.
381 \endlist
382
383 The default tile mode for each property is BorderImage.Stretch.
384*/
385
386QQuickBorderImageMesh::TileMode QQuickBorderImageMesh::horizontalTileMode() const
387{
388 return m_horizontalTileMode;
389}
390
391void QQuickBorderImageMesh::setHorizontalTileMode(TileMode t)
392{
393 if (t == m_horizontalTileMode)
394 return;
395 m_horizontalTileMode = t;
396 Q_EMIT horizontalTileModeChanged();
397 Q_EMIT geometryChanged();
398}
399
400QQuickBorderImageMesh::TileMode QQuickBorderImageMesh::verticalTileMode() const
401{
402 return m_verticalTileMode;
403}
404
405void QQuickBorderImageMesh::setVerticalTileMode(TileMode t)
406{
407 if (t == m_verticalTileMode)
408 return;
409
410 m_verticalTileMode = t;
411 Q_EMIT verticalTileModeChanged();
412 Q_EMIT geometryChanged();
413}
414
415QT_END_NAMESPACE
416
417#include "moc_qquickshadereffectmesh_p.cpp"
418

source code of qtdeclarative/src/quick/items/qquickshadereffectmesh.cpp