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

mcallegari / qlcplus / 12610312303

04 Jan 2025 12:09PM UTC coverage: 31.65% (-0.01%) from 31.664%
12610312303

Pull #1659

github

mcallegari
ui: fix some more cross universe cases
Pull Request #1659: RGBPanel: implement cross universe and 16bit profiles

99 of 214 new or added lines in 10 files covered. (46.26%)

234 existing lines in 6 files now uncovered.

14141 of 44679 relevant lines covered (31.65%)

26856.33 hits per line

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

42.02
/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

93
QIcon RGBMatrix::getIcon() const
×
94
{
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
    {
149
        delete copy;
×
150
        copy = NULL;
151
    }
152
    if (addToDoc == true && doc->addFunction(copy) == false)
1✔
153
    {
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
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();
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) */
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
257
QRecursiveMutex& RGBMatrix::algorithmMutex()
×
258
{
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✔
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

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

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

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✔
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
        {
444
            setColor(0, QColor::fromRgb(QRgb(root.readElementText().toUInt())));
×
445
        }
446
        else if (root.name() == KXMLQLCRGBMatrixEndColor)
12✔
447
        {
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
        }
459
        else if (root.name() == KXMLQLCRGBMatrixProperty)
×
460
        {
461
            QString name = root.attributes().value(KXMLQLCRGBMatrixPropertyName).toString();
×
462
            QString value = root.attributes().value(KXMLQLCRGBMatrixPropertyValue).toString();
×
463
            setProperty(name, value);
×
464
            root.skipCurrentElement();
×
465
        }
466
        else if (root.name() == KXMLQLCRGBMatrixDimmerControl)
×
467
        {
468
            setDimmerControl(root.readElementText().toInt());
×
469
        }
470
        else
471
        {
472
            qWarning() << Q_FUNC_INFO << "Unknown RGB matrix tag:" << root.name();
×
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();
527
        doc->writeStartElement(KXMLQLCRGBMatrixProperty);
×
528
        doc->writeAttribute(KXMLQLCRGBMatrixPropertyName, it.key());
×
529
        doc->writeAttribute(KXMLQLCRGBMatrixPropertyValue, it.value());
×
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

543
void RGBMatrix::tap()
×
544
{
545
    if (stopped() == false)
×
546
    {
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
        {
551
            roundCheck();
×
552
            resetElapsed();
×
553
        }
554
    }
555
}
×
556

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

562
        m_group = doc()->fixtureGroup(m_fixtureGroupID);
×
563
        if (m_group == NULL)
×
564
        {
565
            // No fixture group to control
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
            {
577
                RGBScript *script = static_cast<RGBScript*> (m_algorithm);
×
578
                QHashIterator<QString, QString> it(m_properties);
×
579
                while (it.hasNext())
×
580
                {
581
                    it.next();
582
                    script->setProperty(it.key(), it.value());
×
583
                }
584
            }
585
        }
586
    }
587

588
    m_roundTime->restart();
×
589

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

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

597
    {
598
        QMutexLocker algorithmLocker(&m_algorithmMutex);
×
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.
607
        if (duration() == 0)
×
608
            return;
609

610
        // Invalid/nonexistent script
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
617
            if (elapsed() < MasterTimer::tick())
×
618
            {
619
                if (tempoType() == Beats)
×
620
                    m_stepBeatDuration = beatsToTime(duration(), timer->beatTimeDuration());
×
621

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

630
    if (isPaused() == false)
×
631
    {
632
        // Increment the ms elapsed time
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
        {
648
            roundCheck();
×
649
        }
650
        else if (tempoType() == Beats)
×
651
        {
652
            if (timer->isBeat())
×
653
            {
654
                incrementElapsedBeats();
×
655
                qDebug() << "Elapsed beats:" << elapsedBeats() << ", time elapsed:" << elapsed() << ", step time:" << m_stepBeatDuration;
656
                if (elapsedBeats() % duration() == 0)
×
657
                {
658
                    roundCheck();
×
659
                    resetElapsed();
×
660
                }
661
            }
662
            else if (elapsed() >= m_stepBeatDuration && (uint)timer->timeToNextBeat() > m_stepBeatDuration / 16)
×
663
            {
664
                qDebug() << "Elapsed exceeded";
665
                roundCheck();
×
666
            }
667
        }
668
    }
669
}
670

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
    {
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())
×
690
                fader->setFadeOut(true, fadeout);
×
691
        }
692
    }
693

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
{
707
    QMutexLocker algorithmLocker(&m_algorithmMutex);
×
708
    if (m_algorithm == NULL)
×
709
        return;
710

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

714
    m_roundTime->restart();
×
715

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

NEW
722
FadeChannel *RGBMatrix::getFader(Universe *universe, quint32 fixtureID, quint32 channel)
×
723
{
724
    // get the universe Fader first. If doesn't exist, create it
NEW
725
    if (universe == NULL)
×
726
        return NULL;
727

NEW
728
    QSharedPointer<GenericFader> fader = m_fadersMap.value(universe->id(), QSharedPointer<GenericFader>());
×
UNCOV
729
    if (fader.isNull())
×
730
    {
NEW
731
        fader = universe->requestFader();
×
732
        fader->adjustIntensity(getAttributeValue(Intensity));
×
733
        fader->setBlendMode(blendMode());
×
734
        fader->setName(name());
×
735
        fader->setParentFunctionID(id());
×
NEW
736
        m_fadersMap[universe->id()] = fader;
×
737
    }
738

NEW
739
    return fader->getChannelFader(doc(), universe, fixtureID, channel);
×
740
}
741

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

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

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

770
        QLCFixtureHead head = fxi->head(grpHead.head);
×
771

772
        if (pt.y() >= map.count() || pt.x() >= map[pt.y()].count())
×
773
            continue;
×
774

775
        uint col = map[pt.y()][pt.x()];
×
NEW
776
        QVector<quint32> channelList;
×
NEW
777
        QVector<uchar> valueList;
×
778

779
        if (m_controlMode == ControlModeRgb)
×
780
        {
NEW
781
            channelList = head.rgbChannels();
×
782

NEW
783
            if (channelList.size() == 3)
×
784
            {
NEW
785
                valueList.append(qRed(col));
×
NEW
786
                valueList.append(qGreen(col));
×
NEW
787
                valueList.append(qBlue(col));
×
788
            }
789
            else
790
            {
NEW
791
                channelList = head.cmyChannels();
×
792

NEW
793
                if (channelList.size() == 3)
×
794
                {
795
                    // CMY color mixing
NEW
796
                    QColor cmyCol(col);
×
NEW
797
                    valueList.append(cmyCol.cyan());
×
NEW
798
                    valueList.append(cmyCol.magenta());
×
NEW
799
                    valueList.append(cmyCol.yellow());
×
800
                }
801
            }
802
        }
803
        else if (m_controlMode == ControlModeShutter)
×
804
        {
NEW
805
            channelList = head.shutterChannels();
×
806

NEW
807
            if (channelList.size())
×
808
            {
809
                // make sure only one channel is in the list
NEW
810
                channelList.resize(1);
×
NEW
811
                valueList.append(rgbToGrey(col));
×
812
            }
813
        }
NEW
814
        else if (m_controlMode == ControlModeDimmer || m_dimmerControl)
×
815
        {
816
            // Collect all dimmers that affect current head:
817
            // They are the master dimmer (affects whole fixture)
818
            // and per-head dimmer.
819
            //
820
            // If there are no RGB or CMY channels, the least important* dimmer channel
821
            // is used to create grayscale image.
822
            //
823
            // The rest of the dimmer channels are set to full if dimmer control is
824
            // enabled and target color is > 0 (see
825
            // http://www.qlcplus.org/forum/viewtopic.php?f=29&t=11090)
826
            //
827
            // Note: If there is only one head, and only one dimmer channel,
828
            // make it a master dimmer in fixture definition.
829
            //
830
            // *least important - per head dimmer if present,
831
            // otherwise per fixture dimmer if present
832

NEW
833
            quint32 masterDim = fxi->masterIntensityChannel();
×
NEW
834
            quint32 headDim = head.channelNumber(QLCChannel::Intensity, QLCChannel::MSB);
×
835

NEW
836
            if (masterDim != QLCChannel::invalid())
×
837
            {
NEW
838
                channelList.append(masterDim);
×
NEW
839
                valueList.append(rgbToGrey(col));
×
840
            }
841

NEW
842
            if (headDim != QLCChannel::invalid() && headDim != masterDim)
×
843
            {
NEW
844
                channelList.append(headDim);
×
NEW
845
                valueList.append(col == 0 ? 0 : 255);
×
UNCOV
846
            }
×
847
        }
848
        else
849
        {
NEW
850
            if (m_controlMode == ControlModeWhite)
×
NEW
851
                channelList.append(head.channelNumber(QLCChannel::White, QLCChannel::MSB));
×
NEW
852
            else if (m_controlMode == ControlModeAmber)
×
NEW
853
                channelList.append(head.channelNumber(QLCChannel::Amber, QLCChannel::MSB));
×
NEW
854
            else if (m_controlMode == ControlModeUV)
×
NEW
855
                channelList.append(head.channelNumber(QLCChannel::UV, QLCChannel::MSB));
×
856

NEW
857
            valueList.append(rgbToGrey(col));
×
858
        }
859

NEW
860
        quint32 absAddress = fxi->universeAddress();
×
861

NEW
862
        for (int i = 0; i < channelList.count(); i++)
×
863
        {
NEW
864
            if (channelList.at(i) == QLCChannel::invalid())
×
NEW
865
                continue;
×
866

NEW
867
            quint32 universeIndex = floor((absAddress + channelList.at(i)) / 512);
×
868

NEW
869
            FadeChannel *fc = getFader(universes.at(universeIndex), grpHead.fxi, channelList.at(i));
×
NEW
870
            updateFaderValues(fc, valueList.at(i), fadeTime);
×
871
        }
872
    }
873
}
×
874

