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

mcallegari / qlcplus / 7252848206

18 Dec 2023 07:26PM UTC coverage: 32.067% (+0.001%) from 32.066%
7252848206

push

github

mcallegari
Code style review #1427

199 of 628 new or added lines in 101 files covered. (31.69%)

8 existing lines in 2 files now uncovered.

15169 of 47304 relevant lines covered (32.07%)

23733.74 hits per line

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

39.96
/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 KXMLQLCRGBMatrixFixtureGroup    QString("FixtureGroup")
40
#define KXMLQLCRGBMatrixDimmerControl   QString("DimmerControl")
41

42
#define KXMLQLCRGBMatrixProperty        QString("Property")
43
#define KXMLQLCRGBMatrixPropertyName    QString("Name")
44
#define KXMLQLCRGBMatrixPropertyValue   QString("Value")
45

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

54
/****************************************************************************
55
 * Initialization
56
 ****************************************************************************/
57

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

78
    RGBScript scr = doc->rgbScriptsCache()->script("Stripes");
18✔
79
    setAlgorithm(scr.clone());
9✔
80
}
9✔
81

82
RGBMatrix::~RGBMatrix()
11✔
83
{
84
    delete m_algorithm;
9✔
85
    delete m_roundTime;
9✔
86
    delete m_stepHandler;
9✔
87
}
11✔
88

89
QIcon RGBMatrix::getIcon() const
×
90
{
91
    return QIcon(":/rgbmatrix.png");
×
92
}
93

94
void RGBMatrix::setTotalDuration(quint32 msec)
1✔
95
{
96
    QMutexLocker algorithmLocker(&m_algorithmMutex);
1✔
97

98
    if (m_algorithm == NULL)
1✔
99
        return;
×
100

101
    FixtureGroup* grp = doc()->fixtureGroup(fixtureGroup());
1✔
102
    if (grp == NULL)
1✔
103
        return;
×
104

105
    int steps = m_algorithm->rgbMapStepCount(grp->size());
1✔
106
    setDuration(msec / steps);
1✔
107
}
108

109
quint32 RGBMatrix::totalDuration()
3✔
110
{
111
    QMutexLocker algorithmLocker(&m_algorithmMutex);
6✔
112

113
    if (m_algorithm == NULL)
3✔
114
        return 0;
×
115

116
    FixtureGroup* grp = doc()->fixtureGroup(fixtureGroup());
3✔
117
    if (grp == NULL)
3✔
118
        return 0;
1✔
119

120
    qDebug () << "Algorithm steps:" << m_algorithm->rgbMapStepCount(grp->size());
2✔
121
    return m_algorithm->rgbMapStepCount(grp->size()) * duration();
2✔
122
}
123

124
void RGBMatrix::setDimmerControl(bool dimmerControl)
1✔
125
{
126
    m_dimmerControl = dimmerControl;
1✔
127
}
1✔
128

129
bool RGBMatrix::dimmerControl() const
2✔
130
{
131
    return m_dimmerControl;
2✔
132
}
133

134
/****************************************************************************
135
 * Copying
136
 ****************************************************************************/
137

138
Function* RGBMatrix::createCopy(Doc* doc, bool addToDoc)
1✔
139
{
140
    Q_ASSERT(doc != NULL);
1✔
141

142
    Function* copy = new RGBMatrix(doc);
1✔
143
    if (copy->copyFrom(this) == false)
1✔
144
    {
145
        delete copy;
×
146
        copy = NULL;
×
147
    }
148
    if (addToDoc == true && doc->addFunction(copy) == false)
1✔
149
    {
150
        delete copy;
×
151
        copy = NULL;
×
152
    }
153

154
    return copy;
1✔
155
}
156

157
bool RGBMatrix::copyFrom(const Function* function)
1✔
158
{
159
    const RGBMatrix* mtx = qobject_cast<const RGBMatrix*> (function);
1✔
160
    if (mtx == NULL)
1✔
161
        return false;
×
162

163
    setDimmerControl(mtx->dimmerControl());
1✔
164
    setFixtureGroup(mtx->fixtureGroup());
1✔
165
    if (mtx->algorithm() != NULL)
1✔
166
        setAlgorithm(mtx->algorithm()->clone());
1✔
167
    else
168
        setAlgorithm(NULL);
×
169
    setStartColor(mtx->startColor());
1✔
170
    setEndColor(mtx->endColor());
1✔
171

172
    return Function::copyFrom(function);
1✔
173
}
174

175
/****************************************************************************
176
 * Fixtures
177
 ****************************************************************************/
178

179
quint32 RGBMatrix::fixtureGroup() const
37✔
180
{
181
    return m_fixtureGroupID;
37✔
182
}
183

184
void RGBMatrix::setFixtureGroup(quint32 id)
8✔
185
{
186
    m_fixtureGroupID = id;
8✔
187
    {
188
        QMutexLocker algoLocker(&m_algorithmMutex);
8✔
189
        m_group = doc()->fixtureGroup(m_fixtureGroupID);
8✔
190
    }
191
    m_stepsCount = stepsCount();
8✔
192
}
8✔
193

