• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

mcallegari / qlcplus / 12611954629

04 Jan 2025 04:17PM UTC coverage: 31.468% (-0.5%) from 31.963%
12611954629

Pull #1649

github

web-flow
Merge d17d3ec9b into 3ed321c32
Pull Request #1649: Function Wizard: create Effects

2 of 53 new or added lines in 2 files covered. (3.77%)

2597 existing lines in 15 files now uncovered.

14093 of 44785 relevant lines covered (31.47%)

26791.82 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

41.49
/engine/src/rgbmatrix.cpp
1
/*
2
  Q Light Controller Plus
3
  rgbmatrix.cpp
4

5
  Copyright (c) Heikki Junnila
6
                Massimo Callegari
7

8
  Licensed under the Apache License, Version 2.0 (the "License");
9
  you may not use this file except in compliance with the License.
10
  You may obtain a copy of the License at
11

12
      http://www.apache.org/licenses/LICENSE-2.0.txt
13

14
  Unless required by applicable law or agreed to in writing, software
15
  distributed under the License is distributed on an "AS IS" BASIS,
16
  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17
  See the License for the specific language governing permissions and
18
  limitations under the License.
19
*/
20

21
#include <QXmlStreamReader>
22
#include <QXmlStreamWriter>
23
#include <QCoreApplication>
24
#include <QElapsedTimer>
25
#include <QDebug>
26
#include <cmath>
27
#include <QDir>
28

29
#include "rgbscriptscache.h"
30
#include "qlcfixturehead.h"
31
#include "fixturegroup.h"
32
#include "genericfader.h"
33
#include "fadechannel.h"
34
#include "rgbmatrix.h"
35
#include "doc.h"
36

37
#define KXMLQLCRGBMatrixStartColor      QString("MonoColor")
38
#define KXMLQLCRGBMatrixEndColor        QString("EndColor")
39
#define KXMLQLCRGBMatrixColor           QString("Color")
40
#define KXMLQLCRGBMatrixColorIndex      QString("Index")
41

42
#define KXMLQLCRGBMatrixFixtureGroup    QString("FixtureGroup")
43
#define KXMLQLCRGBMatrixDimmerControl   QString("DimmerControl")
44

45
#define KXMLQLCRGBMatrixProperty        QString("Property")
46
#define KXMLQLCRGBMatrixPropertyName    QString("Name")
47
#define KXMLQLCRGBMatrixPropertyValue   QString("Value")
48

49
#define KXMLQLCRGBMatrixControlMode         QString("ControlMode")
50
#define KXMLQLCRGBMatrixControlModeRgb      QString("RGB")
51
#define KXMLQLCRGBMatrixControlModeAmber    QString("Amber")
52
#define KXMLQLCRGBMatrixControlModeWhite    QString("White")
53
#define KXMLQLCRGBMatrixControlModeUV       QString("UV")
54
#define KXMLQLCRGBMatrixControlModeDimmer   QString("Dimmer")
55
#define KXMLQLCRGBMatrixControlModeShutter  QString("Shutter")
56

57
/****************************************************************************
58
 * Initialization
59
 ****************************************************************************/
60

61
RGBMatrix::RGBMatrix(Doc* doc)
9✔
62
    : Function(doc, Function::RGBMatrixType)
63
    , m_dimmerControl(false)
64
    , m_fixtureGroupID(FixtureGroup::invalidId())
9✔
65
    , m_group(NULL)
66
    , m_algorithm(NULL)
67
#if QT_VERSION < QT_VERSION_CHECK(5, 14, 0)
68
    , m_algorithmMutex(QMutex::Recursive)
69
#endif
70
    , m_stepHandler(new RGBMatrixStep())
9✔
71
    , m_roundTime(new QElapsedTimer())
9✔
72
    , m_stepsCount(0)
73
    , m_stepBeatDuration(0)
74
    , m_controlMode(RGBMatrix::ControlModeRgb)
18✔
75
{
76
    setName(tr("New RGB Matrix"));
9✔
77
    setDuration(500);
9✔
78

79
    m_rgbColors.fill(QColor(), RGBAlgorithmColorDisplayCount);
9✔
80
    setColor(0, Qt::red);
9✔
81

82
    RGBScript scr = doc->rgbScriptsCache()->script("Stripes");
18✔
83
    setAlgorithm(scr.clone());
9✔
84
}
9✔
85

86
RGBMatrix::~RGBMatrix()
11✔
87
{
88
    delete m_algorithm;
9✔
89
    delete m_roundTime;
9✔
90
    delete m_stepHandler;
18✔
91
}
11✔
92

UNCOV
93
QIcon RGBMatrix::getIcon() const
×
94
{
UNCOV
95
    return QIcon(":/rgbmatrix.png");
×
96
}
97

98
void RGBMatrix::setTotalDuration(quint32 msec)
1✔
99
{
100
    QMutexLocker algorithmLocker(&m_algorithmMutex);
1✔
101

102
    if (m_algorithm == NULL)
1✔
103
        return;
104

105
    FixtureGroup* grp = doc()->fixtureGroup(fixtureGroup());
1✔
106
    if (grp == NULL)
1✔
107
        return;
108

109
    int steps = m_algorithm->rgbMapStepCount(grp->size());
1✔
110
    setDuration(msec / steps);
1✔
111
}
112

113
quint32 RGBMatrix::totalDuration()
3✔
114
{
115
    QMutexLocker algorithmLocker(&m_algorithmMutex);
3✔
116

117
    if (m_algorithm == NULL)
3✔
118
        return 0;
119

120
    FixtureGroup* grp = doc()->fixtureGroup(fixtureGroup());
3✔
121
    if (grp == NULL)
3✔
122
        return 0;
123

124
    //qDebug () << "Algorithm steps:" << m_algorithm->rgbMapStepCount(grp->size());
125
    return m_algorithm->rgbMapStepCount(grp->size()) * duration();
2✔
126
}
127

128
void RGBMatrix::setDimmerControl(bool dimmerControl)
1✔
129
{
130
    m_dimmerControl = dimmerControl;
1✔
131
}
1✔
132

133
bool RGBMatrix::dimmerControl() const
2✔
134
{
135
    return m_dimmerControl;
2✔
136
}
137