875
uchar RGBMatrix::rgbToGrey(uint col)
×
876
{
877
    // the weights are taken from
878
    // https://en.wikipedia.org/wiki/YUV#SDTV_with_BT.601
879
    return (0.299 * qRed(col) + 0.587 * qGreen(col) + 0.114 * qBlue(col));
×
880
}
881

882
/*********************************************************************
883
 * Attributes
884
 *********************************************************************/
885

886
int RGBMatrix::adjustAttribute(qreal fraction, int attributeId)
×
887
{
888
    int attrIndex = Function::adjustAttribute(fraction, attributeId);
×
889

890
    if (attrIndex == Intensity)
×
891
    {
892
        foreach (QSharedPointer<GenericFader> fader, m_fadersMap.values())
×
893
        {
894
            if (!fader.isNull())
×
895
                fader->adjustIntensity(getAttributeValue(Function::Intensity));
×
896
        }
897
    }
898

899
    return attrIndex;
×
900
}
901

902
/*************************************************************************
903
 * Blend mode
904
 *************************************************************************/
905

906
void RGBMatrix::setBlendMode(Universe::BlendMode mode)
×
907
{
908
    if (mode == blendMode())
×
909
        return;
910

911
    foreach (QSharedPointer<GenericFader> fader, m_fadersMap.values())
×
912
    {
913
        if (!fader.isNull())
×
914
            fader->setBlendMode(mode);
×
915
    }
916

917
    Function::setBlendMode(mode);
×
918
    emit changed(id());
×
919
}
920

