Merge lp:~loic.molinari/ubuntu-ui-toolkit/ubuntu-ui-toolkit-shape-overlay into lp:ubuntu-ui-toolkit/staging
- ubuntu-ui-toolkit-shape-overlay
- Merge into staging
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 |
Related bugs: |
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.
PS Jenkins bot (ps-jenkins) wrote : Posted in a previous version of this proposal | # |
PS Jenkins bot (ps-jenkins) wrote : Posted in a previous version of this proposal | # |
FAILED: Continuous integration, rev:1326
http://
Executed test runs:
FAILURE: http://
FAILURE: http://
FAILURE: http://
FAILURE: http://
FAILURE: http://
Click here to trigger a rebuild:
http://
Zsombor Egri (zsombi) wrote : Posted in a previous version of this proposal | # |
Some preliminary comments inline.
PS Jenkins bot (ps-jenkins) wrote : Posted in a previous version of this proposal | # |
FAILED: Continuous integration, rev:1335
http://
Executed test runs:
FAILURE: http://
FAILURE: http://
FAILURE: http://
FAILURE: http://
FAILURE: http://
Click here to trigger a rebuild:
http://
- 1337. By Loïc Molinari
-
Merged main branch.
- 1338. By Loïc Molinari
-
Updated components.api.
PS Jenkins bot (ps-jenkins) wrote : | # |
FAILED: Continuous integration, rev:1338
http://
Executed test runs:
UNSTABLE: http://
FAILURE: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
UNSTABLE: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
Click here to trigger a rebuild:
http://
- 1339. By Loïc Molinari
-
Made use of qBound() instead of qMin()/qMax().
PS Jenkins bot (ps-jenkins) wrote : | # |
FAILED: Continuous integration, rev:1339
http://
Executed test runs:
UNSTABLE: http://
SUCCESS: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
UNSTABLE: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
Click here to trigger a rebuild:
http://
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.
Zsombor Egri (zsombi) wrote : | # |
Let's try to land this in 1.2 still!
PS Jenkins bot (ps-jenkins) : | # |
Preview Diff
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 | +} |
FAILED: Continuous integration, rev:1325 jenkins. qa.ubuntu. com/job/ ubuntu- sdk-team- ubuntu- ui-toolkit- staging- ci/1296/ jenkins. qa.ubuntu. com/job/ generic- deb-autopilot- vivid-touch/ 428/console jenkins. qa.ubuntu. com/job/ ubuntu- sdk-team- ubuntu- ui-toolkit- staging- vivid-amd64- ci/29/console jenkins. qa.ubuntu. com/job/ ubuntu- sdk-team- ubuntu- ui-toolkit- staging- vivid-armhf- ci/29/console jenkins. qa.ubuntu. com/job/ ubuntu- sdk-team- ubuntu- ui-toolkit- staging- vivid-i386- ci/29/console jenkins. qa.ubuntu. com/job/ generic- mediumtests- builder- vivid-armhf/ 428/console
http://
Executed test runs:
FAILURE: http://
FAILURE: http://
FAILURE: http://
FAILURE: http://
FAILURE: http://
Click here to trigger a rebuild: s-jenkins. ubuntu- ci:8080/ job/ubuntu- sdk-team- ubuntu- ui-toolkit- staging- ci/1296/ rebuild
http://