Files
dmf-studio-rnd/shared/V210.hpp
T
2026-06-23 18:36:39 +03:00

94 lines
3.2 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#pragma once
#include <array>
#include <cstddef>
#include <cstdint>
namespace dmf::v210 {
// SMPTE 75% color bars — 10-bit limited range
// Y: 64 (black) to 940 (white)
// Cb/Cr: 64 to 960, 512 = neutral grey
struct Color { uint16_t y, cb, cr; };
constexpr std::array<Color, 7> SMPTE_BARS = {{
{721, 512, 512}, // white
{674, 176, 543}, // yellow
{581, 589, 176}, // cyan
{534, 253, 207}, // green
{251, 771, 817}, // magenta
{204, 435, 848}, // red
{111, 848, 481}, // blue
}};
// IRE 11-step greyscale bars
// from total black to 100% white
// Y_10bit = 64 + (IRE / 100) × (940 64) Rec. 709/Rec. 2020
// Y_10bit = 64 + (IRE × 8.76)
constexpr std::array<Color, 11> IRE_BARS = {{
{64, 512, 512}, // IRE 0
{152, 512, 512}, // IRE 10
{239, 512, 512}, // IRE 20 and so on
{327, 512, 512},
{414, 512, 512},
{502, 512, 512},
{590, 512, 512},
{677, 512, 512},
{765, 512, 512},
{852, 512, 512},
{940, 512, 512},
}};
// Pack 6 pixels into 4 x 32-bit V210 words (16 bytes total).
//
// V210 is 4:2:2 — each pair of pixels shares one Cb and one Cr sample.
// The three pairs in a block map to words like this (bits [9:0],[19:10],[29:20]):
// word 0: Cb(pair0) | Y(px0) | Cr(pair0)
// word 1: Y(px1) | Cb(pair1) | Y(px2)
// word 2: Cr(pair1) | Y(px3) | Cb(pair2)
// word 3: Y(px4) | Cr(pair2) | Y(px5)
inline void pack_block(
uint8_t* out,
Color p01, uint16_t y0, uint16_t y1, // pair 01
Color p23, uint16_t y2, uint16_t y3, // pair 23
Color p45, uint16_t y4, uint16_t y5) // pair 45
{
auto* w = reinterpret_cast<uint32_t*>(out);
w[0] = (p01.cb & 0x3FFu) | ((y0 & 0x3FFu) << 10) | ((p01.cr & 0x3FFu) << 20);
w[1] = (y1 & 0x3FFu) | ((p23.cb & 0x3FFu) << 10) | ((y2 & 0x3FFu) << 20);
w[2] = (p23.cr & 0x3FFu) | ((y3 & 0x3FFu) << 10) | ((p45.cb & 0x3FFu) << 20);
w[3] = (y4 & 0x3FFu) | ((p45.cr & 0x3FFu) << 10) | ((y5 & 0x3FFu) << 20);
}
// Write one horizontal line of bars.
// `stride` is the line size in bytes as returned by MXL (configInfo.discrete.sliceSizes[0]).
// Bytes beyond the active pixels are already zeroed by the mmap, so no explicit padding needed.
inline void write_bar_line(uint8_t* line, int width, uint32_t /*stride*/)
{
const int n = static_cast<int>(SMPTE_BARS.size());
const int blocks = width / 6; // one V210 block = 6 pixels = 16 bytes
for (int b = 0; b < blocks; b++) {
int x = b * 6;
auto color = [&](int px) -> const Color& {
return SMPTE_BARS[static_cast<size_t>(px * n / width)];
};
const Color& c01 = color(x);
const Color& c23 = color(x + 2);
const Color& c45 = color(x + 4);
pack_block(line + b * 16,
c01, c01.y, color(x+1).y,
c23, c23.y, color(x+3).y,
c45, c45.y, color(x+5).y);
}
}
// Fill an entire frame buffer with color bars.
inline void fill_frame(uint8_t* buf, int width, int height, uint32_t stride)
{
for (int y = 0; y < height; y++) {
write_bar_line(buf + static_cast<ptrdiff_t>(y) * stride, width, stride);
}
}
} // namespace dmf::v210