194
QList<quint32> RGBMatrix::components()
2✔
195
{
196
    if (m_group != NULL)
2✔
197
        return m_group->fixtureList();
1✔
198

199
    return QList<quint32>();
1✔
200
}
201

202
/****************************************************************************
203
 * Algorithm
204
 ****************************************************************************/
205

206
void RGBMatrix::setAlgorithm(RGBAlgorithm* algo)
13✔
207
{
208
    {
209
        QMutexLocker algorithmLocker(&m_algorithmMutex);
26✔
210
        delete m_algorithm;
13✔
211
        m_algorithm = algo;
13✔
212

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

234
    emit changed(id());
13✔
235
}
13✔
236

237
RGBAlgorithm* RGBMatrix::algorithm() const
18✔
238
{
239
    return m_algorithm;
18✔
240
}
241

242
#if QT_VERSION < QT_VERSION_CHECK(5, 14, 0)
243
QMutex& RGBMatrix::algorithmMutex()
244
{
245
    return m_algorithmMutex;
246
}
247
#else
248
QRecursiveMutex& RGBMatrix::algorithmMutex()
×
249
{
250
    return m_algorithmMutex;
×
251
}
252
#endif
253

254

255
int RGBMatrix::stepsCount()
24✔
256
{
257
    QMutexLocker algorithmLocker(&m_algorithmMutex);
48✔
258

259
    if (m_algorithm == NULL)
24✔
260
        return 0;
×
261

262
    FixtureGroup* grp = doc()->fixtureGroup(fixtureGroup());
24✔
263
    if (grp != NULL)
24✔
264
        return m_algorithm->rgbMapStepCount(grp->size());
7✔
265

266
    return 0;
17✔
267
}
268

269
void RGBMatrix::previewMap(int step, RGBMatrixStep *handler)
7✔
270
{
271
    QMutexLocker algorithmLocker(&m_algorithmMutex);
7✔
272
    if (m_algorithm == NULL || handler == NULL)
7✔
273
        return;
×
274

275
    if (m_group == NULL)
7✔
276
        m_group = doc()->fixtureGroup(fixtureGroup());
1✔
277

278
    if (m_group != NULL)
7✔
279
        m_algorithm->rgbMap(m_group->size(), handler->stepColor().rgb(), step, handler->m_map);
6✔
280
}
281

282
/****************************************************************************
283
 * Color
284
 ****************************************************************************/
285

286
void RGBMatrix::setStartColor(const QColor& c)
6✔
287
{
288
    m_startColor = c;
6✔
289
    {
290
        QMutexLocker algorithmLocker(&m_algorithmMutex);
12✔
291
        if (m_algorithm != NULL)
6✔
292
        {
293
            m_algorithm->setColors(m_startColor, m_endColor);
6✔
294
            updateColorDelta();
6✔
295
        }
296
    }
297
    emit changed(id());
6✔
298
}
6✔
299

300
QColor RGBMatrix::startColor() const
7✔
301
{
302
    return m_startColor;
7✔
303
}
304

305
void RGBMatrix::setEndColor(const QColor &c)
6✔
306
{
307
    m_endColor = c;
6✔
308
    {
309
        QMutexLocker algorithmLocker(&m_algorithmMutex);
12✔
310
        if (m_algorithm != NULL)
6✔
311
        {
312
            m_algorithm->setColors(m_startColor, m_endColor);
6✔
313
            updateColorDelta();
6✔
314
        }
315
    }
316
    emit changed(id());
6✔
317
}
6✔
318

319
QColor RGBMatrix::endColor() const
8✔
320
{
321
    return m_endColor;
8✔
322
}
323

324
void RGBMatrix::updateColorDelta()
12✔
325
{
326
    m_stepHandler->calculateColorDelta(m_startColor, m_endColor);
12✔
327
}
12✔
328

329
/************************************************************************
330
 * Properties
331
 ************************************************************************/
332

333
void RGBMatrix::setProperty(QString propName, QString value)
1✔
334
{
335
    QMutexLocker algoLocker(&m_algorithmMutex);
1✔
336
    m_properties[propName] = value;
1✔
337
    if (m_algorithm != NULL && m_algorithm->type() == RGBAlgorithm::Script)
1✔
338
    {
339
        RGBScript *script = static_cast<RGBScript*> (m_algorithm);
1✔
340
        script->setProperty(propName, value);
1✔
341
    }
342
    m_stepsCount = stepsCount();
1✔
343
}
1✔
344

345
QString RGBMatrix::property(QString propName)
3✔
346
{
347
    QMutexLocker algoLocker(&m_algorithmMutex);
6✔
348

349
    /** If the property is cached, then return it right away */
350
    if (m_properties.contains(propName))
3✔
351
        return m_properties[propName];
1✔
352

353
    /** Otherwise, let's retrieve it from the Script */
354
    if (m_algorithm != NULL && m_algorithm->type() == RGBAlgorithm::Script)
2✔
355
    {
356
        RGBScript *script = static_cast<RGBScript*> (m_algorithm);
2✔
357
        return script->property(propName);
2✔
358
    }
359

360
    return QString();
×
361
}
362

363
/****************************************************************************
364
 * Load & Save
365
 ****************************************************************************/
366

367
bool RGBMatrix::loadXML(QXmlStreamReader &root)
3✔
368
{
369
    if (root.name() != KXMLQLCFunction)
3✔
370
    {
371
        qWarning() << Q_FUNC_INFO << "Function node not found";
1✔
372
        return false;
1✔
373
    }
374

375
    if (root.attributes().value(KXMLQLCFunctionType).toString() != typeToString(Function::RGBMatrixType))
2✔
376
    {
377
        qWarning() << Q_FUNC_INFO << "Function is not an RGB matrix";
1✔
378
        return false;
1✔
379
    }
380

381
    /* Load matrix contents */
382
    while (root.readNextStartElement())
9✔
383
    {
384
        if (root.name() == KXMLQLCFunctionSpeed)
8✔
385
        {
386
            loadXMLSpeed(root);
1✔
387
        }
388
        else if (root.name() == KXMLQLCRGBAlgorithm)
7✔
389
        {
390
            setAlgorithm(RGBAlgorithm::loader(doc(), root));
1✔
391
        }
392
        else if (root.name() == KXMLQLCRGBMatrixFixtureGroup)
6✔
393
        {
394
            setFixtureGroup(root.readElementText().toUInt());
1✔
395
        }
396
        else if (root.name() == KXMLQLCFunctionDirection)
5✔
397
        {
398
            loadXMLDirection(root);
1✔
399
        }
400
        else if (root.name() == KXMLQLCFunctionRunOrder)
4✔
401
        {
402
            loadXMLRunOrder(root);
1✔
403
        }
404
        else if (root.name() == KXMLQLCRGBMatrixStartColor)
3✔
405
        {
406
            setStartColor(QColor::fromRgb(QRgb(root.readElementText().toUInt())));
1✔
407
        }
408
        else if (root.name() == KXMLQLCRGBMatrixEndColor)
2✔
409
        {
410
            setEndColor(QColor::fromRgb(QRgb(root.readElementText().toUInt())));
1✔
411
        }
412
        else if (root.name() == KXMLQLCRGBMatrixControlMode)
1✔
413
        {
414
            setControlMode(stringToControlMode(root.readElementText()));
1✔
415
        }
416
        else if (root.name() == KXMLQLCRGBMatrixProperty)
×
417
        {
418
            QString name = root.attributes().value(KXMLQLCRGBMatrixPropertyName).toString();
×
419
            QString value = root.attributes().value(KXMLQLCRGBMatrixPropertyValue).toString();
×
420
            setProperty(name, value);
×
421
            root.skipCurrentElement();
×
422
        }
423
        else if (root.name() == KXMLQLCRGBMatrixDimmerControl)
×
424
        {
425
            setDimmerControl(root.readElementText().toInt());
×
426
        }
427
        else
428
        {
429
            qWarning() << Q_FUNC_INFO << "Unknown RGB matrix tag:" << root.name();
×
430
            root.skipCurrentElement();
×
431
        }
432
    }
433

434
    return true;
1✔
435
}
436

437
bool RGBMatrix::saveXML(QXmlStreamWriter *doc)
1✔
438
{
439
    Q_ASSERT(doc != NULL);
1✔
440

441
    /* Function tag */
442
    doc->writeStartElement(KXMLQLCFunction);
1✔
443

444
    /* Common attributes */
445
    saveXMLCommon(doc);
1✔
446

447
    /* Speeds */
448
    saveXMLSpeed(doc);
1✔
449

450
    /* Direction */
451
    saveXMLDirection(doc);
1✔
452

453
    /* Run order */
454
    saveXMLRunOrder(doc);
1✔
455

456
    /* Algorithm */
457
    if (m_algorithm != NULL)
1✔
458
        m_algorithm->saveXML(doc);
1✔
459

460
    /* LEGACY - Dimmer Control */
461
    if (dimmerControl())
1✔
462
        doc->writeTextElement(KXMLQLCRGBMatrixDimmerControl, QString::number(dimmerControl()));
×
463

464
    /* Start Color */
465
    doc->writeTextElement(KXMLQLCRGBMatrixStartColor, QString::number(startColor().rgb()));
1✔
466

467
    /* End Color */
468
    if (endColor().isValid())
1✔
469
        doc->writeTextElement(KXMLQLCRGBMatrixEndColor, QString::number(endColor().rgb()));
1✔
470

471
    /* Control Mode */
472
    doc->writeTextElement(KXMLQLCRGBMatrixControlMode, RGBMatrix::controlModeToString(m_controlMode));
1✔
473

474
    /* Fixture Group */
475
    doc->writeTextElement(KXMLQLCRGBMatrixFixtureGroup, QString::number(fixtureGroup()));
1✔
476

477
    /* Properties */
478
    QHashIterator<QString, QString> it(m_properties);
1✔
479
    while (it.hasNext())
1✔
480
    {
481
        it.next();
×
482
        doc->writeStartElement(KXMLQLCRGBMatrixProperty);
×
483
        doc->writeAttribute(KXMLQLCRGBMatrixPropertyName, it.key());
×
484
        doc->writeAttribute(KXMLQLCRGBMatrixPropertyValue, it.value());
×
485
        doc->writeEndElement();
×
486
    }
487

488
    /* End the <Function> tag */
489
    doc->writeEndElement();
1✔
490

491
    return true;
2✔
492
}
493

494
/****************************************************************************
495
 * Running
496
 ****************************************************************************/
497

498
void RGBMatrix::tap()
×
499
{
500
    if (stopped() == false)
×
501
    {
502
        FixtureGroup* grp = doc()->fixtureGroup(fixtureGroup());
×
503
        // Filter out taps that are too close to each other
504
        if (grp != NULL && uint(m_roundTime->elapsed()) >= (duration() / 4))
×
505
        {
506
            roundCheck();
×
507
            resetElapsed();
×
508
        }
509
    }
510
}
×
511

512
void RGBMatrix::preRun(MasterTimer *timer)
×
513
{
514
    {
515
        QMutexLocker algorithmLocker(&m_algorithmMutex);
×
516

517
        m_group = doc()->fixtureGroup(m_fixtureGroupID);
×
518
        if (m_group == NULL)
×
519
        {
520
            // No fixture group to control
521
            stop(FunctionParent::master());
×
522
            return;
×
523
        }
524

525
        if (m_algorithm != NULL)
×
526
        {
527
            // Copy direction from parent class direction
528
            m_stepHandler->initializeDirection(direction(), m_startColor, m_endColor, m_stepsCount);
×
529

530
            if (m_algorithm->type() == RGBAlgorithm::Script)
×
531
            {
532
                RGBScript *script = static_cast<RGBScript*> (m_algorithm);
×
533
                QHashIterator<QString, QString> it(m_properties);
×
NEW
534
                while (it.hasNext())
×
535
                {
536
                    it.next();
×
537
                    script->setProperty(it.key(), it.value());
×
538
                }
539
            }
540
        }
541
    }
542

543
    m_roundTime->restart();
×
544

545
    Function::preRun(timer);
×
546
}
547

548
void RGBMatrix::write(MasterTimer *timer, QList<Universe *> universes)
×
549
{
550
    Q_UNUSED(timer);
551

552
    {
553
        QMutexLocker algorithmLocker(&m_algorithmMutex);
×
554
        if (m_group == NULL)
×
555
        {
556
            // No fixture group to control
557
            stop(FunctionParent::master());
×
558
            return;
×
559
        }
560

561
        // No time to do anything.
562
        if (duration() == 0)
×
563
            return;
×
564

565
        // Invalid/nonexistent script
566
        if (m_algorithm == NULL || m_algorithm->apiVersion() == 0)
×
567
            return;
×
568

569
        if (isPaused() == false)
×
570
        {
571
            // Get a new map every time elapsed is reset to zero
572
            if (elapsed() < MasterTimer::tick())
×
573
            {
574
                if (tempoType() == Beats)
×
575
                    m_stepBeatDuration = beatsToTime(duration(), timer->beatTimeDuration());
×
576

577
                //qDebug() << "RGBMatrix step" << m_stepHandler->currentStepIndex() << ", color:" << QString::number(m_stepHandler->stepColor().rgb(), 16);
578
                m_algorithm->rgbMap(m_group->size(), m_stepHandler->stepColor().rgb(),
×
579
                                    m_stepHandler->currentStepIndex(), m_stepHandler->m_map);
×
580
                updateMapChannels(m_stepHandler->m_map, m_group, universes);
×
581
            }
582
        }
583
    }
584

585
    if (isPaused() == false)
×
586
    {
587
        // Increment the ms elapsed time
588
        incrementElapsed();
×
589

590
        /* Check if we need to change direction, stop completely or go to next step
591
         * The cases are:
592
         * 1- time tempo type: act normally, on ms elapsed time
593
         * 2- beat tempo type, beat occurred: check if the elapsed beats is a multiple of
594
         *    the step beat duration. If so, proceed to the next step
595
         * 3- beat tempo type, not beat: if the ms elapsed time reached the step beat
596
         *    duration in ms, and the ms time to the next beat is not less than 1/16 of
597
         *    the step beat duration in ms, then proceed to the next step. If the ms time to the
598
         *    next beat is less than 1/16 of the step beat duration in ms, then defer the step
599
         *    change to case #2, to resync the matrix to the next beat
600
         */
601
        if (tempoType() == Time && elapsed() >= duration())
×
602
        {
603
            roundCheck();
×
604
        }
605
        else if (tempoType() == Beats)
×
606
        {
607
            if (timer->isBeat())
×
608
            {
609
                incrementElapsedBeats();
×
610
                qDebug() << "Elapsed beats:" << elapsedBeats() << ", time elapsed:" << elapsed() << ", step time:" << m_stepBeatDuration;
×
611
                if (elapsedBeats() % duration() == 0)
×
612
                {
613
                    roundCheck();
×
614
                    resetElapsed();
×
615
                }
616
            }
617
            else if (elapsed() >= m_stepBeatDuration && (uint)timer->timeToNextBeat() > m_stepBeatDuration / 16)
×
618
            {
619
                qDebug() << "Elapsed exceeded";
×
620
                roundCheck();
×
621
            }
622
        }
623
    }
624
}
625

626
void RGBMatrix::postRun(MasterTimer *timer, QList<Universe *> universes)
×
627
{
628
    uint fadeout = overrideFadeOutSpeed() == defaultSpeed() ? fadeOutSpeed() : overrideFadeOutSpeed();
×
629

630
    /* If no fade out is needed, dismiss all the requested faders.
631
     * Otherwise, set all the faders to fade out and let Universe dismiss them
632
     * when done */
633
    if (fadeout == 0)
×
634
    {
635
        dismissAllFaders();
×
636
    }
637
    else
638
    {
639
        if (tempoType() == Beats)
×
640
            fadeout = beatsToTime(fadeout, timer->beatTimeDuration());
×
641

642
        foreach (QSharedPointer<GenericFader> fader, m_fadersMap.values())
×
643
        {
644
            if (!fader.isNull())
×
645
                fader->setFadeOut(true, fadeout);
×
646
        }
647
    }
648

649
    m_fadersMap.clear();
×
650

651
    {
652
        QMutexLocker algorithmLocker(&m_algorithmMutex);
×
653
        if (m_algorithm != NULL)
×
654
            m_algorithm->postRun();
×
655
    }
656

657
    Function::postRun(timer, universes);
×
658
}
×
659

660
void RGBMatrix::roundCheck()
×
661
{
662
    QMutexLocker algorithmLocker(&m_algorithmMutex);
×
663
    if (m_algorithm == NULL)
×
664
        return;
×
665

666
    if (m_stepHandler->checkNextStep(runOrder(), m_startColor, m_endColor, m_stepsCount) == false)
×
667
        stop(FunctionParent::master());
×
668

669
    m_roundTime->restart();
×
670

671
    if (tempoType() == Beats)
×
672
        roundElapsed(m_stepBeatDuration);
×
673
    else
674
        roundElapsed(duration());
×
675
}
676

677
FadeChannel *RGBMatrix::getFader(QList<Universe *> universes, quint32 universeID, quint32 fixtureID, quint32 channel)
×
678
{
679
    // get the universe Fader first. If doesn't exist, create it
680
    QSharedPointer<GenericFader> fader = m_fadersMap.value(universeID, QSharedPointer<GenericFader>());
×
681
    if (fader.isNull())
×
682
    {
683
        fader = universes[universeID]->requestFader();
×
684
        fader->adjustIntensity(getAttributeValue(Intensity));
×
685
        fader->setBlendMode(blendMode());
×
686
        fader->setName(name());
×
687
        fader->setParentFunctionID(id());
×
688
        m_fadersMap[universeID] = fader;
×
689
    }
690

691
    return fader->getChannelFader(doc(), universes[universeID], fixtureID, channel);
×
692
}
693

694
void RGBMatrix::updateFaderValues(FadeChannel *fc, uchar value, uint fadeTime)
×
695
{
696
    fc->setStart(fc->current());
×
697
    fc->setTarget(value);
×
698
    fc->setElapsed(0);
×
699
    fc->setReady(false);
×
700
    // fade in/out depends on target value
701
    if (value == 0)
×
702
        fc->setFadeTime(fadeOutSpeed());
×
703
    else
704
        fc->setFadeTime(fadeTime);
×
705
}
×
706

707
void RGBMatrix::updateMapChannels(const RGBMap& map, const FixtureGroup *grp, QList<Universe *> universes)
×
708
{
709
    uint fadeTime = (overrideFadeInSpeed() == defaultSpeed()) ? fadeInSpeed() : overrideFadeInSpeed();
×
710

711
    // Create/modify fade channels for ALL heads in the group
712
    QMapIterator<QLCPoint, GroupHead> it(grp->headsMap());
×
713
    while (it.hasNext())
×
714
    {
715
        it.next();
×
716
        QLCPoint pt = it.key();
×
717
        GroupHead grpHead = it.value();
×
718
        Fixture *fxi = doc()->fixture(grpHead.fxi);
×
719
        if (fxi == NULL)
×
720
            continue;
×
721

722
        QLCFixtureHead head = fxi->head(grpHead.head);
×
723

724
        if (pt.y() >= map.count() || pt.x() >= map[pt.y()].count())
×
725
            continue;
×
726

727
        uint col = map[pt.y()][pt.x()];
×
728

729
        if (m_controlMode == ControlModeRgb)
×
730
        {
731
            QVector <quint32> rgb = head.rgbChannels();
×
732
            QVector <quint32> cmy = head.cmyChannels();
×
733

734
            if (rgb.size() == 3)
×
735
            {
736
                // RGB color mixing
737
                FadeChannel *fc = getFader(universes, fxi->universe(), grpHead.fxi, rgb.at(0));
×
738
                updateFaderValues(fc, qRed(col), fadeTime);
×
739

740
                fc = getFader(universes, fxi->universe(), grpHead.fxi, rgb.at(1));
×
741
                updateFaderValues(fc, qGreen(col), fadeTime);
×
742

743
                fc = getFader(universes, fxi->universe(), grpHead.fxi, rgb.at(2));
×
744
                updateFaderValues(fc, qBlue(col), fadeTime);
×
745
            }
746
            else if (cmy.size() == 3)
×
747
            {
748
                // CMY color mixing
749
                QColor cmyCol(col);
×
750

751
                FadeChannel *fc = getFader(universes, fxi->universe(), grpHead.fxi, cmy.at(0));
×
752
                updateFaderValues(fc, cmyCol.cyan(), fadeTime);
×
753

754
                fc = getFader(universes, fxi->universe(), grpHead.fxi, cmy.at(1));
×
755
                updateFaderValues(fc, cmyCol.magenta(), fadeTime);
×
756

757
                fc = getFader(universes, fxi->universe(), grpHead.fxi, cmy.at(2));
×
758
                updateFaderValues(fc, cmyCol.yellow(), fadeTime);
×
759
            }
760
        }
761
        else if (m_controlMode == ControlModeWhite)
×
762
        {
763
            quint32 white = head.channelNumber(QLCChannel::White, QLCChannel::MSB);
×
764

765
            if (white != QLCChannel::invalid())
×
766
            {
767
                FadeChannel *fc = getFader(universes, fxi->universe(), grpHead.fxi, white);
×
768
                updateFaderValues(fc, rgbToGrey(col), fadeTime);
×
769
            }
770
        }
771
        else if (m_controlMode == ControlModeAmber)
×
772
        {
773
            quint32 amber = head.channelNumber(QLCChannel::Amber, QLCChannel::MSB);
×
774

775
            if (amber != QLCChannel::invalid())
×
776
            {
777
                FadeChannel *fc = getFader(universes, fxi->universe(), grpHead.fxi, amber);
×
778
                updateFaderValues(fc, rgbToGrey(col), fadeTime);
×
779
            }
780
        }
781
        else if (m_controlMode == ControlModeUV)
×
782
        {
783
            quint32 uv = head.channelNumber(QLCChannel::UV, QLCChannel::MSB);
×
784

785
            if (uv != QLCChannel::invalid())
×
786
            {
787
                FadeChannel *fc = getFader(universes, fxi->universe(), grpHead.fxi, uv);
×
788
                updateFaderValues(fc, rgbToGrey(col), fadeTime);
×
789
            }
790
        }
791
        else if (m_controlMode == ControlModeShutter)
×
792
        {
793
            QVector <quint32> shutters = head.shutterChannels();
×
794

795
            if (shutters.size())
×
796
            {
797
                FadeChannel *fc = getFader(universes, fxi->universe(), grpHead.fxi, shutters.first());
×
798
                updateFaderValues(fc, rgbToGrey(col), fadeTime);
×
799
            }
800
        }
801

802
        if (m_controlMode == ControlModeDimmer || m_dimmerControl)
×
803
        {
804
            quint32 masterDim = fxi->masterIntensityChannel();
×
805
            quint32 headDim = head.channelNumber(QLCChannel::Intensity, QLCChannel::MSB);
×
806
            QVector <quint32> dimmers;
×
807

808
            // Collect all dimmers that affect current head:
809
            // They are the master dimmer (affects whole fixture)
810
            // and per-head dimmer.
811
            //
812
            // If there are no RGB or CMY channels, the least important* dimmer channel
813
            // is used to create grayscale image.
814
            //
815
            // The rest of the dimmer channels are set to full if dimmer control is
816
            // enabled and target color is > 0 (see
817
            // http://www.qlcplus.org/forum/viewtopic.php?f=29&t=11090)
818
            //
819
            // Note: If there is only one head, and only one dimmer channel,
820
            // make it a master dimmer in fixture definition.
821
            //
822
            // *least important - per head dimmer if present,
823
            // otherwise per fixture dimmer if present
824

825
            if (masterDim != QLCChannel::invalid())
×
826
                dimmers << masterDim;
×
827

828
            if (headDim != QLCChannel::invalid() && headDim != masterDim)
×
829
                dimmers << headDim;
×
830

831
            if (dimmers.size())
×
832
            {
833
                // Set dimmer to value of the color (e.g. for PARs)
834
                FadeChannel *fc = getFader(universes, fxi->universe(), grpHead.fxi, dimmers.last());
×
835
                updateFaderValues(fc, rgbToGrey(col), fadeTime);
×
836
                dimmers.pop_back();
×
837
            }
838

839
            // Set the rest of the dimmer channels to full on
840
            foreach (quint32 ch, dimmers)
×
841
            {
842
                FadeChannel *fc = getFader(universes, fxi->universe(), grpHead.fxi, ch);
×
843
                updateFaderValues(fc, col == 0 ? 0 : 255, fadeTime);
×
844
            }
845
        }
846
    }
847
}
×
848

849
uchar RGBMatrix::rgbToGrey(uint col)
×
850
{
851
    // the weights are taken from
852
    // https://en.wikipedia.org/wiki/YUV#SDTV_with_BT.601
853
    return (0.299 * qRed(col) + 0.587 * qGreen(col) + 0.114 * qBlue(col));
×
854
}
855

856
/*********************************************************************
857
 * Attributes
858
 *********************************************************************/
859

860
int RGBMatrix::adjustAttribute(qreal fraction, int attributeId)
×
861
{
862
    int attrIndex = Function::adjustAttribute(fraction, attributeId);
×
863

864
    if (attrIndex == Intensity)
×
865
    {
866
        foreach (QSharedPointer<GenericFader> fader, m_fadersMap.values())
×
867
        {
868
            if (!fader.isNull())
×
869
                fader->adjustIntensity(getAttributeValue(Function::Intensity));
×
870
        }
871
    }
872

873
    return attrIndex;
×
874
}
875

876
/*************************************************************************
877
 * Blend mode
878
 *************************************************************************/
879

880
void RGBMatrix::setBlendMode(Universe::BlendMode mode)
×
881
{
882
    if (mode == blendMode())
×
883
        return;
×
884

885
    foreach (QSharedPointer<GenericFader> fader, m_fadersMap.values())
×
886
    {
887
        if (!fader.isNull())
×
888
            fader->setBlendMode(mode);
×
889
    }
890

891
    Function::setBlendMode(mode);
×
892
    emit changed(id());
×
893
}
894

895
/*************************************************************************
896
 * Control Mode
897
 *************************************************************************/
898

899
RGBMatrix::ControlMode RGBMatrix::controlMode() const
1✔
900
{
901
    return m_controlMode;
1✔
902
}
903

904
void RGBMatrix::setControlMode(RGBMatrix::ControlMode mode)
2✔
905
{
906
    m_controlMode = mode;
2✔
907
    emit changed(id());
2✔
908
}
2✔
909

910
RGBMatrix::ControlMode RGBMatrix::stringToControlMode(QString mode)
1✔
911
{
912
    if (mode == KXMLQLCRGBMatrixControlModeRgb)
1✔
913
        return ControlModeRgb;
1✔
914
    else if (mode == KXMLQLCRGBMatrixControlModeAmber)
×
915
        return ControlModeAmber;
×
916
    else if (mode == KXMLQLCRGBMatrixControlModeWhite)
×
917
        return ControlModeWhite;
×
918
    else if (mode == KXMLQLCRGBMatrixControlModeUV)
×
919
        return ControlModeUV;
×
920
    else if (mode == KXMLQLCRGBMatrixControlModeDimmer)
×
921
        return ControlModeDimmer;
×
922
    else if (mode == KXMLQLCRGBMatrixControlModeShutter)
×
923
        return ControlModeShutter;
×
924

925
    return ControlModeRgb;
×
926
}
927

928
QString RGBMatrix::controlModeToString(RGBMatrix::ControlMode mode)
1✔
929
{
930
    switch(mode)
1✔
931
    {
932
        default:
1✔
933
        case ControlModeRgb:
934
            return QString(KXMLQLCRGBMatrixControlModeRgb);
1✔
935
        break;
936
        case ControlModeAmber:
×
937
            return QString(KXMLQLCRGBMatrixControlModeAmber);
×
938
        break;
939
        case ControlModeWhite:
×
940
            return QString(KXMLQLCRGBMatrixControlModeWhite);
×
941
        break;
942
        case ControlModeUV:
×
943
            return QString(KXMLQLCRGBMatrixControlModeUV);
×
944
        break;
945
        case ControlModeDimmer:
×
946
            return QString(KXMLQLCRGBMatrixControlModeDimmer);
×
947
        break;
948
        case ControlModeShutter:
×
949
            return QString(KXMLQLCRGBMatrixControlModeShutter);
×
950
        break;
951
    }
952
}
953

954

955
/*************************************************************************
956
 *************************************************************************
957
 *                          RGBMatrixStep class
958
 *************************************************************************
959
 *************************************************************************/
960

961
RGBMatrixStep::RGBMatrixStep()
10✔
962
    : m_direction(Function::Forward)
963
    , m_currentStepIndex(0)
964
    , m_stepColor(QColor())
965
    , m_crDelta(0)
966
    , m_cgDelta(0)
967
    , m_cbDelta(0)
10✔
968
{
969

970
}
10✔
971

972
void RGBMatrixStep::setCurrentStepIndex(int index)
×
973
{
974
    m_currentStepIndex = index;
×
975
}
×
976

977
int RGBMatrixStep::currentStepIndex() const
1✔
978
{
979
    return m_currentStepIndex;
1✔
980
}
981

982
void RGBMatrixStep::calculateColorDelta(QColor startColor, QColor endColor)
12✔
983
{
984
    m_crDelta = 0;
12✔
985
    m_cgDelta = 0;
12✔
986
    m_cbDelta = 0;
12✔
987

988
    if (endColor.isValid())
12✔
989
    {
990
        m_crDelta = endColor.red() - startColor.red();
5✔
991
        m_cgDelta = endColor.green() - startColor.green();
5✔
992
        m_cbDelta = endColor.blue() - startColor.blue();
5✔
993

994
        //qDebug() << "Color deltas:" << m_crDelta << m_cgDelta << m_cbDelta;
995
    }
996
}
12✔
997

998
void RGBMatrixStep::setStepColor(QColor color)
×
999
{
1000
    m_stepColor = color;
×
1001
}
×
1002

1003
QColor RGBMatrixStep::stepColor()
6✔
1004
{
1005
    return m_stepColor;
6✔
1006
}
1007

1008
void RGBMatrixStep::updateStepColor(int stepIndex, QColor startColor, int stepsCount)
×
1009
{
1010
    if (stepsCount <= 0)
×
1011
        return;
×
1012

1013
    if (stepsCount == 1)
×
1014
    {
1015
        m_stepColor = startColor;
×
1016
    }
1017
    else
1018
    {
1019
        m_stepColor.setRed(startColor.red() + (m_crDelta * stepIndex / (stepsCount - 1)));
×
1020
        m_stepColor.setGreen(startColor.green() + (m_cgDelta * stepIndex / (stepsCount - 1)));
×
1021
        m_stepColor.setBlue(startColor.blue() + (m_cbDelta * stepIndex / (stepsCount - 1)));
×
1022
    }
1023

1024
    //qDebug() << "RGBMatrix step" << stepIndex << ", color:" << QString::number(m_stepColor.rgb(), 16);
1025
}
1026

1027
void RGBMatrixStep::initializeDirection(Function::Direction direction, QColor startColor, QColor endColor, int stepsCount)
×
1028
{
1029
    m_direction = direction;
×
1030

1031
    if (m_direction == Function::Forward)
×
1032
    {
1033
        setCurrentStepIndex(0);
×
1034
        setStepColor(startColor);
×
1035
    }
1036
    else
1037
    {
1038
        setCurrentStepIndex(stepsCount - 1);
×
1039

1040
        if (endColor.isValid())
×
1041
            setStepColor(endColor);
×
1042
        else
1043
            setStepColor(startColor);
×
1044
    }
1045

1046
    calculateColorDelta(startColor, endColor);
×
1047
}
×
1048

1049
bool RGBMatrixStep::checkNextStep(Function::RunOrder order,
×
1050
                                  QColor startColor, QColor endColor, int stepsNumber)
1051
{
1052
    if (order == Function::PingPong)
×
1053
    {
1054
        if (m_direction == Function::Forward && (m_currentStepIndex + 1) == stepsNumber)
×
1055
        {
1056
            m_direction = Function::Backward;
×
1057
            m_currentStepIndex = stepsNumber - 2;
×
1058
            if (endColor.isValid())
×
1059
                m_stepColor = endColor;
×
1060

1061
            updateStepColor(m_currentStepIndex, startColor, stepsNumber);
×
1062
        }
1063
        else if (m_direction == Function::Backward && (m_currentStepIndex - 1) < 0)
×
1064
        {
1065
            m_direction = Function::Forward;
×
1066
            m_currentStepIndex = 1;
×
1067
            m_stepColor = startColor;
×
1068
            updateStepColor(m_currentStepIndex, startColor, stepsNumber);
×
1069
        }
1070
        else
1071
        {
1072
            if (m_direction == Function::Forward)
×
1073
                m_currentStepIndex++;
×
1074
            else
1075
                m_currentStepIndex--;
×
1076
            updateStepColor(m_currentStepIndex, startColor, stepsNumber);
×
1077
        }
1078
    }
1079
    else if (order == Function::SingleShot)
×
1080
    {
1081
        if (m_direction == Function::Forward)
×
1082
        {
1083
            if (m_currentStepIndex >= stepsNumber - 1)
×
1084
                return false;
×
1085
            else
1086
            {
1087
                m_currentStepIndex++;
×
1088
                updateStepColor(m_currentStepIndex, startColor, stepsNumber);
×
1089
            }
1090
        }
1091
        else
1092
        {
1093
            if (m_currentStepIndex <= 0)
×
1094
                return false;
×
1095
            else
1096
            {
1097
                m_currentStepIndex--;
×
1098
                updateStepColor(m_currentStepIndex, startColor, stepsNumber);
×
1099
            }
1100
        }
1101
    }
1102
    else
1103
    {
1104
        if (m_direction == Function::Forward)
×
1105
        {
1106
            if (m_currentStepIndex >= stepsNumber - 1)
×
1107
            {
1108
                m_currentStepIndex = 0;
×
1109
                m_stepColor = startColor;
×
1110
            }
1111
            else
1112
            {
1113
                m_currentStepIndex++;
×
1114
                updateStepColor(m_currentStepIndex, startColor, stepsNumber);
×
1115
            }
1116
        }
1117
        else
1118
        {
1119
            if (m_currentStepIndex <= 0)
×
1120
            {
1121
                m_currentStepIndex = stepsNumber - 1;
×
1122
                if (endColor.isValid())
×
1123
                    m_stepColor = endColor;
×
1124
            }
1125
            else
1126
            {
1127
                m_currentStepIndex--;
×
1128
                updateStepColor(m_currentStepIndex, startColor, stepsNumber);
×
1129
            }
1130
        }
1131
    }
1132

1133
    return true;
×
1134
}
1135

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

© 2025 Coveralls, Inc