~loic.molinari/ubuntu-ui-toolkit/ubuntu-ui-toolkit-dynamic-shapes-for-new-design

« back to all changes in this revision

Viewing changes to src/Ubuntu/Components/plugin/privates/shape/shadow.cpp

  • Committer: Loïc Molinari
  • Date: 2016-03-23 10:50:18 UTC
  • Revision ID: loic.molinari@canonical.com-20160323105018-snlrk6x8aepojb8h
Added support for distance and angle for outer shadows and fixed some bugs.

Show diffs side-by-side

added added

removed removed

Lines of Context:
16
16
 * Author: Loïc Molinari <loic.molinari@canonical.com>
17
17
 */
18
18
 
19
 
// FIXME(loicm):
20
 
// - Floating-point values for the radius and shadow sizes are rounded for now
21
 
//   because the CPU-based shape rendering and gaussian blur don't support
22
 
//   sub-pixel rendering.
23
 
// - Should use a texture atlas to allow batching of nodes with different shadow
24
 
//   and radius sizes.
25
 
// - Should try using half-sized texture with bilinear filtering.
 
19
// FIXME(loicm): Should try using half-sized texture to speed up CPU based
 
20
//     shadow rendering.
26
21
 
27
22
#include "shadow.h"
28
23
#include "utils.h"
29
24
#include <QtGui/QOpenGLFunctions>
 
25
#include <QtGui/QGuiApplication>
30
26
 
31
27
const UCShadow::Style defaultStyle = UCShadow::Outer;
32
28
const UCShadow::Shape defaultShape = UCShadow::Squircle;
33
29
const QRgb defaultColor = qRgba(0, 0, 0, 255);
34
30
const int defaultShadow = 25;
35
31
const int maxShadow = 128;
 
32
const float maxDistance = 255.0f;
36
33
 
37
34
// --- Shaders ---
38
35
 
138
135
UCShadowNode::UCShadowNode(UCShadow::Style style, UCShadow::Shape shape)
139
136
    : QSGGeometryNode()
140
137
    , m_material(style)
141
 
    , m_geometry(attributeSet(), 20, 34, GL_UNSIGNED_SHORT)
 
138
    , m_geometry(attributeSet(),
 
139
                 style == UCShadow::Outer ? outerVerticesCount : innerVerticesCount,
 
140
                 style == UCShadow::Outer ? outerIndicesCount : innerIndicesCount,
 
141
                 GL_UNSIGNED_SHORT)
142
142
    , m_shadow(0)
143
143
    , m_newShadow(0)
144
144
    , m_radius(0)
148
148
    , m_newShape(shape)
149
149
{
150
150
    setFlag(QSGNode::UsePreprocess);
151
 
    memcpy(m_geometry.indexData(), indices(), 34 * sizeof(quint16));
 
151
    memcpy(m_geometry.indexData(),
 
152
           style == UCShadow::Outer ? outerIndices() : innerIndices(),
 
153
           (style == UCShadow::Outer ? outerIndicesCount : innerIndicesCount) * sizeof(quint16));
152
154
    m_geometry.setDrawingMode(GL_TRIANGLE_STRIP);
153
155
    m_geometry.setIndexDataPattern(QSGGeometry::StaticPattern);
154
156
    m_geometry.setVertexDataPattern(QSGGeometry::AlwaysUploadPattern);
158
160
}
159
161
 
160
162
// static
161
 
const quint16* UCShadowNode::indices()
 
163
const quint16* UCShadowNode::outerIndices()
 
164
{
 
165
    // The geometry is made of 9 vertices indexed with a triangle strip mode.
 
166
    //     0 --- 1 --- 2
 
167
    //     |  /  |  /  |
 
168
    //     3 --- 4 --- 5
 
169
    //     |  /  |  /  |
 
170
    //     6 --- 7 --- 8
 
171
    static const quint16 indices[] = {
 
172
        0, 3, 1, 4, 2, 5,
 
173
        5, 3,  // Degenerate triangle.
 
174
        3, 6, 4, 7, 5, 8
 
175
    };
 
176
    return indices;
 
177
}
 
