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

mcallegari / qlcplus / 14831601168

05 May 2025 07:47AM UTC coverage: 31.878%. Remained the same
14831601168

push

github

web-flow
Merge pull request #1748 from shaforostoff/memleaks_may2025

Fix several memory leaks and simplify QElapsedTimer usage

1 of 23 new or added lines in 6 files covered. (4.35%)

4 existing lines in 3 files now uncovered.

16424 of 51521 relevant lines covered (31.88%)

19076.92 hits per line

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

41.55
/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 "rgbimage.h"
36
#include "doc.h"
37

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

43
#define KXMLQLCRGBMatrixFixtureGroup    QStringLiteral("FixtureGroup")
44
#define KXMLQLCRGBMatrixDimmerControl   QStringLiteral("DimmerControl")
45

46
#define KXMLQLCRGBMatrixProperty        QStringLiteral("Property")
47
#define KXMLQLCRGBMatrixPropertyName    QStringLiteral("Name")
48
#define KXMLQLCRGBMatrixPropertyValue   QStringLiteral("Value")
49

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

58
/****************************************************************************
59
 * Initialization
60
 ****************************************************************************/
61

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

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

84
    setAlgorithm(RGBAlgorithm::algorithm(doc, "Stripes"));
9✔
85
}
9✔
86

87
RGBMatrix::~RGBMatrix()
11✔
88
{
89
    //if (m_runAlgorithm != NULL)
90
    //    delete m_runAlgorithm;
91
    delete m_algorithm;
9✔
92
    delete m_stepHandler;
9✔
93
}
11✔
94

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

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

104
    if (m_algorithm == NULL)
1✔
105
        return;
×
106

107
    FixtureGroup *grp = doc()->fixtureGroup(fixtureGroup());
1✔
108
    if (grp == NULL)
1✔
109
        return;
×
110

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

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

119
    if (m_algorithm == NULL)
3✔
120
        return 0;
×
121

122
    FixtureGroup *grp = doc()->fixtureGroup(fixtureGroup());
3✔
123
    if (grp == NULL)
3✔
124
        return 0;
1✔
125

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

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

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

140
/****************************************************************************
141
 * Copying
142
 ****************************************************************************/
143

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

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

160
    return copy;
1✔
161
}
162

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

169
    setDimmerControl(mtx->dimmerControl());
1✔
170
    setFixtureGroup(mtx->fixtureGroup());
1✔
171

172
    m_rgbColors.clear();
1✔
173
    foreach (QColor col, mtx->getColors())
7✔
174
        m_rgbColors.append(col);
6✔
175

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

181
    setControlMode(mtx->controlMode());
1✔
182

183
    return Function::copyFrom(function);
1✔
184
}
185

186
/****************************************************************************
187
 * Fixtures
188
 ****************************************************************************/
189

190
quint32 RGBMatrix::fixtureGroup() const
35✔
191
{
192
    return m_fixtureGroupID;
35✔
193
}
194

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

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

210
    return QList<quint32>();
1✔
211
}
212

213
/****************************************************************************
214
 * Algorithm
215
 ****************************************************************************/
216

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

224
        m_requestEngineCreation = true;
13✔
225

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

247
    emit changed(id());
13✔
248
}
13✔
249

250
RGBAlgorithm *RGBMatrix::algorithm() const
18✔
251
{
252
    return m_algorithm;
18✔
253
}
254

255
#if QT_VERSION < QT_VERSION_CHECK(5, 14, 0)
256
QMutex& RGBMatrix::algorithmMutex()
257
{
258
    return m_algorithmMutex;
259
}
260
#else
261
QRecursiveMutex& RGBMatrix::algorithmMutex()
×
262
{
263
    return m_algorithmMutex;
×
264
}
265
#endif
266

267

268
int RGBMatrix::stepsCount()
2✔
269
{
270
    return m_stepsCount;
2✔
271
}
272

273
int RGBMatrix::algorithmStepsCount()
22✔
274
{
275
    QMutexLocker algorithmLocker(&m_algorithmMutex);
22✔
276

277
    if (m_algorithm == NULL)
22✔
278
        return 0;
×
279

280
    FixtureGroup *grp = doc()->fixtureGroup(fixtureGroup());
22✔
281
    if (grp != NULL)
22✔
282
        return m_algorithm->rgbMapStepCount(grp->size());
6✔
283

284
    return 0;
16✔
285
}
22✔
286