138
/****************************************************************************
139
 * Copying
140
 ****************************************************************************/
141

142
Function* RGBMatrix::createCopy(Doc* doc, bool addToDoc)
1✔
143
{
144
    Q_ASSERT(doc != NULL);
145

146
    Function* copy = new RGBMatrix(doc);
1✔
147
    if (copy->copyFrom(this) == false)
1✔
148
    {
UNCOV
149
        delete copy;
×
150
        copy = NULL;
151
    }
152
    if (addToDoc == true && doc->addFunction(copy) == false)
1✔
153
    {
UNCOV
154
        delete copy;
×
155
        copy = NULL;
156
    }
157

158
    return copy;
1✔
159
}
160

161
bool RGBMatrix::copyFrom(const Function* function)
1✔
162
{
163
    const RGBMatrix* mtx = qobject_cast<const RGBMatrix*> (function);
164
    if (mtx == NULL)
1✔
165
        return false;
166

167
    setDimmerControl(mtx->dimmerControl());
1✔
168
    setFixtureGroup(mtx->fixtureGroup());
1✔
169

170
    m_rgbColors.clear();
1✔
171
    foreach (QColor col, mtx->getColors())
12✔
172
        m_rgbColors.append(col);
5✔
173

174
    if (mtx->algorithm() != NULL)
1✔
175
        setAlgorithm(mtx->algorithm()->clone());
1✔
176
    else
UNCOV
177
        setAlgorithm(NULL);
×
178

179
    setControlMode(mtx->controlMode());
1✔
180

181
    return Function::copyFrom(function);
1✔
182
}
183

184
/****************************************************************************
185
 * Fixtures
186
 ****************************************************************************/
187

188
quint32 RGBMatrix::fixtureGroup() const
37✔
189
{
190
    return m_fixtureGroupID;
37✔
191
}
192

193
void RGBMatrix::setFixtureGroup(quint32 id)
8✔
194
{
195
    m_fixtureGroupID = id;
8✔
196
    {
197
        QMutexLocker algoLocker(&m_algorithmMutex);
8✔
198
        m_group = doc()->fixtureGroup(m_fixtureGroupID);
8✔
199
    }
200
    m_stepsCount = stepsCount();
8✔
201
}
8✔
202

203
QList<quint32> RGBMatrix::components()
2✔
204
{
205
    if (m_group != NULL)
2✔
206
        return m_group->fixtureList();
1✔
207

208
    return QList<quint32>();
209
}
210

211
/****************************************************************************
212
 * Algorithm
213
 ****************************************************************************/
214

215
void RGBMatrix::setAlgorithm(RGBAlgorithm* algo)
13✔
216
{
217
    {
218
        QMutexLocker algorithmLocker(&m_algorithmMutex);
13✔
219
        delete m_algorithm;
13✔
220
        m_algorithm = algo;
13✔
221

222
        /** If there's been a change of Script algorithm "on the fly",
223
         *  then re-apply the properties currently set in this RGBMatrix */
224
        if (m_algorithm != NULL && m_algorithm->type() == RGBAlgorithm::Script)
13✔
225
        {
226
            RGBScript *script = static_cast<RGBScript*> (m_algorithm);
13✔
227
            QHashIterator<QString, QString> it(m_properties);
13✔
228
            while (it.hasNext())
13✔
229
            {
230
                it.next();
UNCOV
231
                if (script->setProperty(it.key(), it.value()) == false)
×
232
                {
233
                    /** If the new algorithm doesn't expose a property,
234
                     *  then remove it from the cached list, otherwise
235
                     *  it would be carried around forever (and saved on XML) */
UNCOV
236
                    m_properties.take(it.key());
×
237
                }
238
            }
239
        }
240
    }
241
    m_stepsCount = stepsCount();
13✔
242

243
    emit changed(id());
13✔
244
}
13✔
245

246
RGBAlgorithm* RGBMatrix::algorithm() const
18✔
247
{
248
    return m_algorithm;
18✔
249
}
250

251
#if QT_VERSION < QT_VERSION_CHECK(5, 14, 0)
252
QMutex& RGBMatrix::algorithmMutex()
253
{
254
    return m_algorithmMutex;
255
}
256
#else
UNCOV
257
QRecursiveMutex& RGBMatrix::algorithmMutex()
×
258
{
UNCOV
259
    return m_algorithmMutex;
×
260
}
261
#endif
262

263

264
int RGBMatrix::stepsCount()
24✔
265
{
266
    QMutexLocker algorithmLocker(&m_algorithmMutex);
24✔
267

268
    if (m_algorithm == NULL)
24✔
269
        return 0;
270

271
    FixtureGroup* grp = doc()->fixtureGroup(fixtureGroup());
24✔
272
    if (grp != NULL)
24✔
273
        return m_algorithm->rgbMapStepCount(grp->size());
7✔
274

275
    return 0;
276
}
277

278
void RGBMatrix::previewMap(int step, RGBMatrixStep *handler)
7✔
279
{
280
    QMutexLocker algorithmLocker(&m_algorithmMutex);
7✔
281
    if (m_algorithm == NULL || handler == NULL)
7✔
282
        return;
283

284
    if (m_group == NULL)
7✔
285
        m_group = doc()->fixtureGroup(fixtureGroup());
1✔
286

287
    if (m_group != NULL)
7✔
288
    {
289
        setMapColors();
6✔
290
        m_algorithm->rgbMap(m_group->size(), handler->stepColor().rgb(), step, handler->m_map);
6✔
291
    }
292
}
293

294
/****************************************************************************
295
 * Color
296
 ****************************************************************************/
297

298
void RGBMatrix::setColor(int i, QColor c)
25✔
299
{
300
    if (i < 0)
25✔
301
        return;
302

303
    if (i >= m_rgbColors.count())
25✔
UNCOV
304
        m_rgbColors.resize(i + 1);
×
305

306
    m_rgbColors.replace(i, c);
25✔
307
    {
308
        QMutexLocker algorithmLocker(&m_algorithmMutex);
25✔
309
        if (m_algorithm != NULL)
25✔
310
        {
311
            m_algorithm->setColors(m_rgbColors);
16✔
312
            updateColorDelta();
16✔
313
        }
314
    }
315
    setMapColors();
25✔
316
    emit changed(id());
25✔
317
}
318

