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