287
void RGBMatrix::previewMap(int step, RGBMatrixStep *handler)
7✔
288
{
289
    QMutexLocker algorithmLocker(&m_algorithmMutex);
7✔
290
    if (m_algorithm == NULL || handler == NULL)
7✔
291
        return;
×
292

293
    if (m_group == NULL)
7✔
294
        m_group = doc()->fixtureGroup(fixtureGroup());
1✔
295

296
    if (m_group != NULL)
7✔
297
    {
298
        setMapColors(m_algorithm);
6✔
299
        m_algorithm->rgbMap(m_group->size(), handler->stepColor().rgb(), step, handler->m_map);
6✔
300
    }
301
}
7✔
302

303
/****************************************************************************
304
 * Color
305
 ****************************************************************************/
306

307
void RGBMatrix::setColor(int i, QColor c)
25✔
308
{
309
    if (i < 0)
25✔
310
        return;
×
311

312
    if (i >= m_rgbColors.count())
25✔
313
        m_rgbColors.resize(i + 1);
×
314

315
    m_rgbColors.replace(i, c);
25✔
316
    {
317
        QMutexLocker algorithmLocker(&m_algorithmMutex);
25✔
318
        if (m_algorithm != NULL)
25✔
319
        {
320
            m_algorithm->setColors(m_rgbColors);
16✔
321
            updateColorDelta();
16✔
322
        }
323
    }
25✔
324
    setMapColors(m_algorithm);
25✔
325
    emit changed(id());
25✔
326
}
327

328
QColor RGBMatrix::getColor(int i) const
10✔
329
{
330
    if (i < 0 || i >= m_rgbColors.count())
10✔
331
        return QColor();
×
332

333
    return m_rgbColors.at(i);
10✔
334
}
335

336
QVector<QColor> RGBMatrix::getColors() const
1✔
337
{
338
    return m_rgbColors;
1✔
339
}
340

341
void RGBMatrix::updateColorDelta()
16✔
342
{
343
    m_stepHandler->calculateColorDelta(m_rgbColors[0], m_rgbColors[1], m_algorithm);
16✔
344
}
16✔
345

346
void RGBMatrix::setMapColors(RGBAlgorithm *algorithm)
31✔
347
{
348
    QMutexLocker algorithmLocker(&m_algorithmMutex);
31✔
349
    if (algorithm == NULL)
31✔
350
        return;
9✔
351

352
    if (algorithm->apiVersion() < 3)
22✔
353
        return;
22✔
354

355
    if (m_group == NULL)
×
356
        m_group = doc()->fixtureGroup(fixtureGroup());
×
357

358
    if (m_group != NULL)
×
359
    {
360
        QVector<unsigned int> rawColors;
×
361
        for (int i = 0; i < algorithm->acceptColors(); i++)
×
362
        {
363
            QColor col = m_rgbColors.at(i);
×
364
            rawColors.append(col.isValid() ? col.rgb() : 0);
×
365
        }
366

367
        algorithm->rgbMapSetColors(rawColors);
×
368
    }
×
369
}
31✔
370

371
/************************************************************************
372
 * Properties
373
 ************************************************************************/
374

375
void RGBMatrix::setProperty(QString propName, QString value)
1✔
376
{
377
    QMutexLocker algoLocker(&m_algorithmMutex);
1✔
378
    m_properties[propName] = value;
1✔
379
    if (m_algorithm != NULL && m_algorithm->type() == RGBAlgorithm::Script)
1✔
380
    {
381
        RGBScript *script = static_cast<RGBScript*> (m_algorithm);
1✔
382
        script->setProperty(propName, value);
1✔
383

384
        QVector<uint> colors = script->rgbMapGetColors();
1✔
385
        for (int i = 0; i < colors.count(); i++)
1✔
386
            setColor(i, QColor::fromRgb(colors.at(i)));
×
387
    }
1✔
388
    m_stepsCount = algorithmStepsCount();
1✔
389
}
1✔
390

391
QString RGBMatrix::property(QString propName)
3✔
392
{
393
    QMutexLocker algoLocker(&m_algorithmMutex);
3✔
394

395
    /** If the property is cached, then return it right away */
396
    QMap<QString, QString>::iterator it = m_properties.find(propName);
3✔
397
    if (it != m_properties.end())
3✔
398
        return it.value();
1✔
399

400
    /** Otherwise, let's retrieve it from the Script */
401
    if (m_algorithm != NULL && m_algorithm->type() == RGBAlgorithm::Script)
2✔
402
    {
403
        RGBScript *script = static_cast<RGBScript*> (m_algorithm);
2✔
404
        return script->property(propName);
2✔
405
    }
406

407
    return QString();
×
408
}
3✔
409

410
/****************************************************************************
411
 * Load & Save
412
 ****************************************************************************/
413