319
QColor RGBMatrix::getColor(int i) const
10✔
320
{
321
    if (i < 0 || i >= m_rgbColors.count())
10✔
322
        return QColor();
323

324
    return m_rgbColors.at(i);
325
}
326

327
QVector<QColor> RGBMatrix::getColors() const
1✔
328
{
329
    return m_rgbColors;
1✔
330
}
331

332
void RGBMatrix::updateColorDelta()
16✔
333
{
334
    m_stepHandler->calculateColorDelta(m_rgbColors[0], m_rgbColors[1], m_algorithm);
32✔
335
}
16✔
336

337
void RGBMatrix::setMapColors()
31✔
338
{
339
    QMutexLocker algorithmLocker(&m_algorithmMutex);
31✔
340
    if (m_algorithm == NULL)
31✔
341
        return;
342

343
    if (m_algorithm->apiVersion() < 3)
22✔
344
        return;
345

UNCOV
346
    if (m_group == NULL)
×
UNCOV
347
        m_group = doc()->fixtureGroup(fixtureGroup());
×
348

UNCOV
349
    if (m_group != NULL)
×
350
    {
UNCOV
351
        QVector<unsigned int> rawColors;
×
UNCOV
352
        for (int i = 0; i < m_algorithm->acceptColors(); i++)
×
353
        {
UNCOV
354
            QColor col = m_rgbColors.at(i);
×
UNCOV
355
            rawColors.append(col.isValid() ? col.rgb() : 0);
×
356
        }
357

UNCOV
358
        m_algorithm->rgbMapSetColors(rawColors);
×
359
    }
360
}
361

362
/************************************************************************
363
 * Properties
364
 ************************************************************************/
365

366
void RGBMatrix::setProperty(QString propName, QString value)
1✔
367
{
368
    QMutexLocker algoLocker(&m_algorithmMutex);
1✔
369
    m_properties[propName] = value;
1✔
370
    if (m_algorithm != NULL && m_algorithm->type() == RGBAlgorithm::Script)
1✔
371
    {
372
        RGBScript *script = static_cast<RGBScript*> (m_algorithm);
1✔
373
        script->setProperty(propName, value);
1✔
374

375
        QVector<uint> colors = script->rgbMapGetColors();
2✔
376
        for (int i = 0; i < colors.count(); i++)
1✔
UNCOV
377
            setColor(i, QColor::fromRgb(colors.at(i)));
×
378
    }
379
    m_stepsCount = stepsCount();
1✔
380
}
1✔
381

382
QString RGBMatrix::property(QString propName)
3✔
383
{
384
    QMutexLocker algoLocker(&m_algorithmMutex);
3✔
385

386
    /** If the property is cached, then return it right away */
387
    if (m_properties.contains(propName))
3✔
388
        return m_properties[propName];
1✔
389

390
    /** Otherwise, let's retrieve it from the Script */
391
    if (m_algorithm != NULL && m_algorithm->type() == RGBAlgorithm::Script)
2✔
392
    {
393
        RGBScript *script = static_cast<RGBScript*> (m_algorithm);
2✔
394
        return script->property(propName);
2✔
395
    }
396

397
    return QString();
398
}
399

400
/****************************************************************************
401
 * Load & Save
402
 ****************************************************************************/
403

404
bool RGBMatrix::loadXML(QXmlStreamReader &root)
3✔
405
{
406
    if (root.name() != KXMLQLCFunction)
6✔
407
    {
408
        qWarning() << Q_FUNC_INFO << "Function node not found";
1✔
409
        return false;
1✔
410
    }
411

412
    if (root.attributes().value(KXMLQLCFunctionType).toString() != typeToString(Function::RGBMatrixType))
4✔
413
    {
414
        qWarning() << Q_FUNC_INFO << "Function is not an RGB matrix";
1✔
415
        return false;
1✔
416
    }
417

418
    /* Load matrix contents */
419
    while (root.readNextStartElement())
12✔
420
    {
421
        if (root.name() == KXMLQLCFunctionSpeed)
22✔
422
        {
423
            loadXMLSpeed(root);
1✔
424
        }
425
        else if (root.name() == KXMLQLCRGBAlgorithm)
20✔
426
        {
427
            setAlgorithm(RGBAlgorithm::loader(doc(), root));
1✔
428
        }
429
        else if (root.name() == KXMLQLCRGBMatrixFixtureGroup)
18✔
430
        {
431
            setFixtureGroup(root.readElementText().toUInt());
1✔
432
        }
433
        else if (root.name() == KXMLQLCFunctionDirection)
16✔
434
        {
435
            loadXMLDirection(root);
1✔
436
        }
437
        else if (root.name() == KXMLQLCFunctionRunOrder)
14✔
438
        {
439
            loadXMLRunOrder(root);
1✔
440
        }
441
        // Legacy support
442
        else if (root.name() == KXMLQLCRGBMatrixStartColor)
12✔
443
        {
UNCOV
444
            setColor(0, QColor::fromRgb(QRgb(root.readElementText().toUInt())));
×
445
        }
446
        else if (root.name() == KXMLQLCRGBMatrixEndColor)
12✔
447
        {
UNCOV
448
            setColor(1, QColor::fromRgb(QRgb(root.readElementText().toUInt())));
×
449
        }
450
        else if (root.name() == KXMLQLCRGBMatrixColor)
12✔
451
        {
452
            int colorIdx = root.attributes().value(KXMLQLCRGBMatrixColorIndex).toInt();
5✔
453
            setColor(colorIdx, QColor::fromRgb(QRgb(root.readElementText().toUInt())));
5✔
454
        }
455
        else if (root.name() == KXMLQLCRGBMatrixControlMode)
2✔
456
        {
457
            setControlMode(stringToControlMode(root.readElementText()));
1✔
458
        }
UNCOV
459
        else if (root.name() == KXMLQLCRGBMatrixProperty)
×
460
        {
UNCOV
461
            QString name = root.attributes().value(KXMLQLCRGBMatrixPropertyName).toString();
×
UNCOV
462
            QString value = root.attributes().value(KXMLQLCRGBMatrixPropertyValue).toString();
×
463
            setProperty(name, value);
×
UNCOV
464
            root.skipCurrentElement();
×
465
        }
UNCOV
466
        else if (root.name() == KXMLQLCRGBMatrixDimmerControl)
×
467
        {
UNCOV
468
            setDimmerControl(root.readElementText().toInt());
×
469
        }
470
        else
471
        {
UNCOV
472
            qWarning() << Q_FUNC_INFO << "Unknown RGB matrix tag:" << root.name();
×
UNCOV
473
            root.skipCurrentElement();
×
474
        }
475
    }
476

477
    return true;
478
}
479

