GCC Code Coverage Report


Directory: ./
File: channeloutput/PanelInterleaveHandler.cpp
Date: 2026-10-29 08:11:58
Exec Total Coverage
Lines: 29 524 5.5%
Functions: 6 74 8.1%
Branches: 25 590 4.2%

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 #include "PanelInterleaveHandler.h"
13
14 #include <cstdint>
15 #include <vector>
16
17 15 PanelInterleaveHandler::PanelInterleaveHandler(int pw, int ph, int ps, int pi) :
18 15 m_panelWidth(pw), m_panelHeight(ph), m_panelScan(ps), m_interleave(pi) {
19
1/2
✓ Branch 0 taken 15 times.
✗ Branch 1 not taken.
15 m_map.resize(m_panelWidth * m_panelHeight);
20 15 }
21
22 15 bool PanelInterleaveHandler::mapFitsFrame() const {
23 // Callers address their frame buffers as [bit][mapped y][chain * xSpan][gpio],
24 // where the buffer holds exactly m_panelScan rows and each panel occupies
25 // xSpan = m_panelWidth * m_panelHeight / (m_panelScan * 2) columns; the
26 // extra columns are where a scan rate below half the panel height folds
27 // its rows. Only y in [0, m_panelHeight / 2) is ever mapped - the two
28 // halves of the panel are driven together - so that is the range checked.
29 // A map that leaves the frame does not merely look wrong on the panel, it
30 // writes past the end of the allocation, so it has to be refused here
31 // rather than trusted by every caller in turn.
32
3/6
✓ Branch 0 taken 15 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 15 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✓ Branch 5 taken 15 times.
15 if (m_panelScan <= 0 || m_panelWidth <= 0 || m_panelHeight <= 0) {
33 ✗ return false;
34 }
35 15 const int xSpan = m_panelWidth * m_panelHeight / (m_panelScan * 2);
36
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 15 times.
15 if (xSpan <= 0) {
37 ✗ return false;
38 }
39
2/2
✓ Branch 0 taken 244 times.
✓ Branch 1 taken 15 times.
259 for (int y = 0; y < (m_panelHeight / 2); ++y) {
40
2/2
✓ Branch 0 taken 15936 times.
✓ Branch 1 taken 244 times.
16180 for (int x = 0; x < m_panelWidth; ++x) {
41 // the map holds uint16_t, so a negative mapping has already
42 // wrapped to a large value and trips these same checks
43 15936 const auto& p = m_map[y * m_panelWidth + x];
44
2/4
✓ Branch 0 taken 15936 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 15936 times.
15936 if (p.first >= xSpan || p.second >= m_panelScan) {
45 ✗ return false;
46 }
47 }
48 }
49 15 return true;
50 }
51
52 class NoInterleaveHandler : public PanelInterleaveHandler {
53 public:
54 15 NoInterleaveHandler(int pw, int ph, int ps, int pi) :
55 15 PanelInterleaveHandler(pw, ph, ps, pi) {
56
2/2
✓ Branch 0 taken 488 times.
✓ Branch 1 taken 15 times.
503 for (int y = 0; y < m_panelHeight; ++y) {
57
2/2
✓ Branch 0 taken 31872 times.
✓ Branch 1 taken 488 times.
32360 for (int x = 0; x < m_panelWidth; ++x) {
58 31872 int idx = (y * m_panelWidth + x);
59 31872 m_map[idx] = std::make_pair(x, y);
60 }
61 }
62 15 }
63 60 virtual ~NoInterleaveHandler() {}
64 };
65
66 /*
67 For simple interleave of 16: on 32 wide panel
68 63----48 31----16
69
70 47----32 15----0
71 */
72 class SimpleInterleaveHandler : public PanelInterleaveHandler {
73 public:
74 ✗ SimpleInterleaveHandler(int pw, int ph, int ps, int pi, bool flip) :
75 PanelInterleaveHandler(pw, ph, ps, pi),
76 ✗ m_flipRows(flip) {
77 ✗ for (int y = 0; y < m_panelHeight; ++y) {
78 ✗ int y2 = y;
79 ✗ mapRow(y2);
80 ✗ for (int x = 0; x < m_panelWidth; ++x) {
81 ✗ int x2 = x;
82 ✗ mapCol(y, x2);
83 ✗ int idx = (y * m_panelWidth + x);
84 ✗ m_map[idx] = std::make_pair(x2, y2);
85 }
86 }
87 ✗ }
88 ✗ virtual ~SimpleInterleaveHandler() {}
89
90 ✗ virtual void mapRow(int& y) {
91 ✗ while (y >= m_panelScan) {
92 ✗ y -= m_panelScan;
93 }
94 ✗ }
95 ✗ virtual void mapCol(int y, int& x) {
96 ✗ int whichInt = x / m_interleave;
97 ✗ if (m_flipRows) {
98 // swap each pair of scan blocks (rows 0..scan-1 with scan..2*scan-1
99 // and so on). For a power of two scan this is y ^ scan; the
100 // division also covers scans like 6.
101 ✗ int block = y / m_panelScan;
102 ✗ y = (block ^ 1) * m_panelScan + y % m_panelScan;
103 }
104 ✗ int offInInt = x % m_interleave;
105 ✗ int mult = (m_panelHeight / m_panelScan / 2) - 1 - y / m_panelScan;
106 ✗ x = m_interleave * (whichInt * m_panelHeight / m_panelScan / 2 + mult) + offInInt;
107 ✗ }
108
109 private:
110 const bool m_flipRows;
111 };
112
113 /*
114 On 32 wide panel:
115 63----48 15---0
116 47----32 31--16
117 */
118 class ZigZagInterleaveHandler : public PanelInterleaveHandler {
119 public:
120 ✗ ZigZagInterleaveHandler(int pw, int ph, int ps, int pi) :
121 ✗ PanelInterleaveHandler(pw, ph, ps, pi) {
122 ✗ for (int y = 0; y < m_panelHeight; ++y) {
123 ✗ int y2 = y;
124 ✗ mapRow(y2);
125 ✗ for (int x = 0; x < m_panelWidth; ++x) {
126 ✗ int x2 = x;
127 ✗ mapCol(y, x2);
128 ✗ int idx = (y * m_panelWidth + x);
129 ✗ m_map[idx] = std::make_pair(x2, y2);
130 }
131 }
132 ✗ }
133 ✗ virtual ~ZigZagInterleaveHandler() {}
134
135 ✗ virtual void mapRow(int& y) {
136 ✗ while (y >= m_panelScan) {
137 ✗ y -= m_panelScan;
138 }
139 ✗ }
140 ✗ virtual void mapCol(int y, int& x) {
141 ✗ int whichInt = x / m_interleave;
142 ✗ int offInInt = x % m_interleave;
143 ✗ int mult = y / m_panelScan;
144
145 ✗ if (m_panelScan == 2) {
146 ✗ if ((y & 0x2) == 0) {
147 ✗ offInInt = m_interleave - 1 - offInInt;
148 }
149 ✗ } else if ((whichInt & 0x1) == 1) {
150 ✗ mult = (y < m_panelScan ? y + m_panelScan : y - m_panelScan) / m_panelScan;
151 }
152 ✗ x = m_interleave * (whichInt * m_panelHeight / m_panelScan / 2 + mult) + offInInt;
153 ✗ }
154 };
155
156 /*
157 For 32w panel:
158 32----47 0----15
159 63----48 31----16
160 */
161 class ZigZagClusterInterleaveHandler : public PanelInterleaveHandler {
162 public:
163 ✗ ZigZagClusterInterleaveHandler(int pw, int ph, int ps, int pi) :
164 ✗ PanelInterleaveHandler(pw, ph, ps, pi) {
165 ✗ for (int y = 0; y < m_panelHeight; ++y) {
166 ✗ int y2 = y;
167 ✗ mapRow(y2);
168 ✗ for (int x = 0; x < m_panelWidth; ++x) {
169 ✗ int x2 = x;
170 ✗ mapCol(y, x2);
171 ✗ int idx = (y * m_panelWidth + x);
172 ✗ m_map[idx] = std::make_pair(x2, y2);
173 }
174 }
175 ✗ }
176 ✗ virtual ~ZigZagClusterInterleaveHandler() {}
177
178 ✗ virtual void mapRow(int& y) {
179 ✗ while (y >= m_panelScan) {
180 ✗ y -= m_panelScan;
181 }
182 ✗ }
183 ✗ virtual void mapCol(int y, int& x) {
184 ✗ int whichInt = x / m_interleave;
185 ✗ int offInInt = x % m_interleave;
186 ✗ int mult = y / m_panelScan;
187
188 ✗ if (m_panelScan == 2) {
189 // AC_MAPPING code - stealing codepath for scan 1/2 and zigzag8
190 // cluster -- the ordinal number of a group of 8 linear lights on a half-frame
191 // starting in the top left corner of a half-module; bits 3,2 from y, 1,0 from x
192 ✗ int tc = whichInt | (y << 1) & 0xc;
193 // mapped cluster - this reverses the effects of unusual wiring on this panel
194 // address bits are shifted around and bit3 is negated to achieve linear counting
195 ✗ uint8_t map_cb = (~tc & 8) | (tc & 4) >> 2 | (tc & 2) << 1 | (tc & 1) << 1;
196 // scale up from cluster to pixel counts and account for reverse-running clusters
197 ✗ x = map_cb * 8 + (x & 0x7 ^ (((~y >> 1) & 1) * 7));
198 ✗ return;
199 ✗ } else if (m_interleave == 4) {
200 ✗ if ((whichInt & 0x1) == 1) {
201 ✗ mult = (y < m_panelScan ? y + m_panelScan : y - m_panelScan) / m_panelScan;
202 }
203 } else {
204 ✗ int tmp = (y * 2) / m_panelScan;
205 ✗ if ((tmp & 0x2) == 0) {
206 ✗ offInInt = m_interleave - 1 - offInInt;
207 }
208 }
209 ✗ x = m_interleave * (whichInt * m_panelHeight / m_panelScan / 2 + mult) + offInInt;
210 }
211 };
212
213 class StripeClusterInterleaveHandler : public PanelInterleaveHandler {
214 public:
215 static constexpr int MAPPING[8][4] = {
216 { 32, 48, 96, 112 },
217 { 32, 48, 96, 112 },
218 { 40, 56, 104, 120 },
219 { 40, 56, 104, 120 },
220 { 0, 16, 64, 80 },
221 { 0, 16, 64, 80 },
222 { 8, 24, 72, 88 },
223 { 8, 24, 72, 88 }
224 };
225
226 ✗ StripeClusterInterleaveHandler(int pw, int ph, int ps, int pi) :
227 ✗ PanelInterleaveHandler(pw, ph, ps, pi) {
228 ✗ for (int y = 0; y < m_panelHeight; ++y) {
229 ✗ int y2 = y;
230 ✗ mapRow(y2);
231 ✗ for (int x = 0; x < m_panelWidth; ++x) {
232 ✗ int x2 = x;
233 ✗ mapCol(y, x2);
234 ✗ int idx = (y * m_panelWidth + x);
235 ✗ m_map[idx] = std::make_pair(x2, y2);
236 }
237 }
238 ✗ }
239 ✗ virtual ~StripeClusterInterleaveHandler() {}
240
241 ✗ virtual void mapRow(int& y) {
242 ✗ while (y >= m_panelScan) {
243 ✗ y -= m_panelScan;
244 }
245 ✗ }
246 ✗ virtual void mapCol(int y, int& x) {
247 ✗ int whichInt = x / m_interleave;
248 ✗ int offInInt = x % m_interleave;
249 ✗ x = MAPPING[y % 8][whichInt % 4] + offInInt;
250 ✗ }
251 };
252
253 class PixelMapper {
254 public:
255 ✗ virtual ~PixelMapper() {}
256
257 // Get the name of this PixelMapper. Each PixelMapper needs to have a name
258 // so that it can be referred to with command line flags.
259 virtual const char* GetName() const = 0;
260
261 // Pixel mappers receive the chain and parallel information and
262 // might receive optional user-parameters, e.g. from command line flags.
263 //
264 // This is a single string containing the parameters.
265 // You can be used from simple scalar parameters, such as the angle for
266 // the rotate transformer, or more complex parameters that describe a mapping
267 // of panels for instance.
268 // Keep it concise (as people will give parameters on the command line) and
269 // don't use semicolons in your string (as they are
270 // used to separate pixel mappers on the command line).
271 //
272 // For instance, the rotate transformer is invoked like this
273 // --led-pixel-mapper=rotate:90
274 // And the parameter that is passed to SetParameter() is "90".
275 //
276 // Returns 'true' if parameter was parsed successfully.
277 ✗ virtual bool SetParameters(int chain, int parallel,
278 const char* parameter_string) {
279 ✗ return true;
280 }
281
282 // Given a underlying matrix (width, height), returns the
283 // visible (width, height) after the mapping.
284 // E.g. a 90 degree rotation might map matrix=(64, 32) -> visible=(32, 64)
285 // Some multiplexing matrices will double the height and half the width.
286 //
287 // While not technically necessary, one would expect that the number of
288 // pixels stay the same, so
289 // matrix_width * matrix_height == (*visible_width) * (*visible_height);
290 //
291 // Returns boolean "true" if the mapping can be successfully done with this
292 // mapper.
293 virtual bool GetSizeMapping(int matrix_width, int matrix_height,
294 int* visible_width, int* visible_height)
295 const = 0;
296
297 // Map where a visible pixel (x,y) is mapped to the underlying matrix (x,y).
298 //
299 // To be convienently stateless, the first parameters are the full
300 // matrix width and height.
301 //
302 // So for many multiplexing methods this means to map a panel to a double
303 // length and half height panel (32x16 -> 64x8).
304 // The logic_x, logic_y are output parameters and guaranteed not to be
305 // nullptr.
306 virtual void MapVisibleToMatrix(int matrix_width, int matrix_height,
307 int visible_x, int visible_y,
308 int* matrix_x, int* matrix_y) const = 0;
309 };
310 class MultiplexMapper : public PixelMapper {
311 public:
312 // Function that edits the original rows and columns of the panels
313 // provided by the user to the actual underlying mapping. This is called
314 // before we do the actual set-up of the GPIO mapping as this influences
315 // the hardware interface.
316 // This is so that the user can provide the rows/columns they see.
317 virtual void EditColsRows(int* cols, int* rows) const = 0;
318 };
319
320 class MultiplexMapperBase : public MultiplexMapper {
321 public:
322 ✗ MultiplexMapperBase(const char* name, int stretch_factor) :
323 ✗ name_(name), panel_stretch_factor_(stretch_factor) {}
324
325 // This method is const, but we sneakily remember the original size
326 // of the panels so that we can more easily quantize things.
327 // So technically, we're stateful, but let's pretend we're not changing
328 // state. In the context this is used, it is never accessed in multiple
329 // threads.
330 ✗ virtual void EditColsRows(int* cols, int* rows) const {
331 ✗ panel_rows_ = *rows;
332 ✗ panel_cols_ = *cols;
333
334 ✗ *rows /= panel_stretch_factor_;
335 ✗ *cols *= panel_stretch_factor_;
336 ✗ }
337
338 ✗ virtual bool GetSizeMapping(int matrix_width, int matrix_height,
339 int* visible_width, int* visible_height) const {
340 // Matrix width has been altered. Alter it back.
341 ✗ *visible_width = matrix_width / panel_stretch_factor_;
342 ✗ *visible_height = matrix_height * panel_stretch_factor_;
343 ✗ return true;
344 }
345
346 ✗ virtual const char* GetName() const { return name_; }
347
348 // The MapVisibleToMatrix() as required by PanelMatrix here breaks it
349 // down to the individual panel, so that derived classes only need to
350 // implement MapSinglePanel().
351 ✗ virtual void MapVisibleToMatrix(int matrix_width, int matrix_height,
352 int visible_x, int visible_y,
353 int* matrix_x, int* matrix_y) const {
354 ✗ const int chained_panel = visible_x / panel_cols_;
355 ✗ const int parallel_panel = visible_y / panel_rows_;
356
357 ✗ const int within_panel_x = visible_x % panel_cols_;
358 ✗ const int within_panel_y = visible_y % panel_rows_;
359
360 int new_x, new_y;
361 ✗ MapSinglePanel(within_panel_x, within_panel_y, &new_x, &new_y);
362 ✗ *matrix_x = chained_panel * panel_stretch_factor_ * panel_cols_ + new_x;
363 ✗ *matrix_y = parallel_panel * panel_rows_ / panel_stretch_factor_ + new_y;
364 ✗ }
365
366 // Map the coordinates for a single panel. This is to be overridden in
367 // derived classes.
368 // Input parameter is the visible position on the matrix, and this method
369 // should return the internal multiplexed position.
370 virtual void MapSinglePanel(int visible_x, int visible_y,
371 int* matrix_x, int* matrix_y) const = 0;
372
373 protected:
374 const char* const name_;
375 const int panel_stretch_factor_;
376
377 mutable int panel_cols_;
378 mutable int panel_rows_;
379 };
380
381 /* ========================================================================
382 * Multiplexer implementations.
383 *
384 * Extend MultiplexMapperBase and implement MapSinglePanel. You only have
385 * to worry about the mapping within a single panel, the overall panel
386 * construction with chains and parallel is already taken care of.
387 *
388 * Don't forget to register the new multiplexer sin CreateMultiplexMapperList()
389 * below. After that, the new mapper is available in the --led-multiplexing
390 * option.
391 */
392 class StripeMultiplexMapper : public MultiplexMapperBase {
393 public:
394 ✗ StripeMultiplexMapper() :
395 ✗ MultiplexMapperBase("Stripe", 2) {}
396
397 ✗ void MapSinglePanel(int x, int y, int* matrix_x, int* matrix_y) const {
398 ✗ const bool is_top_stripe = (y % (panel_rows_ / 2)) < panel_rows_ / 4;
399 ✗ *matrix_x = is_top_stripe ? x + panel_cols_ : x;
400 ✗ *matrix_y = ((y / (panel_rows_ / 2)) * (panel_rows_ / 4) + y % (panel_rows_ / 4));
401 ✗ }
402 };
403
404 class FlippedStripeMultiplexMapper : public MultiplexMapperBase {
405 public:
406 ✗ FlippedStripeMultiplexMapper() :
407 ✗ MultiplexMapperBase("FlippedStripe", 2) {}
408
409 ✗ void MapSinglePanel(int x, int y, int* matrix_x, int* matrix_y) const {
410 ✗ const bool is_top_stripe = (y % (panel_rows_ / 2)) >= panel_rows_ / 4;
411 ✗ *matrix_x = is_top_stripe ? x + panel_cols_ : x;
412 ✗ *matrix_y = ((y / (panel_rows_ / 2)) * (panel_rows_ / 4) + y % (panel_rows_ / 4));
413 ✗ }
414 };
415
416 class CheckeredMultiplexMapper : public MultiplexMapperBase {
417 public:
418 ✗ CheckeredMultiplexMapper() :
419 ✗ MultiplexMapperBase("Checkered", 2) {}
420
421 ✗ void MapSinglePanel(int x, int y, int* matrix_x, int* matrix_y) const {
422 ✗ const bool is_top_check = (y % (panel_rows_ / 2)) < panel_rows_ / 4;
423 ✗ const bool is_left_check = (x < panel_cols_ / 2);
424 ✗ if (is_top_check) {
425 ✗ *matrix_x = is_left_check ? x + panel_cols_ / 2 : x + panel_cols_;
426 } else {
427 ✗ *matrix_x = is_left_check ? x : x + panel_cols_ / 2;
428 }
429 ✗ *matrix_y = ((y / (panel_rows_ / 2)) * (panel_rows_ / 4) + y % (panel_rows_ / 4));
430 ✗ }
431 };
432
433 class SpiralMultiplexMapper : public MultiplexMapperBase {
434 public:
435 ✗ SpiralMultiplexMapper() :
436 ✗ MultiplexMapperBase("Spiral", 2) {}
437
438 ✗ void MapSinglePanel(int x, int y, int* matrix_x, int* matrix_y) const {
439 ✗ const bool is_top_stripe = (y % (panel_rows_ / 2)) < panel_rows_ / 4;
440 ✗ const int panel_quarter = panel_cols_ / 4;
441 ✗ const int quarter = x / panel_quarter;
442 ✗ const int offset = x % panel_quarter;
443 ✗ *matrix_x = ((2 * quarter * panel_quarter) + (is_top_stripe
444 ✗ ? panel_quarter - 1 - offset
445 : panel_quarter + offset));
446 ✗ *matrix_y = ((y / (panel_rows_ / 2)) * (panel_rows_ / 4) + y % (panel_rows_ / 4));
447 ✗ }
448 };
449
450 class ZStripeMultiplexMapper : public MultiplexMapperBase {
451 public:
452 ✗ ZStripeMultiplexMapper(const char* name, int even_vblock_offset, int odd_vblock_offset) :
453 MultiplexMapperBase(name, 2),
454 ✗ even_vblock_offset_(even_vblock_offset),
455 ✗ odd_vblock_offset_(odd_vblock_offset) {}
456
457 ✗ void MapSinglePanel(int x, int y, int* matrix_x, int* matrix_y) const {
458 static const int tile_width = 8;
459 static const int tile_height = 4;
460
461 ✗ const int vert_block_is_odd = ((y / tile_height) % 2);
462
463 ✗ const int even_vblock_shift = (1 - vert_block_is_odd) * even_vblock_offset_;
464 ✗ const int odd_vblock_shitf = vert_block_is_odd * odd_vblock_offset_;
465
466 ✗ *matrix_x = x + ((x + even_vblock_shift) / tile_width) * tile_width + odd_vblock_shitf;
467 ✗ *matrix_y = (y % tile_height) + tile_height * (y / (tile_height * 2));
468 ✗ }
469
470 private:
471 const int even_vblock_offset_;
472 const int odd_vblock_offset_;
473 };
474
475 class CoremanMapper : public MultiplexMapperBase {
476 public:
477 ✗ CoremanMapper() :
478 ✗ MultiplexMapperBase("coreman", 2) {}
479
480 ✗ void MapSinglePanel(int x, int y, int* matrix_x, int* matrix_y) const {
481 ✗ const bool is_left_check = (x < panel_cols_ / 2);
482
483 ✗ if ((y <= 7) || ((y >= 16) && (y <= 23))) {
484 ✗ *matrix_x = ((x / (panel_cols_ / 2)) * panel_cols_) + (x % (panel_cols_ / 2));
485 ✗ if ((y & (panel_rows_ / 4)) == 0) {
486 ✗ *matrix_y = (y / (panel_rows_ / 2)) * (panel_rows_ / 4) + (y % (panel_rows_ / 4));
487 }
488 } else {
489 ✗ *matrix_x = is_left_check ? x + panel_cols_ / 2 : x + panel_cols_;
490 ✗ *matrix_y = (y / (panel_rows_ / 2)) * (panel_rows_ / 4) + y % (panel_rows_ / 4);
491 }
492 ✗ }
493 };
494
495 class Kaler2ScanMapper : public MultiplexMapperBase {
496 public:
497 ✗ Kaler2ScanMapper() :
498 ✗ MultiplexMapperBase("Kaler2Scan", 4) {}
499
500 ✗ void MapSinglePanel(int x, int y, int* matrix_x, int* matrix_y) const {
501 // Now we have a 128x4 matrix
502 ✗ int offset = ((y % 4) / 2) == 0 ? -1 : 1; // Add o substract
503 ✗ int deltaOffset = offset < 0 ? 7 : 8;
504 ✗ int deltaColumn = ((y % 8) / 4) == 0 ? 64 : 0;
505
506 ✗ *matrix_y = (y % 2 + (y / 8) * 2);
507 ✗ *matrix_x = deltaColumn + (16 * (x / 8)) + deltaOffset + ((x % 8) * offset);
508 ✗ }
509 };
510
511 class P10MapperZ : public MultiplexMapperBase {
512 public:
513 ✗ P10MapperZ() :
514 ✗ MultiplexMapperBase("P10-128x4-Z", 4) {}
515 // supports this panel: https://www.aliexpress.com/item/2017-Special-Offer-P10-Outdoor-Smd-Full-Color-Led-Display-Module-320x160mm-1-2-Scan-Outdoor/32809267439.html?spm=a2g0s.9042311.0.0.Ob0jEw
516 // with --led-row-addr-type=2 flag
517 ✗ void MapSinglePanel(int x, int y, int* matrix_x, int* matrix_y) const {
518 ✗ int yComp = 0;
519 ✗ if (y == 0 || y == 1 || y == 8 || y == 9) {
520 ✗ yComp = 127;
521 ✗ } else if (y == 2 || y == 3 || y == 10 || y == 11) {
522 ✗ yComp = 112;
523 ✗ } else if (y == 4 || y == 5 || y == 12 || y == 13) {
524 ✗ yComp = 111;
525 ✗ } else if (y == 6 || y == 7 || y == 14 || y == 15) {
526 ✗ yComp = 96;
527 }
528
529 ✗ if (y == 0 || y == 1 || y == 4 || y == 5 ||
530 ✗ y == 8 || y == 9 || y == 12 || y == 13) {
531 ✗ *matrix_x = yComp - x;
532 ✗ *matrix_x -= (24 * ((int)(x / 8)));
533 } else {
534 ✗ *matrix_x = yComp + x;
535 ✗ *matrix_x -= (40 * ((int)(x / 8)));
536 }
537
538 ✗ if (y == 0 || y == 2 || y == 4 || y == 6) {
539 ✗ *matrix_y = 3;
540 ✗ } else if (y == 1 || y == 3 || y == 5 || y == 7) {
541 ✗ *matrix_y = 2;
542 ✗ } else if (y == 8 || y == 10 || y == 12 || y == 14) {
543 ✗ *matrix_y = 1;
544 ✗ } else if (y == 9 || y == 11 || y == 13 || y == 15) {
545 ✗ *matrix_y = 0;
546 }
547 ✗ }
548 };
549
550 class QiangLiQ8 : public MultiplexMapperBase {
551 public:
552 ✗ QiangLiQ8() :
553 ✗ MultiplexMapperBase("QiangLiQ8", 2) {}
554
555 ✗ void MapSinglePanel(int x, int y, int* matrix_x, int* matrix_y) const {
556 ✗ const int column = x + (4 + 4 * (x / 4));
557 ✗ *matrix_x = column;
558 ✗ if ((y >= 15 && y <= 19) || (y >= 5 && y <= 9)) {
559 ✗ const int reverseColumn = x + (4 * (x / 4));
560 ✗ *matrix_x = reverseColumn;
561 }
562 ✗ *matrix_y = y % 5 + (y / 10) * 5;
563 ✗ }
564 };
565
566 class InversedZStripe : public MultiplexMapperBase {
567 public:
568 ✗ InversedZStripe() :
569 ✗ MultiplexMapperBase("InversedZStripe", 2) {}
570
571 ✗ void MapSinglePanel(int x, int y, int* matrix_x, int* matrix_y) const {
572 static const int tile_width = 8;
573 static const int tile_height = 4;
574
575 ✗ const int vert_block_is_odd = ((y / tile_height) % 2);
576 ✗ const int evenOffset[8] = { 7, 5, 3, 1, -1, -3, -5, -7 };
577
578 ✗ if (vert_block_is_odd) {
579 ✗ *matrix_x = x + (x / tile_width) * tile_width;
580 } else {
581 ✗ *matrix_x = x + (x / tile_width) * tile_width + 8 + evenOffset[x % 8];
582 }
583 ✗ *matrix_y = (y % tile_height) + tile_height * (y / (tile_height * 2));
584 ✗ }
585 };
586
587 /*
588 * Vairous P10 1R1G1B Outdoor implementations for 16x16 modules with separate
589 * RGB LEDs, e.g.:
590 * https://www.ledcontrollercard.com/english/p10-outdoor-rgb-led-module-160x160mm-dip.html
591 *
592 */
593 class P10Outdoor1R1G1BMultiplexBase : public MultiplexMapperBase {
594 public:
595 ✗ P10Outdoor1R1G1BMultiplexBase(const char* name) :
596 ✗ MultiplexMapperBase(name, 2) {}
597
598 ✗ void MapSinglePanel(int x, int y, int* matrix_x, int* matrix_y) const {
599 ✗ const int vblock_is_odd = (y / tile_height_) % 2;
600 ✗ const int vblock_is_even = 1 - vblock_is_odd;
601 ✗ const int even_vblock_shift = vblock_is_even * even_vblock_offset_;
602 ✗ const int odd_vblock_shift = vblock_is_odd * odd_vblock_offset_;
603
604 ✗ MapPanel(x, y, matrix_x, matrix_y,
605 vblock_is_even, vblock_is_odd,
606 even_vblock_shift, odd_vblock_shift);
607 ✗ }
608
609 protected:
610 virtual void MapPanel(int x, int y, int* matrix_x, int* matrix_y,
611 int vblock_is_even, int vblock_is_odd,
612 int even_vblock_shift, int odd_vblock_shift) const = 0;
613
614 static const int tile_width_ = 8;
615 static const int tile_height_ = 4;
616 static const int even_vblock_offset_ = 0;
617 static const int odd_vblock_offset_ = 8;
618 };
619
620 class P10Outdoor1R1G1BMultiplexMapper1 : public P10Outdoor1R1G1BMultiplexBase {
621 public:
622 ✗ P10Outdoor1R1G1BMultiplexMapper1() :
623 ✗ P10Outdoor1R1G1BMultiplexBase("P10Outdoor1R1G1-1") {}
624
625 protected:
626 ✗ void MapPanel(int x, int y, int* matrix_x, int* matrix_y,
627 const int vblock_is_even, const int vblock_is_odd,
628 const int even_vblock_shift, const int odd_vblock_shift) const {
629 ✗ *matrix_x = tile_width_ * (1 + vblock_is_even + 2 * (x / tile_width_)) - (x % tile_width_) - 1;
630 ✗ *matrix_y = (y % tile_height_) + tile_height_ * (y / (tile_height_ * 2));
631 ✗ }
632 };
633
634 class P10Outdoor1R1G1BMultiplexMapper2 : public P10Outdoor1R1G1BMultiplexBase {
635 public:
636 ✗ P10Outdoor1R1G1BMultiplexMapper2() :
637 ✗ P10Outdoor1R1G1BMultiplexBase("P10Outdoor1R1G1-2") {}
638
639 protected:
640 ✗ void MapPanel(int x, int y, int* matrix_x, int* matrix_y,
641 const int vblock_is_even, const int vblock_is_odd,
642 const int even_vblock_shift, const int odd_vblock_shift) const {
643 ✗ *matrix_x = vblock_is_even
644 ✗ ? tile_width_ * (1 + 2 * (x / tile_width_)) - (x % tile_width_) - 1
645 ✗ : x + ((x + even_vblock_shift) / tile_width_) * tile_width_ + odd_vblock_shift;
646 ✗ *matrix_y = (y % tile_height_) + tile_height_ * (y / (tile_height_ * 2));
647 ✗ }
648 };
649
650 class P10Outdoor1R1G1BMultiplexMapper3 : public P10Outdoor1R1G1BMultiplexBase {
651 public:
652 ✗ P10Outdoor1R1G1BMultiplexMapper3() :
653 ✗ P10Outdoor1R1G1BMultiplexBase("P10Outdoor1R1G1-3") {}
654
655 protected:
656 ✗ void MapPanel(int x, int y, int* matrix_x, int* matrix_y,
657 const int vblock_is_even, const int vblock_is_odd,
658 const int even_vblock_shift, const int odd_vblock_shift) const {
659 ✗ *matrix_x = vblock_is_odd
660 ✗ ? tile_width_ * (2 + 2 * (x / tile_width_)) - (x % tile_width_) - 1
661 ✗ : x + ((x + even_vblock_shift) / tile_width_) * tile_width_ + odd_vblock_shift;
662 ✗ *matrix_y = (y % tile_height_) + tile_height_ * (y / (tile_height_ * 2));
663 ✗ }
664 };
665
666 class P10CoremanMapper : public MultiplexMapperBase {
667 public:
668 ✗ P10CoremanMapper() :
669 ✗ MultiplexMapperBase("P10CoremanMapper", 4) {}
670
671 ✗ void MapSinglePanel(int x, int y, int* matrix_x, int* matrix_y) const {
672 // Row offset 8,8,8,8,0,0,0,0,8,8,8,8,0,0,0,0
673 ✗ int mulY = (y & 4) > 0 ? 0 : 8;
674
675 // Row offset 9,9,8,8,1,1,0,0,9,9,8,8,1,1,0,0
676 ✗ mulY += (y & 2) > 0 ? 0 : 1;
677 ✗ mulY += (x >> 2) & ~1; // Drop lsb
678
679 ✗ *matrix_x = (mulY << 3) + x % 8;
680 ✗ *matrix_y = (y & 1) + ((y >> 2) & ~1);
681 ✗ }
682 };
683
684 /*
685 * P8 1R1G1B Outdoor P8-5S-V3.2-HX 20x40
686 */
687 class P8Outdoor1R1G1BMultiplexBase : public MultiplexMapperBase {
688 public:
689 ✗ P8Outdoor1R1G1BMultiplexBase(const char* name) :
690 ✗ MultiplexMapperBase(name, 2) {}
691
692 ✗ void MapSinglePanel(int x, int y, int* matrix_x, int* matrix_y) const {
693 ✗ const int vblock_is_odd = (y / tile_height_) % 2;
694 ✗ const int vblock_is_even = 1 - vblock_is_odd;
695 ✗ const int even_vblock_shift = vblock_is_even * even_vblock_offset_;
696 ✗ const int odd_vblock_shift = vblock_is_odd * odd_vblock_offset_;
697
698 ✗ MapPanel(x, y, matrix_x, matrix_y,
699 vblock_is_even, vblock_is_odd,
700 even_vblock_shift, odd_vblock_shift);
701 ✗ }
702
703 protected:
704 virtual void MapPanel(int x, int y, int* matrix_x, int* matrix_y,
705 int vblock_is_even, int vblock_is_odd,
706 int even_vblock_shift, int odd_vblock_shift) const = 0;
707
708 static const int tile_width_ = 8;
709 static const int tile_height_ = 5;
710 static const int even_vblock_offset_ = 0;
711 static const int odd_vblock_offset_ = 8;
712 };
713
714 class P8Outdoor1R1G1BMultiplexMapper : public P8Outdoor1R1G1BMultiplexBase {
715 public:
716 ✗ P8Outdoor1R1G1BMultiplexMapper() :
717 ✗ P8Outdoor1R1G1BMultiplexBase("P8Outdoor1R1G1") {}
718
719 protected:
720 ✗ void MapPanel(int x, int y, int* matrix_x, int* matrix_y,
721 const int vblock_is_even, const int vblock_is_odd,
722 const int even_vblock_shift, const int odd_vblock_shift) const {
723 ✗ *matrix_x = vblock_is_even
724 ✗ ? tile_width_ * (1 + tile_width_ - 2 * (x / tile_width_)) + tile_width_ - (x % tile_width_) - 1
725 ✗ : tile_width_ * (1 + tile_width_ - 2 * (x / tile_width_)) - tile_width_ + (x % tile_width_);
726
727 ✗ *matrix_y = (tile_height_ - y % tile_height_) + tile_height_ * (1 - y / (tile_height_ * 2)) - 1;
728 ✗ }
729 };
730
731 class P10Outdoor32x16HalfScanMapper : public MultiplexMapperBase {
732 public:
733 ✗ P10Outdoor32x16HalfScanMapper() :
734 ✗ MultiplexMapperBase("P10Outdoor32x16HalfScan", 4) {}
735
736 ✗ void MapSinglePanel(int x, int y, int* matrix_x, int* matrix_y) const {
737 ✗ int base = (x / 8) * 32;
738 ✗ bool reverse = (y % 4) / 2 == 0;
739 ✗ int offset = (3 - ((y % 8) / 2)) * 8;
740 ✗ int dx = x % 8;
741
742 ✗ *matrix_y = (y / 8 == 0) ? ((y % 2 == 0) ? 0 : 1) : ((y % 2 == 0) ? 2 : 3);
743 ✗ *matrix_x = base + (reverse ? offset + (7 - dx) : offset + dx);
744 ✗ }
745 };
746
747 class P10Outdoor32x16QuarterScanMapper : public MultiplexMapperBase {
748 public:
749 ✗ P10Outdoor32x16QuarterScanMapper() :
750 ✗ MultiplexMapperBase("P10Outdoor32x16QuarterScanMapper", 4) {}
751 // P10 quarter scan panel, e.g. https://www.ebay.com.au/itm/175517677191
752
753 ✗ void EditColsRows(int* cols, int* rows) const {
754 ✗ panel_rows_ = *rows;
755 ✗ panel_cols_ = *cols;
756
757 ✗ *rows /= panel_stretch_factor_ / 2; // has half stretch factor in y compared to x
758 ✗ *cols *= panel_stretch_factor_;
759 ✗ }
760
761 ✗ void MapSinglePanel(int x, int y, int* matrix_x, int* matrix_y) const {
762 ✗ int cell_starting_point = (x / 8) * 32;
763 ✗ int delta_x = x % 8;
764 ✗ int offset = (3 - (y / 4)) * 8;
765 ✗ *matrix_x = cell_starting_point + delta_x + offset;
766 ✗ *matrix_y = y % 4;
767 ✗ }
768 };
769
770 class P3Outdoor64x64MultiplexMapper : public MultiplexMapperBase {
771 public:
772 ✗ P3Outdoor64x64MultiplexMapper() :
773 ✗ MultiplexMapperBase("P3Outdoor64x64MultiplexMapper", 2) {}
774 // P3 RGB panel 64x64
775 // with pattern [1] [3]
776 // | \ |
777 // [0] [2]
778
779 ✗ void MapSinglePanel(int x, int y, int* matrix_x, int* matrix_y) const {
780 ✗ const bool is_top_stripe = (y % (panel_rows_ / 2)) < panel_rows_ / 4;
781 ✗ *matrix_x = ((x * 2) + (is_top_stripe ? 1 : 0));
782 ✗ *matrix_y = ((y / (panel_rows_ / 2)) * (panel_rows_ / 4) + y % (panel_rows_ / 4));
783 ✗ }
784 };
785
786 class DoubleZMultiplexMapper : public MultiplexMapperBase {
787 public:
788 ✗ DoubleZMultiplexMapper() :
789 ✗ MultiplexMapperBase("DoubleZ", 2) {}
790
791 ✗ void MapSinglePanel(int x, int y, int* matrix_x, int* matrix_y) const {
792 ✗ const int quarter_rows = panel_rows_ / 4;
793 ✗ const int quarter_cols = panel_cols_ / 4;
794
795 ✗ const int y_quarter = y / quarter_rows;
796 ✗ const int x_quarter = x / quarter_cols;
797
798 ✗ const int offset_y = y % quarter_rows;
799 ✗ int offset_x = x % quarter_cols;
800
801 ✗ const bool flip_quarter = (y_quarter == 1 || y_quarter == 3);
802 ✗ if (flip_quarter) {
803 ✗ offset_x = quarter_cols - 1 - offset_x;
804 }
805
806 ✗ const bool is_top_stripe = !((y % (panel_rows_ / 2)) < quarter_rows);
807
808 ✗ const int base_x = 2 * x_quarter * quarter_cols;
809 ✗ const int top_offset = is_top_stripe
810 ✗ ? (quarter_cols - 1 - offset_x)
811 : (quarter_cols + offset_x);
812
813 ✗ *matrix_x = base_x + top_offset;
814 ✗ *matrix_y = (y_quarter / 2) * quarter_rows + offset_y;
815 ✗ }
816 };
817
818 class PiRGBInterleaveHandler : public PanelInterleaveHandler {
819 public:
820 ✗ PiRGBInterleaveHandler(int pw, int ph, int ps, int pi) :
821 ✗ PanelInterleaveHandler(pw, ph, ps, 8) {
822 // buildHandler only passes the indexes handled here
823 ✗ MultiplexMapperBase* base = nullptr;
824 ✗ switch (pi) {
825 ✗ case 1:
826 ✗ base = new StripeMultiplexMapper();
827 ✗ break;
828 ✗ case 2:
829 ✗ base = new CheckeredMultiplexMapper();
830 ✗ break;
831 ✗ case 3:
832 ✗ base = new SpiralMultiplexMapper();
833 ✗ break;
834 ✗ case 4:
835 ✗ base = new ZStripeMultiplexMapper("ZStripe", 0, 8);
836 ✗ break;
837 ✗ case 5:
838 ✗ base = new ZStripeMultiplexMapper("ZnMirrorZStripe", 4, 4);
839 ✗ break;
840 ✗ case 6:
841 ✗ base = new CoremanMapper();
842 ✗ break;
843 ✗ case 7:
844 ✗ base = new Kaler2ScanMapper();
845 ✗ break;
846 ✗ case 8:
847 ✗ base = new ZStripeMultiplexMapper("ZStripeUneven", 8, 0);
848 ✗ break;
849 ✗ case 9:
850 ✗ base = new P10MapperZ();
851 ✗ break;
852 ✗ case 10:
853 ✗ base = new QiangLiQ8();
854 ✗ break;
855 ✗ case 11:
856 ✗ base = new InversedZStripe();
857 ✗ break;
858 ✗ case 12:
859 ✗ base = new P10Outdoor1R1G1BMultiplexMapper1();
860 ✗ break;
861 ✗ case 13:
862 ✗ base = new P10Outdoor1R1G1BMultiplexMapper2();
863 ✗ break;
864 ✗ case 14:
865 ✗ base = new P10Outdoor1R1G1BMultiplexMapper3();
866 ✗ break;
867 ✗ case 15:
868 ✗ base = new P10CoremanMapper();
869 ✗ break;
870 ✗ case 16:
871 ✗ base = new P8Outdoor1R1G1BMultiplexMapper();
872 ✗ break;
873 ✗ case 17:
874 ✗ base = new FlippedStripeMultiplexMapper();
875 ✗ break;
876 ✗ case 18:
877 ✗ base = new P10Outdoor32x16HalfScanMapper();
878 ✗ break;
879 ✗ case 19:
880 ✗ base = new P10Outdoor32x16QuarterScanMapper();
881 ✗ break;
882 ✗ case 20:
883 ✗ base = new P3Outdoor64x64MultiplexMapper();
884 ✗ break;
885 ✗ case 21:
886 ✗ base = new DoubleZMultiplexMapper();
887 ✗ break;
888 }
889 ✗ base->EditColsRows(&pw, &ph);
890 ✗ for (int y = 0; y < m_panelHeight; ++y) {
891 ✗ for (int x = 0; x < m_panelWidth; ++x) {
892 ✗ int idx = (y * m_panelWidth + x);
893
894 ✗ int x1 = x;
895 ✗ int y1 = y;
896 ✗ base->MapVisibleToMatrix(m_panelWidth, m_panelHeight, x1, y1, &x1, &y1);
897 ✗ m_map[idx] = std::make_pair(x1, y1);
898 }
899 }
900 ✗ delete base;
901 ✗ }
902 ✗ virtual ~PiRGBInterleaveHandler() {}
903
904 // the rpi-rgb-led-matrix multiplex mappers this handler can wrap
905 static constexpr int NUM_MAPPERS = 21;
906 };
907
908 15 static PanelInterleaveHandler* buildHandler(const std::string& type, int panelWidth, int panelHeight, int panelScan) {
909
1/2
✓ Branch 0 taken 15 times.
✗ Branch 1 not taken.
15 if ((panelScan * 2) == panelHeight) {
910
2/6
✓ Branch 0 taken 15 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 15 times.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
15 return new NoInterleaveHandler(panelWidth, panelHeight, panelScan, 0);
911 }
912 ✗ if (type == "4z") {
913 ✗ return new ZigZagInterleaveHandler(panelWidth, panelHeight, panelScan, 4);
914 ✗ } else if (type == "8z") {
915 ✗ return new ZigZagInterleaveHandler(panelWidth, panelHeight, panelScan, 8);
916 ✗ } else if (type == "16z") {
917 ✗ return new ZigZagInterleaveHandler(panelWidth, panelHeight, panelScan, 16);
918 ✗ } else if (type == "32z") {
919 ✗ return new ZigZagInterleaveHandler(panelWidth, panelHeight, panelScan, 32);
920 ✗ } else if (type == "40z") {
921 ✗ return new ZigZagInterleaveHandler(panelWidth, panelHeight, panelScan, 40);
922 ✗ } else if (type == "1f") {
923 ✗ return new SimpleInterleaveHandler(panelWidth, panelHeight, panelScan, 1, true);
924 ✗ } else if (type == "2f") {
925 ✗ return new SimpleInterleaveHandler(panelWidth, panelHeight, panelScan, 2, true);
926 ✗ } else if (type == "4f") {
927 ✗ return new SimpleInterleaveHandler(panelWidth, panelHeight, panelScan, 4, true);
928 ✗ } else if (type == "8f") {
929 ✗ return new SimpleInterleaveHandler(panelWidth, panelHeight, panelScan, 8, true);
930 ✗ } else if (type == "16f") {
931 ✗ return new SimpleInterleaveHandler(panelWidth, panelHeight, panelScan, 16, true);
932 ✗ } else if (type == "32f") {
933 ✗ return new SimpleInterleaveHandler(panelWidth, panelHeight, panelScan, 32, true);
934 ✗ } else if (type == "40f") {
935 ✗ return new SimpleInterleaveHandler(panelWidth, panelHeight, panelScan, 40, true);
936 ✗ } else if (type == "64f") {
937 ✗ return new SimpleInterleaveHandler(panelWidth, panelHeight, panelScan, 64, true);
938 ✗ } else if (type == "80f") {
939 ✗ return new SimpleInterleaveHandler(panelWidth, panelHeight, panelScan, 80, true);
940 ✗ } else if (type == "1") {
941 ✗ return new SimpleInterleaveHandler(panelWidth, panelHeight, panelScan, 1, false);
942 ✗ } else if (type == "2") {
943 ✗ return new SimpleInterleaveHandler(panelWidth, panelHeight, panelScan, 2, false);
944 ✗ } else if (type == "4") {
945 ✗ return new SimpleInterleaveHandler(panelWidth, panelHeight, panelScan, 4, false);
946 ✗ } else if (type == "8") {
947 ✗ return new SimpleInterleaveHandler(panelWidth, panelHeight, panelScan, 8, false);
948 ✗ } else if (type == "16") {
949 ✗ return new SimpleInterleaveHandler(panelWidth, panelHeight, panelScan, 16, false);
950 ✗ } else if (type == "32") {
951 ✗ return new SimpleInterleaveHandler(panelWidth, panelHeight, panelScan, 32, false);
952 ✗ } else if (type == "40") {
953 ✗ return new SimpleInterleaveHandler(panelWidth, panelHeight, panelScan, 40, false);
954 ✗ } else if (type == "64") {
955 ✗ return new SimpleInterleaveHandler(panelWidth, panelHeight, panelScan, 64, false);
956 ✗ } else if (type == "80") {
957 ✗ return new SimpleInterleaveHandler(panelWidth, panelHeight, panelScan, 80, false);
958 ✗ } else if (type == "8c") {
959 ✗ return new ZigZagClusterInterleaveHandler(panelWidth, panelHeight, panelScan, 8);
960 ✗ } else if (type == "16c") {
961 ✗ return new ZigZagClusterInterleaveHandler(panelWidth, panelHeight, panelScan, 16);
962 ✗ } else if (type == "8s") {
963 ✗ return new StripeClusterInterleaveHandler(panelWidth, panelHeight, panelScan, 8);
964 ✗ } else if (type == "16s") {
965 ✗ return new StripeClusterInterleaveHandler(panelWidth, panelHeight, panelScan, 16);
966 ✗ } else if (type.starts_with("RPi")) {
967 ✗ std::string idx = type.substr(3);
968 ✗ int idxValue = std::atoi(idx.c_str());
969 ✗ if (idxValue >= 1 && idxValue <= PiRGBInterleaveHandler::NUM_MAPPERS) {
970 ✗ return new PiRGBInterleaveHandler(panelWidth, panelHeight, panelScan, idxValue);
971 }
972 // an unknown index falls back like any other unknown type
973 ✗ }
974
975 ✗ return new NoInterleaveHandler(panelWidth, panelHeight, panelScan, 0);
976 }
977
978 15 PanelInterleaveHandler* PanelInterleaveHandler::createHandler(const std::string& type, int panelWidth, int panelHeight, int panelScan) {
979 // The handlers fold rows by the scan as they are built (a scan of 0 or
980 // below loops forever in mapRow), so this cannot wait for mapFitsFrame().
981 // The UI only offers valid sizes; a hand-edited config may not.
982
4/8
✓ Branch 0 taken 15 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 15 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 15 times.
✗ Branch 5 not taken.
✗ Branch 6 not taken.
✓ Branch 7 taken 15 times.
15 if (panelWidth <= 0 || panelHeight <= 0 || panelScan <= 0 || panelScan > panelHeight) {
983 ✗ return nullptr;
984 }
985 15 PanelInterleaveHandler* handler = buildHandler(type, panelWidth, panelHeight, panelScan);
986
3/6
✓ Branch 0 taken 15 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 15 times.
✗ Branch 4 not taken.
✓ Branch 5 taken 15 times.
15 if (handler && !handler->mapFitsFrame()) {
987 // Unknown interleave types fall back to no interleave at all, which
988 // is only a valid mapping when the panel scans half its rows. On a
989 // panel that scans fewer, the unfolded rows index past the end of the
990 // frame buffer, so refuse the handler and let the caller disable the
991 // output instead of corrupting the heap on the first frame.
992 ✗ delete handler;
993 ✗ return nullptr;
994 }
995 15 return handler;
996 }
997
998 #ifdef STANDALONE
999
1000 int main(int argc, char* argv[]) {
1001 // Example usage of PanelInterleaveHandler
1002 int panelWidth = 32;
1003 int panelHeight = 32;
1004 int panelScan = 8;
1005
1006 PanelInterleaveHandler* handler = PanelInterleaveHandler::createHandler(argv[1], panelWidth, panelHeight, panelScan);
1007
1008 for (int x = 0; x < panelWidth; ++x) {
1009 int xOut = x;
1010 int yOut = 0;
1011 handler->map(xOut, yOut);
1012 printf("%d %d -> %d %d ", x, 0, xOut, yOut);
1013 xOut = x;
1014 yOut = 8;
1015 handler->map(xOut, yOut);
1016 printf("%d %d -> %d %d\n", x, 8, xOut, yOut);
1017 }
1018 int x = 5, y = 3;
1019 handler->map(x, y);
1020
1021 delete handler;
1022 return 0;
1023 }
1024
1025 #endif
1026