414
bool RGBMatrix::loadXML(QXmlStreamReader &root)
3✔
415
{
416
    if (root.name() != KXMLQLCFunction)
3✔
417
    {
418
        qWarning() << Q_FUNC_INFO << "Function node not found";
1✔
419
        return false;
1✔
420
    }
421

422
    if (root.attributes().value(KXMLQLCFunctionType).toString() != typeToString(Function::RGBMatrixType))
4✔
423
    {
424
        qWarning() << Q_FUNC_INFO << "Function is not an RGB matrix";
1✔
425
        return false;
1✔
426
    }
427

428
    /* Load matrix contents */
429
    while (root.readNextStartElement())
12✔
430
    {
431
        if (root.name() == KXMLQLCFunctionSpeed)
11✔
432
        {
433
            loadXMLSpeed(root);
1✔
434
        }
435
        else if (root.name() == KXMLQLCRGBAlgorithm)
10✔
436
        {
437
            setAlgorithm(RGBAlgorithm::loader(doc(), root));
1✔
438
        }
439
        else if (root.name() == KXMLQLCRGBMatrixFixtureGroup)
9✔
440
        {
441
            setFixtureGroup(root.readElementText().toUInt());
1✔
442
        }
443
        else if (root.name() == KXMLQLCFunctionDirection)
8✔
444
        {
445
            loadXMLDirection(root);
1✔
446
        }
447
        else if (root.name() == KXMLQLCFunctionRunOrder)
7✔
448
        {
449
            loadXMLRunOrder(root);
1✔
450
        }
451
        // Legacy support
452
        else if (root.name() == KXMLQLCRGBMatrixStartColor)
6✔
453
        {
454
            setColor(0, QColor::fromRgb(QRgb(root.readElementText().toUInt())));
×
455
        }
456
        else if (root.name() == KXMLQLCRGBMatrixEndColor)
6✔
457
        {
458
            setColor(1, QColor::fromRgb(QRgb(root.readElementText().toUInt())));
×
459
        }
460
        else if (root.name() == KXMLQLCRGBMatrixColor)
6✔
461
        {
462
            int colorIdx = root.attributes().value(KXMLQLCRGBMatrixColorIndex).toInt();
10✔
463
            setColor(colorIdx, QColor::fromRgb(QRgb(root.readElementText().toUInt())));
5✔
464
        }
465
        else if (root.name() == KXMLQLCRGBMatrixControlMode)
1✔
466
        {
467
            setControlMode(stringToControlMode(root.readElementText()));
1✔
468
        }
469
        else if (root.name() == KXMLQLCRGBMatrixProperty)
×
470
        {
471
            QString name = root.attributes().value(KXMLQLCRGBMatrixPropertyName).toString();
×
472
            QString value = root.attributes().value(KXMLQLCRGBMatrixPropertyValue).toString();
×
473
            setProperty(name, value);
×
474
            root.skipCurrentElement();
×
475
        }
×
476
        else if (root.name() == KXMLQLCRGBMatrixDimmerControl)
×
477
        {
478
            setDimmerControl(root.readElementText().toInt());
×
479
        }
480
        else
481
        {
482
            qWarning() << Q_FUNC_INFO << "Unknown RGB matrix tag:" << root.name();
×
483
            root.skipCurrentElement();
×
484
        }
485
    }
486

487
    return true;
1✔
488
}
489

490
bool RGBMatrix::saveXML(QXmlStreamWriter *doc)
1✔
491
{
492
    Q_ASSERT(doc != NULL);
1✔
493

494
    /* Function tag */
495
    doc->writeStartElement(KXMLQLCFunction);
2✔
496

497
    /* Common attributes */
498
    saveXMLCommon(doc);
1✔
499

500
    /* Speeds */
501
    saveXMLSpeed(doc);
1✔
502

503
    /* Direction */
504
    saveXMLDirection(doc);
1✔
505

506
    /* Run order */
507
    saveXMLRunOrder(doc);
1✔
508

509
    /* Algorithm */
510
    if (m_algorithm != NULL)
1✔
511
        m_algorithm->saveXML(doc);
1✔
512

513
    /* LEGACY - Dimmer Control */
514
    if (dimmerControl())
1✔
515
        doc->writeTextElement(KXMLQLCRGBMatrixDimmerControl, QString::number(dimmerControl()));
×
516

517
    /* Colors */
518
    for (int i = 0; i < m_rgbColors.count(); i++)
6✔
519
    {
520
        doc->writeStartElement(KXMLQLCRGBMatrixColor);
10✔
521
        doc->writeAttribute(KXMLQLCRGBMatrixColorIndex, QString::number(i));
10✔
522
        doc->writeCharacters(QString::number(m_rgbColors.at(i).rgb()));
5✔
523
        doc->writeEndElement();
5✔
524
    }
525

526
    /* Control Mode */
527
    doc->writeTextElement(KXMLQLCRGBMatrixControlMode, RGBMatrix::controlModeToString(m_controlMode));
2✔
528

529
    /* Fixture Group */
530
    doc->writeTextElement(KXMLQLCRGBMatrixFixtureGroup, QString::number(fixtureGroup()));
2✔
531

532
    /* Properties */
533
    QMapIterator<QString, QString> it(m_properties);
1✔
534
    while (it.hasNext())
1✔
535
    {
536
        it.next();
×
537
        doc->writeStartElement(KXMLQLCRGBMatrixProperty);
×
538
        doc->writeAttribute(KXMLQLCRGBMatrixPropertyName, it.key());
×
539
        doc->writeAttribute(KXMLQLCRGBMatrixPropertyValue, it.value());
×
540
        doc->writeEndElement();
×
541
    }
542

543
    /* End the <Function> tag */
544
    doc->writeEndElement();
1✔
545

546
    return true;
1✔
547
}
1✔
548