480
bool RGBMatrix::saveXML(QXmlStreamWriter *doc)
1✔
481
{
482
    Q_ASSERT(doc != NULL);
483

484
    /* Function tag */
485
    doc->writeStartElement(KXMLQLCFunction);
1✔
486

487
    /* Common attributes */
488
    saveXMLCommon(doc);
1✔
489

490
    /* Speeds */
491
    saveXMLSpeed(doc);
1✔
492

493
    /* Direction */
494
    saveXMLDirection(doc);
1✔
495

496
    /* Run order */
497
    saveXMLRunOrder(doc);
1✔
498

499
    /* Algorithm */
500
    if (m_algorithm != NULL)
1✔
501
        m_algorithm->saveXML(doc);
1✔
502

503
    /* LEGACY - Dimmer Control */
504
    if (dimmerControl())
1✔
505
        doc->writeTextElement(KXMLQLCRGBMatrixDimmerControl, QString::number(dimmerControl()));
×
506

507
    /* Colors */
508
    for (int i = 0; i < m_rgbColors.count(); i++)
6✔
509
    {
510
        doc->writeStartElement(KXMLQLCRGBMatrixColor);
5✔
511
        doc->writeAttribute(KXMLQLCRGBMatrixColorIndex, QString::number(i));
5✔
512
        doc->writeCharacters(QString::number(m_rgbColors.at(i).rgb()));
5✔
513
        doc->writeEndElement();
5✔
514
    }
515

516
    /* Control Mode */
517
    doc->writeTextElement(KXMLQLCRGBMatrixControlMode, RGBMatrix::controlModeToString(m_controlMode));
1✔
518

519
    /* Fixture Group */
520
    doc->writeTextElement(KXMLQLCRGBMatrixFixtureGroup, QString::number(fixtureGroup()));
1✔
521

522
    /* Properties */
523
    QHashIterator<QString, QString> it(m_properties);
1✔
524
    while (it.hasNext())
1✔
525
    {
526
        it.next();
UNCOV
527
        doc->writeStartElement(KXMLQLCRGBMatrixProperty);
×
UNCOV
528
        doc->writeAttribute(KXMLQLCRGBMatrixPropertyName, it.key());
×
529
        doc->writeAttribute(KXMLQLCRGBMatrixPropertyValue, it.value());
×
UNCOV
530
        doc->writeEndElement();
×
531
    }
532

533
    /* End the <Function> tag */
534
    doc->writeEndElement();
1✔
535

536
    return true;
1✔
537
}
538

539
/****************************************************************************
540
 * Running
541
 ****************************************************************************/
542

UNCOV
543
void RGBMatrix::tap()
×
544
{
UNCOV
545
    if (stopped() == false)
×
546
    {
UNCOV
547
        FixtureGroup* grp = doc()->fixtureGroup(fixtureGroup());
×
548
        // Filter out taps that are too close to each other
549
        if (grp != NULL && uint(m_roundTime->elapsed()) >= (duration() / 4))
×
550
        {
UNCOV
551
            roundCheck();
×
UNCOV
552
            resetElapsed();
×
553
        }
554
    }
555
}
×
556

UNCOV
557
void RGBMatrix::preRun(MasterTimer *timer)
×
558
{
559
    {
UNCOV
560
        QMutexLocker algorithmLocker(&m_algorithmMutex);
×
561

UNCOV
562
        m_group = doc()->fixtureGroup(m_fixtureGroupID);
×
563
        if (m_group == NULL)
×
564
        {
565
            // No fixture group to control
UNCOV
566
            stop(FunctionParent::master());
×
567
            return;
568
        }
569

570
        if (m_algorithm != NULL)
×
571
        {
572
            // Copy direction from parent class direction
573
            m_stepHandler->initializeDirection(direction(), m_rgbColors[0], m_rgbColors[1], m_stepsCount, m_algorithm);
×
574

575
            if (m_algorithm->type() == RGBAlgorithm::Script)
×
576
            {
UNCOV
577
                RGBScript *script = static_cast<RGBScript*> (m_algorithm);
×
UNCOV
578
                QHashIterator<QString, QString> it(m_properties);
×
579
                while (it.hasNext())
×
580
                {
581
                    it.next();
UNCOV
582
                    script->setProperty(it.key(), it.value());
×
583
                }
584
            }
585
        }
586
    }
587

UNCOV
588
    m_roundTime->restart();
×
589

UNCOV
590
    Function::preRun(timer);
×
591
}
592

