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 | |
47 | QT_BEGIN_NAMESPACE |
48 | |
49 | static const char qt_position_attribute_name[] = "qt_Vertex" ; |
50 | static const char qt_texcoord_attribute_name[] = "qt_MultiTexCoord0" ; |
51 | |
52 | const char *qtPositionAttributeName() |
53 | { |
54 | return qt_position_attribute_name; |
55 | } |
56 | |
57 | const char *qtTexCoordAttributeName() |
58 | { |
59 | return qt_texcoord_attribute_name; |
60 | } |
61 | |
62 | QQuickShaderEffectMesh::QQuickShaderEffectMesh(QObject *parent) |
63 | : QObject(parent) |
64 | { |
65 | } |
66 | |
67 | QQuickShaderEffectMesh::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 | |
85 | QQuickGridMesh::QQuickGridMesh(QObject *parent) |
86 | : QQuickShaderEffectMesh(parent) |
87 | , m_resolution(1, 1) |
88 | { |
89 | } |
90 | |
91 | bool 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 | |
133 | QSGGeometry *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 | |
231 | void 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 | |
243 | QSize 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 | */ |
306 | QQuickBorderImageMesh::QQuickBorderImageMesh(QObject *parent) |
307 | : QQuickShaderEffectMesh(parent), m_border(new QQuickScaleGrid(this)), |
308 | m_horizontalTileMode(QQuickBorderImageMesh::Stretch), |
309 | m_verticalTileMode(QQuickBorderImageMesh::Stretch) |
310 | { |
311 | } |
312 | |
313 | bool QQuickBorderImageMesh::validateAttributes(const QVector<QByteArray> &attributes, int *posIndex) |
314 | { |
315 | Q_UNUSED(attributes); |
316 | Q_UNUSED(posIndex); |
317 | return true; |
318 | } |
319 | |
320 | QSGGeometry *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 | */ |
372 | QQuickScaleGrid *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 | */ |
383 | QSize QQuickBorderImageMesh::size() const |
384 | { |
385 | return m_size; |
386 | } |
387 | |
388 | void 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 | |
412 | QQuickBorderImageMesh::TileMode QQuickBorderImageMesh::horizontalTileMode() const |
413 | { |
414 | return m_horizontalTileMode; |
415 | } |
416 | |
417 | void 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 | |
426 | QQuickBorderImageMesh::TileMode QQuickBorderImageMesh::verticalTileMode() const |
427 | { |
428 | return m_verticalTileMode; |
429 | } |
430 | |
431 | void 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 | |
441 | QT_END_NAMESPACE |
442 | |
443 | #include "moc_qquickshadereffectmesh_p.cpp" |
444 | |