multiplex-mappers.cc 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476
  1. // -*- mode: c++; c-basic-offset: 2; indent-tabs-mode: nil; -*-
  2. // Copyright (C) 2017 Henner Zeller <h.zeller@acm.org>
  3. //
  4. // This program is free software; you can redistribute it and/or modify
  5. // it under the terms of the GNU General Public License as published by
  6. // the Free Software Foundation version 2.
  7. //
  8. // This program is distributed in the hope that it will be useful,
  9. // but WITHOUT ANY WARRANTY; without even the implied warranty of
  10. // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  11. // GNU General Public License for more details.
  12. //
  13. // You should have received a copy of the GNU General Public License
  14. // along with this program. If not, see <http://gnu.org/licenses/gpl-2.0.txt>
  15. #include "multiplex-mappers-internal.h"
  16. namespace rgb_matrix {
  17. namespace internal {
  18. // A Pixel Mapper maps physical pixels locations to the internal logical
  19. // mapping in a panel or panel-assembly, which depends on the wiring.
  20. class MultiplexMapperBase : public MultiplexMapper {
  21. public:
  22. MultiplexMapperBase(const char *name, int stretch_factor)
  23. : name_(name), panel_stretch_factor_(stretch_factor) {}
  24. // This method is const, but we sneakily remember the original size
  25. // of the panels so that we can more easily quantize things.
  26. // So technically, we're stateful, but let's pretend we're not changing
  27. // state. In the context this is used, it is never accessed in multiple
  28. // threads.
  29. virtual void EditColsRows(int *cols, int *rows) const {
  30. panel_rows_ = *rows;
  31. panel_cols_ = *cols;
  32. *rows /= panel_stretch_factor_;
  33. *cols *= panel_stretch_factor_;
  34. }
  35. virtual bool GetSizeMapping(int matrix_width, int matrix_height,
  36. int *visible_width, int *visible_height) const {
  37. // Matrix width has been altered. Alter it back.
  38. *visible_width = matrix_width / panel_stretch_factor_;
  39. *visible_height = matrix_height * panel_stretch_factor_;
  40. return true;
  41. }
  42. virtual const char *GetName() const { return name_; }
  43. // The MapVisibleToMatrix() as required by PanelMatrix here breaks it
  44. // down to the individual panel, so that derived classes only need to
  45. // implement MapSinglePanel().
  46. virtual void MapVisibleToMatrix(int matrix_width, int matrix_height,
  47. int visible_x, int visible_y,
  48. int *matrix_x, int *matrix_y) const {
  49. const int chained_panel = visible_x / panel_cols_;
  50. const int parallel_panel = visible_y / panel_rows_;
  51. const int within_panel_x = visible_x % panel_cols_;
  52. const int within_panel_y = visible_y % panel_rows_;
  53. int new_x, new_y;
  54. MapSinglePanel(within_panel_x, within_panel_y, &new_x, &new_y);
  55. *matrix_x = chained_panel * panel_stretch_factor_*panel_cols_ + new_x;
  56. *matrix_y = parallel_panel * panel_rows_/panel_stretch_factor_ + new_y;
  57. }
  58. // Map the coordinates for a single panel. This is to be overridden in
  59. // derived classes.
  60. // Input parameter is the visible position on the matrix, and this method
  61. // should return the internal multiplexed position.
  62. virtual void MapSinglePanel(int visible_x, int visible_y,
  63. int *matrix_x, int *matrix_y) const = 0;
  64. protected:
  65. const char *const name_;
  66. const int panel_stretch_factor_;
  67. mutable int panel_cols_;
  68. mutable int panel_rows_;
  69. };
  70. /* ========================================================================
  71. * Multiplexer implementations.
  72. *
  73. * Extend MultiplexMapperBase and implement MapSinglePanel. You only have
  74. * to worry about the mapping within a single panel, the overall panel
  75. * construction with chains and parallel is already taken care of.
  76. *
  77. * Don't forget to register the new multiplexer sin CreateMultiplexMapperList()
  78. * below. After that, the new mapper is available in the --led-multiplexing
  79. * option.
  80. */
  81. class StripeMultiplexMapper : public MultiplexMapperBase {
  82. public:
  83. StripeMultiplexMapper() : MultiplexMapperBase("Stripe", 2) {}
  84. void MapSinglePanel(int x, int y, int *matrix_x, int *matrix_y) const {
  85. const bool is_top_stripe = (y % (panel_rows_/2)) < panel_rows_/4;
  86. *matrix_x = is_top_stripe ? x + panel_cols_ : x;
  87. *matrix_y = ((y / (panel_rows_/2)) * (panel_rows_/4)
  88. + y % (panel_rows_/4));
  89. }
  90. };
  91. class FlippedStripeMultiplexMapper : public MultiplexMapperBase {
  92. public:
  93. FlippedStripeMultiplexMapper() : MultiplexMapperBase("FlippedStripe", 2) {}
  94. void MapSinglePanel(int x, int y, int *matrix_x, int *matrix_y) const {
  95. const bool is_top_stripe = (y % (panel_rows_/2)) >= panel_rows_/4;
  96. *matrix_x = is_top_stripe ? x + panel_cols_ : x;
  97. *matrix_y = ((y / (panel_rows_/2)) * (panel_rows_/4)
  98. + y % (panel_rows_/4));
  99. }
  100. };
  101. class CheckeredMultiplexMapper : public MultiplexMapperBase {
  102. public:
  103. CheckeredMultiplexMapper() : MultiplexMapperBase("Checkered", 2) {}
  104. void MapSinglePanel(int x, int y, int *matrix_x, int *matrix_y) const {
  105. const bool is_top_check = (y % (panel_rows_/2)) < panel_rows_/4;
  106. const bool is_left_check = (x < panel_cols_/2);
  107. if (is_top_check) {
  108. *matrix_x = is_left_check ? x+panel_cols_/2 : x+panel_cols_;
  109. } else {
  110. *matrix_x = is_left_check ? x : x + panel_cols_/2;
  111. }
  112. *matrix_y = ((y / (panel_rows_/2)) * (panel_rows_/4)
  113. + y % (panel_rows_/4));
  114. }
  115. };
  116. class SpiralMultiplexMapper : public MultiplexMapperBase {
  117. public:
  118. SpiralMultiplexMapper() : MultiplexMapperBase("Spiral", 2) {}
  119. void MapSinglePanel(int x, int y, int *matrix_x, int *matrix_y) const {
  120. const bool is_top_stripe = (y % (panel_rows_/2)) < panel_rows_/4;
  121. const int panel_quarter = panel_cols_/4;
  122. const int quarter = x / panel_quarter;
  123. const int offset = x % panel_quarter;
  124. *matrix_x = ((2*quarter*panel_quarter)
  125. + (is_top_stripe
  126. ? panel_quarter - 1 - offset
  127. : panel_quarter + offset));
  128. *matrix_y = ((y / (panel_rows_/2)) * (panel_rows_/4)
  129. + y % (panel_rows_/4));
  130. }
  131. };
  132. class ZStripeMultiplexMapper : public MultiplexMapperBase {
  133. public:
  134. ZStripeMultiplexMapper(const char *name, int even_vblock_offset, int odd_vblock_offset)
  135. : MultiplexMapperBase(name, 2),
  136. even_vblock_offset_(even_vblock_offset),
  137. odd_vblock_offset_(odd_vblock_offset) {}
  138. void MapSinglePanel(int x, int y, int *matrix_x, int *matrix_y) const {
  139. static const int tile_width = 8;
  140. static const int tile_height = 4;
  141. const int vert_block_is_odd = ((y / tile_height) % 2);
  142. const int even_vblock_shift = (1 - vert_block_is_odd) * even_vblock_offset_;
  143. const int odd_vblock_shitf = vert_block_is_odd * odd_vblock_offset_;
  144. *matrix_x = x + ((x + even_vblock_shift) / tile_width) * tile_width + odd_vblock_shitf;
  145. *matrix_y = (y % tile_height) + tile_height * (y / (tile_height * 2));
  146. }
  147. private:
  148. const int even_vblock_offset_;
  149. const int odd_vblock_offset_;
  150. };
  151. class CoremanMapper : public MultiplexMapperBase {
  152. public:
  153. CoremanMapper() : MultiplexMapperBase("coreman", 2) {}
  154. void MapSinglePanel(int x, int y, int *matrix_x, int *matrix_y) const {
  155. const bool is_left_check = (x < panel_cols_/2);
  156. if ((y <= 7) || ((y >= 16) && (y <= 23))){
  157. *matrix_x = ((x / (panel_cols_/2)) * panel_cols_) + (x % (panel_cols_/2));
  158. if ((y & (panel_rows_/4)) == 0) {
  159. *matrix_y = (y / (panel_rows_/2)) * (panel_rows_/4) + (y % (panel_rows_/4));
  160. }
  161. } else {
  162. *matrix_x = is_left_check ? x + panel_cols_/2 : x + panel_cols_;
  163. *matrix_y = (y / (panel_rows_/2)) * (panel_rows_/4) + y % (panel_rows_/4);
  164. }
  165. }
  166. };
  167. class Kaler2ScanMapper : public MultiplexMapperBase {
  168. public:
  169. Kaler2ScanMapper() : MultiplexMapperBase("Kaler2Scan", 4) {}
  170. void MapSinglePanel(int x, int y, int *matrix_x, int *matrix_y) const {
  171. // Now we have a 128x4 matrix
  172. int offset = ((y%4)/2) == 0 ? -1 : 1;// Add o substract
  173. int deltaOffset = offset < 0 ? 7:8;
  174. int deltaColumn = ((y%8)/4)== 0 ? 64 : 0;
  175. *matrix_y = (y%2+(y/8)*2);
  176. *matrix_x = deltaColumn + (16 * (x/8)) + deltaOffset + ((x%8) * offset);
  177. }
  178. };
  179. class P10MapperZ : public MultiplexMapperBase {
  180. public:
  181. P10MapperZ() : MultiplexMapperBase("P10-128x4-Z", 4) {}
  182. // 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
  183. // with --led-row-addr-type=2 flag
  184. void MapSinglePanel(int x, int y, int *matrix_x, int *matrix_y) const {
  185. int yComp = 0;
  186. if (y == 0 || y == 1 || y == 8 || y == 9) {
  187. yComp = 127;
  188. }
  189. else if (y == 2 || y == 3 || y == 10 || y == 11) {
  190. yComp = 112;
  191. }
  192. else if (y == 4 || y == 5 || y == 12 || y == 13) {
  193. yComp = 111;
  194. }
  195. else if (y == 6 || y == 7 || y == 14 || y == 15) {
  196. yComp = 96;
  197. }
  198. if (y == 0 || y == 1 || y == 4 || y == 5 ||
  199. y == 8 || y == 9 || y == 12 || y == 13) {
  200. *matrix_x = yComp - x;
  201. *matrix_x -= (24 * ((int)(x / 8)));
  202. }
  203. else {
  204. *matrix_x = yComp + x;
  205. *matrix_x -= (40 * ((int)(x / 8)));
  206. }
  207. if (y == 0 || y == 2 || y == 4 || y == 6) {
  208. *matrix_y = 3;
  209. }
  210. else if (y == 1 || y == 3 || y == 5 || y == 7) {
  211. *matrix_y = 2;
  212. }
  213. else if (y == 8 || y == 10 || y == 12 || y == 14) {
  214. *matrix_y = 1;
  215. }
  216. else if (y == 9 || y == 11 || y == 13 || y == 15) {
  217. *matrix_y = 0;
  218. }
  219. }
  220. };
  221. class QiangLiQ8 : public MultiplexMapperBase {
  222. public:
  223. QiangLiQ8() : MultiplexMapperBase("QiangLiQ8", 2) {}
  224. void MapSinglePanel(int x, int y, int *matrix_x, int *matrix_y) const {
  225. const int column = x + (4+ 4*(x/4));
  226. *matrix_x = column;
  227. if ((y >= 15 && y <=19) || (y >= 5 && y <= 9)) {
  228. const int reverseColumn = x + (4*(x/4));
  229. *matrix_x = reverseColumn;
  230. }
  231. *matrix_y = y % 5 + (y/10) *5;
  232. }
  233. };
  234. class InversedZStripe : public MultiplexMapperBase {
  235. public:
  236. InversedZStripe() : MultiplexMapperBase("InversedZStripe", 2) {}
  237. void MapSinglePanel(int x, int y, int *matrix_x, int *matrix_y) const {
  238. static const int tile_width = 8;
  239. static const int tile_height = 4;
  240. const int vert_block_is_odd = ((y / tile_height) % 2);
  241. const int evenOffset[8] = {7, 5, 3, 1, -1, -3, -5, -7};
  242. if (vert_block_is_odd) {
  243. *matrix_x = x + (x / tile_width) * tile_width;
  244. } else {
  245. *matrix_x = x + (x / tile_width) * tile_width + 8 + evenOffset[x % 8];
  246. }
  247. *matrix_y = (y % tile_height) + tile_height * (y / (tile_height * 2));
  248. }
  249. };
  250. /*
  251. * Vairous P10 1R1G1B Outdoor implementations for 16x16 modules with separate
  252. * RGB LEDs, e.g.:
  253. * https://www.ledcontrollercard.com/english/p10-outdoor-rgb-led-module-160x160mm-dip.html
  254. *
  255. */
  256. class P10Outdoor1R1G1BMultiplexBase : public MultiplexMapperBase {
  257. public:
  258. P10Outdoor1R1G1BMultiplexBase(const char *name)
  259. : MultiplexMapperBase(name, 2) {}
  260. void MapSinglePanel(int x, int y, int *matrix_x, int *matrix_y) const {
  261. const int vblock_is_odd = (y / tile_height_) % 2;
  262. const int vblock_is_even = 1 - vblock_is_odd;
  263. const int even_vblock_shift = vblock_is_even * even_vblock_offset_;
  264. const int odd_vblock_shift = vblock_is_odd * odd_vblock_offset_;
  265. MapPanel(x, y, matrix_x, matrix_y,
  266. vblock_is_even, vblock_is_odd,
  267. even_vblock_shift, odd_vblock_shift);
  268. }
  269. protected:
  270. virtual void MapPanel(int x, int y, int *matrix_x, int *matrix_y,
  271. int vblock_is_even, int vblock_is_odd,
  272. int even_vblock_shift, int odd_vblock_shift) const = 0;
  273. static const int tile_width_ = 8;
  274. static const int tile_height_ = 4;
  275. static const int even_vblock_offset_ = 0;
  276. static const int odd_vblock_offset_ = 8;
  277. };
  278. class P10Outdoor1R1G1BMultiplexMapper1 : public P10Outdoor1R1G1BMultiplexBase {
  279. public:
  280. P10Outdoor1R1G1BMultiplexMapper1()
  281. : P10Outdoor1R1G1BMultiplexBase("P10Outdoor1R1G1-1") {}
  282. protected:
  283. void MapPanel(int x, int y, int *matrix_x, int *matrix_y,
  284. const int vblock_is_even, const int vblock_is_odd,
  285. const int even_vblock_shift, const int odd_vblock_shift) const {
  286. *matrix_x = tile_width_ * (1 + vblock_is_even + 2 * (x / tile_width_))
  287. - (x % tile_width_) - 1;
  288. *matrix_y = (y % tile_height_) + tile_height_ * (y / (tile_height_ * 2));
  289. }
  290. };
  291. class P10Outdoor1R1G1BMultiplexMapper2 : public P10Outdoor1R1G1BMultiplexBase {
  292. public:
  293. P10Outdoor1R1G1BMultiplexMapper2()
  294. : P10Outdoor1R1G1BMultiplexBase("P10Outdoor1R1G1-2") {}
  295. protected:
  296. void MapPanel(int x, int y, int *matrix_x, int *matrix_y,
  297. const int vblock_is_even, const int vblock_is_odd,
  298. const int even_vblock_shift, const int odd_vblock_shift) const {
  299. *matrix_x = vblock_is_even
  300. ? tile_width_ * (1 + 2 * (x / tile_width_)) - (x % tile_width_) - 1
  301. : x + ((x + even_vblock_shift) / tile_width_) * tile_width_ + odd_vblock_shift;
  302. *matrix_y = (y % tile_height_) + tile_height_ * (y / (tile_height_ * 2));
  303. }
  304. };
  305. class P10Outdoor1R1G1BMultiplexMapper3 : public P10Outdoor1R1G1BMultiplexBase {
  306. public:
  307. P10Outdoor1R1G1BMultiplexMapper3()
  308. : P10Outdoor1R1G1BMultiplexBase("P10Outdoor1R1G1-3") {}
  309. protected:
  310. void MapPanel(int x, int y, int *matrix_x, int *matrix_y,
  311. const int vblock_is_even, const int vblock_is_odd,
  312. const int even_vblock_shift, const int odd_vblock_shift) const {
  313. *matrix_x = vblock_is_odd
  314. ? tile_width_ * (2 + 2 * (x / tile_width_)) - (x % tile_width_) - 1
  315. : x + ((x + even_vblock_shift) / tile_width_) * tile_width_ + odd_vblock_shift;
  316. *matrix_y = (y % tile_height_) + tile_height_ * (y / (tile_height_ * 2));
  317. }
  318. };
  319. class P10CoremanMapper : public MultiplexMapperBase {
  320. public:
  321. P10CoremanMapper() : MultiplexMapperBase("P10CoremanMapper", 4) {}
  322. void MapSinglePanel(int x, int y, int *matrix_x, int *matrix_y) const {
  323. //Row offset 8,8,8,8,0,0,0,0,8,8,8,8,0,0,0,0
  324. int mulY = (y & 4) > 0 ? 0 : 8;
  325. //Row offset 9,9,8,8,1,1,0,0,9,9,8,8,1,1,0,0
  326. mulY += (y & 2) > 0 ? 0 : 1;
  327. mulY += (x >> 2) & ~1; //Drop lsb
  328. *matrix_x = (mulY << 3) + x % 8;
  329. *matrix_y = (y & 1) + ((y >> 2) & ~1);
  330. }
  331. };
  332. /*
  333. * P8 1R1G1B Outdoor P8-5S-V3.2-HX 20x40
  334. */
  335. class P8Outdoor1R1G1BMultiplexBase : public MultiplexMapperBase {
  336. public:
  337. P8Outdoor1R1G1BMultiplexBase(const char *name)
  338. : MultiplexMapperBase(name, 2) {}
  339. void MapSinglePanel(int x, int y, int *matrix_x, int *matrix_y) const {
  340. const int vblock_is_odd = (y / tile_height_) % 2;
  341. const int vblock_is_even = 1 - vblock_is_odd;
  342. const int even_vblock_shift = vblock_is_even * even_vblock_offset_;
  343. const int odd_vblock_shift = vblock_is_odd * odd_vblock_offset_;
  344. MapPanel(x, y, matrix_x, matrix_y,
  345. vblock_is_even, vblock_is_odd,
  346. even_vblock_shift, odd_vblock_shift);
  347. }
  348. protected:
  349. virtual void MapPanel(int x, int y, int *matrix_x, int *matrix_y,
  350. int vblock_is_even, int vblock_is_odd,
  351. int even_vblock_shift, int odd_vblock_shift) const = 0;
  352. static const int tile_width_ = 8;
  353. static const int tile_height_ = 5;
  354. static const int even_vblock_offset_ = 0;
  355. static const int odd_vblock_offset_ = 8;
  356. };
  357. class P8Outdoor1R1G1BMultiplexMapper : public P8Outdoor1R1G1BMultiplexBase {
  358. public:
  359. P8Outdoor1R1G1BMultiplexMapper()
  360. : P8Outdoor1R1G1BMultiplexBase("P8Outdoor1R1G1") {}
  361. protected:
  362. void MapPanel(int x, int y, int *matrix_x, int *matrix_y,
  363. const int vblock_is_even, const int vblock_is_odd,
  364. const int even_vblock_shift, const int odd_vblock_shift) const {
  365. *matrix_x = vblock_is_even
  366. ? tile_width_ * (1 + tile_width_ - 2 * (x / tile_width_)) + tile_width_ - (x % tile_width_) - 1
  367. : tile_width_ * (1 + tile_width_ - 2 * (x / tile_width_)) - tile_width_ + (x % tile_width_);
  368. *matrix_y = (tile_height_ - y % tile_height_) + tile_height_ * (1 - y / (tile_height_ * 2)) -1;
  369. }
  370. };
  371. /*
  372. * Here is where the registration happens.
  373. * If you add an instance of the mapper here, it will automatically be
  374. * made available in the --led-multiplexing commandline option.
  375. */
  376. static MuxMapperList *CreateMultiplexMapperList() {
  377. MuxMapperList *result = new MuxMapperList();
  378. // Here, register all multiplex mappers from above.
  379. result->push_back(new StripeMultiplexMapper());
  380. result->push_back(new CheckeredMultiplexMapper());
  381. result->push_back(new SpiralMultiplexMapper());
  382. result->push_back(new ZStripeMultiplexMapper("ZStripe", 0, 8));
  383. result->push_back(new ZStripeMultiplexMapper("ZnMirrorZStripe", 4, 4));
  384. result->push_back(new CoremanMapper());
  385. result->push_back(new Kaler2ScanMapper());
  386. result->push_back(new ZStripeMultiplexMapper("ZStripeUneven", 8, 0));
  387. result->push_back(new P10MapperZ());
  388. result->push_back(new QiangLiQ8());
  389. result->push_back(new InversedZStripe());
  390. result->push_back(new P10Outdoor1R1G1BMultiplexMapper1());
  391. result->push_back(new P10Outdoor1R1G1BMultiplexMapper2());
  392. result->push_back(new P10Outdoor1R1G1BMultiplexMapper3());
  393. result->push_back(new P10CoremanMapper());
  394. result->push_back(new P8Outdoor1R1G1BMultiplexMapper());
  395. result->push_back(new FlippedStripeMultiplexMapper());
  396. return result;
  397. }
  398. const MuxMapperList &GetRegisteredMultiplexMappers() {
  399. static const MuxMapperList *all_mappers = CreateMultiplexMapperList();
  400. return *all_mappers;
  401. }
  402. } // namespace internal
  403. } // namespace rgb_matrix