UNCOV
593
void RGBMatrix::write(MasterTimer *timer, QList<Universe *> universes)
×
594
{
595
    Q_UNUSED(timer);
596

597
    {
UNCOV
598
        QMutexLocker algorithmLocker(&m_algorithmMutex);
×
UNCOV
599
        if (m_group == NULL)
×
600
        {
601
            // No fixture group to control
602
            stop(FunctionParent::master());
×
603
            return;
604
        }
605

606
        // No time to do anything.
UNCOV
607
        if (duration() == 0)
×
608
            return;
609

610
        // Invalid/nonexistent script
UNCOV
611
        if (m_algorithm == NULL || m_algorithm->apiVersion() == 0)
×
612
            return;
×
613

614
        if (isPaused() == false)
×
615
        {
616
            // Get a new map every time elapsed is reset to zero
UNCOV
617
            if (elapsed() < MasterTimer::tick())
×
618
            {
UNCOV
619
                if (tempoType() == Beats)
×
UNCOV
620
                    m_stepBeatDuration = beatsToTime(duration(), timer->beatTimeDuration());
×
621

622
                //qDebug() << "RGBMatrix step" << m_stepHandler->currentStepIndex() << ", color:" << QString::number(m_stepHandler->stepColor().rgb(), 16);
UNCOV
623
                m_algorithm->rgbMap(m_group->size(), m_stepHandler->stepColor().rgb(),
×
UNCOV
624
                                    m_stepHandler->currentStepIndex(), m_stepHandler->m_map);
×
UNCOV
625
                updateMapChannels(m_stepHandler->m_map, m_group, universes);
×
626
            }
627
        }
628
    }
629

UNCOV
630
    if (isPaused() == false)
×
631
    {
632
        // Increment the ms elapsed time
UNCOV
633
        incrementElapsed();
×
634

635
        /* Check if we need to change direction, stop completely or go to next step
636
         * The cases are:
637
         * 1- time tempo type: act normally, on ms elapsed time
638
         * 2- beat tempo type, beat occurred: check if the elapsed beats is a multiple of
639
         *    the step beat duration. If so, proceed to the next step
640
         * 3- beat tempo type, not beat: if the ms elapsed time reached the step beat
641
         *    duration in ms, and the ms time to the next beat is not less than 1/16 of
642
         *    the step beat duration in ms, then proceed to the next step. If the ms time to the
643
         *    next beat is less than 1/16 of the step beat duration in ms, then defer the step
644
         *    change to case #2, to resync the matrix to the next beat
645
         */
646
        if (tempoType() == Time && elapsed() >= duration())
×
647
        {
UNCOV
648
            roundCheck();
×
649
        }
650
        else if (tempoType() == Beats)
×
651
        {
UNCOV
652
            if (timer->isBeat())
×
653
            {
654
                incrementElapsedBeats();
×
655
                qDebug() << "Elapsed beats:" << elapsedBeats() << ", time elapsed:" << elapsed() << ", step time:" << m_stepBeatDuration;
UNCOV
656
                if (elapsedBeats() % duration() == 0)
×
657
                {
658
                    roundCheck();
×
659
                    resetElapsed();
×
660
                }
661
            }
UNCOV
662
            else if (elapsed() >= m_stepBeatDuration && (uint)timer->timeToNextBeat() > m_stepBeatDuration / 16)
×
663
            {
664
                qDebug() << "Elapsed exceeded";
UNCOV
665
                roundCheck();
×
666
            }
667
        }
668
    }
669
}
670

UNCOV
671
void RGBMatrix::postRun(MasterTimer *timer, QList<Universe *> universes)
×
672
{
673
    uint fadeout = overrideFadeOutSpeed() == defaultSpeed() ? fadeOutSpeed() : overrideFadeOutSpeed();
×
674

675
    /* If no fade out is needed, dismiss all the requested faders.
676
     * Otherwise, set all the faders to fade out and let Universe dismiss them
677
     * when done */
678
    if (fadeout == 0)
×
679
    {
UNCOV
680
        dismissAllFaders();
×
681
    }
682
    else
683
    {
684
        if (tempoType() == Beats)
×
685
            fadeout = beatsToTime(fadeout, timer->beatTimeDuration());
×
686

687
        foreach (QSharedPointer<GenericFader> fader, m_fadersMap.values())
×
688
        {
689
            if (!fader.isNull())
×
UNCOV
690
                fader->setFadeOut(true, fadeout);
×
691
        }
692
    }
693

UNCOV
694
    m_fadersMap.clear();
×
695

696
    {
697
        QMutexLocker algorithmLocker(&m_algorithmMutex);
×
698
        if (m_algorithm != NULL)
×
699
            m_algorithm->postRun();
×
700
    }
701

702
    Function::postRun(timer, universes);
×
703
}
×
704

705
void RGBMatrix::roundCheck()
×
706
{
UNCOV
707
    QMutexLocker algorithmLocker(&m_algorithmMutex);
×
708
    if (m_algorithm == NULL)
×
709
        return;
710

UNCOV
711
    if (m_stepHandler->checkNextStep(runOrder(), m_rgbColors[0], m_rgbColors[1], m_stepsCount) == false)
×
UNCOV
712
        stop(FunctionParent::master());
×
713

714
    m_roundTime->restart();
×
715

UNCOV
716
    if (tempoType() == Beats)
×
717
        roundElapsed(m_stepBeatDuration);
×
718
    else
719
        roundElapsed(duration());
×
720
}
721

UNCOV
722
FadeChannel *RGBMatrix::getFader(QList<Universe *> universes, quint32 universeID, quint32 fixtureID, quint32 channel)
×
723
{
724
    // get the universe Fader first. If doesn't exist, create it
725
    QSharedPointer<GenericFader> fader = m_fadersMap.value(universeID, QSharedPointer<GenericFader>());
×
726
    if (fader.isNull())
×
727
    {
728
        fader = universes[universeID]->requestFader();
×
UNCOV
729
        fader->adjustIntensity(getAttributeValue(Intensity));
×
730
        fader->setBlendMode(blendMode());
×
UNCOV
731
        fader->setName(name());
×
732
        fader->setParentFunctionID(id());
×
733
        m_fadersMap[universeID] = fader;
×
734
    }
735

UNCOV
736
    return fader->getChannelFader(doc(), universes[universeID], fixtureID, channel);
×
737
}
738

739
void RGBMatrix::updateFaderValues(FadeChannel *fc, uchar value, uint fadeTime)
×
740
{
741
    fc->setStart(fc->current());
×
742
    fc->setTarget(value);
×
UNCOV
743
    fc->setElapsed(0);
×
744
    fc->setReady(false);
×
745
    // fade in/out depends on target value
UNCOV
746
    if (value == 0)
×
747
        fc->setFadeTime(fadeOutSpeed());
×
748
    else
UNCOV
749
        fc->setFadeTime(fadeTime);
×
750
}
×
751

