Merge lp:~loic.molinari/ubuntu-ui-toolkit/ubuntu-ui-toolkit-shape-overlay into lp:ubuntu-ui-toolkit/staging

Proposed by Loïc Molinari
Status: Superseded
Proposed branch: lp:~loic.molinari/ubuntu-ui-toolkit/ubuntu-ui-toolkit-shape-overlay
Merge into: lp:ubuntu-ui-toolkit/staging
Prerequisite: lp:~loic.molinari/ubuntu-ui-toolkit/ubuntu-ui-toolkit-shape-rewrite
Diff against target: 1088 lines (+1010/-1)
8 files modified
modules/Ubuntu/Components/plugin/plugin.cpp (+2/-0)
modules/Ubuntu/Components/plugin/plugin.pro (+5/-1)
modules/Ubuntu/Components/plugin/plugin.qrc (+2/-0)
modules/Ubuntu/Components/plugin/shaders/shapeoverlay.frag (+65/-0)
modules/Ubuntu/Components/plugin/shaders/shapeoverlay.vert (+49/-0)
modules/Ubuntu/Components/plugin/ucubuntushapeoverlay.cpp (+445/-0)
modules/Ubuntu/Components/plugin/ucubuntushapeoverlay.h (+112/-0)
tests/resources/ubuntushape/UbuntuShapeOverlayTest.qml (+330/-0)
To merge this branch: bzr merge lp:~loic.molinari/ubuntu-ui-toolkit/ubuntu-ui-toolkit-shape-overlay
Reviewer Review Type Date Requested Status
PS Jenkins bot continuous-integration Needs Fixing
Zsombor Egri Needs Fixing
Review via email: mp+243324@code.launchpad.net

This proposal has been superseded by a proposal from 2015-03-04.

Commit message

[UbuntuShape] Added overlay support.

Description of the change

[UbuntuShape] Added overlay support.

To post a comment you must log in.
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
Revision history for this message
Zsombor Egri (zsombi) wrote :

Some preliminary comments inline.

review: Needs Fixing
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
1336. By Loïc Molinari

Merged main branch.

1337. By Loïc Molinari

Merged main branch.

1338. By Loïc Molinari

Updated components.api.

1339. By Loïc Molinari

Made use of qBound() instead of qMin()/qMax().