921
/*************************************************************************
922
 * Control Mode
923
 *************************************************************************/
924

925
RGBMatrix::ControlMode RGBMatrix::controlMode() const
2✔
926
{
927
    return m_controlMode;
2✔
928
}
929

930
void RGBMatrix::setControlMode(RGBMatrix::ControlMode mode)
3✔
931
{
932
    m_controlMode = mode;
3✔
933
    emit changed(id());
3✔
934
}
3✔
935

936
RGBMatrix::ControlMode RGBMatrix::stringToControlMode(QString mode)
1✔
937
{
938
    if (mode == KXMLQLCRGBMatrixControlModeRgb)
1✔
939
        return ControlModeRgb;
940
    else if (mode == KXMLQLCRGBMatrixControlModeAmber)
×
941
        return ControlModeAmber;
942
    else if (mode == KXMLQLCRGBMatrixControlModeWhite)
×
943
        return ControlModeWhite;
944
    else if (mode == KXMLQLCRGBMatrixControlModeUV)
×
945
        return ControlModeUV;
946
    else if (mode == KXMLQLCRGBMatrixControlModeDimmer)
×
947
        return ControlModeDimmer;
948
    else if (mode == KXMLQLCRGBMatrixControlModeShutter)
×
949
        return ControlModeShutter;
×
950

951
    return ControlModeRgb;
952
}
953