549
/****************************************************************************
550
 * Running
551
 ****************************************************************************/
552

553
void RGBMatrix::tap()
×
554
{
555
    if (stopped() == false)
×
556
    {
557
        FixtureGroup *grp = doc()->fixtureGroup(fixtureGroup());
×
558
        // Filter out taps that are too close to each other
NEW
559
        if (grp != NULL && uint(m_roundTime.elapsed()) >= (duration() / 4))
×
560
        {
561
            roundCheck();
×
562
            resetElapsed();
×
563
        }
564
    }
565
}
×
566

567
void RGBMatrix::checkEngineCreation()
×
568
{
569
    m_runAlgorithm = m_algorithm;
×
570
    m_requestEngineCreation = false;
×
571
}
×
572

573
void RGBMatrix::preRun(MasterTimer *timer)
×
574
{
575
    {
576
        QMutexLocker algorithmLocker(&m_algorithmMutex);
×
577

578
        m_group = doc()->fixtureGroup(m_fixtureGroupID);
×
579
        if (m_group == NULL)
×
580
        {
581
            // No fixture group to control
582
            stop(FunctionParent::master());
×
583
            return;
×
584
        }
585

586
        if (m_algorithm != NULL)
×
587
        {
588
            checkEngineCreation();
×
589

590
            // Copy direction from parent class direction
591
            m_stepHandler->initializeDirection(direction(), m_rgbColors[0], m_rgbColors[1], m_stepsCount, m_runAlgorithm);
×
592

593
            if (m_runAlgorithm->type() == RGBAlgorithm::Script)
×
594
            {
595
                RGBScript *script = static_cast<RGBScript*> (m_runAlgorithm);
×
596
                QMapIterator<QString, QString> it(m_properties);
×
597
                while (it.hasNext())
×
598
                {
599
                    it.next();
×
600
                    script->setProperty(it.key(), it.value());
×
601
                }
602
            }
×
603
            else if (m_runAlgorithm->type() == RGBAlgorithm::Image)
×
604
            {
605
                RGBImage *image = static_cast<RGBImage*> (m_runAlgorithm);
×
606
                if (image->animatedSource())
×
607
                    image->rewindAnimation();
×
608
            }
609
        }
610
    }
×
611

NEW
612
    m_roundTime.restart();
×
613

614
    Function::preRun(timer);
×
615
}
616