178
 
 
179
// static
 
180
const quint16* UCShadowNode::innerIndices()
162
181
{
163
182
    // The geometry is made of 20 vertices indexed with a triangle strip mode.
164
183
    //     0 ------ 1 ------ 2
209
228
 
210
229
void UCShadowNode::setStyle(UCShadow::Style style)
211
230
{
 
231
    DASSERT(style != m_style);
 
232
    m_geometry.allocate(
 
233
        style == UCShadow::Outer ? outerVerticesCount : innerVerticesCount,
 
234
        style == UCShadow::Outer ? outerIndicesCount : innerIndicesCount);
 
235
    memcpy(m_geometry.indexData(),
 
236
           style == UCShadow::Outer ? outerIndices() : innerIndices(),
 
237
           (style == UCShadow::Outer ? outerIndicesCount : innerIndicesCount) * sizeof(quint16));
212
238
    m_material.setStyle(style);
213
239
    m_style = style;
214
240
}
215
241
 
216
242
// FIXME(loicm) Clean up.
217
243
void UCShadowNode::updateGeometry(
218
 
    const QSizeF& itemSize, float shadow, float radius, QRgb color)
 
244
    const QSizeF& itemSize, float shadow, float radius, float angle, float distance, QRgb color)
219
245
{
220
246
    UCShadowNode::Vertex* v = reinterpret_cast<UCShadowNode::Vertex*>(m_geometry.vertexData());
 
247
    const float devicePixelRatio = qGuiApp->devicePixelRatio();
221
248
    const float w = static_cast<float>(itemSize.width());
222
249
    const float h = static_cast<float>(itemSize.height());
223
250
    // Rounded down since renderShape() doesn't support sub-pixel rendering.
224
251
    const float maxSize = floorf(qMin(w, h) * 0.5f);
225
252
    const float clampedShadow = qMin(shadow, maxSize);
 
253
    const float deviceShadow = clampedShadow * devicePixelRatio;
226
254
    // FIXME(loicm) The diagonal at rounded integers pos prevents rasterising pixels on a side.
227
255
    const float clampedRadius = qMin(radius, maxSize);
228
 
    const float textureSize = 2.0f * clampedShadow + clampedRadius;
 
256
    const float deviceRadius = clampedRadius * devicePixelRatio;
 
257
    const float textureSize = 2.0f * deviceShadow + deviceRadius;
229
258
    const float textureSizeRounded = getStride(static_cast<int>(textureSize), 1, textureStride);
230
259
    const float textureOffset = (textureSizeRounded - textureSize) / textureSizeRounded;
231
260
    const float textureFactor = (1.0f - textureOffset) / textureSize;
232
261
    const quint32 packedColor = packColor(color);
233
262
 
234
263
    if (m_style == UCShadow::Outer) {
 
264
        float s, c;
 
265
        // Get the offsets. Adding 180° to cast the shadow according to the
 
266
        // virtual light position and using the opposite angle to rotate counter
 
267
        // clockwise.
 
268
        sincosf((180.0f - angle) * (M_PI / 180.0f), &s, &c);
 
269
        const float offsetY = s * distance;
 
270
        const float offsetX = c * distance;
 
271
        const float deviceW = w * devicePixelRatio;
 
272
        const float deviceH = h * devicePixelRatio;
 
273
        const float middleS = (deviceW * 0.5f + deviceShadow) * textureFactor + textureOffset;
 
274
        const float middleT = (deviceH * 0.5f + deviceShadow) * textureFactor + textureOffset;
 
275
 
235
276
        // 1st row.
236
 
        v[0].x = -clampedShadow;
237
 
        v[0].y = -clampedShadow;
 
277
        v[0].x = -clampedShadow + offsetX;
 
278
        v[0].y = -clampedShadow + offsetY;
238
279
        v[0].s = textureOffset;
239
280
        v[0].t = textureOffset;
240
281
        v[0].color = packedColor;
241
 
        v[1].x = w * 0.5f;
242
 
        v[1].y = -clampedShadow;
243
 
        v[1].s = (w * 0.5f + clampedShadow) * textureFactor + textureOffset;
 
282
        v[1].x = w * 0.5f + offsetX;
 
283
        v[1].y = -clampedShadow + offsetY;
 
284
        v[1].s = middleS;
244
285
        v[1].t = textureOffset;
245
286
        v[1].color = packedColor;
246
 
        v[2].x = w + clampedShadow;
247
 
        v[2].y = -clampedShadow;
 
287
        v[2].x = w + clampedShadow + offsetX;
 
288
        v[2].y = -clampedShadow + offsetY;
248
289
        v[2].s = textureOffset;
249
290
        v[2].t = textureOffset;
250
291
        v[2].color = packedColor;
251
292
 
252
293
        // 2nd row.
253
 
        v[3].x = clampedRadius;
254
 
        v[3].y = 0.0f;
255
 
        v[3].s = (clampedShadow + clampedRadius) * textureFactor + textureOffset;
256
 
        v[3].t = clampedShadow * textureFactor + textureOffset;
 
294
        v[3].x = -clampedShadow + offsetX;
 
295
        v[3].y = h * 0.5f + offsetY;
 
296
        v[3].s = textureOffset;
 
297
        v[3].t = middleT;
257
298
        v[3].color = packedColor;
258
 
        v[4].x = w * 0.5f;
259
 
        v[4].y = 0.0f;
260
 
        v[4].s = (w * 0.5f + clampedShadow) * textureFactor + textureOffset;
261
 
        v[4].t = clampedShadow * textureFactor + textureOffset;
 
299
        v[4].x = w * 0.5f + offsetX;
 
300
        v[4].y = h * 0.5f + offsetY;
 
301
        v[4].s = middleS;
 
302
        v[4].t = middleT;
262
303
        v[4].color = packedColor;
263
 
        v[5].x = w - clampedRadius;
264
 
        v[5].y = 0.0f;
265
 
        v[5].s = (clampedShadow + clampedRadius) * textureFactor + textureOffset;
266
 
        v[5].t = clampedShadow * textureFactor + textureOffset;
 
304
        v[5].x = w + clampedShadow + offsetX;
 
305
        v[5].y = h * 0.5f + offsetY;
 
306
        v[5].s = textureOffset;
 
307
        v[5].t = middleT;
267
308
        v[5].color = packedColor;
268
309
 
269
310
        // 3rd row.
270
 
        v[6].x = 0.0f;
271
 
        v[6].y = clampedRadius;
272
 
        v[6].s = clampedShadow * textureFactor + textureOffset;
273
 
        v[6].t = (clampedShadow + clampedRadius) * textureFactor + textureOffset;
 
311
        v[6].x = -clampedShadow + offsetX;
 
312
        v[6].y = h + clampedShadow + offsetY;
 
313
        v[6].s = textureOffset;
 
314
        v[6].t = textureOffset;
274
315
        v[6].color = packedColor;
275
 
        v[7].x = w;
276
 
        v[7].y = clampedRadius;
277
 
        v[7].s = clampedShadow * textureFactor + textureOffset;
278
 
        v[7].t = (clampedShadow + clampedRadius) * textureFactor + textureOffset;
 
316
        v[7].x = w * 0.5f + offsetX;
 
317
        v[7].y = h + clampedShadow + offsetY;
 
318
        v[7].s = middleS;
 
319
        v[7].t = textureOffset;
279
320
        v[7].color = packedColor;
280
 
 
281
 
        // 4th row.
282
 
        v[8].x = -clampedShadow;
283
 
        v[8].y = h * 0.5f;
 
321
        v[8].x = w + clampedShadow + offsetX;
 
322
        v[8].y = h + clampedShadow + offsetY;
284
323
        v[8].s = textureOffset;
285
 
        v[8].t = (h * 0.5f + clampedShadow) * textureFactor + textureOffset;
 
324
        v[8].t = textureOffset;
286
325
        v[8].color = packedColor;
287
 
        v[9].x = 0.0f;
288
 
        v[9].y = h * 0.5f;
289
 
        v[9].s = clampedShadow * textureFactor + textureOffset;
290
 
        v[9].t = (h * 0.5f + clampedShadow) * textureFactor + textureOffset;
291
 
        v[9].color = packedColor;
292
 
        v[10].x = w;
293
 
        v[10].y = h * 0.5f;
294
 
        v[10].s = clampedShadow * textureFactor + textureOffset;
295
 
        v[10].t = (h * 0.5f + clampedShadow) * textureFactor + textureOffset;
296
 
        v[10].color = packedColor;
297
 
        v[11].x = w + clampedShadow;
298
 
        v[11].y = h * 0.5f;
299
 
        v[11].s = textureOffset;
300
 
        v[11].t = (h * 0.5f + clampedShadow) * textureFactor + textureOffset;
301
 
        v[11].color = packedColor;
302
 
 
303
 
        // 5th row.
304
 
        v[12].x = 0.0f;
305
 
        v[12].y = h - clampedRadius;
306
 
        v[12].s = clampedShadow * textureFactor + textureOffset;
307
 
        v[12].t = (clampedShadow + clampedRadius) * textureFactor + textureOffset;
308
 
        v[12].color = packedColor;
309
 
        v[13].x = w;
310
 
        v[13].y = h - clampedRadius;
311
 
        v[13].s = clampedShadow * textureFactor + textureOffset;
312
 
        v[13].t = (clampedShadow + clampedRadius) * textureFactor + textureOffset;
313
 
        v[13].color = packedColor;
314
 
 
315
 
        // 6th row.
316
 
        v[14].x = clampedRadius;
317
 
        v[14].y = h;
318
 
        v[14].s = (clampedShadow + clampedRadius) * textureFactor + textureOffset;
319
 
        v[14].t = clampedShadow * textureFactor + textureOffset;
320
 
        v[14].color = packedColor;
321
 
        v[15].x = w * 0.5f;
322
 
        v[15].y = h;
323
 
        v[15].s = (w * 0.5f + clampedShadow) * textureFactor + textureOffset;
324
 
        v[15].t = clampedShadow * textureFactor + textureOffset;
325
 
        v[15].color = packedColor;
326
 
        v[16].x = w - clampedRadius;
327
 
        v[16].y = h;
328
 
        v[16].s = (clampedShadow + clampedRadius) * textureFactor + textureOffset;
329
 
        v[16].t = clampedShadow * textureFactor + textureOffset;
330
 
        v[16].color = packedColor;
331
 
 
332
 
        // 7th row.
333
 
        v[17].x = -clampedShadow;
334
 
        v[17].y = h + clampedShadow;
335
 
        v[17].s = textureOffset;
336
 
        v[17].t = textureOffset;
337
 
        v[17].color = packedColor;
338
 
        v[18].x = w * 0.5f;
339
 
        v[18].y = h + clampedShadow;
340
 
        v[18].s = (w * 0.5f + clampedShadow) * textureFactor + textureOffset;
341
 
        v[18].t = textureOffset;
342
 
        v[18].color = packedColor;
343
 
        v[19].x = w + clampedShadow;
344
 
        v[19].y = h + clampedShadow;
345
 
        v[19].s = textureOffset;
346
 
        v[19].t = textureOffset;
347
 
        v[19].color = packedColor;
348
326
 
349
327
    } else {
350
328
        // 1st row.
465
443
    markDirty(QSGNode::DirtyGeometry);
466
444
 
467
445
    // Update data for the preprocess() call.
468
 
    if (m_shadow != static_cast<quint8>(clampedShadow)) {
469
 
        m_newShadow = static_cast<quint8>(clampedShadow);
 
446
    if (m_shadow != static_cast<quint8>(deviceShadow)) {
 
447
        m_newShadow = static_cast<quint8>(deviceShadow);
470
448
    }
471
 
    if (m_radius != static_cast<quint8>(clampedRadius)) {
472
 
        m_newRadius = static_cast<quint8>(clampedRadius);
 
449
    if (m_radius != static_cast<quint8>(deviceRadius)) {
 
450
        m_newRadius = static_cast<quint8>(deviceRadius);
473
451
    }
474
452
}
475
453
 
478
456
UCShadow::UCShadow(QQuickItem* parent)
479
457
    : QQuickItem(parent)
480
458
    , m_color(defaultColor)
 
459
    , m_angle(0)
 
460
    , m_distance(0)
481
461
    , m_size(defaultShadow)
482
462
    , m_radius(defaultRadius)
483
463
    , m_style(defaultStyle)
511
491
 
512
492
void UCShadow::setSize(qreal size)
513
493
{
514
 
    const quint8 clampedSize = static_cast<quint8>(qBound(0, qRound(size), maxShadow));
515
 
    if (m_size != clampedSize) {
516
 
        m_size = clampedSize;
 
494
    STATIC_ASSERT(maxShadow <= 255);  // Quantized to 8 bits.
 
495
    const quint8 quantizedSize = static_cast<quint8>(qBound(0, qRound(size), maxShadow));
 
496
    if (m_size != quantizedSize) {
 
497
        m_size = quantizedSize;
517
498
        update();
518
499
        Q_EMIT sizeChanged();
519
500
    }
521
502
 
522
503
void UCShadow::setRadius(qreal radius)
523
504
{
524
 
    const quint8 clampedRadius = static_cast<quint8>(qBound(0, qRound(radius), maxRadius));
525
 
    if (m_radius != clampedRadius) {
526
 
        m_radius = clampedRadius;
 
505
    STATIC_ASSERT(maxRadius <= 255);  // Quantized to 8 bits.
 
506
    const quint8 quantizedRadius = static_cast<quint8>(qBound(0, qRound(radius), maxRadius));
 
507
    if (m_radius != quantizedRadius) {
 
508
        m_radius = quantizedRadius;
527
509
        update();
528
510
        Q_EMIT radiusChanged();
529
511
    }
539
521
    }
540
522
}
541
523
 
 
524
void UCShadow::setAngle(qreal angle)
 
525
{
 
526
    float quantizedAngle = fmodf(static_cast<float>(angle), 360.0f);
 
527
    if (quantizedAngle < 0.0f) {
 
528
        quantizedAngle += 360.0f;
 
529
    }
 
530
    quantizedAngle = quantizeToU16(quantizedAngle, 360.0f);
 
531
    if (m_angle != quantizedAngle) {
 
532
        m_angle = quantizedAngle;
 
533
        update();
 
534
        Q_EMIT angleChanged();
 
535
    }
 
536
}
 
537
 
 
538
void UCShadow::setDistance(qreal distance)
 
539
{
 
540
    const quint16 quantizedDistance =
 
541
        quantizeToU16(qBound(0.0f, static_cast<float>(distance), maxDistance), maxDistance);
 
542
    if (m_distance != quantizedDistance) {
 
543
        m_distance = quantizedDistance;
 
544
        update();
 
545
        Q_EMIT distanceChanged();
 
546
    }
 
547
}
 
548
 
542
549
QSGNode* UCShadow::updatePaintNode(QSGNode* oldNode, UpdatePaintNodeData* data)
543
550
{
544
551
    Q_UNUSED(data);
545
552
 
546
553
    const QSizeF itemSize(width(), height());
547
 
    if (itemSize.isEmpty() || m_size <= 0.0f) {
 
554
    if (itemSize.isEmpty() || m_size <= 0.0f || qAlpha(m_color) == 0) {
548
555
        delete oldNode;
549
556
        return NULL;
550
557
    }
565
572
    }
566
573
 
567
574
    node->updateGeometry(
568
 
        itemSize, static_cast<float>(m_size), static_cast<float>(m_radius), m_color);
 
575
        itemSize, static_cast<float>(m_size), static_cast<float>(m_radius),
 
576
        unquantizeFromU16(m_angle, 360.0f), unquantizeFromU16(m_distance, 255.0f), m_color);
569
577
 
570
578
    return node;
571
579
}