954
QString RGBMatrix::controlModeToString(RGBMatrix::ControlMode mode)
1✔
955
{
956
    switch(mode)
1✔
957
    {
958
        default:
1✔
959
        case ControlModeRgb:
960
            return QString(KXMLQLCRGBMatrixControlModeRgb);
1✔
961
        break;
962
        case ControlModeAmber:
×
963
            return QString(KXMLQLCRGBMatrixControlModeAmber);
×
964
        break;
965
        case ControlModeWhite:
×
966
            return QString(KXMLQLCRGBMatrixControlModeWhite);
×
967
        break;
968
        case ControlModeUV:
×
969
            return QString(KXMLQLCRGBMatrixControlModeUV);
×
970
        break;
971
        case ControlModeDimmer:
×
972
            return QString(KXMLQLCRGBMatrixControlModeDimmer);
×
973
        break;
974
        case ControlModeShutter:
×
975
            return QString(KXMLQLCRGBMatrixControlModeShutter);
×
976
        break;
977
    }
978
}
979

980

981
/*************************************************************************
982
 *************************************************************************
983
 *                          RGBMatrixStep class
984
 *************************************************************************
985
 *************************************************************************/
986

987
RGBMatrixStep::RGBMatrixStep()
10✔
988
    : m_direction(Function::Forward)
989
    , m_currentStepIndex(0)
990
    , m_stepColor(QColor())
991
    , m_crDelta(0)
992
    , m_cgDelta(0)
993
    , m_cbDelta(0)
10✔
994
{
995

996
}
10✔
997

998
void RGBMatrixStep::setCurrentStepIndex(int index)
×
999
{
1000
    m_currentStepIndex = index;
×
1001
}
×
1002

1003
int RGBMatrixStep::currentStepIndex() const
1✔
1004
{
1005
    return m_currentStepIndex;
1✔
1006
}
1007

1008
void RGBMatrixStep::calculateColorDelta(QColor startColor, QColor endColor, RGBAlgorithm *algorithm)
16✔
1009
{
1010
    m_crDelta = 0;
16✔
1011
    m_cgDelta = 0;
16✔
1012
    m_cbDelta = 0;
16✔
1013

1014
    if (endColor.isValid() && algorithm != NULL && algorithm->acceptColors() > 1)
16✔
1015
    {
1016
        m_crDelta = endColor.red() - startColor.red();
10✔
1017
        m_cgDelta = endColor.green() - startColor.green();
10✔
1018
        m_cbDelta = endColor.blue() - startColor.blue();
10✔
1019

1020
        //qDebug() << "Color deltas:" << m_crDelta << m_cgDelta << m_cbDelta;
1021
    }
1022
}
16✔
1023

1024
void RGBMatrixStep::setStepColor(QColor color)
×
1025
{
1026
    m_stepColor = color;
×
1027
}
×
1028

1029
QColor RGBMatrixStep::stepColor()
6✔
1030
{
1031
    return m_stepColor;
6✔
1032
}
1033

1034
void RGBMatrixStep::updateStepColor(int stepIndex, QColor startColor, int stepsCount)
×
1035
{
1036
    if (stepsCount <= 0)
×
1037
        return;
1038

1039
    if (stepsCount == 1)
×
1040
    {
1041
        m_stepColor = startColor;
×
1042
    }
1043
    else
1044
    {
1045
        m_stepColor.setRed(startColor.red() + (m_crDelta * stepIndex / (stepsCount - 1)));
×
1046
        m_stepColor.setGreen(startColor.green() + (m_cgDelta * stepIndex / (stepsCount - 1)));
×
1047
        m_stepColor.setBlue(startColor.blue() + (m_cbDelta * stepIndex / (stepsCount - 1)));
×
1048
    }
1049

1050
    //qDebug() << "RGBMatrix step" << stepIndex << ", color:" << QString::number(m_stepColor.rgb(), 16);
1051
}
1052