617
void RGBMatrix::write(MasterTimer *timer, QList<Universe *> universes)
×
618
{
619
    Q_UNUSED(timer);
620

621
    {
622
        QMutexLocker algorithmLocker(&m_algorithmMutex);
×
623
        if (m_group == NULL)
×
624
        {
625
            // No fixture group to control
626
            stop(FunctionParent::master());
×
627
            return;
×
628
        }
629

630
        // No time to do anything.
631
        if (duration() == 0)
×
632
            return;
×
633

634
        if (m_algorithm != NULL && m_requestEngineCreation)
×
635
            checkEngineCreation();
×
636

637
        // Invalid/nonexistent script
638
        if (m_runAlgorithm == NULL || m_runAlgorithm->apiVersion() == 0)
×
639
            return;
×
640

641
        if (isPaused() == false)
×
642
        {
643
            // Get a new map every time elapsed is reset to zero
644
            if (elapsed() < MasterTimer::tick())
×
645
            {
646
                if (tempoType() == Beats)
×
647
                    m_stepBeatDuration = beatsToTime(duration(), timer->beatTimeDuration());
×
648

649
                //qDebug() << "RGBMatrix step" << m_stepHandler->currentStepIndex() << ", color:" << QString::number(m_stepHandler->stepColor().rgb(), 16);
650
                m_runAlgorithm->rgbMap(m_group->size(), m_stepHandler->stepColor().rgb(),
×
651
                                       m_stepHandler->currentStepIndex(), m_stepHandler->m_map);
×
652
                updateMapChannels(m_stepHandler->m_map, m_group, universes);
×
653
            }
654
        }
655
    }
×
656

657
    if (isPaused() == false)
×
658
    {
659
        // Increment the ms elapsed time
660
        incrementElapsed();
×
661

662
        /* Check if we need to change direction, stop completely or go to next step
663
         * The cases are:
664
         * 1- time tempo type: act normally, on ms elapsed time
665
         * 2- beat tempo type, beat occurred: check if the elapsed beats is a multiple of
666
         *    the step beat duration. If so, proceed to the next step
667
         * 3- beat tempo type, not beat: if the ms elapsed time reached the step beat
668
         *    duration in ms, and the ms time to the next beat is not less than 1/16 of
669
         *    the step beat duration in ms, then proceed to the next step. If the ms time to the
670
         *    next beat is less than 1/16 of the step beat duration in ms, then defer the step
671
         *    change to case #2, to resync the matrix to the next beat
672
         */
673
        if (tempoType() == Time && elapsed() >= duration())
×
674
        {
675
            roundCheck();
×
676
        }
677
        else if (tempoType() == Beats)
×
678
        {
679
            if (timer->isBeat())
×
680
            {
681
                incrementElapsedBeats();
×
682
                qDebug() << "Elapsed beats:" << elapsedBeats() << ", time elapsed:" << elapsed() << ", step time:" << m_stepBeatDuration;
×
683
                if (elapsedBeats() % duration() == 0)
×
684
                {
685
                    roundCheck();
×
686
                    resetElapsed();
×
687
                }
688
            }
689
            else if (elapsed() >= m_stepBeatDuration && (uint)timer->timeToNextBeat() > m_stepBeatDuration / 16)
×
690
            {
691
                qDebug() << "Elapsed exceeded";
×
692
                roundCheck();
×
693
            }
694
        }
695
    }
696
}
697

698
void RGBMatrix::postRun(MasterTimer *timer, QList<Universe *> universes)
×
699
{
700
    uint fadeout = overrideFadeOutSpeed() == defaultSpeed() ? fadeOutSpeed() : overrideFadeOutSpeed();
×
701

702
    /* If no fade out is needed, dismiss all the requested faders.
703
     * Otherwise, set all the faders to fade out and let Universe dismiss them
704
     * when done */
705
    if (fadeout == 0)
×
706
    {
707
        dismissAllFaders();
×
708
    }
709
    else
710
    {
711
        if (tempoType() == Beats)
×
712
            fadeout = beatsToTime(fadeout, timer->beatTimeDuration());
×
713

714
        foreach (QSharedPointer<GenericFader> fader, m_fadersMap)
×
715
        {
716
            if (!fader.isNull())
×
717
                fader->setFadeOut(true, fadeout);
×
718
        }
×
719
    }
720

721
    m_fadersMap.clear();
×
722

723
    {
724
        QMutexLocker algorithmLocker(&m_algorithmMutex);
×
725
        checkEngineCreation();
×
726
        if (m_runAlgorithm != NULL)
×
727
            m_runAlgorithm->postRun();
×
728
    }
×
729

730
    Function::postRun(timer, universes);
×
731
}
×
732

733
void RGBMatrix::roundCheck()
×
734
{
735
    QMutexLocker algorithmLocker(&m_algorithmMutex);
×
736
    if (m_algorithm == NULL)
×
737
        return;
×
738

739
    if (m_stepHandler->checkNextStep(runOrder(), m_rgbColors[0], m_rgbColors[1], m_stepsCount) == false)
×
740
        stop(FunctionParent::master());
×
741

NEW
742
    m_roundTime.restart();
×
743

744
    if (tempoType() == Beats)
×
745
        roundElapsed(m_stepBeatDuration);
×
746
    else
747
        roundElapsed(duration());
×
748
}
×
749

750
FadeChannel *RGBMatrix::getFader(Universe *universe, quint32 fixtureID, quint32 channel)
×
751
{
752
    // get the universe Fader first. If doesn't exist, create it
753
    if (universe == NULL)
×
754
        return NULL;
×
755

756
    QSharedPointer<GenericFader> fader = m_fadersMap.value(universe->id(), QSharedPointer<GenericFader>());
×
757
    if (fader.isNull())
×
758
    {
759
        fader = universe->requestFader();
×
760
        fader->adjustIntensity(getAttributeValue(Intensity));
×
761
        fader->setBlendMode(blendMode());
×
762
        fader->setName(name());
×
763
        fader->setParentFunctionID(id());
×
764
        m_fadersMap[universe->id()] = fader;
×
765
    }
766

767
    return fader->getChannelFader(doc(), universe, fixtureID, channel);
×
768
}
×
769