752
void RGBMatrix::updateMapChannels(const RGBMap& map, const FixtureGroup *grp, QList<Universe *> universes)
×
753
{
UNCOV
754
    uint fadeTime = (overrideFadeInSpeed() == defaultSpeed()) ? fadeInSpeed() : overrideFadeInSpeed();
×
755

756
    // Create/modify fade channels for ALL heads in the group
UNCOV
757
    QMapIterator<QLCPoint, GroupHead> it(grp->headsMap());
×
758
    while (it.hasNext())
×
759
    {
760
        it.next();
UNCOV
761
        QLCPoint pt = it.key();
×
762
        GroupHead grpHead = it.value();
×
UNCOV
763
        Fixture *fxi = doc()->fixture(grpHead.fxi);
×
764
        if (fxi == NULL)
×
UNCOV
765
            continue;
×
766

UNCOV
767
        QLCFixtureHead head = fxi->head(grpHead.head);
×
768

769
        if (pt.y() >= map.count() || pt.x() >= map[pt.y()].count())
×
UNCOV
770
            continue;
×
771

772
        uint col = map[pt.y()][pt.x()];
×
773

774
        if (m_controlMode == ControlModeRgb)
×
775
        {
776
            QVector <quint32> rgb = head.rgbChannels();
×
UNCOV
777
            QVector <quint32> cmy = head.cmyChannels();
×
778

779
            if (rgb.size() == 3)
×
780
            {
781
                // RGB color mixing
782
                FadeChannel *fc = getFader(universes, fxi->universe(), grpHead.fxi, rgb.at(0));
×
UNCOV
783
                updateFaderValues(fc, qRed(col), fadeTime);
×
784

UNCOV
785
                fc = getFader(universes, fxi->universe(), grpHead.fxi, rgb.at(1));
×
786
                updateFaderValues(fc, qGreen(col), fadeTime);
×
787

788
                fc = getFader(universes, fxi->universe(), grpHead.fxi, rgb.at(2));
×
789
                updateFaderValues(fc, qBlue(col), fadeTime);
×
790
            }
UNCOV
791
            else if (cmy.size() == 3)
×
792
            {
793
                // CMY color mixing
794
                QColor cmyCol(col);
×
795

796
                FadeChannel *fc = getFader(universes, fxi->universe(), grpHead.fxi, cmy.at(0));
×
UNCOV
797
                updateFaderValues(fc, cmyCol.cyan(), fadeTime);
×
798

799
                fc = getFader(universes, fxi->universe(), grpHead.fxi, cmy.at(1));
×
UNCOV
800
                updateFaderValues(fc, cmyCol.magenta(), fadeTime);
×
801

UNCOV
802
                fc = getFader(universes, fxi->universe(), grpHead.fxi, cmy.at(2));
×
803
                updateFaderValues(fc, cmyCol.yellow(), fadeTime);
×
804
            }
805
        }
806
        else if (m_controlMode == ControlModeWhite)
×
807
        {
UNCOV
808
            quint32 white = head.channelNumber(QLCChannel::White, QLCChannel::MSB);
×
809

UNCOV
810
            if (white != QLCChannel::invalid())
×
811
            {
UNCOV
812
                FadeChannel *fc = getFader(universes, fxi->universe(), grpHead.fxi, white);
×
UNCOV
813
                updateFaderValues(fc, rgbToGrey(col), fadeTime);
×
814
            }
815
        }
UNCOV
816
        else if (m_controlMode == ControlModeAmber)
×
817
        {
UNCOV
818
            quint32 amber = head.channelNumber(QLCChannel::Amber, QLCChannel::MSB);
×
819

UNCOV
820
            if (amber != QLCChannel::invalid())
×
821
            {
UNCOV
822
                FadeChannel *fc = getFader(universes, fxi->universe(), grpHead.fxi, amber);
×
UNCOV
823
                updateFaderValues(fc, rgbToGrey(col), fadeTime);
×
824
            }
825
        }
826
        else if (m_controlMode == ControlModeUV)
×
827
        {
UNCOV
828
            quint32 uv = head.channelNumber(QLCChannel::UV, QLCChannel::MSB);
×
829

UNCOV
830
            if (uv != QLCChannel::invalid())
×
831
            {
832
                FadeChannel *fc = getFader(universes, fxi->universe(), grpHead.fxi, uv);
×
UNCOV
833
                updateFaderValues(fc, rgbToGrey(col), fadeTime);
×
834
            }
835
        }
836
        else if (m_controlMode == ControlModeShutter)
×
837
        {
UNCOV
838
            QVector <quint32> shutters = head.shutterChannels();
×
839

UNCOV
840
            if (shutters.size())
×
841
            {
UNCOV
842
                FadeChannel *fc = getFader(universes, fxi->universe(), grpHead.fxi, shutters.first());
×
843
                updateFaderValues(fc, rgbToGrey(col), fadeTime);
×
844
            }
845
        }
846

UNCOV
847
        if (m_controlMode == ControlModeDimmer || m_dimmerControl)
×
848
        {
UNCOV
849
            quint32 masterDim = fxi->masterIntensityChannel();
×
850
            quint32 headDim = head.channelNumber(QLCChannel::Intensity, QLCChannel::MSB);
×
UNCOV
851
            QVector <quint32> dimmers;
×
852

853
            // Collect all dimmers that affect current head:
854
            // They are the master dimmer (affects whole fixture)
855
            // and per-head dimmer.
856
            //
857
            // If there are no RGB or CMY channels, the least important* dimmer channel
858
            // is used to create grayscale image.
859
            //
860
            // The rest of the dimmer channels are set to full if dimmer control is
861
            // enabled and target color is > 0 (see
862
            // http://www.qlcplus.org/forum/viewtopic.php?f=29&t=11090)
863
            //
864
            // Note: If there is only one head, and only one dimmer channel,
865
            // make it a master dimmer in fixture definition.
866
            //
867
            // *least important - per head dimmer if present,
868
            // otherwise per fixture dimmer if present
869

870
            if (masterDim != QLCChannel::invalid())
×
871
                dimmers << masterDim;
872

UNCOV
873
            if (headDim != QLCChannel::invalid() && headDim != masterDim)
×
874
                dimmers << headDim;
875

UNCOV
876
            if (dimmers.size())
×
877
            {
878
                // Set dimmer to value of the color (e.g. for PARs)
UNCOV
879
                FadeChannel *fc = getFader(universes, fxi->universe(), grpHead.fxi, dimmers.last());
×
UNCOV
880
                updateFaderValues(fc, rgbToGrey(col), fadeTime);
×
881
                dimmers.pop_back();
882
            }
883

884
            // Set the rest of the dimmer channels to full on
UNCOV
885
            foreach (quint32 ch, dimmers)
×
886
            {
UNCOV
887
                FadeChannel *fc = getFader(universes, fxi->universe(), grpHead.fxi, ch);
×
888
                updateFaderValues(fc, col == 0 ? 0 : 255, fadeTime);
×
889
            }
890
        }
891
    }
892
}
×
893

