GCC Code Coverage Report


Directory: ./
File: channeloutput/processors/RemapOutputProcessor.cpp
Date: 2026-10-29 08:11:58
Exec Total Coverage
Lines: 61 91 67.0%
Functions: 5 5 100.0%
Branches: 44 83 53.0%

Line Branch Exec Source
1 /*
2 * This file is part of the Falcon Player (FPP) and is Copyright (C)
3 * 2013-2022 by the Falcon Player Developers.
4 *
5 * The Falcon Player (FPP) is free software, and is covered under
6 * multiple Open Source licenses. Please see the included 'LICENSES'
7 * file for descriptions of what files are covered by each license.
8 *
9 * This source file is covered under the LGPL v2.1 as described in the
10 * included LICENSE.LGPL file.
11 */
12
13 #include "fpp-pch.h"
14
15 #include "fpp-json.h"
16
17 #include "../../log.h"
18
19 #include "RemapOutputProcessor.h"
20 #include "OutputProcessor.h"
21
22 #include <climits>
23
24 9 RemapOutputProcessor::RemapOutputProcessor(const Json::Value& config) {
25
2/4
✓ Branch 0 taken 9 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 9 times.
✗ Branch 3 not taken.
9 description = config["description"].asString();
26
2/4
✓ Branch 0 taken 9 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 9 times.
✗ Branch 3 not taken.
9 active = config["active"].asInt() ? true : false;
27
3/4
✓ Branch 0 taken 9 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 8 times.
✓ Branch 3 taken 1 times.
9 sourceChannel = config["source"].asInt();
28
2/4
✓ Branch 0 taken 8 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 8 times.
✗ Branch 3 not taken.
8 destChannel = config["destination"].asInt();
29
2/4
✓ Branch 0 taken 8 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 8 times.
✗ Branch 3 not taken.
8 count = config["count"].asInt();
30
2/4
✓ Branch 0 taken 8 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 8 times.
✗ Branch 3 not taken.
8 loops = config["loops"].asInt();
31
2/4
✓ Branch 0 taken 8 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 8 times.
✗ Branch 3 not taken.
8 reverse = config["reverse"].asInt();
32
33
1/2
✓ Branch 0 taken 8 times.
✗ Branch 1 not taken.
8 ProcessModelConfig(config, model, sourceChannel, count);
34
35
1/2
✓ Branch 0 taken 8 times.
✗ Branch 1 not taken.
8 LogInfo(VB_CHANNELOUT, "Remapped Channels: %d-%d => %d-%d (%d total channels copied in %d loop(s))\n",
36 sourceChannel + 1, sourceChannel + count,
37 destChannel, destChannel + (count * loops) - 1, (count * loops), loops);
38
39 // channel numbers need to be 0 based
40 8 --destChannel;
41
2/4
✓ Branch 0 taken 8 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 8 times.
8 if (count < 1 || loops < 1) {
42 // ProcessData still copies one channel per loop for a count below 2
43 ✗ count = 0;
44 ✗ loops = 0;
45 }
46
1/2
✓ Branch 0 taken 8 times.
✗ Branch 1 not taken.
8 tempBuffer.resize(count);
47 11 }
48
49 2 RemapOutputProcessor::RemapOutputProcessor(int src, int dst, int c, int l, int r) {
50 2 active = true;
51 2 sourceChannel = src;
52 2 destChannel = dst;
53 2 count = c;
54 2 loops = l;
55 2 reverse = r;
56
2/4
✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 2 times.
2 if (count < 1 || loops < 1) {
57 ✗ count = 0;
58 ✗ loops = 0;
59 }
60
1/2
✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
2 tempBuffer.resize(count);
61 2 }
62
63 8 RemapOutputProcessor::~RemapOutputProcessor() {
64 8 }
65 15 void RemapOutputProcessor::GetRequiredChannelRanges(const std::function<void(int, int)>& addRange) {
66
2/4
✓ Branch 0 taken 15 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 15 times.
15 if (count < 1 || loops < 1) {
67 ✗ return;
68 }
69 // 64 bit: the UI allows counts and loops large enough that loops * count
70 // overflows an int and would report a small, valid looking range
71 15 int64_t min = std::min(sourceChannel, destChannel);
72 15 int64_t max = std::max(sourceChannel, destChannel);
73 15 max += (int64_t)loops * count - 1;
74
1/2
✓ Branch 0 taken 15 times.
✗ Branch 1 not taken.
15 addRange((int)min, (int)std::min<int64_t>(max, INT_MAX));
75 }
76
77 211 void RemapOutputProcessor::ProcessData(unsigned char* channelData) const {
78
3/5
✓ Branch 0 taken 181 times.
✓ Branch 1 taken 15 times.
✓ Branch 2 taken 15 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
211 switch (reverse) {
79 181 case 0: // No reverse
80
2/2
✓ Branch 0 taken 196 times.
✓ Branch 1 taken 181 times.
377 for (int l = 0; l < loops; l++) {
81
1/2
✓ Branch 0 taken 196 times.
✗ Branch 1 not taken.
196 if (count > 1) {
82 // the source and destination ranges may overlap
83 196 memmove(channelData + destChannel + (l * count),
84 196 channelData + sourceChannel,
85 196 count);
86 } else {
87 ✗ channelData[destChannel + l] = channelData[sourceChannel];
88 }
89 }
90 181 break;
91
92 15 case 1: // Reverse channels individually
93
2/2
✓ Branch 0 taken 15 times.
✓ Branch 1 taken 15 times.
30 for (int l = 0; l < loops; l++) {
94
1/2
✓ Branch 0 taken 15 times.
✗ Branch 1 not taken.
15 if (count > 1) {
95
1/2
✓ Branch 0 taken 15 times.
✗ Branch 1 not taken.
15 if (!l) { // First loop, reverse while copying
96 // Copy the required section of channel data to the scratch buffer
97 15 memcpy(tempBuffer.data(), channelData + sourceChannel, count);
98
2/2
✓ Branch 0 taken 135 times.
✓ Branch 1 taken 15 times.
150 for (int c = 0; c < count; c++) {
99 135 channelData[destChannel + c] = tempBuffer[count - 1 - c];
100 }
101 } else { // Subsequent loops, just copy first reversed block for speed
102 ✗ memcpy(channelData + destChannel + (l * count),
103 ✗ channelData + destChannel,
104 ✗ count);
105 }
106 } else { // Can't reverse 1 channel so just copy
107 ✗ channelData[destChannel + l] = channelData[sourceChannel];
108 }
109 }
110 15 break;
111
112 15 case 2: // Reverse as a string of RGB pixels
113
2/2
✓ Branch 0 taken 15 times.
✓ Branch 1 taken 15 times.
30 for (int l = 0; l < loops; l++) {
114
1/2
✓ Branch 0 taken 15 times.
✗ Branch 1 not taken.
15 if (count > 1) {
115
1/2
✓ Branch 0 taken 15 times.
✗ Branch 1 not taken.
15 if (!l) { // First loop, reverse pixels while copying
116 // Copy the required section of channel data to the scratch buffer
117 15 memcpy(tempBuffer.data(), channelData + sourceChannel, count);
118
2/2
✓ Branch 0 taken 45 times.
✓ Branch 1 taken 15 times.
60 for (int c = 0; c < count - 2;) {
119 45 channelData[destChannel + c + 0] = tempBuffer[count - 1 - c - 2];
120 45 channelData[destChannel + c + 1] = tempBuffer[count - 1 - c - 1];
121 45 channelData[destChannel + c + 2] = tempBuffer[count - 1 - c - 0];
122 45 c += 3;
123 }
124 } else { // Subsequent loops, just copy first reversed block for speed
125 ✗ memcpy(channelData + destChannel + (l * count),
126 ✗ channelData + destChannel,
127 ✗ count);
128 }
129 } else {
130 // Shouldn't ever get here, can't reverse pixels if only 1 channel
131 ✗ channelData[destChannel + l] = channelData[sourceChannel];
132 }
133 }
134 15 break;
135
136 ✗ case 3: // Reverse as a string of RGBW pixels
137 ✗ for (int l = 0; l < loops; l++) {
138 ✗ if (count > 1) {
139 ✗ if (!l) { // First loop, reverse pixels while copying
140 // Copy the required section of channel data to the scratch buffer
141 ✗ memcpy(tempBuffer.data(), channelData + sourceChannel, count);
142 ✗ for (int c = 0; c < count - 3;) {
143 ✗ channelData[destChannel + c + 0] = tempBuffer[count - 1 - c - 3];
144 ✗ channelData[destChannel + c + 1] = tempBuffer[count - 1 - c - 2];
145 ✗ channelData[destChannel + c + 2] = tempBuffer[count - 1 - c - 1];
146 ✗ channelData[destChannel + c + 3] = tempBuffer[count - 1 - c - 0];
147 ✗ c += 4;
148 }
149 } else { // Subsequent loops, just copy first reversed block for speed
150 ✗ memcpy(channelData + destChannel + (l * count),
151 ✗ channelData + destChannel,
152 ✗ count);
153 }
154 } else {
155 // Shouldn't ever get here, can't reverse pixels if only 1 channel
156 ✗ channelData[destChannel + l] = channelData[sourceChannel];
157 }
158 }
159 ✗ break;
160 }
161 211 }
162