770
void RGBMatrix::updateFaderValues(FadeChannel *fc, uchar value, uint fadeTime)
×
771
{
772
    fc->setStart(fc->current());
×
773
    fc->setTarget(value);
×
774
    fc->setElapsed(0);
×
775
    fc->setReady(false);
×
776
    // fade in/out depends on target value
777
    if (value == 0)
×
778
        fc->setFadeTime(fadeOutSpeed());
×
779
    else
780
        fc->setFadeTime(fadeTime);
×
781
}
×
782

783
void RGBMatrix::updateMapChannels(const RGBMap& map, const FixtureGroup *grp, QList<Universe *> universes)
×
784
{
785
    uint fadeTime = (overrideFadeInSpeed() == defaultSpeed()) ? fadeInSpeed() : overrideFadeInSpeed();
×
786

787
    // Create/modify fade channels for ALL heads in the group
788
    QMapIterator<QLCPoint, GroupHead> it(grp->headsMap());
×
789
    while (it.hasNext())
×
790
    {
791
        it.next();
×
792
        QLCPoint pt = it.key();
×
793
        GroupHead grpHead = it.value();
×
794
        Fixture *fxi = doc()->fixture(grpHead.fxi);
×
795
        if (fxi == NULL)
×
796
            continue;
×
797

798
        QLCFixtureHead head = fxi->head(grpHead.head);
×
799

800
        if (pt.y() >= map.count() || pt.x() >= map[pt.y()].count())
×
801
            continue;
×
802

803
        uint col = map[pt.y()][pt.x()];
×
804
        QVector<quint32> channelList;
×
805
        QVector<uchar> valueList;
×
806

807
        if (m_controlMode == ControlModeRgb)
×
808
        {
809
            channelList = head.rgbChannels();
×
810

811
            if (channelList.size() == 3)
×
812
            {
813
                valueList.append(qRed(col));
×
814
                valueList.append(qGreen(col));
×
815
                valueList.append(qBlue(col));
×
816
            }
817
            else
818
            {
819
                channelList = head.cmyChannels();
×
820

821
                if (channelList.size() == 3)
×
822
                {
823
                    // CMY color mixing
824
                    QColor cmyCol(col);
×
825
                    valueList.append(cmyCol.cyan());
×
826
                    valueList.append(cmyCol.magenta());
×
827
                    valueList.append(cmyCol.yellow());
×
828
                }
829
            }
830
        }
831
        else if (m_controlMode == ControlModeShutter)
×
832
        {
833
            channelList = head.shutterChannels();
×
834

835
            if (channelList.size())
×
836
            {
837
                // make sure only one channel is in the list
838
                channelList.resize(1);
×
839
                valueList.append(rgbToGrey(col));
×
840
            }
841
        }
842
        else if (m_controlMode == ControlModeDimmer || m_dimmerControl)
×
843
        {
844
            // Collect all dimmers that affect current head:
845
            // They are the master dimmer (affects whole fixture)
846
            // and per-head dimmer.
847
            //
848
            // If there are no RGB or CMY channels, the least important* dimmer channel
849
            // is used to create grayscale image.
850
            //
851
            // The rest of the dimmer channels are set to full if dimmer control is
852
            // enabled and target color is > 0 (see
853
            // http://www.qlcplus.org/forum/viewtopic.php?f=29&t=11090)
854
            //
855
            // Note: If there is only one head, and only one dimmer channel,
856
            // make it a master dimmer in fixture definition.
857
            //
858
            // *least important - per head dimmer if present,
859
            // otherwise per fixture dimmer if present
860

861
            quint32 masterDim = fxi->masterIntensityChannel();
×
862
            quint32 headDim = head.channelNumber(QLCChannel::Intensity, QLCChannel::MSB);
×
863

864
            if (masterDim != QLCChannel::invalid())
×
865
            {
866
                channelList.append(masterDim);
×
867
                valueList.append(rgbToGrey(col));
×
868
            }
869

870
            if (headDim != QLCChannel::invalid() && headDim != masterDim)
×
871
            {
872
                channelList.append(headDim);
×
873
                valueList.append(col == 0 ? 0 : 255);
×
874
            }
875
        }
×
876
        else
877
        {
878
            if (m_controlMode == ControlModeWhite)
×
879
                channelList.append(head.channelNumber(QLCChannel::White, QLCChannel::MSB));
×
880
            else if (m_controlMode == ControlModeAmber)
×
881
                channelList.append(head.channelNumber(QLCChannel::Amber, QLCChannel::MSB));
×
882
            else if (m_controlMode == ControlModeUV)
×
883
                channelList.append(head.channelNumber(QLCChannel::UV, QLCChannel::MSB));
×
884

885
            valueList.append(rgbToGrey(col));
×
886
        }
887

888
        quint32 absAddress = fxi->universeAddress();
×
889

890
        for (int i = 0; i < channelList.count(); i++)
×
891
        {
892
            if (channelList.at(i) == QLCChannel::invalid())
×
893
                continue;
×
894

895
            quint32 universeIndex = floor((absAddress + channelList.at(i)) / 512);
×
896

897
            FadeChannel *fc = getFader(universes.at(universeIndex), grpHead.fxi, channelList.at(i));
×
898
            updateFaderValues(fc, valueList.at(i), fadeTime);
×
899
        }
900
    }
×
901
}
×
902

