/ src / include / piomatter / matrixmap.h
matrixmap.h
  1  #pragma once
  2  
  3  #include <stdexcept>
  4  #include <vector>
  5  
  6  namespace piomatter {
  7  
  8  using matrix_map = std::vector<int>;
  9  
 10  enum orientation { normal, r180, ccw, cw };
 11  
 12  int orientation_normal(int width, int height, int x, int y) {
 13      return x + width * y;
 14  }
 15  
 16  int orientation_r180(int width, int height, int x, int y) {
 17      x = width - x - 1;
 18      y = height - y - 1;
 19      return orientation_normal(width, height, x, y);
 20  }
 21  
 22  int orientation_ccw(int width, int height, int x, int y) {
 23      return orientation_normal(height, width, y, width - x - 1);
 24  }
 25  
 26  int orientation_cw(int width, int height, int x, int y) {
 27      return orientation_normal(height, width, y - height - 1, x);
 28  }
 29  
 30  namespace {
 31  template <typename Cb>
 32  void submap(std::vector<int> &result, int width, int height, int start_x,
 33              int dx, int count_x_in, int start_y, int dy, int count_y,
 34              int half_panel_height, const Cb &cb) {
 35  
 36      for (int y = start_y; count_y; count_y -= 2, y += dy) {
 37          for (int x = start_x, count_x = count_x_in; count_x--; x += dx) {
 38              result.push_back(cb(width, height, x, y));
 39              result.push_back(cb(width, height, x, y + dy * half_panel_height));
 40          }
 41      }
 42  }
 43  } // namespace
 44  
 45  template <typename Cb>
 46  matrix_map make_matrixmap(size_t width, size_t height, size_t n_addr_lines,
 47                            bool serpentine, const Cb &cb) {
 48  
 49      size_t panel_height = 2 << n_addr_lines;
 50      if (height % panel_height != 0) {
 51          throw std::range_error("Height does not evenly divide panel height");
 52      }
 53  
 54      size_t half_panel_height = 1u << n_addr_lines;
 55      size_t v_panels = height / panel_height;
 56      size_t pixels_across = width * v_panels;
 57      matrix_map result;
 58      result.reserve(width * height);
 59  
 60      for (size_t i = 0; i < half_panel_height; i++) {
 61          for (size_t j = 0; j < pixels_across; j++) {
 62              int panel_no = j / width;
 63              int panel_idx = j % width;
 64              int x, y0, y1;
 65  
 66              if (serpentine && panel_no % 2) {
 67                  x = width - panel_idx - 1;
 68                  y0 = (panel_no + 1) * panel_height - i - 1;
 69                  y1 = (panel_no + 1) * panel_height - i - half_panel_height - 1;
 70              } else {
 71                  x = panel_idx;
 72                  y0 = panel_no * panel_height + i;
 73                  y1 = panel_no * panel_height + i + half_panel_height;
 74              }
 75              result.push_back(cb(width, height, x, y0));
 76              result.push_back(cb(width, height, x, y1));
 77          }
 78      }
 79  
 80      return result;
 81  }
 82  
 83  struct matrix_geometry {
 84      template <typename Cb>
 85      matrix_geometry(size_t pixels_across, size_t n_addr_lines, int n_planes,
 86                      size_t width, size_t height, bool serpentine, const Cb &cb)
 87          : pixels_across(pixels_across), n_addr_lines(n_addr_lines),
 88            n_planes(n_planes), width(width),
 89            height(height), map{make_matrixmap(width, height, n_addr_lines,
 90                                               serpentine, cb)} {
 91          size_t pixels_down = 2u << n_addr_lines;
 92          if (map.size() != pixels_down * pixels_across) {
 93              throw std::range_error(
 94                  "map size does not match calculated pixel count");
 95          }
 96      }
 97      size_t pixels_across, n_addr_lines;
 98      int n_planes;
 99      size_t width, height;
100      matrix_map map;
101  };
102  } // namespace piomatter