UNCOV
894
uchar RGBMatrix::rgbToGrey(uint col)
×
895
{
896
    // the weights are taken from
897
    // https://en.wikipedia.org/wiki/YUV#SDTV_with_BT.601
UNCOV
898
    return (0.299 * qRed(col) + 0.587 * qGreen(col) + 0.114 * qBlue(col));
×
899
}
900

901
/*********************************************************************
902
 * Attributes
903
 *********************************************************************/
904

UNCOV
905
int RGBMatrix::adjustAttribute(qreal fraction, int attributeId)
×
906
{
UNCOV
907
    int attrIndex = Function::adjustAttribute(fraction, attributeId);
×
908

UNCOV
909
    if (attrIndex == Intensity)
×
910
    {
UNCOV
911
        foreach (QSharedPointer<GenericFader> fader, m_fadersMap.values())
×
912
        {
UNCOV
913
            if (!fader.isNull())
×
UNCOV
914
                fader->adjustIntensity(getAttributeValue(Function::Intensity));
×
915
        }
916
    }
917

UNCOV
918
    return attrIndex;
×
919
}
920

921
/*************************************************************************
922
 * Blend mode
923
 *************************************************************************/
924

UNCOV
925
void RGBMatrix::setBlendMode(Universe::BlendMode mode)
×
926
{
UNCOV
927
    if (mode == blendMode())
×
928
        return;
929

UNCOV
930
    foreach (QSharedPointer<GenericFader> fader, m_fadersMap.values())
×
931
    {
UNCOV
932
        if (!fader.isNull())
×
UNCOV
933
            fader->setBlendMode(mode);
×
934
    }
935

UNCOV
936
    Function::setBlendMode(mode);
×
937
    emit changed(id());
×
938
}
939

940
/*************************************************************************
941
 * Control Mode
942
 *************************************************************************/
943

944
RGBMatrix::ControlMode RGBMatrix::controlMode() const
2✔
945
{
946
    return m_controlMode;
2✔
947
}
948

949
void RGBMatrix::setControlMode(RGBMatrix::ControlMode mode)
3✔
950
{
951
    m_controlMode = mode;
3✔
952
    emit changed(id());
3✔
953
}
3✔
954

955
RGBMatrix::ControlMode RGBMatrix::stringToControlMode(QString mode)
1✔
956
{
957
    if (mode == KXMLQLCRGBMatrixControlModeRgb)
1✔
958
        return ControlModeRgb;
UNCOV
959
    else if (mode == KXMLQLCRGBMatrixControlModeAmber)
×
960
        return ControlModeAmber;
UNCOV
961
    else if (mode == KXMLQLCRGBMatrixControlModeWhite)
×
962
        return ControlModeWhite;
UNCOV
963
    else if (mode == KXMLQLCRGBMatrixControlModeUV)
×
964
        return ControlModeUV;
UNCOV
965
    else if (mode == KXMLQLCRGBMatrixControlModeDimmer)
×
966
        return ControlModeDimmer;
UNCOV
967
    else if (mode == KXMLQLCRGBMatrixControlModeShutter)
×
UNCOV
968
        return ControlModeShutter;
×
969

970
    return ControlModeRgb;
971
}
972

973
QString RGBMatrix::controlModeToString(RGBMatrix::ControlMode mode)
1✔
974
{
975
    switch(mode)
1✔
976
    {
977
        default:
1✔
978
        case ControlModeRgb:
979
            return QString(KXMLQLCRGBMatrixControlModeRgb);
1✔
980
        break;
UNCOV
981
        case ControlModeAmber:
×
UNCOV
982
            return QString(KXMLQLCRGBMatrixControlModeAmber);
×
983
        break;
UNCOV
984
        case ControlModeWhite:
×
UNCOV
985
            return QString(KXMLQLCRGBMatrixControlModeWhite);
×
986
        break;
UNCOV
987
        case ControlModeUV:
×
UNCOV
988
            return QString(KXMLQLCRGBMatrixControlModeUV);
×
989
        break;
UNCOV
990
        case ControlModeDimmer:
×
UNCOV
991
            return QString(KXMLQLCRGBMatrixControlModeDimmer);
×
992
        break;
UNCOV
993
        case ControlModeShutter:
×
UNCOV
994
            return QString(KXMLQLCRGBMatrixControlModeShutter);
×
995
        break;
996
    }
997
}
998

999

1000
/*************************************************************************
1001
 *************************************************************************
1002
 *                          RGBMatrixStep class
1003
 *************************************************************************
1004
 *************************************************************************/
1005

1006
RGBMatrixStep::RGBMatrixStep()
10✔
1007
    : m_direction(Function::Forward)
1008
    , m_currentStepIndex(0)
1009
    , m_stepColor(QColor())
1010
    , m_crDelta(0)
1011
    , m_cgDelta(0)
1012
    , m_cbDelta(0)
10✔
1013
{
1014

1015
}
10✔
1016

UNCOV
1017
void RGBMatrixStep::setCurrentStepIndex(int index)
×
1018
{
UNCOV
1019
    m_currentStepIndex = index;
×
1020
}
×
1021

1022
int RGBMatrixStep::currentStepIndex() const
1✔
1023
{
1024
    return m_currentStepIndex;
1✔
1025
}
1026

