pixel-mapper.cc 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338
  1. // -*- mode: c++; c-basic-offset: 2; indent-tabs-mode: nil; -*-
  2. // Copyright (C) 2018 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 "pixel-mapper.h"
  16. #include <assert.h>
  17. #include <stdio.h>
  18. #include <stdlib.h>
  19. #include <string.h>
  20. #include <map>
  21. namespace rgb_matrix {
  22. namespace {
  23. class RotatePixelMapper : public PixelMapper {
  24. public:
  25. RotatePixelMapper() : angle_(0) {}
  26. virtual const char *GetName() const { return "Rotate"; }
  27. virtual bool SetParameters(int chain, int parallel, const char *param) {
  28. if (param == NULL || strlen(param) == 0) {
  29. angle_ = 0;
  30. return true;
  31. }
  32. char *errpos;
  33. const int angle = strtol(param, &errpos, 10);
  34. if (*errpos != '\0') {
  35. fprintf(stderr, "Invalid rotate parameter '%s'\n", param);
  36. return false;
  37. }
  38. if (angle % 90 != 0) {
  39. fprintf(stderr, "Rotation needs to be multiple of 90 degrees\n");
  40. return false;
  41. }
  42. angle_ = (angle + 360) % 360;
  43. return true;
  44. }
  45. virtual bool GetSizeMapping(int matrix_width, int matrix_height,
  46. int *visible_width, int *visible_height)
  47. const {
  48. if (angle_ % 180 == 0) {
  49. *visible_width = matrix_width;
  50. *visible_height = matrix_height;
  51. } else {
  52. *visible_width = matrix_height;
  53. *visible_height = matrix_width;
  54. }
  55. return true;
  56. }
  57. virtual void MapVisibleToMatrix(int matrix_width, int matrix_height,
  58. int x, int y,
  59. int *matrix_x, int *matrix_y) const {
  60. switch (angle_) {
  61. case 0:
  62. *matrix_x = x;
  63. *matrix_y = y;
  64. break;
  65. case 90:
  66. *matrix_x = matrix_width - y - 1;
  67. *matrix_y = x;
  68. break;
  69. case 180:
  70. *matrix_x = matrix_width - x - 1;
  71. *matrix_y = matrix_height - y - 1;
  72. break;
  73. case 270:
  74. *matrix_x = y;
  75. *matrix_y = matrix_height - x - 1;
  76. break;
  77. }
  78. }
  79. private:
  80. int angle_;
  81. };
  82. class MirrorPixelMapper : public PixelMapper {
  83. public:
  84. MirrorPixelMapper() : horizontal_(true) {}
  85. virtual const char *GetName() const { return "Mirror"; }
  86. virtual bool SetParameters(int chain, int parallel, const char *param) {
  87. if (param == NULL || strlen(param) == 0) {
  88. horizontal_ = true;
  89. return true;
  90. }
  91. if (strlen(param) != 1) {
  92. fprintf(stderr, "Mirror parameter should be a single "
  93. "character:'V' or 'H'\n");
  94. }
  95. switch (*param) {
  96. case 'V':
  97. case 'v':
  98. horizontal_ = false;
  99. break;
  100. case 'H':
  101. case 'h':
  102. horizontal_ = true;
  103. break;
  104. default:
  105. fprintf(stderr, "Mirror parameter should be either 'V' or 'H'\n");
  106. return false;
  107. }
  108. return true;
  109. }
  110. virtual bool GetSizeMapping(int matrix_width, int matrix_height,
  111. int *visible_width, int *visible_height)
  112. const {
  113. *visible_height = matrix_height;
  114. *visible_width = matrix_width;
  115. return true;
  116. }
  117. virtual void MapVisibleToMatrix(int matrix_width, int matrix_height,
  118. int x, int y,
  119. int *matrix_x, int *matrix_y) const {
  120. if (horizontal_) {
  121. *matrix_x = matrix_width - 1 - x;
  122. *matrix_y = y;
  123. } else {
  124. *matrix_x = x;
  125. *matrix_y = matrix_height - 1 - y;
  126. }
  127. }
  128. private:
  129. bool horizontal_;
  130. };
  131. // If we take a long chain of panels and arrange them in a U-shape, so
  132. // that after half the panels we bend around and continue below. This way
  133. // we have a panel that has double the height but only uses one chain.
  134. // A single chain display with four 32x32 panels can then be arranged in this
  135. // 64x64 display:
  136. // [<][<][<][<] }- Raspbery Pi connector
  137. //
  138. // can be arranged in this U-shape
  139. // [<][<] }----- Raspberry Pi connector
  140. // [>][>]
  141. //
  142. // This works for more than one chain as well. Here an arrangement with
  143. // two chains with 8 panels each
  144. // [<][<][<][<] }-- Pi connector #1
  145. // [>][>][>][>]
  146. // [<][<][<][<] }--- Pi connector #2
  147. // [>][>][>][>]
  148. class UArrangementMapper : public PixelMapper {
  149. public:
  150. UArrangementMapper() : parallel_(1) {}
  151. virtual const char *GetName() const { return "U-mapper"; }
  152. virtual bool SetParameters(int chain, int parallel, const char *param) {
  153. if (chain < 2) { // technically, a chain of 2 would work, but somewhat pointless
  154. fprintf(stderr, "U-mapper: need at least --led-chain=4 for useful folding\n");
  155. return false;
  156. }
  157. if (chain % 2 != 0) {
  158. fprintf(stderr, "U-mapper: Chain (--led-chain) needs to be divisible by two\n");
  159. return false;
  160. }
  161. parallel_ = parallel;
  162. return true;
  163. }
  164. virtual bool GetSizeMapping(int matrix_width, int matrix_height,
  165. int *visible_width, int *visible_height)
  166. const {
  167. *visible_width = (matrix_width / 64) * 32; // Div at 32px boundary
  168. *visible_height = 2 * matrix_height;
  169. if (matrix_height % parallel_ != 0) {
  170. fprintf(stderr, "%s For parallel=%d we would expect the height=%d "
  171. "to be divisible by %d ??\n",
  172. GetName(), parallel_, matrix_height, parallel_);
  173. return false;
  174. }
  175. return true;
  176. }
  177. virtual void MapVisibleToMatrix(int matrix_width, int matrix_height,
  178. int x, int y,
  179. int *matrix_x, int *matrix_y) const {
  180. const int panel_height = matrix_height / parallel_;
  181. const int visible_width = (matrix_width / 64) * 32;
  182. const int slab_height = 2 * panel_height; // one folded u-shape
  183. const int base_y = (y / slab_height) * panel_height;
  184. y %= slab_height;
  185. if (y < panel_height) {
  186. x += matrix_width / 2;
  187. } else {
  188. x = visible_width - x - 1;
  189. y = slab_height - y - 1;
  190. }
  191. *matrix_x = x;
  192. *matrix_y = base_y + y;
  193. }
  194. private:
  195. int parallel_;
  196. };
  197. class VerticalMapper : public PixelMapper {
  198. public:
  199. VerticalMapper() {}
  200. virtual const char *GetName() const { return "V-mapper"; }
  201. virtual bool SetParameters(int chain, int parallel, const char *param) {
  202. chain_ = chain;
  203. parallel_ = parallel;
  204. // optional argument :Z allow for every other panel to be flipped
  205. // upside down so that cabling can be shorter:
  206. // [ O < I ] without Z [ O < I ]
  207. // ,---^ <---- ^
  208. // [ O < I ] [ I > O ]
  209. // ,---^ with Z ^
  210. // [ O < I ] ---> [ O < I ]
  211. z_ = (param && strcasecmp(param, "Z") == 0);
  212. return true;
  213. }
  214. virtual bool GetSizeMapping(int matrix_width, int matrix_height,
  215. int *visible_width, int *visible_height)
  216. const {
  217. *visible_width = matrix_width * parallel_ / chain_;
  218. *visible_height = matrix_height * chain_ / parallel_;
  219. #if 0
  220. fprintf(stderr, "%s: C:%d P:%d. Turning W:%d H:%d Physical "
  221. "into W:%d H:%d Virtual\n",
  222. GetName(), chain_, parallel_,
  223. *visible_width, *visible_height, matrix_width, matrix_height);
  224. #endif
  225. return true;
  226. }
  227. virtual void MapVisibleToMatrix(int matrix_width, int matrix_height,
  228. int x, int y,
  229. int *matrix_x, int *matrix_y) const {
  230. const int panel_width = matrix_width / chain_;
  231. const int panel_height = matrix_height / parallel_;
  232. const int x_panel_start = y / panel_height * panel_width;
  233. const int y_panel_start = x / panel_width * panel_height;
  234. const int x_within_panel = x % panel_width;
  235. const int y_within_panel = y % panel_height;
  236. const bool needs_flipping = z_ && (y / panel_height) % 2 == 1;
  237. *matrix_x = x_panel_start + (needs_flipping
  238. ? panel_width - 1 - x_within_panel
  239. : x_within_panel);
  240. *matrix_y = y_panel_start + (needs_flipping
  241. ? panel_height - 1 - y_within_panel
  242. : y_within_panel);
  243. }
  244. private:
  245. bool z_;
  246. int chain_;
  247. int parallel_;
  248. };
  249. typedef std::map<std::string, PixelMapper*> MapperByName;
  250. static void RegisterPixelMapperInternal(MapperByName *registry,
  251. PixelMapper *mapper) {
  252. assert(mapper != NULL);
  253. std::string lower_name;
  254. for (const char *n = mapper->GetName(); *n; n++)
  255. lower_name.append(1, tolower(*n));
  256. (*registry)[lower_name] = mapper;
  257. }
  258. static MapperByName *CreateMapperMap() {
  259. MapperByName *result = new MapperByName();
  260. // Register all the default PixelMappers here.
  261. RegisterPixelMapperInternal(result, new RotatePixelMapper());
  262. RegisterPixelMapperInternal(result, new UArrangementMapper());
  263. RegisterPixelMapperInternal(result, new VerticalMapper());
  264. RegisterPixelMapperInternal(result, new MirrorPixelMapper());
  265. return result;
  266. }
  267. static MapperByName *GetMapperMap() {
  268. static MapperByName *singleton_instance = CreateMapperMap();
  269. return singleton_instance;
  270. }
  271. } // anonymous namespace
  272. // Public API.
  273. void RegisterPixelMapper(PixelMapper *mapper) {
  274. RegisterPixelMapperInternal(GetMapperMap(), mapper);
  275. }
  276. std::vector<std::string> GetAvailablePixelMappers() {
  277. std::vector<std::string> result;
  278. MapperByName *m = GetMapperMap();
  279. for (MapperByName::const_iterator it = m->begin(); it != m->end(); ++it) {
  280. result.push_back(it->second->GetName());
  281. }
  282. return result;
  283. }
  284. const PixelMapper *FindPixelMapper(const char *name,
  285. int chain, int parallel,
  286. const char *parameter) {
  287. std::string lower_name;
  288. for (const char *n = name; *n; n++) lower_name.append(1, tolower(*n));
  289. MapperByName::const_iterator found = GetMapperMap()->find(lower_name);
  290. if (found == GetMapperMap()->end()) {
  291. fprintf(stderr, "%s: no such mapper\n", name);
  292. return NULL;
  293. }
  294. PixelMapper *mapper = found->second;
  295. if (mapper == NULL) return NULL; // should not happen.
  296. if (!mapper->SetParameters(chain, parallel, parameter))
  297. return NULL; // Got parameter, but couldn't deal with it.
  298. return mapper;
  299. }
  300. } // namespace rgb_matrix