| 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 GPL v2 as described in the | ||
| 10 | * included LICENSE.GPL file. | ||
| 11 | */ | ||
| 12 | |||
| 13 | #include "fpp-pch.h" | ||
| 14 | |||
| 15 | #include "fpp-json.h" | ||
| 16 | #include "common.h" | ||
| 17 | #include "Sequence.h" | ||
| 18 | |||
| 19 | #include <cmath> | ||
| 20 | #include <unistd.h> | ||
| 21 | |||
| 22 | #include "../Warnings.h" | ||
| 23 | #include "ChannelOutputSetup.h" | ||
| 24 | #include "../log.h" | ||
| 25 | |||
| 26 | #include "GammaLUT.h" | ||
| 27 | #include "RGBMatrix.h" | ||
| 28 | #include "overlays/PixelOverlay.h" | ||
| 29 | |||
| 30 | #include "Plugin.h" | ||
| 31 | class RGBMatrixPlugin : public FPPPlugins::Plugin, public FPPPlugins::ChannelOutputPlugin { | ||
| 32 | public: | ||
| 33 | ✗ | RGBMatrixPlugin() : | |
| 34 | ✗ | FPPPlugins::Plugin("RGBMatrix") { | |
| 35 | ✗ | } | |
| 36 | ✗ | virtual ChannelOutput* createChannelOutput(unsigned int startChannel, unsigned int channelCount) override { | |
| 37 | ✗ | return new RGBMatrixOutput(startChannel, channelCount); | |
| 38 | } | ||
| 39 | }; | ||
| 40 | |||
| 41 | extern "C" { | ||
| 42 | ✗ | FPPPlugins::Plugin* createPlugin() { | |
| 43 | ✗ | return new RGBMatrixPlugin(); | |
| 44 | } | ||
| 45 | } | ||
| 46 | |||
| 47 | // The library's own pin-mapping table. Framebuffer's constructor abort()s when | ||
| 48 | // a mapping is asked for more parallel chains than it has pins for, so the | ||
| 49 | // limit has to be checked against the same table before handing it options -- | ||
| 50 | // a hand-kept copy here had already drifted (it said regular-pi1 had 2). | ||
| 51 | #include "../lib/hardware-mapping.h" | ||
| 52 | |||
| 53 | // Mirrors Framebuffer::InitHardwareMapping(): 0 for an unknown mapping name, | ||
| 54 | // otherwise the number of parallel chains the mapping has data pins for. | ||
| 55 | ✗ | static int MaxParallelChains(const char* name) { | |
| 56 | ✗ | if (name == nullptr || *name == '\0') { | |
| 57 | ✗ | name = "regular"; | |
| 58 | } | ||
| 59 | ✗ | for (const HardwareMapping* h = matrix_hardware_mappings; h->name; ++h) { | |
| 60 | ✗ | if (strcasecmp(h->name, name) != 0) { | |
| 61 | ✗ | continue; | |
| 62 | } | ||
| 63 | ✗ | if (h->max_parallel_chains) { | |
| 64 | ✗ | return h->max_parallel_chains; | |
| 65 | } | ||
| 66 | ✗ | int n = 0; | |
| 67 | ✗ | n += (h->p0_r1 | h->p0_g1 | h->p0_r2 | h->p0_g2) ? 1 : 0; | |
| 68 | ✗ | n += (h->p1_r1 | h->p1_g1 | h->p1_r2 | h->p1_g2) ? 1 : 0; | |
| 69 | ✗ | n += (h->p2_r1 | h->p2_g1 | h->p2_r2 | h->p2_g2) ? 1 : 0; | |
| 70 | ✗ | n += (h->p3_r1 | h->p3_g1 | h->p3_r2 | h->p3_g2) ? 1 : 0; | |
| 71 | ✗ | n += (h->p4_r1 | h->p4_g1 | h->p4_r2 | h->p4_g2) ? 1 : 0; | |
| 72 | ✗ | n += (h->p5_r1 | h->p5_g1 | h->p5_r2 | h->p5_g2) ? 1 : 0; | |
| 73 | ✗ | return n; | |
| 74 | } | ||
| 75 | ✗ | return 0; | |
| 76 | } | ||
| 77 | |||
| 78 | ///////////////////////////////////////////////////////////////////////////// | ||
| 79 | |||
| 80 | /* | ||
| 81 | * | ||
| 82 | */ | ||
| 83 | ✗ | RGBMatrixOutput::RGBMatrixOutput(unsigned int startChannel, | |
| 84 | ✗ | unsigned int channelCount) : | |
| 85 | ChannelOutput(startChannel, channelCount), | ||
| 86 | ✗ | m_canvas(NULL), | |
| 87 | ✗ | m_rgbmatrix(NULL), | |
| 88 | ✗ | m_colorOrder("RGB"), | |
| 89 | ✗ | m_panelWidth(32), | |
| 90 | ✗ | m_panelHeight(16), | |
| 91 | ✗ | m_panels(0), | |
| 92 | ✗ | m_width(0), | |
| 93 | ✗ | m_height(0), | |
| 94 | ✗ | m_rows(0), | |
| 95 | ✗ | m_outputs(0), | |
| 96 | ✗ | m_longestChain(0), | |
| 97 | ✗ | m_invertedData(0) { | |
| 98 | ✗ | LogDebug(VB_CHANNELOUT, "RGBMatrixOutput::RGBMatrixOutput(%u, %u)\n", | |
| 99 | startChannel, channelCount); | ||
| 100 | ✗ | } | |
| 101 | |||
| 102 | /* | ||
| 103 | * | ||
| 104 | */ | ||
| 105 | ✗ | RGBMatrixOutput::~RGBMatrixOutput() { | |
| 106 | ✗ | LogDebug(VB_CHANNELOUT, "RGBMatrixOutput::~RGBMatrixOutput()\n"); | |
| 107 | |||
| 108 | ✗ | if (m_rgbmatrix) | |
| 109 | ✗ | delete m_rgbmatrix; | |
| 110 | ✗ | if (m_matrix) | |
| 111 | ✗ | delete m_matrix; | |
| 112 | ✗ | if (m_panelMatrix) | |
| 113 | ✗ | delete m_panelMatrix; | |
| 114 | ✗ | } | |
| 115 | |||
| 116 | /* | ||
| 117 | * | ||
| 118 | */ | ||
| 119 | ✗ | int RGBMatrixOutput::Init(Json::Value config) { | |
| 120 | ✗ | LogDebug(VB_CHANNELOUT, "RGBMatrixOutput::Init(JSON)\n"); | |
| 121 | ✗ | std::string model = GetFileContents("/proc/device-tree/model"); | |
| 122 | ✗ | if (startsWith(model, "Raspberry Pi 5") || | |
| 123 | ✗ | startsWith(model, "Raspberry Pi Compute Module 5")) { | |
| 124 | ✗ | LogErr(VB_CHANNELOUT, "RGBMatrix does work on Raspberry Pi 5\n"); | |
| 125 | ✗ | AddOutputConfigWarning(50, "LED panel output: not supported on the Raspberry Pi 5 / CM5"); | |
| 126 | ✗ | return 0; | |
| 127 | } | ||
| 128 | ✗ | if (m_channelCount > FPPD_MAX_CHANNELS) { | |
| 129 | ✗ | LogErr(VB_CHANNELOUT, "RGBMatrixOutput::Init: channelCount %u exceeds maximum of %u\n", | |
| 130 | m_channelCount, FPPD_MAX_CHANNELS); | ||
| 131 | ✗ | AddOutputConfigWarning(50, "LED panel output: channel count exceeds the maximum supported"); | |
| 132 | ✗ | return 0; | |
| 133 | } | ||
| 134 | |||
| 135 | ✗ | m_panelWidth = config["panelWidth"].asInt(); | |
| 136 | ✗ | m_panelHeight = config["panelHeight"].asInt(); | |
| 137 | |||
| 138 | ✗ | std::string modules = GetFileContents("/proc/modules"); | |
| 139 | ✗ | if (modules.contains("snd_bcm2835")) { | |
| 140 | ✗ | LogInfo(VB_CHANNELOUT, "snd_bcm2835 is loaded, attempting to unload."); | |
| 141 | ✗ | system("rmmod snd_bcm2835"); | |
| 142 | ✗ | system("modprobe snd_dummy"); | |
| 143 | } | ||
| 144 | ✗ | modules = GetFileContents("/proc/modules"); | |
| 145 | ✗ | if (modules.contains("snd_bcm2835")) { | |
| 146 | ✗ | LogWarn(VB_CHANNELOUT, "snd_bcm2835 is stil loaded. Cannot proceed."); | |
| 147 | ✗ | std::string nvresults; | |
| 148 | ✗ | urlPut("http://127.0.0.1/api/settings/rebootFlag", "1", nvresults); | |
| 149 | ✗ | std::string errStr = "RGBMatrix cannot run with snd_bcm2835 enabled. Please reboot."; | |
| 150 | ✗ | AddOutputConfigWarning(50, errStr); | |
| 151 | ✗ | errStr += "\n"; | |
| 152 | ✗ | LogErr(VB_CHANNELOUT, errStr); | |
| 153 | ✗ | return false; | |
| 154 | ✗ | } | |
| 155 | |||
| 156 | ✗ | if (!m_panelWidth) | |
| 157 | ✗ | m_panelWidth = 32; | |
| 158 | |||
| 159 | ✗ | if (!m_panelHeight) | |
| 160 | ✗ | m_panelHeight = 16; | |
| 161 | |||
| 162 | ✗ | m_invertedData = config["invertedData"].asInt(); | |
| 163 | ✗ | m_colorOrder = config["colorOrder"].asString(); | |
| 164 | |||
| 165 | ✗ | m_panelMatrix = | |
| 166 | ✗ | new PanelMatrix(m_panelWidth, m_panelHeight, m_invertedData); | |
| 167 | |||
| 168 | ✗ | if (!m_panelMatrix) { | |
| 169 | ✗ | LogErr(VB_CHANNELOUT, "Unable to create PanelMatrix\n"); | |
| 170 | ✗ | return 0; | |
| 171 | } | ||
| 172 | |||
| 173 | ✗ | for (int i = 0; i < config["panels"].size(); i++) { | |
| 174 | ✗ | Json::Value p = config["panels"][i]; | |
| 175 | ✗ | char orientation = 'N'; | |
| 176 | ✗ | std::string o = p["orientation"].asString(); | |
| 177 | |||
| 178 | ✗ | if (!o.empty()) | |
| 179 | ✗ | orientation = o[0]; | |
| 180 | |||
| 181 | ✗ | if (p["colorOrder"].asString() == "") | |
| 182 | ✗ | p["colorOrder"] = m_colorOrder; | |
| 183 | |||
| 184 | // a refused panel (out of range) must not count toward the outputs or chains below | ||
| 185 | ✗ | if (!m_panelMatrix->AddPanel(p["outputNumber"].asInt(), | |
| 186 | ✗ | p["panelNumber"].asInt(), orientation, | |
| 187 | ✗ | p["xOffset"].asInt(), p["yOffset"].asInt(), | |
| 188 | ✗ | ColorOrderFromString(p["colorOrder"].asString()))) { | |
| 189 | ✗ | continue; | |
| 190 | } | ||
| 191 | |||
| 192 | ✗ | if (p["outputNumber"].asInt() > m_outputs) | |
| 193 | ✗ | m_outputs = p["outputNumber"].asInt(); | |
| 194 | |||
| 195 | ✗ | if (p["panelNumber"].asInt() > m_longestChain) | |
| 196 | ✗ | m_longestChain = p["panelNumber"].asInt(); | |
| 197 | ✗ | } | |
| 198 | |||
| 199 | // Both of these are 0-based, so bump them up by 1 for comparisons | ||
| 200 | ✗ | m_outputs++; | |
| 201 | ✗ | m_longestChain++; | |
| 202 | |||
| 203 | ✗ | m_panels = m_panelMatrix->PanelCount(); | |
| 204 | |||
| 205 | ✗ | int gpioSlowdown = 1; | |
| 206 | ✗ | if (config.isMember("gpioSlowdown")) | |
| 207 | ✗ | gpioSlowdown = config["gpioSlowdown"].asInt(); | |
| 208 | |||
| 209 | ✗ | m_rows = m_panelHeight; | |
| 210 | |||
| 211 | ✗ | m_width = m_panelMatrix->Width(); | |
| 212 | ✗ | m_height = m_panelMatrix->Height(); | |
| 213 | |||
| 214 | ✗ | m_channelCount = m_width * m_height * 3; | |
| 215 | |||
| 216 | ✗ | RGBMatrix::Options options; | |
| 217 | ✗ | rgb_matrix::RuntimeOptions runtimeOptions; | |
| 218 | |||
| 219 | ✗ | runtimeOptions.gpio_slowdown = gpioSlowdown; | |
| 220 | ✗ | runtimeOptions.daemon = 0; | |
| 221 | ✗ | runtimeOptions.drop_privileges = 0; | |
| 222 | ✗ | runtimeOptions.do_gpio_init = true; | |
| 223 | |||
| 224 | ✗ | if (config["wiringPinout"].asString() != "") | |
| 225 | ✗ | options.hardware_mapping = strdup(config["wiringPinout"].asString().c_str()); | |
| 226 | |||
| 227 | ✗ | options.chain_length = m_longestChain; | |
| 228 | ✗ | options.parallel = m_outputs; | |
| 229 | ✗ | options.rows = m_panelHeight; | |
| 230 | ✗ | options.cols = m_panelWidth; | |
| 231 | |||
| 232 | ✗ | if (config.isMember("brightness")) | |
| 233 | ✗ | options.brightness = config["brightness"].asInt(); | |
| 234 | else | ||
| 235 | ✗ | options.brightness = 100; | |
| 236 | |||
| 237 | ✗ | int colorDepth = 8; | |
| 238 | ✗ | if (config.isMember("panelColorDepth")) { | |
| 239 | ✗ | colorDepth = config["panelColorDepth"].asInt(); | |
| 240 | } | ||
| 241 | ✗ | if (colorDepth > 11 || colorDepth < 6) { | |
| 242 | ✗ | colorDepth = 11; | |
| 243 | } | ||
| 244 | ✗ | options.pwm_bits = colorDepth; | |
| 245 | |||
| 246 | ✗ | LogDebug(VB_CHANNELOUT, " chain: %d parallel: %d rows: %d cols: %d brightness: %d colordepth: %d\n", | |
| 247 | options.chain_length, options.parallel, options.rows, options.cols, | ||
| 248 | options.brightness, options.pwm_bits); | ||
| 249 | |||
| 250 | ✗ | if (config.isMember("cpuPWM")) { | |
| 251 | ✗ | options.disable_hardware_pulsing = config["cpuPWM"].asBool(); | |
| 252 | ✗ | if (options.disable_hardware_pulsing) { | |
| 253 | ✗ | LogDebug(VB_CHANNELOUT, "Disabling use of Hardware PWM for OE pin\n"); | |
| 254 | } | ||
| 255 | } | ||
| 256 | ✗ | if (config.isMember("panelInterleave")) { | |
| 257 | ✗ | options.multiplexing = std::atoi(config["panelInterleave"].asString().c_str()); | |
| 258 | |||
| 259 | ✗ | int panelScan = config["panelScan"].asInt(); | |
| 260 | ✗ | if (panelScan == 0) { | |
| 261 | // 1/8 scan by default | ||
| 262 | ✗ | panelScan = m_panelHeight / 2; | |
| 263 | } | ||
| 264 | ✗ | if (panelScan == (m_panelHeight / 2)) { | |
| 265 | ✗ | options.multiplexing = 0; | |
| 266 | } | ||
| 267 | } | ||
| 268 | /* mortification77 | ||
| 269 | |||
| 270 | panelRowAddressType Definitions | ||
| 271 | |||
| 272 | 0 = default | ||
| 273 | 1 = AB-addressed panels | ||
| 274 | 2 = direct row select | ||
| 275 | 3 = ABC-addressed panels | ||
| 276 | 4 = ABC Shift + DE direct (Default: 0). | ||
| 277 | |||
| 278 | */ | ||
| 279 | |||
| 280 | ✗ | if (config.isMember("panelRowAddressType")) { | |
| 281 | ✗ | options.row_address_type = config["panelRowAddressType"].asInt(); | |
| 282 | } | ||
| 283 | |||
| 284 | ✗ | if (config.isMember("panelType")) { | |
| 285 | ✗ | switch (config["panelType"].asInt()) { | |
| 286 | ✗ | case 1: | |
| 287 | ✗ | options.panel_type = "FM6126A"; | |
| 288 | ✗ | break; | |
| 289 | ✗ | case 2: | |
| 290 | ✗ | options.panel_type = "FM6127"; | |
| 291 | ✗ | break; | |
| 292 | ✗ | default: | |
| 293 | ✗ | break; | |
| 294 | } | ||
| 295 | } | ||
| 296 | |||
| 297 | // parallel is the number of outputs (chains) in use; chain_length is the | ||
| 298 | // panels on each and is not limited by the pinout. | ||
| 299 | ✗ | int maxParallel = MaxParallelChains(options.hardware_mapping); | |
| 300 | ✗ | if (maxParallel == 0) { | |
| 301 | ✗ | LogErr(VB_CHANNELOUT, "Unknown LED panel wiring pinout '%s'\n", options.hardware_mapping); | |
| 302 | ✗ | return 0; | |
| 303 | } | ||
| 304 | ✗ | if (options.parallel > maxParallel) { | |
| 305 | ✗ | LogErr(VB_CHANNELOUT, "The %s pinout supports %d output%s, but panels are assigned to %d\n", | |
| 306 | options.hardware_mapping, maxParallel, maxParallel > 1 ? "s" : "", options.parallel); | ||
| 307 | ✗ | return 0; | |
| 308 | } | ||
| 309 | |||
| 310 | ✗ | m_rgbmatrix = RGBMatrix::CreateFromOptions(options, runtimeOptions); | |
| 311 | ✗ | if (!m_rgbmatrix) { | |
| 312 | ✗ | LogErr(VB_CHANNELOUT, "Unable to create RGBMatrix instance\n"); | |
| 313 | ✗ | AddOutputConfigWarning(50, "LED panel output: unable to create RGBMatrix instance from options"); | |
| 314 | ✗ | return 0; | |
| 315 | } | ||
| 316 | |||
| 317 | ✗ | m_canvas = m_rgbmatrix->CreateFrameCanvas(); | |
| 318 | ✗ | FrameCanvas* c2 = m_rgbmatrix->CreateFrameCanvas(); | |
| 319 | ✗ | m_rgbmatrix->SwapOnVSync(c2); | |
| 320 | |||
| 321 | ✗ | m_matrix = new Matrix(m_startChannel, m_width, m_height); | |
| 322 | |||
| 323 | ✗ | if (config.isMember("subMatrices")) { | |
| 324 | ✗ | for (int i = 0; i < config["subMatrices"].size(); i++) { | |
| 325 | ✗ | Json::Value sm = config["subMatrices"][i]; | |
| 326 | |||
| 327 | ✗ | m_matrix->AddSubMatrix( | |
| 328 | ✗ | sm["enabled"].asInt(), | |
| 329 | ✗ | sm["startChannel"].asInt() - 1, | |
| 330 | ✗ | sm["width"].asInt(), | |
| 331 | ✗ | sm["height"].asInt(), | |
| 332 | ✗ | sm["xOffset"].asInt(), | |
| 333 | ✗ | sm["yOffset"].asInt()); | |
| 334 | ✗ | } | |
| 335 | } | ||
| 336 | |||
| 337 | ✗ | GammaLUT::Build(m_gammaCurve, GammaLUT::ParseConfig(config, 2.2f)); | |
| 338 | ✗ | if (PixelOverlayManager::INSTANCE.isAutoCreatePixelOverlayModels()) { | |
| 339 | ✗ | std::string dd = "LED Panels"; | |
| 340 | ✗ | if (config.isMember("LEDPanelMatrixName") && !config["LEDPanelMatrixName"].asString().empty()) { | |
| 341 | ✗ | dd = config["LEDPanelMatrixName"].asString(); | |
| 342 | } | ||
| 343 | ✗ | if (config.isMember("description")) { | |
| 344 | ✗ | dd = config["description"].asString(); | |
| 345 | } | ||
| 346 | ✗ | std::string desc = dd; | |
| 347 | ✗ | int count = 0; | |
| 348 | ✗ | while (PixelOverlayManager::INSTANCE.getModel(desc) != nullptr) { | |
| 349 | ✗ | count++; | |
| 350 | ✗ | desc = dd + "-" + std::to_string(count); | |
| 351 | } | ||
| 352 | ✗ | PixelOverlayManager::INSTANCE.addAutoOverlayModel(desc, | |
| 353 | m_startChannel, m_channelCount, 3, | ||
| 354 | ✗ | "H", m_invertedData ? "BL" : "TL", | |
| 355 | ✗ | m_height, 1); | |
| 356 | ✗ | m_autoCreatedModelName = desc; | |
| 357 | ✗ | } | |
| 358 | ✗ | return ChannelOutput::Init(config); | |
| 359 | ✗ | } | |
| 360 | ✗ | void RGBMatrixOutput::GetRequiredChannelRanges(const std::function<void(int, int)>& addRange) { | |
| 361 | ✗ | addRange(m_startChannel, m_startChannel + m_channelCount - 1); | |
| 362 | ✗ | } | |
| 363 | |||
| 364 | /* | ||
| 365 | * | ||
| 366 | */ | ||
| 367 | ✗ | int RGBMatrixOutput::Close(void) { | |
| 368 | ✗ | LogDebug(VB_CHANNELOUT, "RGBMatrixOutput::Close()\n"); | |
| 369 | |||
| 370 | ✗ | delete m_rgbmatrix; | |
| 371 | ✗ | m_rgbmatrix = nullptr; | |
| 372 | ✗ | m_canvas = nullptr; | |
| 373 | |||
| 374 | ✗ | if (!m_autoCreatedModelName.empty()) { | |
| 375 | ✗ | PixelOverlayManager::INSTANCE.removeAutoOverlayModel(m_autoCreatedModelName); | |
| 376 | } | ||
| 377 | |||
| 378 | ✗ | return ChannelOutput::Close(); | |
| 379 | } | ||
| 380 | |||
| 381 | ✗ | void RGBMatrixOutput::OverlayTestData(unsigned char* channelData, int cycleNum, float percentOfCycle, int testType, const Json::Value& config) { | |
| 382 | ✗ | for (int output = 0; output < m_outputs; output++) { | |
| 383 | ✗ | int panelsOnOutput = m_panelMatrix->m_outputPanels[output].size(); | |
| 384 | ✗ | for (int i = 0; i < panelsOnOutput; i++) { | |
| 385 | ✗ | int panel = m_panelMatrix->m_outputPanels[output][i]; | |
| 386 | |||
| 387 | ✗ | m_panelMatrix->m_panels[panel].drawTestPattern(channelData + m_startChannel, cycleNum, percentOfCycle, testType); | |
| 388 | } | ||
| 389 | } | ||
| 390 | ✗ | } | |
| 391 | |||
| 392 | /* | ||
| 393 | * | ||
| 394 | */ | ||
| 395 | ✗ | void RGBMatrixOutput::PrepData(unsigned char* channelData) { | |
| 396 | ✗ | LogExcess(VB_CHANNELOUT, "RGBMatrixOutput::PrepData(%p)\n", | |
| 397 | channelData); | ||
| 398 | ✗ | m_matrix->OverlaySubMatrices(channelData); | |
| 399 | |||
| 400 | unsigned char r; | ||
| 401 | unsigned char g; | ||
| 402 | unsigned char b; | ||
| 403 | |||
| 404 | ✗ | channelData += m_startChannel; | |
| 405 | |||
| 406 | ✗ | for (int output = 0; output < m_outputs; output++) { | |
| 407 | ✗ | int panelsOnOutput = m_panelMatrix->m_outputPanels[output].size(); | |
| 408 | |||
| 409 | ✗ | for (int i = 0; i < panelsOnOutput; i++) { | |
| 410 | ✗ | int panel = m_panelMatrix->m_outputPanels[output][i]; | |
| 411 | |||
| 412 | ✗ | int chain = (m_longestChain - 1) - m_panelMatrix->m_panels[panel].chain; | |
| 413 | ✗ | for (int y = 0; y < m_panelHeight; y++) { | |
| 414 | ✗ | int px = chain * m_panelWidth; | |
| 415 | ✗ | for (int x = 0; x < m_panelWidth; x++) { | |
| 416 | ✗ | r = m_gammaCurve[channelData[m_panelMatrix->m_panels[panel].pixelMap[(y * m_panelWidth + x) * 3]]]; | |
| 417 | ✗ | g = m_gammaCurve[channelData[m_panelMatrix->m_panels[panel].pixelMap[(y * m_panelWidth + x) * 3 + 1]]]; | |
| 418 | ✗ | b = m_gammaCurve[channelData[m_panelMatrix->m_panels[panel].pixelMap[(y * m_panelWidth + x) * 3 + 2]]]; | |
| 419 | |||
| 420 | ✗ | m_canvas->SetPixel(px, y + (output * m_panelHeight), r, g, b); | |
| 421 | |||
| 422 | ✗ | px++; | |
| 423 | } | ||
| 424 | } | ||
| 425 | } | ||
| 426 | } | ||
| 427 | ✗ | } | |
| 428 | |||
| 429 | /* | ||
| 430 | * | ||
| 431 | */ | ||
| 432 | ✗ | int RGBMatrixOutput::SendData(unsigned char* channelData) { | |
| 433 | ✗ | LogExcess(VB_CHANNELOUT, "RGBMatrixOutput::RawSendData(%p)\n", | |
| 434 | channelData); | ||
| 435 | |||
| 436 | ✗ | m_canvas = m_rgbmatrix->SwapOnVSync(m_canvas); | |
| 437 | ✗ | return m_channelCount; | |
| 438 | } | ||
| 439 | |||
| 440 | /* | ||
| 441 | * | ||
| 442 | */ | ||
| 443 | ✗ | void RGBMatrixOutput::DumpConfig(void) { | |
| 444 | ✗ | LogDebug(VB_CHANNELOUT, "RGBMatrixOutput::DumpConfig()\n"); | |
| 445 | ✗ | LogDebug(VB_CHANNELOUT, " panels : %d\n", m_panels); | |
| 446 | ✗ | LogDebug(VB_CHANNELOUT, " width : %d\n", m_width); | |
| 447 | ✗ | LogDebug(VB_CHANNELOUT, " height : %d\n", m_height); | |
| 448 | ✗ | } | |
| 449 |