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

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