Unmerged revisions

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'modules/Ubuntu/Components/plugin/plugin.cpp'
2--- modules/Ubuntu/Components/plugin/plugin.cpp 2015-02-27 14:02:18 +0000
3+++ modules/Ubuntu/Components/plugin/plugin.cpp 2015-02-27 14:02:19 +0000
4@@ -32,6 +32,7 @@
5 #include "ucqquickimageextension.h"
6 #include "quickutils.h"
7 #include "ucubuntushape.h"
8+#include "ucubuntushapeoverlay.h"
9 #include "inversemouseareatype.h"
10 #include "qquickclipboard.h"
11 #include "qquickmimedata.h"
12@@ -177,6 +178,7 @@
13 qmlRegisterType<UCListItemActions>(uri, 1, 2, "ListItemActions");
14 qmlRegisterUncreatableType<UCViewItemsAttached>(uri, 1, 2, "ViewItems", "Not instantiable");
15 qmlRegisterType<UCUbuntuShape, 1>(uri, 1, 2, "UbuntuShape");
16+ qmlRegisterType<UCUbuntuShapeOverlay>(uri, 1, 2, "UbuntuShapeOverlay");
17 }
18
19 void UbuntuComponentsPlugin::initializeEngine(QQmlEngine *engine, const char *uri)
20
21=== modified file 'modules/Ubuntu/Components/plugin/plugin.pro'
22--- modules/Ubuntu/Components/plugin/plugin.pro 2015-02-27 14:02:18 +0000
23+++ modules/Ubuntu/Components/plugin/plugin.pro 2015-02-27 14:02:19 +0000
24@@ -35,6 +35,7 @@
25 quickutils.h \
26 ucubuntushapetexture.h \
27 ucubuntushape.h \
28+ ucubuntushapeoverlay.h \
29 inversemouseareatype.h \
30 qquickclipboard.h \
31 qquickmimedata.h \
32@@ -89,6 +90,7 @@
33 ucqquickimageextension.cpp \
34 quickutils.cpp \
35 ucubuntushape.cpp \
36+ ucubuntushapeoverlay.cpp \
37 inversemouseareatype.cpp \
38 qquickclipboard.cpp \
39 qquickmimedata.cpp \
40@@ -132,7 +134,9 @@
41
42 OTHER_FILES += \
43 shaders/shape.vert \
44- shaders/shape.frag
45+ shaders/shape.frag \
46+ shaders/shapeoverlay.vert \
47+ shaders/shapeoverlay.frag
48
49 # deployment rules for the plugin
50 installPath = $$[QT_INSTALL_QML]/$$replace(uri, \\., /)
51
52=== modified file 'modules/Ubuntu/Components/plugin/plugin.qrc'
53--- modules/Ubuntu/Components/plugin/plugin.qrc 2015-02-27 14:02:18 +0000
54+++ modules/Ubuntu/Components/plugin/plugin.qrc 2015-02-27 14:02:19 +0000
55@@ -2,5 +2,7 @@
56 <qresource prefix="/uc">
57 <file>shaders/shape.frag</file>
58 <file>shaders/shape.vert</file>
59+ <file>shaders/shapeoverlay.frag</file>
60+ <file>shaders/shapeoverlay.vert</file>
61 </qresource>
62 </RCC>
63
64=== added file 'modules/Ubuntu/Components/plugin/shaders/shapeoverlay.frag'
65--- modules/Ubuntu/Components/plugin/shaders/shapeoverlay.frag 1970-01-01 00:00:00 +0000
66+++ modules/Ubuntu/Components/plugin/shaders/shapeoverlay.frag 2015-02-27 14:02:19 +0000
67@@ -0,0 +1,65 @@
68+// Copyright © 2015 Canonical Ltd.
69+//
70+// This program is free software; you can redistribute it and/or modify
71+// it under the terms of the GNU Lesser General Public License as published by
72+// the Free Software Foundation; version 3.
73+//
74+// This program is distributed in the hope that it will be useful,
75+// but WITHOUT ANY WARRANTY; without even the implied warranty of
76+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
77+// GNU Lesser General Public License for more details.
78+//
79+// You should have received a copy of the GNU Lesser General Public License
80+// along with this program. If not, see <http://www.gnu.org/licenses/>.
81+//
82+// Author: Loïc Molinari <loic.molinari@canonical.com>
83+
84+// Static flow control (branching on a uniform value) is fast on most GPUs (including ultra-low
85+// power ones) because it allows to use the same shader execution path for an entire draw call. We
86+// rely on that technique here (also known as "uber-shader" solution) to avoid the complexity of
87+// dealing with a multiple shaders solution.
88+// FIXME(loicm) Validate GPU behavior with regards to static flow control.
89+
90+uniform sampler2D shapeTexture;
91+uniform sampler2D sourceTexture;
92+uniform lowp float sourceOpacity;
93+uniform lowp float opacity;
94+uniform bool textured;
95+
96+varying mediump vec2 shapeCoord;
97+varying mediump vec4 sourceCoord;
98+varying lowp vec4 backgroundColor;
99+varying mediump vec2 overlayCoord;
100+varying lowp vec4 overlayColor;
101+
102+void main(void)
103+{
104+ // Early texture fetch to cover latency as best as possible.
105+ lowp vec4 shapeData = texture2D(shapeTexture, shapeCoord);
106+
107+ lowp vec4 color = backgroundColor;
108+
109+ // FIXME(loicm) Would be better to use a bitfield but bitwise ops have only been integrated in
110+ // GLSL 1.3 (OpenGL 3) and GLSL ES 3 (OpenGL ES 3).
111+ if (textured) {
112+ // Blend the source over the current color (static flow control prevents the texture fetch).
113+ lowp vec2 axisMask = -sign((sourceCoord.zw * sourceCoord.zw) - vec2(1.0));
114+ lowp float mask = clamp(axisMask.x + axisMask.y, 0.0, 1.0);
115+ lowp vec4 source = texture2D(sourceTexture, sourceCoord.st) * vec4(sourceOpacity * mask);
116+ color = vec4(1.0 - source.a) * color + source;
117+ }
118+
119+ // Blend the overlay over the current color.
120+ lowp vec2 overlayAxisMask = -sign((overlayCoord * overlayCoord) - vec2(1.0));
121+ lowp float overlayMask = clamp(overlayAxisMask.x + overlayAxisMask.y, 0.0, 1.0);
122+ lowp vec4 overlay = overlayColor * vec4(overlayMask);
123+ color = vec4(1.0 - overlay.a) * color + overlay;
124+
125+ // Shape the current color with the mask.
126+ color *= vec4(shapeData.b);
127+
128+ // Blend the border over the current color.
129+ color = vec4(1.0 - shapeData.r) * color + shapeData.gggr;
130+
131+ gl_FragColor = color * vec4(opacity);
132+}
133
134=== added file 'modules/Ubuntu/Components/plugin/shaders/shapeoverlay.vert'
135--- modules/Ubuntu/Components/plugin/shaders/shapeoverlay.vert 1970-01-01 00:00:00 +0000
136+++ modules/Ubuntu/Components/plugin/shaders/shapeoverlay.vert 2015-02-27 14:02:19 +0000
137@@ -0,0 +1,49 @@
138+// Copyright © 2015 Canonical Ltd.
139+//
140+// This program is free software; you can redistribute it and/or modify
141+// it under the terms of the GNU Lesser General Public License as published by
142+// the Free Software Foundation; version 3.
143+//
144+// This program is distributed in the hope that it will be useful,
145+// but WITHOUT ANY WARRANTY; without even the implied warranty of
146+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
147+// GNU Lesser General Public License for more details.
148+//
149+// You should have received a copy of the GNU Lesser General Public License
150+// along with this program. If not, see <http://www.gnu.org/licenses/>.
151+//
152+// Author: Loïc Molinari <loic.molinari@canonical.com>
153+
154+uniform mediump mat4 matrix;
155+uniform bool textured;
156+
157+attribute mediump vec4 positionAttrib;
158+attribute mediump vec2 shapeCoordAttrib;
159+attribute mediump vec4 sourceCoordAttrib;
160+attribute lowp vec4 backgroundColorAttrib;
161+attribute mediump vec2 overlayCoordAttrib;
162+attribute lowp vec4 overlayColorAttrib;
163+
164+// FIXME(loicm) Optimize by reducing/packing varyings.
165+varying mediump vec2 shapeCoord;
166+varying mediump vec4 sourceCoord;
167+varying lowp vec4 backgroundColor;
168+varying mediump vec2 overlayCoord;
169+varying lowp vec4 overlayColor;
170+
171+void main()
172+{
173+ shapeCoord = shapeCoordAttrib;
174+
175+ // FIXME(loicm) Would be better to use a bitfield but bitwise ops have only been integrated in
176+ // GLSL 1.3 (OpenGL 3) and GLSL ES 3 (OpenGL ES 3).
177+ if (textured) {
178+ sourceCoord = sourceCoordAttrib;
179+ }
180+
181+ backgroundColor = backgroundColorAttrib;
182+ overlayCoord = overlayCoordAttrib;
183+ overlayColor = overlayColorAttrib;
184+
185+ gl_Position = matrix * positionAttrib;
186+}
187
188=== added file 'modules/Ubuntu/Components/plugin/ucubuntushapeoverlay.cpp'
189--- modules/Ubuntu/Components/plugin/ucubuntushapeoverlay.cpp 1970-01-01 00:00:00 +0000
190+++ modules/Ubuntu/Components/plugin/ucubuntushapeoverlay.cpp 2015-02-27 14:02:19 +0000
191@@ -0,0 +1,445 @@
192+/*
193+ * Copyright 2015 Canonical Ltd.
194+ *
195+ * This program is free software; you can redistribute it and/or modify
196+ * it under the terms of the GNU Lesser General Public License as published by
197+ * the Free Software Foundation; version 3.
198+ *
199+ * This program is distributed in the hope that it will be useful,
200+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
201+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
202+ * GNU Lesser General Public License for more details.
203+ *
204+ * You should have received a copy of the GNU Lesser General Public License
205+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
206+ *
207+ * Author: Loïc Molinari <loic.molinari@canonical.com>
208+ */
209+
210+// FIXME(loicm) Overlay data is passed as geometry and interpolated as varying so that it can be
211+// batched. It would be very useful to implement it with uniforms and compare the perf by
212+// plotting the rendering speed of a scene with an increasing number of overlaid shapes.
213+
214+#include "ucubuntushapeoverlay.h"
215+
216+// -- Scene graph shader ---
217+
218+ShapeOverlayShader::ShapeOverlayShader()
219+{
220+ setShaderSourceFile(QOpenGLShader::Vertex, QStringLiteral(":/uc/shaders/shapeoverlay.vert"));
221+ setShaderSourceFile(QOpenGLShader::Fragment, QStringLiteral(":/uc/shaders/shapeoverlay.frag"));
222+}
223+
224+char const* const* ShapeOverlayShader::attributeNames() const
225+{
226+ static char const* const attributes[] = {
227+ "positionAttrib", "shapeCoordAttrib", "sourceCoordAttrib", "backgroundColorAttrib",
228+ "overlayCoordAttrib", "overlayColorAttrib", 0
229+ };
230+ return attributes;
231+}
232+
233+// --- Scene graph material ---
234+
235+QSGMaterialType* ShapeOverlayMaterial::type() const
236+{
237+ static QSGMaterialType type;
238+ return &type;
239+}
240+
241+QSGMaterialShader* ShapeOverlayMaterial::createShader() const
242+{
243+ return new ShapeOverlayShader;
244+}
245+
246+// --- Scene graph node ---
247+
248+ShapeOverlayNode::ShapeOverlayNode()
249+ : QSGGeometryNode()
250+ , m_material()
251+ , m_geometry(attributeSet(), ShapeNode::vertexCount, ShapeNode::indexCount,
252+ ShapeNode::indexType)
253+{
254+ memcpy(m_geometry.indexData(), ShapeNode::indices(),
255+ ShapeNode::indexCount * ShapeNode::indexTypeSize);
256+ m_geometry.setDrawingMode(ShapeNode::drawingMode);
257+ m_geometry.setIndexDataPattern(ShapeNode::indexDataPattern);
258+ m_geometry.setVertexDataPattern(ShapeNode::vertexDataPattern);
259+ setMaterial(&m_material);
260+ setGeometry(&m_geometry);
261+}
262+
263+// static
264+const QSGGeometry::AttributeSet& ShapeOverlayNode::attributeSet()
265+{
266+ static const QSGGeometry::Attribute attributes[] = {
267+ QSGGeometry::Attribute::create(0, 2, GL_FLOAT, true),
268+ QSGGeometry::Attribute::create(1, 2, GL_FLOAT),
269+ QSGGeometry::Attribute::create(2, 4, GL_FLOAT),
270+ QSGGeometry::Attribute::create(3, 4, GL_UNSIGNED_BYTE),
271+ QSGGeometry::Attribute::create(4, 2, GL_FLOAT),
272+ QSGGeometry::Attribute::create(5, 4, GL_UNSIGNED_BYTE)
273+ };
274+ static const QSGGeometry::AttributeSet attributeSet = {
275+ 6, sizeof(Vertex), attributes
276+ };
277+ return attributeSet;
278+}
279+
280+// --- QtQuick item ---
281+
282+/*! \qmltype UbuntuShapeOverlay
283+ \instantiates UCUbuntuShapeOverlay
284+ \inqmlmodule Ubuntu.Components 1.2
285+ \ingroup ubuntu
286+ \brief Extended UbuntuShape adding a colored overlay layer.
287+
288+ The UbuntuShapeOverlay is a rounded rectangle (based on a \l
289+ {https://en.wikipedia.org/wiki/Squircle}{squircle}) containing a set of layers composed, from
290+ top to bottom, of a colored rectangle overlay, an optional source image and a background color
291+ (solid or linear gradient). Different properties allow to change the look of the shape.
292+
293+ Example:
294+
295+ \qml
296+ import Ubuntu.Components 1.2
297+
298+ UbuntuShapeOverlay {
299+ backgroundColor: "white"
300+ overlayColor: "black"
301+ overlayGeometry: Qt.rect(0.25, 0.25, 0.5, 0.5)
302+ }
303+ \endqml
304+*/
305+UCUbuntuShapeOverlay::UCUbuntuShapeOverlay(QQuickItem* parent)
306+ : UCUbuntuShape(parent)
307+ , m_overlayX(0)
308+ , m_overlayY(0)
309+ , m_overlayWidth(0)
310+ , m_overlayHeight(0)
311+ , m_overlayColor(qRgba(0, 0, 0, 0))
312+{
313+}
314+
315+/*! \qmlproperty rect UbuntuShapeOverlay::overlayGeometry
316+ \since Ubuntu.Components 1.2
317+
318+ This property sets the overlay rectangle. The default value is the empty rectangle.
319+
320+ It is defined by a position and a size in normalized item coordinates (in the range between 0
321+ and 1) with the origin at the top-left corner. An overlay covering the bottom part and starting
322+ at the vertical center can be done like that:
323+
324+ \qml
325+ UbuntuShapeOverlay {
326+ width: 200; height: 200
327+ overlayColor: Qt.rgba(0.0, 0.0, 0.5, 0.5)
328+ overlayGeometry: Qt.rect(0.0, 0.5, 1.0, 0.5)
329+ }
330+ \endqml
331+
332+ Converting a position and a size in pixels to normalized item coordinates can be done with a
333+ division by the size. Here is an equivalent of the previous code sample:
334+
335+ \qml
336+ UbuntuShapeOverlay {
337+ width: 200; height: 200
338+ overlayColor: Qt.rgba(0.0, 0.0, 0.5, 0.5)
339+ overlayGeometry: Qt.rect(100.0/width, 100.0/height, 200.0/width, 100.0/height)
340+ }
341+ \endqml
342+
343+ A geometry exceeding the item area is cropped.
344+*/
345+void UCUbuntuShapeOverlay::setOverlayGeometry(const QRectF& overlayGeometry)
346+{
347+ // Crop rectangle and pack to 16-bit unsigned integers.
348+ const float x = qMax(0.0f, qMin(1.0f, static_cast<float>(overlayGeometry.x())));
349+ float width = qMax(0.0f, static_cast<float>(overlayGeometry.width()));
350+ if ((x + width) > 1.0f) {
351+ width += 1.0f - (x + width);
352+ }
353+ const float y = qMax(0.0f, qMin(1.0f, static_cast<float>(overlayGeometry.y())));
354+ float height = qMax(0.0f, static_cast<float>(overlayGeometry.height()));
355+ if ((y + height) > 1.0f) {
356+ height += 1.0f - (y + height);
357+ }
358+ const float f32toU16 = static_cast<float>(0xffff);
359+ const quint16 overlayX = static_cast<quint16>(x * f32toU16);
360+ const quint16 overlayY = static_cast<quint16>(y * f32toU16);
361+ const quint16 overlayWidth = static_cast<quint16>(width * f32toU16);
362+ const quint16 overlayHeight = static_cast<quint16>(height * f32toU16);
363+
364+ if ((m_overlayX != overlayX) || (m_overlayY != overlayY) ||
365+ (m_overlayWidth != overlayWidth) || (m_overlayHeight != overlayHeight)) {
366+ m_overlayX = overlayX;
367+ m_overlayY = overlayY;
368+ m_overlayWidth = overlayWidth;
369+ m_overlayHeight = overlayHeight;
370+ update();
371+ Q_EMIT overlayGeometryChanged();
372+ }
373+}
374+
375+/*! \qmlproperty color UbuntuShapeOverlay::overlayColor
376+ \since Ubuntu.Components 1.2
377+
378+ This property sets the color of the overlay rectangle defined by \l overlayGeometry. The default
379+ value is transparent black.
380+*/
381+void UCUbuntuShapeOverlay::setOverlayColor(const QColor& overlayColor)
382+{
383+ const QRgb overlayColorRgb = qRgba(
384+ overlayColor.red(), overlayColor.green(), overlayColor.blue(), overlayColor.alpha());
385+ if (m_overlayColor != overlayColorRgb) {
386+ m_overlayColor = overlayColorRgb;
387+ update();
388+ Q_EMIT overlayColorChanged();
389+ }
390+}
391+
392+QSGNode* UCUbuntuShapeOverlay::createSceneGraphNode() const
393+{
394+ return new ShapeOverlayNode;
395+}
396+
397+// Pack to a premultiplied 32-bit ABGR integer.
398+static quint32 packColor(QRgb color)
399+{
400+ const quint32 a = qAlpha(color);
401+ const quint32 b = ((qBlue(color) * a) + 0xff) >> 8;
402+ const quint32 g = ((qGreen(color) * a) + 0xff) >> 8;
403+ const quint32 r = ((qRed(color) * a) + 0xff) >> 8;
404+ return (a << 24) | ((b & 0xff) << 16) | ((g & 0xff) << 8) | (r & 0xff);
405+}
406+
407+void UCUbuntuShapeOverlay::updateGeometry(
408+ QSGNode* node, float width, float height, float radius, const float shapeCoordinate[][2],
409+ const QVector4D& sourceCoordTransform, const QVector4D& sourceMaskTransform,
410+ const quint32 backgroundColor[4])
411+{
412+ ShapeOverlayNode* shapeOverlayNode = static_cast<ShapeOverlayNode*>(node);
413+ ShapeOverlayNode::Vertex* v = reinterpret_cast<ShapeOverlayNode::Vertex*>(
414+ shapeOverlayNode->geometry()->vertexData());
415+
416+ // Convert radius to normalized coordinates.
417+ const float rw = radius / width;
418+ const float rh = radius / height;
419+
420+ // Get the affine transformation for the overlay coordinates, pixels lying inside the mask
421+ // (values in the range [-1, 1]) will be considered overlaid in the fragment shader.
422+ const float u16toF32 = 1.0f / static_cast<float>(0xffff);
423+ const float invOverlayWidth = 1.0f / (m_overlayWidth * u16toF32);
424+ const float invOverlayHeight = 1.0f / (m_overlayHeight * u16toF32);
425+ const float overlaySx = invOverlayWidth * 2.0f;
426+ const float overlaySy = invOverlayHeight * 2.0f;
427+ const float overlayTx = -((m_overlayX * u16toF32) * invOverlayWidth) * 2.0f - 1.0f;
428+ const float overlayTy = -((m_overlayY * u16toF32) * invOverlayHeight) * 2.0f - 1.0f;
429+
430+ // The overlay affine transformation doesn't exist when width or height is null, whenever that
431+ // is the case we simply set the color to transparent. GPUs handle NaN/Inf values flowlessly.
432+ const quint32 overlayColor = qIsFinite(invOverlayHeight + invOverlayWidth) ?
433+ packColor(m_overlayColor) : 0x00000000;
434+
435+ // Set top row of 4 vertices.
436+ v[0].position[0] = 0.0f;
437+ v[0].position[1] = 0.0f;
438+ v[0].shapeCoordinate[0] = shapeCoordinate[0][0];
439+ v[0].shapeCoordinate[1] = shapeCoordinate[0][1];
440+ v[0].sourceCoordinate[0] = sourceCoordTransform.z();
441+ v[0].sourceCoordinate[1] = sourceCoordTransform.w();
442+ v[0].sourceCoordinate[2] = sourceMaskTransform.z();
443+ v[0].sourceCoordinate[3] = sourceMaskTransform.w();
444+ v[0].backgroundColor = backgroundColor[0];
445+ v[0].overlayCoordinate[0] = overlayTx;
446+ v[0].overlayCoordinate[1] = overlayTy;
447+ v[0].overlayColor = overlayColor;
448+ v[1].position[0] = radius;
449+ v[1].position[1] = 0.0f;
450+ v[1].shapeCoordinate[0] = shapeCoordinate[1][0];
451+ v[1].shapeCoordinate[1] = shapeCoordinate[1][1];
452+ v[1].sourceCoordinate[0] = rw * sourceCoordTransform.x() + sourceCoordTransform.z();
453+ v[1].sourceCoordinate[1] = sourceCoordTransform.w();
454+ v[1].sourceCoordinate[2] = rw * sourceMaskTransform.x() + sourceMaskTransform.z();
455+ v[1].sourceCoordinate[3] = sourceMaskTransform.w();
456+ v[1].backgroundColor = backgroundColor[0];
457+ v[1].overlayCoordinate[0] = rw * overlaySx + overlayTx;
458+ v[1].overlayCoordinate[1] = overlayTy;
459+ v[1].overlayColor = overlayColor;
460+ v[2].position[0] = width - radius;
461+ v[2].position[1] = 0.0f;
462+ v[2].shapeCoordinate[0] = shapeCoordinate[2][0];
463+ v[2].shapeCoordinate[1] = shapeCoordinate[2][1];
464+ v[2].sourceCoordinate[0] = (1.0f - rw) * sourceCoordTransform.x() + sourceCoordTransform.z();
465+ v[2].sourceCoordinate[1] = sourceCoordTransform.w();
466+ v[2].sourceCoordinate[2] = (1.0f - rw) * sourceMaskTransform.x() + sourceMaskTransform.z();
467+ v[2].sourceCoordinate[3] = sourceMaskTransform.w();
468+ v[2].backgroundColor = backgroundColor[0];
469+ v[2].overlayCoordinate[0] = (1.0f - rw) * overlaySx + overlayTx;
470+ v[2].overlayCoordinate[1] = overlayTy;
471+ v[2].overlayColor = overlayColor;
472+ v[3].position[0] = width;
473+ v[3].position[1] = 0.0f;
474+ v[3].shapeCoordinate[0] = shapeCoordinate[3][0];
475+ v[3].shapeCoordinate[1] = shapeCoordinate[3][1];
476+ v[3].sourceCoordinate[0] = sourceCoordTransform.x() + sourceCoordTransform.z();
477+ v[3].sourceCoordinate[1] = sourceCoordTransform.w();
478+ v[3].sourceCoordinate[2] = sourceMaskTransform.x() + sourceMaskTransform.z();
479+ v[3].sourceCoordinate[3] = sourceMaskTransform.w();
480+ v[3].backgroundColor = backgroundColor[0];
481+ v[3].overlayCoordinate[0] = overlaySx + overlayTx;
482+ v[3].overlayCoordinate[1] = overlayTy;
483+ v[3].overlayColor = overlayColor;
484+
485+ // Set middle-top row of 4 vertices.
486+ v[4].position[0] = 0.0f;
487+ v[4].position[1] = radius;
488+ v[4].shapeCoordinate[0] = shapeCoordinate[4][0];
489+ v[4].shapeCoordinate[1] = shapeCoordinate[4][1];
490+ v[4].sourceCoordinate[0] = sourceCoordTransform.z();
491+ v[4].sourceCoordinate[1] = rh * sourceCoordTransform.y() + sourceCoordTransform.w();
492+ v[4].sourceCoordinate[2] = sourceMaskTransform.z();
493+ v[4].sourceCoordinate[3] = rh * sourceMaskTransform.y() + sourceMaskTransform.w();
494+ v[4].backgroundColor = backgroundColor[1];
495+ v[4].overlayCoordinate[0] = overlayTx;
496+ v[4].overlayCoordinate[1] = rh * overlaySy + overlayTy;
497+ v[4].overlayColor = overlayColor;
498+ v[5].position[0] = radius;
499+ v[5].position[1] = radius;
500+ v[5].shapeCoordinate[0] = shapeCoordinate[5][0];
501+ v[5].shapeCoordinate[1] = shapeCoordinate[5][1];
502+ v[5].sourceCoordinate[0] = rw * sourceCoordTransform.x() + sourceCoordTransform.z();
503+ v[5].sourceCoordinate[1] = rh * sourceCoordTransform.y() + sourceCoordTransform.w();
504+ v[5].sourceCoordinate[2] = rw * sourceMaskTransform.x() + sourceMaskTransform.z();
505+ v[5].sourceCoordinate[3] = rh * sourceMaskTransform.y() + sourceMaskTransform.w();
506+ v[5].backgroundColor = backgroundColor[1];
507+ v[5].overlayCoordinate[0] = rw * overlaySx + overlayTx;
508+ v[5].overlayCoordinate[1] = rh * overlaySy + overlayTy;
509+ v[5].overlayColor = overlayColor;
510+ v[6].position[0] = width - radius;
511+ v[6].position[1] = radius;
512+ v[6].shapeCoordinate[0] = shapeCoordinate[6][0];
513+ v[6].shapeCoordinate[1] = shapeCoordinate[6][1];
514+ v[6].sourceCoordinate[0] = (1.0f - rw) * sourceCoordTransform.x() + sourceCoordTransform.z();
515+ v[6].sourceCoordinate[1] = rh * sourceCoordTransform.y() + sourceCoordTransform.w();
516+ v[6].sourceCoordinate[2] = (1.0f - rw) * sourceMaskTransform.x() + sourceMaskTransform.z();
517+ v[6].sourceCoordinate[3] = rh * sourceMaskTransform.y() + sourceMaskTransform.w();
518+ v[6].backgroundColor = backgroundColor[1];
519+ v[6].overlayCoordinate[0] = (1.0f - rw) * overlaySx + overlayTx;
520+ v[6].overlayCoordinate[1] = rh * overlaySy + overlayTy;
521+ v[6].overlayColor = overlayColor;
522+ v[7].position[0] = width;
523+ v[7].position[1] = radius;
524+ v[7].shapeCoordinate[0] = shapeCoordinate[7][0];
525+ v[7].shapeCoordinate[1] = shapeCoordinate[7][1];
526+ v[7].sourceCoordinate[0] = sourceCoordTransform.x() + sourceCoordTransform.z();
527+ v[7].sourceCoordinate[1] = rh * sourceCoordTransform.y() + sourceCoordTransform.w();
528+ v[7].sourceCoordinate[2] = sourceMaskTransform.x() + sourceMaskTransform.z();
529+ v[7].sourceCoordinate[3] = rh * sourceMaskTransform.y() + sourceMaskTransform.w();
530+ v[7].backgroundColor = backgroundColor[1];
531+ v[7].overlayCoordinate[0] = overlaySx + overlayTx;
532+ v[7].overlayCoordinate[1] = rh * overlaySy + overlayTy;
533+ v[7].overlayColor = overlayColor;
534+
535+ // Set middle-bottom row of 4 vertices.
536+ v[8].position[0] = 0.0f;
537+ v[8].position[1] = height - radius;
538+ v[8].shapeCoordinate[0] = shapeCoordinate[8][0];
539+ v[8].shapeCoordinate[1] = shapeCoordinate[8][1];
540+ v[8].sourceCoordinate[0] = sourceCoordTransform.z();
541+ v[8].sourceCoordinate[1] = (1.0f - rh) * sourceCoordTransform.y() + sourceCoordTransform.w();
542+ v[8].sourceCoordinate[2] = sourceMaskTransform.z();
543+ v[8].sourceCoordinate[3] = (1.0f - rh) * sourceMaskTransform.y() + sourceMaskTransform.w();
544+ v[8].backgroundColor = backgroundColor[2];
545+ v[8].overlayCoordinate[0] = overlayTx;
546+ v[8].overlayCoordinate[1] = (1.0f - rh) * overlaySy + overlayTy;
547+ v[8].overlayColor = overlayColor;
548+ v[9].position[0] = radius;
549+ v[9].position[1] = height - radius;
550+ v[9].shapeCoordinate[0] = shapeCoordinate[9][0];
551+ v[9].shapeCoordinate[1] = shapeCoordinate[9][1];
552+ v[9].sourceCoordinate[0] = rw * sourceCoordTransform.x() + sourceCoordTransform.z();
553+ v[9].sourceCoordinate[1] = (1.0f - rh) * sourceCoordTransform.y() + sourceCoordTransform.w();
554+ v[9].sourceCoordinate[2] = rw * sourceMaskTransform.x() + sourceMaskTransform.z();
555+ v[9].sourceCoordinate[3] = (1.0f - rh) * sourceMaskTransform.y() + sourceMaskTransform.w();
556+ v[9].backgroundColor = backgroundColor[2];
557+ v[9].overlayCoordinate[0] = rw * overlaySx + overlayTx;
558+ v[9].overlayCoordinate[1] = (1.0f - rh) * overlaySy + overlayTy;
559+ v[9].overlayColor = overlayColor;
560+ v[10].position[0] = width - radius;
561+ v[10].position[1] = height - radius;
562+ v[10].shapeCoordinate[0] = shapeCoordinate[10][0];
563+ v[10].shapeCoordinate[1] = shapeCoordinate[10][1];
564+ v[10].sourceCoordinate[0] = (1.0f - rw) * sourceCoordTransform.x() + sourceCoordTransform.z();
565+ v[10].sourceCoordinate[1] = (1.0f - rh) * sourceCoordTransform.y() + sourceCoordTransform.w();
566+ v[10].sourceCoordinate[2] = (1.0f - rw) * sourceMaskTransform.x() + sourceMaskTransform.z();
567+ v[10].sourceCoordinate[3] = (1.0f - rh) * sourceMaskTransform.y() + sourceMaskTransform.w();
568+ v[10].backgroundColor = backgroundColor[2];
569+ v[10].overlayCoordinate[0] = (1.0f - rw) * overlaySx + overlayTx;
570+ v[10].overlayCoordinate[1] = (1.0f - rh) * overlaySy + overlayTy;
571+ v[10].overlayColor = overlayColor;
572+ v[11].position[0] = width;
573+ v[11].position[1] = height - radius;
574+ v[11].shapeCoordinate[0] = shapeCoordinate[11][0];
575+ v[11].shapeCoordinate[1] = shapeCoordinate[11][1];
576+ v[11].sourceCoordinate[0] = sourceCoordTransform.x() + sourceCoordTransform.z();
577+ v[11].sourceCoordinate[1] = (1.0f - rh) * sourceCoordTransform.y() + sourceCoordTransform.w();
578+ v[11].sourceCoordinate[2] = sourceMaskTransform.x() + sourceMaskTransform.z();
579+ v[11].sourceCoordinate[3] = (1.0f - rh) * sourceMaskTransform.y() + sourceMaskTransform.w();
580+ v[11].backgroundColor = backgroundColor[2];
581+ v[11].overlayCoordinate[0] = overlaySx + overlayTx;
582+ v[11].overlayCoordinate[1] = (1.0f - rh) * overlaySy + overlayTy;
583+ v[11].overlayColor = overlayColor;
584+
585+ // Set bottom row of 4 vertices.
586+ v[12].position[0] = 0.0f;
587+ v[12].position[1] = height;
588+ v[12].shapeCoordinate[0] = shapeCoordinate[12][0];
589+ v[12].shapeCoordinate[1] = shapeCoordinate[12][1];
590+ v[12].sourceCoordinate[0] = sourceCoordTransform.z();
591+ v[12].sourceCoordinate[1] = sourceCoordTransform.y() + sourceCoordTransform.w();
592+ v[12].sourceCoordinate[2] = sourceMaskTransform.z();
593+ v[12].sourceCoordinate[3] = sourceMaskTransform.y() + sourceMaskTransform.w();
594+ v[12].backgroundColor = backgroundColor[3];
595+ v[12].overlayCoordinate[0] = overlayTx;
596+ v[12].overlayCoordinate[1] = overlaySy + overlayTy;
597+ v[12].overlayColor = overlayColor;
598+ v[13].position[0] = radius;
599+ v[13].position[1] = height;
600+ v[13].shapeCoordinate[0] = shapeCoordinate[13][0];
601+ v[13].shapeCoordinate[1] = shapeCoordinate[13][1];
602+ v[13].sourceCoordinate[0] = rw * sourceCoordTransform.x() + sourceCoordTransform.z();
603+ v[13].sourceCoordinate[1] = sourceCoordTransform.y() + sourceCoordTransform.w();
604+ v[13].sourceCoordinate[2] = rw * sourceMaskTransform.x() + sourceMaskTransform.z();
605+ v[13].sourceCoordinate[3] = sourceMaskTransform.y() + sourceMaskTransform.w();
606+ v[13].backgroundColor = backgroundColor[3];
607+ v[13].overlayCoordinate[0] = rw * overlaySx + overlayTx;
608+ v[13].overlayCoordinate[1] = overlaySy + overlayTy;
609+ v[13].overlayColor = overlayColor;
610+ v[14].position[0] = width - radius;
611+ v[14].position[1] = height;
612+ v[14].shapeCoordinate[0] = shapeCoordinate[14][0];
613+ v[14].shapeCoordinate[1] = shapeCoordinate[14][1];
614+ v[14].sourceCoordinate[0] = (1.0f - rw) * sourceCoordTransform.x() + sourceCoordTransform.z();
615+ v[14].sourceCoordinate[1] = sourceCoordTransform.y() + sourceCoordTransform.w();
616+ v[14].sourceCoordinate[2] = (1.0f - rw) * sourceMaskTransform.x() + sourceMaskTransform.z();
617+ v[14].sourceCoordinate[3] = sourceMaskTransform.y() + sourceMaskTransform.w();
618+ v[14].backgroundColor = backgroundColor[3];
619+ v[14].overlayCoordinate[0] = (1.0f - rw) * overlaySx + overlayTx;
620+ v[14].overlayCoordinate[1] = overlaySy + overlayTy;
621+ v[14].overlayColor = overlayColor;
622+ v[15].position[0] = width;
623+ v[15].position[1] = height;
624+ v[15].shapeCoordinate[0] = shapeCoordinate[15][0];
625+ v[15].shapeCoordinate[1] = shapeCoordinate[15][1];
626+ v[15].sourceCoordinate[0] = sourceCoordTransform.x() + sourceCoordTransform.z();
627+ v[15].sourceCoordinate[1] = sourceCoordTransform.y() + sourceCoordTransform.w();
628+ v[15].sourceCoordinate[2] = sourceMaskTransform.x() + sourceMaskTransform.z();
629+ v[15].sourceCoordinate[3] = sourceMaskTransform.y() + sourceMaskTransform.w();
630+ v[15].backgroundColor = backgroundColor[3];
631+ v[15].overlayCoordinate[0] = overlaySx + overlayTx;
632+ v[15].overlayCoordinate[1] = overlaySy + overlayTy;
633+ v[15].overlayColor = overlayColor;
634+
635+ node->markDirty(QSGNode::DirtyGeometry);
636+}
637
638=== added file 'modules/Ubuntu/Components/plugin/ucubuntushapeoverlay.h'
639--- modules/Ubuntu/Components/plugin/ucubuntushapeoverlay.h 1970-01-01 00:00:00 +0000
640+++ modules/Ubuntu/Components/plugin/ucubuntushapeoverlay.h 2015-02-27 14:02:19 +0000
641@@ -0,0 +1,112 @@
642+/*
643+ * Copyright 2015 Canonical Ltd.
644+ *
645+ * This program is free software; you can redistribute it and/or modify
646+ * it under the terms of the GNU Lesser General Public License as published by
647+ * the Free Software Foundation; version 3.
648+ *
649+ * This program is distributed in the hope that it will be useful,
650+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
651+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
652+ * GNU Lesser General Public License for more details.
653+ *
654+ * You should have received a copy of the GNU Lesser General Public License
655+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
656+ *
657+ * Author: Loïc Molinari <loic.molinari@canonical.com>
658+ */
659+
660+#ifndef UCUBUNTUSHAPEOVERLAY_H
661+#define UCUBUNTUSHAPEOVERLAY_H
662+
663+#include "ucubuntushape.h"
664+
665+// --- Scene graph shader ---
666+
667+class ShapeOverlayShader : public ShapeShader
668+{
669+public:
670+ ShapeOverlayShader();
671+ virtual char const* const* attributeNames() const;
672+};
673+
674+// --- Scene graph material ---
675+
676+class ShapeOverlayMaterial : public ShapeMaterial
677+{
678+public:
679+ virtual QSGMaterialType* type() const;
680+ virtual QSGMaterialShader* createShader() const;
681+};
682+
683+// --- Scene graph node ---
684+
685+class ShapeOverlayNode : public QSGGeometryNode
686+{
687+public:
688+ struct Vertex {
689+ float position[2];
690+ float shapeCoordinate[2];
691+ float sourceCoordinate[4];
692+ quint32 backgroundColor;
693+ float overlayCoordinate[2];
694+ quint32 overlayColor;
695+ };
696+
697+ static const QSGGeometry::AttributeSet& attributeSet();
698+
699+ ShapeOverlayNode();
700+ QSGGeometry* geometry() { return &m_geometry; }
701+
702+private:
703+ ShapeOverlayMaterial m_material;
704+ QSGGeometry m_geometry;
705+};
706+
707+// --- QtQuick item ---
708+
709+class UCUbuntuShapeOverlay : public UCUbuntuShape
710+{
711+ Q_OBJECT
712+
713+ // Overlay properties.
714+ Q_PROPERTY(QRectF overlayGeometry READ overlayGeometry WRITE setOverlayGeometry
715+ NOTIFY overlayGeometryChanged)
716+ Q_PROPERTY(QColor overlayColor READ overlayColor WRITE setOverlayColor
717+ NOTIFY overlayColorChanged)
718+
719+public:
720+ UCUbuntuShapeOverlay(QQuickItem* parent=0);
721+
722+ QRectF overlayGeometry() const {
723+ const float u16ToF32 = 1.0f / static_cast<float>(0xffff);
724+ return QRectF(m_overlayX * u16ToF32, m_overlayY * u16ToF32, m_overlayWidth * u16ToF32,
725+ m_overlayHeight * u16ToF32); }
726+ void setOverlayGeometry(const QRectF& overlayGeometry);
727+ QColor overlayColor() const { return m_overlayColor; }
728+ void setOverlayColor(const QColor& overlayColor);
729+
730+Q_SIGNALS:
731+ void overlayGeometryChanged();
732+ void overlayColorChanged();
733+
734+protected:
735+ virtual QSGNode* createSceneGraphNode() const;
736+ virtual void updateGeometry(
737+ QSGNode* node, float width, float height, float radius, const float shapeCoordinate[][2],
738+ const QVector4D& sourceCoordTransform, const QVector4D& sourceMaskTransform,
739+ const quint32 backgroundColor[4]);
740+
741+private:
742+ quint16 m_overlayX;
743+ quint16 m_overlayY;
744+ quint16 m_overlayWidth;
745+ quint16 m_overlayHeight;
746+ QRgb m_overlayColor;
747+
748+ Q_DISABLE_COPY(UCUbuntuShapeOverlay)
749+};
750+
751+QML_DECLARE_TYPE(UCUbuntuShapeOverlay)
752+
753+#endif // UCUBUNTUSHAPEOVERLAY_H
754
755=== added file 'tests/resources/ubuntushape/UbuntuShapeOverlayTest.qml'
756--- tests/resources/ubuntushape/UbuntuShapeOverlayTest.qml 1970-01-01 00:00:00 +0000
757+++ tests/resources/ubuntushape/UbuntuShapeOverlayTest.qml 2015-02-27 14:02:19 +0000
758@@ -0,0 +1,330 @@
759+/*
760+ * Copyright 2015 Canonical Ltd.
761+ *
762+ * This program is free software; you can redistribute it and/or modify
763+ * it under the terms of the GNU Lesser General Public License as published by
764+ * the Free Software Foundation; version 3.
765+ *
766+ * This program is distributed in the hope that it will be useful,
767+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
768+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
769+ * GNU Lesser General Public License for more details.
770+ *
771+ * You should have received a copy of the GNU Lesser General Public License
772+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
773+ */
774+
775+import QtQuick 2.0
776+import Ubuntu.Components 1.2
777+
778+Item {
779+ id: root
780+ width: 1000
781+ height: 600
782+ focus: true
783+
784+ // Enum to string tables.
785+ property variant backgroundModeTable: [
786+ "SolidColor", "VerticalGradient"
787+ ]
788+ property variant sourceHAlignmentTable: [
789+ "AlignLeft", "AlignHCenter", "AlignRight"
790+ ]
791+ property variant sourceVAlignmentTable: [
792+ "AlignTop", "AlignVCenter", "AlignBottom"
793+ ]
794+ property variant sourceFillModeTable: [
795+ "Stretch", "PreserveAspectFit", "PreserveAspectCrop", "Pad"
796+ ]
797+ property variant wrapModeTable: [
798+ "Transparent", "Repeat"
799+ ]
800+ property variant imageFillModeTable: [
801+ "Stretch", "PreserveAspectFit", "PreserveAspectCrop", "Tile", "TileVertically",
802+ "TileHorizontally", "Pad"
803+ ]
804+
805+ // Zoom properties.
806+ property variant translation: Qt.point(0.0, 0.0)
807+ property real scaleBase: 1.1
808+ property real scaleExponent: 0.0
809+ property real minScaleExponent: 0.0
810+ property real maxScaleExponent: 48.317715856 // Logarithm of 100 to base 1.1 (scaleBase).
811+ property real scaleFactor: 1.0
812+ property real scale: 1.0
813+
814+ // Overlay text properties. French keymapping... :)
815+ property string textOverlayString:
816+ "Zoom (scroll): x " + root.scaleFactor.toFixed(1) + "\n\n" +
817+ "Background colors (a/z): " + shape.backgroundColor + ", " + shape.secondaryBackgroundColor + "\n" +
818+ "Background mode (e): " + root.backgroundModeTable[shape.backgroundMode] + "\n\n" +
819+ "Overlay color (r): " + shape.overlayColor + "\n" +
820+ "Overlay geometry (t/y/u/i): " + shape.overlayGeometry.x.toFixed(2) + ", " + shape.overlayGeometry.y.toFixed(2) + ", " + shape.overlayGeometry.width.toFixed(2) + ", " + shape.overlayGeometry.height.toFixed(2) + "\n\n" +
821+ "Source (o): " + shape.source + "\n" +
822+ "Source opacity (p): " + shape.sourceOpacity.toFixed(2) + "\n" +
823+ "Source fill (q): " + root.sourceFillModeTable[shape.sourceFillMode] + "\n" +
824+ "Source hwrap (s): " + root.wrapModeTable[shape.sourceHorizontalWrapMode] + "\n" +
825+ "Source vwrap (d): " + root.wrapModeTable[shape.sourceVerticalWrapMode] + "\n" +
826+ "Source halign (f): " + root.sourceHAlignmentTable[shape.sourceHorizontalAlignment] + "\n" +
827+ "Source valign (g): " + root.sourceVAlignmentTable[shape.sourceVerticalAlignment] + "\n" +
828+ "Source translation (h/j): " + shape.sourceTranslation.x.toFixed(2) + ", " + shape.sourceTranslation.y.toFixed(2) + "\n" +
829+ "Source scale (k/l): " + shape.sourceScale.x.toFixed(2) + ", " + shape.sourceScale.y.toFixed(2) + "\n\n" +
830+ "Image (deprecated) (m): " + shape.image + "\n" +
831+ "Image fill (w): " + root.imageFillModeTable[img1.fillMode] + "\n" +
832+ "Image halign (x): " + img1.horizontalAlignment + "\n" +
833+ "Image valign (c): " + img1.verticalAlignment + "\n\n" +
834+ "Radius (v): " + "\"" + shape.radius + "\"\n" +
835+ "Border (b): " + "\"" + shape.borderSource + "\"\n\n" +
836+ "Colors (deprecated) (n/,): " + shape.color + ", " + shape.gradientColor
837+
838+ // Main scene.
839+ Item {
840+ id: scene
841+ anchors.fill: parent
842+
843+ Image {
844+ id: background
845+ anchors.fill: parent
846+ source: "background.jpg"
847+ fillMode: Image.Tile
848+ }
849+
850+ // Put the UbuntuShape source image in the middle of a texture atlas. We use img1.
851+ Image { id: img1; visible: false; source: "img1.png"; }
852+ Image { id: img2; visible: false; source: "img2.png"; }
853+ Image { id: img3; visible: false; source: "img3.png"; }
854+ Image { id: img4; visible: false; source: "img4.png"; }
855+
856+ UbuntuShapeOverlay {
857+ id: shape
858+ anchors.fill: parent
859+ anchors.leftMargin: 400
860+ anchors.rightMargin: 100
861+ anchors.topMargin: 100
862+ anchors.bottomMargin: 100
863+ }
864+ }
865+
866+ // Zoom support.
867+ ShaderEffectSource {
868+ id: shaderEffectSource
869+ anchors.fill: scene
870+ sourceItem: scene
871+ hideSource: true
872+ visible: false
873+ smooth: false
874+ }
875+ ShaderEffect {
876+ anchors.fill: scene
877+ property variant tex: shaderEffectSource
878+ property variant translation: root.translation
879+ property real scaleFactor: root.scale
880+ vertexShader: "
881+ uniform mat4 qt_Matrix;
882+ uniform float scaleFactor;
883+ uniform vec2 translation;
884+ attribute vec4 qt_Vertex;
885+ attribute vec2 qt_MultiTexCoord0;
886+ varying vec2 texCoord;
887+ void main() {
888+ texCoord = vec2(scaleFactor) * qt_MultiTexCoord0 + translation;
889+ gl_Position = qt_Matrix * qt_Vertex;
890+ }"
891+ fragmentShader: "
892+ uniform sampler2D tex;
893+ uniform float qt_Opacity;
894+ varying vec2 texCoord;
895+ void main() {
896+ gl_FragColor = texture2D(tex, texCoord) * qt_Opacity;
897+ }"
898+ }
899+
900+ // Text overlay.
901+ Text {
902+ id: textOverlay
903+ width:200
904+ anchors.top: parent.top
905+ anchors.topMargin: 10
906+ anchors.left: parent.left
907+ anchors.leftMargin: 10
908+ font.family: "Ubuntu Mono"
909+ font.pixelSize: 14
910+ font.weight: Font.Bold
911+ color: "black"
912+ text: textOverlayString
913+ }
914+
915+ // Mouse handling.
916+ MouseArea {
917+ id: mouseArea
918+ anchors.fill: parent
919+ acceptedButtons: Qt.LeftButton
920+ hoverEnabled: true
921+
922+ property real lastX: 0.0
923+ property real lastY: 0.0
924+
925+ onPressed: {
926+ if (pressedButtons & Qt.LeftButton) {
927+ lastX = mouseX;
928+ lastY = mouseY;
929+ }
930+ }
931+ onPositionChanged: {
932+ if (pressedButtons & Qt.LeftButton) {
933+ var tx = root.translation.x;
934+ var ty = root.translation.y;
935+ var sx = root.scale / root.width;
936+ var sy = root.scale / root.height;
937+ var x = mouseX - lastX;
938+ var y = mouseY - lastY;
939+ root.translation = Qt.point(Math.max(0.0, Math.min(1.0 - root.scale, tx - sx * x)),
940+ Math.max(0.0, Math.min(1.0 - root.scale, ty - sy * y)));
941+ lastX = mouseX;
942+ lastY = mouseY;
943+ }
944+ }
945+ onWheel: {
946+ root.scaleExponent = Math.max(minScaleExponent, Math.min(maxScaleExponent,
947+ root.scaleExponent + (wheel.angleDelta.y < 0.0 ? -1.0 : 1.0)));
948+ root.scaleFactor = Math.pow(root.scaleBase, root.scaleExponent);
949+ var oldScale = root.scale;
950+ root.scale = 1.0 / root.scaleFactor;
951+ var s = oldScale - root.scale;
952+ var tx = root.translation.x;
953+ var ty = root.translation.y;
954+ var x = mouseX / root.width;
955+ var y = mouseY / root.height;
956+ root.translation = Qt.point(Math.max(0.0, Math.min(1.0 - root.scale, tx + s * x)),
957+ Math.max(0.0, Math.min(1.0 - root.scale, ty + s * y)));
958+ }
959+ }
960+
961+ // Keyboard handling.
962+ Keys.onPressed: {
963+ var shift = Qt.ShiftModifier;
964+
965+ // Background.
966+ if (event.key == Qt.Key_A) {
967+ shape.backgroundColor = Qt.rgba(
968+ Math.random(), Math.random(), Math.random(), Math.random());
969+ } else if (event.key == Qt.Key_Z) {
970+ shape.secondaryBackgroundColor = Qt.rgba(
971+ Math.random(), Math.random(), Math.random(), Math.random());
972+ } else if (event.key == Qt.Key_E) {
973+ shape.backgroundMode = (shape.backgroundMode + 1) % 3;
974+
975+ // Overlay.
976+ } else if (event.key == Qt.Key_R) {
977+ shape.overlayColor = Qt.rgba(
978+ Math.random(), Math.random(), Math.random(), Math.random());
979+ } else if (event.key == Qt.Key_T) {
980+ var x = Math.max(0.0, Math.min(1.0,
981+ shape.overlayGeometry.x + ((event.modifiers & shift) ? 0.005 : -0.005)));
982+ shape.overlayGeometry = Qt.rect(
983+ x, shape.overlayGeometry.y, shape.overlayGeometry.width,
984+ shape.overlayGeometry.height);
985+ } else if (event.key == Qt.Key_Y) {
986+ var y = Math.max(0.0, Math.min(1.0,
987+ shape.overlayGeometry.y + ((event.modifiers & shift) ? 0.005 : -0.005)));
988+ shape.overlayGeometry = Qt.rect(
989+ shape.overlayGeometry.x, y, shape.overlayGeometry.width,
990+ shape.overlayGeometry.height);
991+ } else if (event.key == Qt.Key_U) {
992+ var width = Math.max(0.0, Math.min(1.0,
993+ shape.overlayGeometry.width + ((event.modifiers & shift) ? 0.005 : -0.005)));
994+ shape.overlayGeometry = Qt.rect(
995+ shape.overlayGeometry.x, shape.overlayGeometry.y, width,
996+ shape.overlayGeometry.height);
997+ } else if (event.key == Qt.Key_I) {
998+ var height = Math.max(0.0, Math.min(1.0,
999+ shape.overlayGeometry.height + ((event.modifiers & shift) ? 0.005 : -0.005)));
1000+ shape.overlayGeometry = Qt.rect(
1001+ shape.overlayGeometry.x, shape.overlayGeometry.y, shape.overlayGeometry.width,
1002+ height);
1003+
1004+ // Source.
1005+ } else if (event.key == Qt.Key_O) {
1006+ if (shape.source == null) {
1007+ shape.source = img1;
1008+ } else {
1009+ shape.source = null;
1010+ }
1011+ } else if (event.key == Qt.Key_P) {
1012+ shape.sourceOpacity = Math.max(0.0, Math.min(
1013+ 1.0, shape.sourceOpacity + ((event.modifiers & shift) ? 0.01 : -0.01)));
1014+ } else if (event.key == Qt.Key_Q) {
1015+ shape.sourceFillMode = (shape.sourceFillMode + 1) % 4;
1016+ } else if (event.key == Qt.Key_S) {
1017+ shape.sourceHorizontalWrapMode = (shape.sourceHorizontalWrapMode + 1) % 3;
1018+ } else if (event.key == Qt.Key_D) {
1019+ shape.sourceVerticalWrapMode = (shape.sourceVerticalWrapMode + 1) % 3;
1020+ } else if (event.key == Qt.Key_F) {
1021+ shape.sourceHorizontalAlignment = (shape.sourceHorizontalAlignment + 1) % 3;
1022+ } else if (event.key == Qt.Key_G) {
1023+ shape.sourceVerticalAlignment = (shape.sourceVerticalAlignment + 1) % 3;
1024+ } else if (event.key == Qt.Key_H) {
1025+ shape.sourceTranslation = Qt.vector2d(
1026+ shape.sourceTranslation.x + ((event.modifiers & shift) ? 0.01 : -0.01),
1027+ shape.sourceTranslation.y);
1028+ } else if (event.key == Qt.Key_J) {
1029+ shape.sourceTranslation = Qt.vector2d(
1030+ shape.sourceTranslation.x,
1031+ shape.sourceTranslation.y + ((event.modifiers & shift) ? 0.01 : -0.01));
1032+ } else if (event.key == Qt.Key_K) {
1033+ shape.sourceScale = Qt.vector2d(
1034+ shape.sourceScale.x + ((event.modifiers & shift) ? 0.02 : -0.02),
1035+ shape.sourceScale.y);
1036+ } else if (event.key == Qt.Key_L) {
1037+ shape.sourceScale = Qt.vector2d(
1038+ shape.sourceScale.x,
1039+ shape.sourceScale.y + ((event.modifiers & shift) ? 0.02 : -0.02));
1040+
1041+ // Image.
1042+ } else if (event.key == Qt.Key_M) {
1043+ if (shape.image == null) {
1044+ shape.image = img1;
1045+ } else {
1046+ shape.image = null;
1047+ }
1048+ } else if (event.key == Qt.Key_W) {
1049+ img1.fillMode = (img1.fillMode + 1) % 7;
1050+ } else if (event.key == Qt.Key_X) {
1051+ if (img1.horizontalAlignment == Image.AlignLeft) {
1052+ img1.horizontalAlignment = Image.AlignHCenter;
1053+ } else if (img1.horizontalAlignment == Image.AlignHCenter) {
1054+ img1.horizontalAlignment = Image.AlignRight;
1055+ } else {
1056+ img1.horizontalAlignment = Image.AlignLeft;
1057+ }
1058+ } else if (event.key == Qt.Key_C) {
1059+ if (img1.verticalAlignment == Image.AlignTop) {
1060+ img1.verticalAlignment = Image.AlignVCenter;
1061+ } else if (img1.verticalAlignment == Image.AlignVCenter) {
1062+ img1.verticalAlignment = Image.AlignBottom;
1063+ } else {
1064+ img1.verticalAlignment = Image.AlignTop;
1065+ }
1066+
1067+ // Styling.
1068+ } else if (event.key == Qt.Key_V) {
1069+ shape.radius = (shape.radius == "medium") ? "small" : "medium";
1070+ } else if (event.key == Qt.Key_B) {
1071+ if (shape.borderSource == "radius_idle.sci") {
1072+ shape.borderSource = "radius_pressed.sci";
1073+ } else if (shape.borderSource == "radius_pressed.sci") {
1074+ shape.borderSource = "";
1075+ } else {
1076+ shape.borderSource = "radius_idle.sci";
1077+ }
1078+
1079+ // Colors.
1080+ } else if (event.key == Qt.Key_N) {
1081+ shape.color = Qt.rgba(
1082+ Math.random(), Math.random(), Math.random(), Math.random());
1083+ } else if (event.key == Qt.Key_Comma) {
1084+ shape.gradientColor = Qt.rgba(
1085+ Math.random(), Math.random(), Math.random(), Math.random());
1086+ }
1087+ }
1088+}

Subscribers

People subscribed via source and target branches