1027
void RGBMatrixStep::calculateColorDelta(QColor startColor, QColor endColor, RGBAlgorithm *algorithm)
16✔
1028
{
1029
    m_crDelta = 0;
16✔
1030
    m_cgDelta = 0;
16✔
1031
    m_cbDelta = 0;
16✔
1032

1033
    if (endColor.isValid() && algorithm != NULL && algorithm->acceptColors() > 1)
16✔
1034
    {
1035
        m_crDelta = endColor.red() - startColor.red();
10✔
1036
        m_cgDelta = endColor.green() - startColor.green();
10✔
1037
        m_cbDelta = endColor.blue() - startColor.blue();
10✔
1038

1039
        //qDebug() << "Color deltas:" << m_crDelta << m_cgDelta << m_cbDelta;
1040
    }
1041
}
16✔
1042

UNCOV
1043
void RGBMatrixStep::setStepColor(QColor color)
×
1044
{
UNCOV
1045
    m_stepColor = color;
×
UNCOV
1046
}
×
1047

1048
QColor RGBMatrixStep::stepColor()
6✔
1049
{
1050
    return m_stepColor;
6✔
1051
}
1052

1053
void RGBMatrixStep::updateStepColor(int stepIndex, QColor startColor, int stepsCount)
×
1054
{
1055
    if (stepsCount <= 0)
×
1056
        return;
1057

1058
    if (stepsCount == 1)
×
1059
    {
1060
        m_stepColor = startColor;
×
1061
    }
1062
    else
1063
    {
1064
        m_stepColor.setRed(startColor.red() + (m_crDelta * stepIndex / (stepsCount - 1)));
×
UNCOV
1065
        m_stepColor.setGreen(startColor.green() + (m_cgDelta * stepIndex / (stepsCount - 1)));
×
1066
        m_stepColor.setBlue(startColor.blue() + (m_cbDelta * stepIndex / (stepsCount - 1)));
×
1067
    }
1068

1069
    //qDebug() << "RGBMatrix step" << stepIndex << ", color:" << QString::number(m_stepColor.rgb(), 16);
1070
}
1071

UNCOV
1072
void RGBMatrixStep::initializeDirection(Function::Direction direction, QColor startColor, QColor endColor, int stepsCount, RGBAlgorithm *algorithm)
×
1073
{
1074
    m_direction = direction;
×
1075

1076
    if (m_direction == Function::Forward)
×
1077
    {
UNCOV
1078
        setCurrentStepIndex(0);
×
UNCOV
1079
        setStepColor(startColor);
×
1080
    }
1081
    else
1082
    {
UNCOV
1083
        setCurrentStepIndex(stepsCount - 1);
×
1084

UNCOV
1085
        if (endColor.isValid())
×
UNCOV
1086
            setStepColor(endColor);
×
1087
        else
1088
            setStepColor(startColor);
×
1089
    }
1090

UNCOV
1091
    calculateColorDelta(startColor, endColor, algorithm);
×
UNCOV
1092
}
×
1093

1094
bool RGBMatrixStep::checkNextStep(Function::RunOrder order,
×
1095
                                  QColor startColor, QColor endColor, int stepsNumber)
1096
{
UNCOV
1097
    if (order == Function::PingPong)
×
1098
    {
1099
        if (m_direction == Function::Forward && (m_currentStepIndex + 1) == stepsNumber)
×
1100
        {
UNCOV
1101
            m_direction = Function::Backward;
×
UNCOV
1102
            m_currentStepIndex = stepsNumber - 2;
×
UNCOV
1103
            if (endColor.isValid())
×
UNCOV
1104
                m_stepColor = endColor;
×
1105

UNCOV
1106
            updateStepColor(m_currentStepIndex, startColor, stepsNumber);
×
1107
        }
UNCOV
1108
        else if (m_direction == Function::Backward && (m_currentStepIndex - 1) < 0)
×
1109
        {
1110
            m_direction = Function::Forward;
×
UNCOV
1111
            m_currentStepIndex = 1;
×
UNCOV
1112
            m_stepColor = startColor;
×
UNCOV
1113
            updateStepColor(m_currentStepIndex, startColor, stepsNumber);
×
1114
        }
1115
        else
1116
        {
UNCOV
1117
            if (m_direction == Function::Forward)
×
UNCOV
1118
                m_currentStepIndex++;
×
1119
            else
1120
                m_currentStepIndex--;
×
UNCOV
1121
            updateStepColor(m_currentStepIndex, startColor, stepsNumber);
×
1122
        }
1123
    }
1124
    else if (order == Function::SingleShot)
×
1125
    {
UNCOV
1126
        if (m_direction == Function::Forward)
×
1127
        {
1128
            if (m_currentStepIndex >= stepsNumber - 1)
×
1129
                return false;
1130
            else
1131
            {
UNCOV
1132
                m_currentStepIndex++;
×
UNCOV
1133
                updateStepColor(m_currentStepIndex, startColor, stepsNumber);
×
1134
            }
1135
        }
1136
        else
1137
        {
UNCOV
1138
            if (m_currentStepIndex <= 0)
×
1139
                return false;
1140
            else
1141
            {
UNCOV
1142
                m_currentStepIndex--;
×
UNCOV
1143
                updateStepColor(m_currentStepIndex, startColor, stepsNumber);
×
1144
            }
1145
        }
1146
    }
1147
    else
1148
    {
UNCOV
1149
        if (m_direction == Function::Forward)
×
1150
        {
UNCOV
1151
            if (m_currentStepIndex >= stepsNumber - 1)
×
1152
            {
UNCOV
1153
                m_currentStepIndex = 0;
×
UNCOV
1154
                m_stepColor = startColor;
×
1155
            }
1156
            else
1157
            {
UNCOV
1158
                m_currentStepIndex++;
×
UNCOV
1159
                updateStepColor(m_currentStepIndex, startColor, stepsNumber);
×
1160
            }
1161
        }
1162
        else
1163
        {
UNCOV
1164
            if (m_currentStepIndex <= 0)
×
1165
            {
UNCOV
1166
                m_currentStepIndex = stepsNumber - 1;
×
UNCOV
1167
                if (endColor.isValid())
×
UNCOV
1168
                    m_stepColor = endColor;
×
1169
            }
1170
            else
1171
            {
UNCOV
1172
                m_currentStepIndex--;
×
UNCOV
1173
                updateStepColor(m_currentStepIndex, startColor, stepsNumber);
×
1174
            }
1175
        }
1176
    }
1177

1178
    return true;
1179
}
1180

STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc