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

Proposed by Loïc Molinari
Status: Merged
Approved by: Zsombor Egri
Approved revision: 1339
Merged at revision: 1439
Proposed branch: lp:~loic.molinari/ubuntu-ui-toolkit/ubuntu-ui-toolkit-shape-overlay
Merge into: lp:ubuntu-ui-toolkit/staging
Diff against target: 1104 lines (+1015/-1)
9 files modified
components.api (+5/-0)
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 Approve
Zsombor Egri Approve
Review via email: mp+251808@code.launchpad.net

This proposal supersedes a proposal from 2014-12-01.

Commit message

Added a dedicated overlay shape.

Made use of the new UbuntuShape architecture to create an extended shape with efficient overlay rendering.

Description of the change

Added a dedicated overlay shape.

Made use of the new UbuntuShape architecture to create an extended shape with efficient overlay rendering.

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

Some preliminary comments inline.

review: Needs Fixing
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote : Posted in a previous version of this proposal
review: Needs Fixing (continuous-integration)
1337. By Loïc Molinari

Merged main branch.

1338. By Loïc Molinari

Updated components.api.

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

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

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 :

Small cosmetic comments inline.

Let's discuss whether we create a new FFe then we can keep this in 1.2. If we do so, a small description in "What's new..." documentation section should be also added. Then we land it.

review: Needs Fixing
Revision history for this message
Zsombor Egri (zsombi) wrote :

Let's try to land this in 1.2 still!

review: Approve
Revision history for this message
PS Jenkins bot (ps-jenkins) :
review: Approve (continuous-integration)

Preview Diff

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

Subscribers

People subscribed via source and target branches