903
uchar RGBMatrix::rgbToGrey(uint col)
×
904
{
905
    // the weights are taken from
906
    // https://en.wikipedia.org/wiki/YUV#SDTV_with_BT.601
907
    return (0.299 * qRed(col) + 0.587 * qGreen(col) + 0.114 * qBlue(col));
×
908
}
909

910
/*********************************************************************
911
 * Attributes
912
 *********************************************************************/
913

914
int RGBMatrix::adjustAttribute(qreal fraction, int attributeId)
×
915
{
916
    int attrIndex = Function::adjustAttribute(fraction, attributeId);
×
917

918
    if (attrIndex == Intensity)
×
919
    {
920
        foreach (QSharedPointer<GenericFader> fader, m_fadersMap)
×
921
        {
922
            if (!fader.isNull())
×
923
                fader->adjustIntensity(getAttributeValue(Function::Intensity));
×
924
        }
×
925
    }
926

927
    return attrIndex;
×
928
}
929

930
/*************************************************************************
931
 * Blend mode
932
 *************************************************************************/
933

934
void RGBMatrix::setBlendMode(Universe::BlendMode mode)
×
935
{
936
    if (mode == blendMode())
×
937
        return;
×
938

939
    foreach (QSharedPointer<GenericFader> fader, m_fadersMap)
×
940
    {
941
        if (!fader.isNull())
×
942
            fader->setBlendMode(mode);
×
943
    }
×
944

945
    Function::setBlendMode(mode);
×
946
    emit changed(id());
×
947
}
948

949
/*************************************************************************
950
 * Control Mode
951
 *************************************************************************/
952

953
RGBMatrix::ControlMode RGBMatrix::controlMode() const
2✔
954
{
955
    return m_controlMode;
2✔
956
}
957

958
void RGBMatrix::setControlMode(RGBMatrix::ControlMode mode)
3✔
959
{
960
    m_controlMode = mode;
3✔
961
    emit changed(id());
3✔
962
}
3✔
963

964
RGBMatrix::ControlMode RGBMatrix::stringToControlMode(QString mode)
1✔
965
{
966
    if (mode == KXMLQLCRGBMatrixControlModeRgb)
1✔
967
        return ControlModeRgb;
1✔
968
    else if (mode == KXMLQLCRGBMatrixControlModeAmber)
×
969
        return ControlModeAmber;
×
970
    else if (mode == KXMLQLCRGBMatrixControlModeWhite)
×
971
        return ControlModeWhite;
×
972
    else if (mode == KXMLQLCRGBMatrixControlModeUV)
×
973
        return ControlModeUV;
×
974
    else if (mode == KXMLQLCRGBMatrixControlModeDimmer)
×
975
        return ControlModeDimmer;
×
976
    else if (mode == KXMLQLCRGBMatrixControlModeShutter)
×
977
        return ControlModeShutter;
×
978

979
    return ControlModeRgb;
×
980
}
981

982
QString RGBMatrix::controlModeToString(RGBMatrix::ControlMode mode)
1✔
983
{
984
    switch(mode)
1✔
985
    {
986
        default:
1✔
987
        case ControlModeRgb:
988
            return QString(KXMLQLCRGBMatrixControlModeRgb);
1✔
989
        break;
990
        case ControlModeAmber:
×
991
            return QString(KXMLQLCRGBMatrixControlModeAmber);
×
992
        break;
993
        case ControlModeWhite:
×
994
            return QString(KXMLQLCRGBMatrixControlModeWhite);
×
995
        break;
996
        case ControlModeUV:
×
997
            return QString(KXMLQLCRGBMatrixControlModeUV);
×
998
        break;
999
        case ControlModeDimmer:
×
1000
            return QString(KXMLQLCRGBMatrixControlModeDimmer);
×
1001
        break;
1002
        case ControlModeShutter:
×
1003
            return QString(KXMLQLCRGBMatrixControlModeShutter);
×
1004
        break;
1005
    }
1006
}
1007

