248 lines
8 KiB
C++
248 lines
8 KiB
C++
#pragma once
|
|
|
|
#include "matrixmap.h"
|
|
#include <cassert>
|
|
#include <span>
|
|
#include <vector>
|
|
|
|
namespace piomatter {
|
|
|
|
constexpr int DATA_OVERHEAD = 3;
|
|
constexpr int CLOCKS_PER_DATA = 2;
|
|
constexpr int DELAY_OVERHEAD = 5;
|
|
constexpr int CLOCKS_PER_DELAY = 1;
|
|
|
|
constexpr uint32_t command_data = 1u << 31;
|
|
constexpr uint32_t command_delay = 0;
|
|
|
|
struct gamma_lut {
|
|
gamma_lut(double exponent = 2.2) {
|
|
for (int i = 0; i < 256; i++) {
|
|
auto v = std::max(i, int(round(1023 * pow(i / 255., exponent))));
|
|
lut[i] = v;
|
|
}
|
|
}
|
|
|
|
unsigned convert(unsigned v) {
|
|
if (v >= std::size(lut))
|
|
return 1023;
|
|
return lut[v];
|
|
}
|
|
|
|
void convert_rgb888_packed_to_rgb10(std::vector<uint32_t> &result,
|
|
std::span<const uint8_t> source) {
|
|
result.resize(source.size() / 3);
|
|
for (size_t i = 0, j = 0; i < source.size(); i += 3) {
|
|
uint32_t r = source[i + 0] & 0xff;
|
|
uint32_t g = source[i + 1] & 0xff;
|
|
uint32_t b = source[i + 2] & 0xff;
|
|
result[j++] = (convert(r) << 20) | (convert(g) << 10) | convert(b);
|
|
}
|
|
}
|
|
|
|
void convert_rgb888_to_rgb10(std::vector<uint32_t> &result,
|
|
std::span<const uint32_t> source) {
|
|
result.resize(source.size());
|
|
for (size_t i = 0; i < source.size(); i++) {
|
|
uint32_t data = source[i];
|
|
uint32_t r = (data >> 16) & 0xff;
|
|
uint32_t g = (data >> 8) & 0xff;
|
|
uint32_t b = data & 0xff;
|
|
result[i] = (convert(r) << 20) | (convert(g) << 10) | convert(b);
|
|
}
|
|
}
|
|
|
|
void convert_rgb565_to_rgb10(std::vector<uint32_t> &result,
|
|
std::span<const uint16_t> source) {
|
|
result.resize(source.size());
|
|
for (size_t i = 0; i < source.size(); i++) {
|
|
uint32_t data = source[i];
|
|
unsigned r5 = (data >> 11) & 0x1f;
|
|
unsigned r = (r5 << 3) | (r5 >> 2);
|
|
unsigned g6 = (data >> 5) & 0x3f;
|
|
unsigned g = (g6 << 2) | (g6 >> 4);
|
|
unsigned b5 = (data)&0x1f;
|
|
unsigned b = (b5 << 3) | (b5 >> 2);
|
|
|
|
result[i] = (convert(r) << 20) | (convert(g) << 10) | convert(b);
|
|
}
|
|
}
|
|
|
|
uint16_t lut[256];
|
|
};
|
|
|
|
struct colorspace_rgb565 {
|
|
using data_type = uint16_t;
|
|
static constexpr size_t data_size_in_bytes(size_t n_pixels) {
|
|
return sizeof(data_type) * n_pixels;
|
|
}
|
|
|
|
colorspace_rgb565(float gamma = 2.2) : lut{gamma} {}
|
|
gamma_lut lut;
|
|
const std::span<const uint32_t>
|
|
convert(std::span<const data_type> data_in) {
|
|
lut.convert_rgb565_to_rgb10(rgb10, data_in);
|
|
return rgb10;
|
|
}
|
|
std::vector<uint32_t> rgb10;
|
|
};
|
|
|
|
struct colorspace_rgb888 {
|
|
using data_type = uint32_t;
|
|
static constexpr size_t data_size_in_bytes(size_t n_pixels) {
|
|
return sizeof(data_type) * n_pixels;
|
|
}
|
|
|
|
colorspace_rgb888(float gamma = 2.2) : lut{gamma} {}
|
|
gamma_lut lut;
|
|
const std::span<const uint32_t>
|
|
convert(std::span<const data_type> data_in) {
|
|
lut.convert_rgb888_to_rgb10(rgb10, data_in);
|
|
return rgb10;
|
|
}
|
|
std::vector<uint32_t> rgb10;
|
|
};
|
|
|
|
struct colorspace_rgb888_packed {
|
|
using data_type = uint8_t;
|
|
static constexpr size_t data_size_in_bytes(size_t n_pixels) {
|
|
return sizeof(data_type) * n_pixels * 3;
|
|
}
|
|
|
|
colorspace_rgb888_packed(float gamma = 2.2) : lut{gamma} {}
|
|
gamma_lut lut;
|
|
const std::span<const uint32_t>
|
|
convert(std::span<const data_type> data_in) {
|
|
lut.convert_rgb888_packed_to_rgb10(rgb10, data_in);
|
|
return rgb10;
|
|
}
|
|
std::vector<uint32_t> rgb10;
|
|
};
|
|
|
|
struct colorspace_rgb10 {
|
|
using data_type = uint32_t;
|
|
|
|
const std::span<const uint32_t>
|
|
convert(std::span<const data_type> data_in) {
|
|
return data_in;
|
|
}
|
|
};
|
|
|
|
// Render a buffer in linear RGB10 format into a piomatter stream
|
|
template <typename pinout>
|
|
void protomatter_render_rgb10(std::vector<uint32_t> &result,
|
|
const matrix_geometry &matrixmap,
|
|
const schedule &sched, const uint32_t *pixels) {
|
|
result.clear();
|
|
|
|
int data_count = 0;
|
|
|
|
auto do_data_delay = [&](uint32_t data, int32_t delay) {
|
|
delay = std::max((delay / CLOCKS_PER_DELAY) - DELAY_OVERHEAD, 1);
|
|
assert(delay < 1000000);
|
|
assert(!data_count);
|
|
result.push_back(command_delay | (delay ? delay - 1 : 0));
|
|
result.push_back(data);
|
|
};
|
|
|
|
auto prep_data = [&data_count, &result](uint32_t n) {
|
|
assert(!data_count);
|
|
assert(n);
|
|
assert(n < 60000);
|
|
result.push_back(command_data | (n - 1));
|
|
data_count = n;
|
|
};
|
|
|
|
int32_t active_time;
|
|
|
|
auto do_data_clk_active = [&active_time, &data_count, &result](uint32_t d) {
|
|
bool active = active_time > 0;
|
|
active_time--;
|
|
d |= active ? pinout::oe_active : pinout::oe_inactive;
|
|
assert(data_count);
|
|
data_count--;
|
|
result.push_back(d);
|
|
};
|
|
|
|
auto calc_addr_bits = [](int addr) {
|
|
uint32_t data = 0;
|
|
if (addr & 1)
|
|
data |= (1 << pinout::PIN_ADDR[0]);
|
|
if (addr & 2)
|
|
data |= (1 << pinout::PIN_ADDR[1]);
|
|
if (addr & 4)
|
|
data |= (1 << pinout::PIN_ADDR[2]);
|
|
if constexpr (std::size(pinout::PIN_ADDR) >= 4) {
|
|
if (addr & 8)
|
|
data |= (1 << pinout::PIN_ADDR[3]);
|
|
}
|
|
if constexpr (std::size(pinout::PIN_ADDR) >= 5) {
|
|
if (addr & 16)
|
|
data |= (1 << pinout::PIN_ADDR[4]);
|
|
}
|
|
return data;
|
|
};
|
|
|
|
// illuminate the right row for data in the shift register (the previous
|
|
// address)
|
|
|
|
const size_t n_addr = 1u << matrixmap.n_addr_lines;
|
|
constexpr size_t n_bits = 10u;
|
|
const size_t pixels_across = matrixmap.pixels_across;
|
|
|
|
size_t prev_addr = n_addr - 1;
|
|
uint32_t addr_bits = calc_addr_bits(prev_addr);
|
|
|
|
for (size_t addr = 0; addr < n_addr; addr++) {
|
|
uint32_t active_time = sched.back().active_time;
|
|
for (auto &schedule_ent : sched) {
|
|
uint32_t r_mask = 1 << (20 + schedule_ent.shift);
|
|
uint32_t g_mask = 1 << (10 + schedule_ent.shift);
|
|
uint32_t b_mask = 1 << (0 + schedule_ent.shift);
|
|
|
|
prep_data(pixels_across);
|
|
auto mapiter = matrixmap.map.begin() +
|
|
matrixmap.n_lanes * addr * pixels_across;
|
|
for (size_t x = 0; x < pixels_across; x++) {
|
|
uint32_t data = addr_bits;
|
|
for (size_t px = 0; px < matrixmap.n_lanes; px++) {
|
|
assert(mapiter != matrixmap.map.end());
|
|
auto pixel0 = pixels[*mapiter++];
|
|
auto r_bit = pixel0 & r_mask;
|
|
auto g_bit = pixel0 & g_mask;
|
|
auto b_bit = pixel0 & b_mask;
|
|
|
|
if (r_bit)
|
|
data |= (1 << pinout::PIN_RGB[px * 3 + 0]);
|
|
if (g_bit)
|
|
data |= (1 << pinout::PIN_RGB[px * 3 + 1]);
|
|
if (b_bit)
|
|
data |= (1 << pinout::PIN_RGB[px * 3 + 2]);
|
|
}
|
|
|
|
do_data_clk_active(data);
|
|
}
|
|
|
|
do_data_delay(addr_bits | pinout::oe_active,
|
|
active_time * CLOCKS_PER_DATA / CLOCKS_PER_DELAY -
|
|
DELAY_OVERHEAD);
|
|
|
|
do_data_delay(addr_bits | pinout::oe_inactive,
|
|
pinout::post_oe_delay);
|
|
do_data_delay(addr_bits | pinout::oe_inactive | pinout::lat_bit,
|
|
pinout::post_latch_delay);
|
|
|
|
active_time = schedule_ent.active_time;
|
|
|
|
// with oe inactive, set address bits to illuminate THIS line
|
|
if (addr != prev_addr) {
|
|
addr_bits = calc_addr_bits(addr);
|
|
do_data_delay(addr_bits | pinout::oe_inactive,
|
|
pinout::post_addr_delay);
|
|
prev_addr = addr;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
} // namespace piomatter
|