1053
void RGBMatrixStep::initializeDirection(Function::Direction direction, QColor startColor, QColor endColor, int stepsCount, RGBAlgorithm *algorithm)
×
1054
{
1055
    m_direction = direction;
×
1056

1057
    if (m_direction == Function::Forward)
×
1058
    {
1059
        setCurrentStepIndex(0);
×
1060
        setStepColor(startColor);
×
1061
    }
1062
    else
1063
    {
1064
        setCurrentStepIndex(stepsCount - 1);
×
1065

1066
        if (endColor.isValid())
×
1067
            setStepColor(endColor);
×
1068
        else
1069
            setStepColor(startColor);
×
1070
    }
1071

1072
    calculateColorDelta(startColor, endColor, algorithm);
×
1073
}
×
1074

1075
bool RGBMatrixStep::checkNextStep(Function::RunOrder order,
×
1076
                                  QColor startColor, QColor endColor, int stepsNumber)
1077
{
1078
    if (order == Function::PingPong)
×
1079
    {
1080
        if (m_direction == Function::Forward && (m_currentStepIndex + 1) == stepsNumber)
×
1081
        {
1082
            m_direction = Function::Backward;
×
1083
            m_currentStepIndex = stepsNumber - 2;
×
1084
            if (endColor.isValid())
×
1085
                m_stepColor = endColor;
×
1086

1087
            updateStepColor(m_currentStepIndex, startColor, stepsNumber);
×
1088
        }
1089
        else if (m_direction == Function::Backward && (m_currentStepIndex - 1) < 0)
×
1090
        {
1091
            m_direction = Function::Forward;
×
1092
            m_currentStepIndex = 1;
×
1093
            m_stepColor = startColor;
×
1094
            updateStepColor(m_currentStepIndex, startColor, stepsNumber);
×
1095
        }
1096
        else
1097
        {
1098
            if (m_direction == Function::Forward)
×
1099
                m_currentStepIndex++;
×
1100
            else
1101
                m_currentStepIndex--;
×
1102
            updateStepColor(m_currentStepIndex, startColor, stepsNumber);
×
1103
        }
1104
    }
1105
    else if (order == Function::SingleShot)
×
1106
    {
1107
        if (m_direction == Function::Forward)
×
1108
        {
1109
            if (m_currentStepIndex >= stepsNumber - 1)
×
1110
                return false;
1111
            else
1112
            {
1113
                m_currentStepIndex++;
×
1114
                updateStepColor(m_currentStepIndex, startColor, stepsNumber);
×
1115
            }
1116
        }
1117
        else
1118
        {
1119
            if (m_currentStepIndex <= 0)
×
1120
                return false;
1121
            else
1122
            {
1123
                m_currentStepIndex--;
×
1124
                updateStepColor(m_currentStepIndex, startColor, stepsNumber);
×
1125
            }
1126
        }
1127
    }
1128
    else
1129
    {
1130
        if (m_direction == Function::Forward)
×
1131
        {
1132
            if (m_currentStepIndex >= stepsNumber - 1)
×
1133
            {
1134
                m_currentStepIndex = 0;
×
1135
                m_stepColor = startColor;
×
1136
            }
1137
            else
1138
            {
1139
                m_currentStepIndex++;
×
1140
                updateStepColor(m_currentStepIndex, startColor, stepsNumber);
×
1141
            }
1142
        }
1143
        else
1144
        {
1145
            if (m_currentStepIndex <= 0)
×
1146
            {
1147
                m_currentStepIndex = stepsNumber - 1;
×
1148
                if (endColor.isValid())
×
1149
                    m_stepColor = endColor;
×
1150
            }
1151
            else
1152
            {
1153
                m_currentStepIndex--;
×
1154
                updateStepColor(m_currentStepIndex, startColor, stepsNumber);
×
1155
            }
1156
        }
1157
    }
1158

1159
    return true;
1160
}
1161

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