1008

1009
/*************************************************************************
1010
 *************************************************************************
1011
 *                          RGBMatrixStep class
1012
 *************************************************************************
1013
 *************************************************************************/
1014

1015
RGBMatrixStep::RGBMatrixStep()
10✔
1016
    : m_direction(Function::Forward)
10✔
1017
    , m_currentStepIndex(0)
10✔
1018
    , m_stepColor(QColor())
10✔
1019
    , m_crDelta(0)
10✔
1020
    , m_cgDelta(0)
10✔
1021
    , m_cbDelta(0)
10✔
1022
{
1023

1024
}
10✔
1025

1026
void RGBMatrixStep::setCurrentStepIndex(int index)
×
1027
{
1028
    m_currentStepIndex = index;
×
1029
}
×
1030

1031
int RGBMatrixStep::currentStepIndex() const
1✔
1032
{
1033
    return m_currentStepIndex;
1✔
1034
}
1035

1036
void RGBMatrixStep::calculateColorDelta(QColor startColor, QColor endColor, RGBAlgorithm *algorithm)
16✔
1037
{
1038
    m_crDelta = 0;
16✔
1039
    m_cgDelta = 0;
16✔
1040
    m_cbDelta = 0;
16✔
1041

1042
    if (endColor.isValid() && algorithm != NULL && algorithm->acceptColors() > 1)
16✔
1043
    {
1044
        m_crDelta = endColor.red() - startColor.red();
10✔
1045
        m_cgDelta = endColor.green() - startColor.green();
10✔
1046
        m_cbDelta = endColor.blue() - startColor.blue();
10✔
1047

1048
        //qDebug() << "Color deltas:" << m_crDelta << m_cgDelta << m_cbDelta;
1049
    }
1050
}
16✔
1051

1052
void RGBMatrixStep::setStepColor(QColor color)
×
1053
{
1054
    m_stepColor = color;
×
1055
}
×
1056

1057
QColor RGBMatrixStep::stepColor()
6✔
1058
{
1059
    return m_stepColor;
6✔
1060
}
1061

1062
void RGBMatrixStep::updateStepColor(int stepIndex, QColor startColor, int stepsCount)
×
1063
{
1064
    if (stepsCount <= 0)
×
1065
        return;
×
1066

1067
    if (stepsCount == 1)
×
1068
    {
1069
        m_stepColor = startColor;
×
1070
    }
1071
    else
1072
    {
1073
        m_stepColor.setRed(startColor.red() + (m_crDelta * stepIndex / (stepsCount - 1)));
×
1074
        m_stepColor.setGreen(startColor.green() + (m_cgDelta * stepIndex / (stepsCount - 1)));
×
1075
        m_stepColor.setBlue(startColor.blue() + (m_cbDelta * stepIndex / (stepsCount - 1)));
×
1076
    }
1077

1078
    //qDebug() << "RGBMatrix step" << stepIndex << ", color:" << QString::number(m_stepColor.rgb(), 16);
1079
}
1080

1081
void RGBMatrixStep::initializeDirection(Function::Direction direction, QColor startColor, QColor endColor, int stepsCount, RGBAlgorithm *algorithm)
×
1082
{
1083
    m_direction = direction;
×
1084

1085
    if (m_direction == Function::Forward)
×
1086
    {
1087
        setCurrentStepIndex(0);
×
1088
        setStepColor(startColor);
×
1089
    }
1090
    else
1091
    {
1092
        setCurrentStepIndex(stepsCount - 1);
×
1093

1094
        if (endColor.isValid())
×
1095
            setStepColor(endColor);
×
1096
        else
1097
            setStepColor(startColor);
×
1098
    }
1099

1100
    calculateColorDelta(startColor, endColor, algorithm);
×
1101
}
×
1102

1103
bool RGBMatrixStep::checkNextStep(Function::RunOrder order,
×
1104
                                  QColor startColor, QColor endColor, int stepsNumber)
1105
{
1106
    if (order == Function::PingPong)
×
1107
    {
1108
        if (m_direction == Function::Forward && (m_currentStepIndex + 1) == stepsNumber)
×
1109
        {
1110
            m_direction = Function::Backward;
×
1111
            m_currentStepIndex = stepsNumber - 2;
×
1112
            if (endColor.isValid())
×
1113
                m_stepColor = endColor;
×
1114

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

1187
    return true;
×
1188
}
1189

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