render.h
1 #pragma once 2 3 #include "matrixmap.h" 4 #include <cassert> 5 #include <span> 6 #include <vector> 7 8 namespace piomatter { 9 10 constexpr unsigned DATA_OVERHEAD = 3; 11 constexpr unsigned CLOCKS_PER_DATA = 2; 12 constexpr unsigned DELAY_OVERHEAD = 5; 13 constexpr unsigned CLOCKS_PER_DELAY = 1; 14 15 constexpr uint32_t command_data = 1u << 31; 16 constexpr uint32_t command_delay = 0; 17 18 struct gamma_lut { 19 gamma_lut(double exponent = 2.2) { 20 for (int i = 0; i < 256; i++) { 21 auto v = std::max(i, int(round(1023 * pow(i / 255, exponent)))); 22 lut[i] = v; 23 } 24 } 25 26 unsigned convert(unsigned v) { 27 if (v >= std::size(lut)) 28 return 1023; 29 return lut[v]; 30 } 31 32 void convert_rgb888_packed_to_rgb10(std::vector<uint32_t> &result, 33 std::span<const uint8_t> source) { 34 result.resize(source.size() / 3); 35 for (size_t i = 0, j = 0; i < source.size(); i += 3) { 36 uint32_t r = source[i + 0] & 0xff; 37 uint32_t g = source[i + 1] & 0xff; 38 uint32_t b = source[i + 2] & 0xff; 39 result[j++] = (convert(r) << 20) | (convert(g) << 10) | convert(b); 40 } 41 } 42 43 void convert_rgb888_to_rgb10(std::vector<uint32_t> &result, 44 std::span<const uint32_t> source) { 45 result.resize(source.size()); 46 for (size_t i = 0; i < source.size(); i++) { 47 uint32_t data = source[i]; 48 uint32_t r = (data >> 16) & 0xff; 49 uint32_t g = (data >> 8) & 0xff; 50 uint32_t b = data & 0xff; 51 result[i] = (convert(r) << 20) | (convert(g) << 10) | convert(b); 52 } 53 } 54 55 void convert_rgb565_to_rgb10(std::vector<uint32_t> &result, 56 std::span<const uint16_t> source) { 57 result.resize(source.size()); 58 for (size_t i = 0; i < source.size(); i++) { 59 uint32_t data = source[i]; 60 unsigned r5 = (data >> 11) & 0x1f; 61 unsigned r = (r5 << 3) | (r5 >> 2); 62 unsigned g6 = (data >> 5) & 0x3f; 63 unsigned g = (g6 << 2) | (g6 >> 4); 64 unsigned b5 = (data)&0x1f; 65 unsigned b = (b5 << 3) | (b5 >> 2); 66 67 result[i] = (convert(r) << 20) | (convert(g) << 10) | convert(b); 68 } 69 } 70 71 uint16_t lut[256]; 72 }; 73 74 struct colorspace_rgb565 { 75 using data_type = uint16_t; 76 static constexpr size_t data_size_in_bytes(size_t n_pixels) { 77 return sizeof(data_type) * n_pixels; 78 } 79 80 colorspace_rgb565(float gamma = 2.2) : lut{gamma} {} 81 gamma_lut lut; 82 const std::span<const uint32_t> 83 convert(std::span<const data_type> data_in) { 84 lut.convert_rgb565_to_rgb10(rgb10, data_in); 85 return rgb10; 86 } 87 std::vector<uint32_t> rgb10; 88 }; 89 90 struct colorspace_rgb888 { 91 using data_type = uint32_t; 92 static constexpr size_t data_size_in_bytes(size_t n_pixels) { 93 return sizeof(data_type) * n_pixels; 94 } 95 96 colorspace_rgb888(float gamma = 2.2) : lut{gamma} {} 97 gamma_lut lut; 98 const std::span<const uint32_t> 99 convert(std::span<const data_type> data_in) { 100 lut.convert_rgb888_to_rgb10(rgb10, data_in); 101 return rgb10; 102 } 103 std::vector<uint32_t> rgb10; 104 }; 105 106 struct colorspace_rgb888_packed { 107 using data_type = uint8_t; 108 static constexpr size_t data_size_in_bytes(size_t n_pixels) { 109 return sizeof(data_type) * n_pixels * 3; 110 } 111 112 colorspace_rgb888_packed(float gamma = 2.2) : lut{gamma} {} 113 gamma_lut lut; 114 const std::span<const uint32_t> 115 convert(std::span<const data_type> data_in) { 116 lut.convert_rgb888_packed_to_rgb10(rgb10, data_in); 117 return rgb10; 118 } 119 std::vector<uint32_t> rgb10; 120 }; 121 122 struct colorspace_rgb10 { 123 using data_type = uint32_t; 124 125 const std::span<const uint32_t> 126 convert(std::span<const data_type> data_in) { 127 return data_in; 128 } 129 }; 130 131 // Render a buffer in linear RGB10 format into a piomatter stream 132 template <typename pinout> 133 void protomatter_render_rgb10(std::vector<uint32_t> &result, 134 const matrix_geometry &matrixmap, 135 const uint32_t *pixels) { 136 result.clear(); 137 138 int data_count = 0; 139 140 auto do_delay = [&](uint32_t delay) { 141 if (delay == 0) 142 return; 143 assert(delay < 1000000); 144 assert(!data_count); 145 result.push_back(command_delay | (delay ? delay - 1 : 0)); 146 }; 147 148 auto prep_data = [&data_count, &result](uint32_t n) { 149 assert(!data_count); 150 assert(n); 151 assert(n < 60000); 152 result.push_back(command_data | (n - 1)); 153 data_count = n; 154 }; 155 156 auto do_data = [&](uint32_t d) { 157 assert(data_count); 158 data_count--; 159 result.push_back(d); 160 }; 161 162 auto do_data_delay = [&](uint32_t d, uint32_t delay) { 163 prep_data(1); 164 do_data(d); 165 do_delay(delay); 166 }; 167 168 auto calc_addr_bits = [](int addr) { 169 uint32_t data = 0; 170 if (addr & 1) 171 data |= (1 << pinout::PIN_ADDR[0]); 172 if (addr & 2) 173 data |= (1 << pinout::PIN_ADDR[1]); 174 if (addr & 4) 175 data |= (1 << pinout::PIN_ADDR[2]); 176 if constexpr (std::size(pinout::PIN_ADDR) >= 4) { 177 if (addr & 8) 178 data |= (1 << pinout::PIN_ADDR[3]); 179 } 180 if constexpr (std::size(pinout::PIN_ADDR) >= 5) { 181 if (addr & 16) 182 data |= (1 << pinout::PIN_ADDR[4]); 183 } 184 return data; 185 }; 186 187 auto add_pixels = [&do_data, &result](uint32_t addr_bits, bool r0, bool g0, 188 bool b0, bool r1, bool g1, bool b1, 189 bool active) { 190 uint32_t data = 191 (active ? pinout::oe_active : pinout::oe_inactive) | addr_bits; 192 if (r0) 193 data |= (1 << pinout::PIN_RGB[0]); 194 if (g0) 195 data |= (1 << pinout::PIN_RGB[1]); 196 if (b0) 197 data |= (1 << pinout::PIN_RGB[2]); 198 if (r1) 199 data |= (1 << pinout::PIN_RGB[3]); 200 if (g1) 201 data |= (1 << pinout::PIN_RGB[4]); 202 if (b1) 203 data |= (1 << pinout::PIN_RGB[5]); 204 205 do_data(data); 206 do_data(data | pinout::clk_bit); 207 }; 208 209 int last_bit = 0; 210 // illuminate the right row for data in the shift register (the previous 211 // address) 212 213 const size_t n_addr = 1u << matrixmap.n_addr_lines; 214 const int n_planes = matrixmap.n_planes; 215 constexpr size_t n_bits = 10u; 216 unsigned offset = n_bits - n_planes; 217 const size_t pixels_across = matrixmap.pixels_across; 218 219 size_t prev_addr = n_addr - 1; 220 uint32_t addr_bits = calc_addr_bits(prev_addr); 221 222 for (size_t addr = 0; addr < n_addr; addr++) { 223 // printf("addr=%zu/%zu\n", addr, n_addr); 224 for (int bit = n_planes - 1; bit >= 0; bit--) { 225 // printf("bit=%d/%d\n", bit, n_planes); 226 uint32_t r = 1 << (20 + offset + bit); 227 uint32_t g = 1 << (10 + offset + bit); 228 uint32_t b = 1 << (0 + offset + bit); 229 230 // the shortest /OE we can do is one DATA_OVERHEAD... 231 // TODO: should make sure desired duration of MSB is at least 232 // `pixels_across` 233 uint32_t desired_duration = 1 << last_bit; 234 last_bit = bit; 235 236 prep_data(2 * pixels_across); 237 auto mapiter = matrixmap.map.begin() + 2 * addr * pixels_across; 238 for (size_t x = 0; x < pixels_across; x++) { 239 assert(mapiter != matrixmap.map.end()); 240 auto pixel0 = pixels[*mapiter++]; 241 auto r0 = pixel0 & r; 242 auto g0 = pixel0 & g; 243 auto b0 = pixel0 & b; 244 assert(mapiter != matrixmap.map.end()); 245 auto pixel1 = pixels[*mapiter++]; 246 auto r1 = pixel1 & r; 247 auto g1 = pixel1 & g; 248 auto b1 = pixel1 & b; 249 250 add_pixels(addr_bits, r0, g0, b0, r1, g1, b1, 251 x < desired_duration); 252 } 253 254 // hold /OE low until desired time has elapsed to illuminate the 255 // LAST line 256 int remain = desired_duration - pixels_across; 257 if (remain > 0) { 258 do_data_delay(addr_bits | pinout::oe_active, 259 remain * CLOCKS_PER_DATA - DELAY_OVERHEAD); 260 } 261 262 do_data_delay(addr_bits | pinout::oe_inactive, 263 pinout::post_oe_delay); 264 do_data_delay(addr_bits | pinout::oe_inactive | pinout::lat_bit, 265 pinout::post_latch_delay); 266 267 // with oe inactive, set address bits to illuminate THIS line 268 if (addr != prev_addr) { 269 addr_bits = calc_addr_bits(addr); 270 do_data_delay(addr_bits | pinout::oe_inactive, 271 pinout::post_addr_delay); 272 prev_addr = addr; 273 } 274 } 275 } 276 } 277 278 } // namespace piomatter