#pragma once #include #include #include #include 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 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 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 0–1 Color p23, uint16_t y2, uint16_t y3, // pair 2–3 Color p45, uint16_t y4, uint16_t y5) // pair 4–5 { auto* w = reinterpret_cast(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 an arbitrary bar palette. template inline void write_palette_line(uint8_t* line, int width, const std::array& palette) { const int n = static_cast(N); const int blocks = width / 6; for (int b = 0; b < blocks; b++) { int x = b * 6; auto color = [&](int px) -> const Color& { return palette[static_cast(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 a frame with a solid color. inline void fill_solid(uint8_t* buf, int width, int height, uint32_t stride, Color c) { const int blocks = width / 6; for (int b = 0; b < blocks; b++) pack_block(buf + b * 16, c, c.y, c.y, c, c.y, c.y, c, c.y, c.y); for (int y = 1; y < height; y++) std::memcpy(buf + static_cast(y) * stride, buf, blocks * 16); } // SMPTE 75% color bars. inline void fill_colorbars(uint8_t* buf, int width, int height, uint32_t stride) { for (int y = 0; y < height; y++) write_palette_line(buf + static_cast(y) * stride, width, SMPTE_BARS); } // IRE 11-step greyscale ramp. inline void fill_ire_ramp(uint8_t* buf, int width, int height, uint32_t stride) { for (int y = 0; y < height; y++) write_palette_line(buf + static_cast(y) * stride, width, IRE_BARS); } // 10-bit limited black (Y=64, Cb=Cr=512). inline void fill_black(uint8_t* buf, int width, int height, uint32_t stride) { fill_solid(buf, width, height, stride, {64, 512, 512}); } // 10-bit limited white (Y=940, Cb=Cr=512). inline void fill_white(uint8_t* buf, int width, int height, uint32_t stride) { fill_solid(buf, width, height, stride, {940, 512, 512}); } inline void UYVYtoV210(uint8_t* src_buf, uint8_t* dst_buf, int width, int height, uint32_t src_stride, uint32_t dst_stride) { const uint8_t* src = src_buf; uint8_t* dst = dst_buf; const int blocks = width / 6; for (int y = 0; y < height; y++) { for (int b = 0; b < blocks; b++) { const uint8_t* mp = src + b * 12; // 3 macropixels = 12 bytes // mp[0]=U0, mp[1]=Y0, mp[2]=V0, mp[3]=Y1 // mp[4]=U1, mp[5]=Y2, mp[6]=V1, mp[7]=Y3 // mp[8]=U2, mp[9]=Y4, mp[10]=V2, mp[11]=Y5 dmf::v210::pack_block(dst + b * 16, {0, static_cast(mp[0]<<2), static_cast(mp[2]<<2)}, static_cast(mp[1]<<2), static_cast(mp[3]<<2), {0, static_cast(mp[4]<<2), static_cast(mp[6]<<2)}, static_cast(mp[5]<<2), static_cast(mp[7]<<2), {0, static_cast(mp[8]<<2), static_cast(mp[10]<<2)}, static_cast(mp[9]<<2), static_cast(mp[11]<<2)); } src += src_stride; dst += dst_stride; } } inline void YUV422P10toV210( const uint16_t* y, const uint16_t* u, const uint16_t* v, uint8_t* dst, int width, int height, int y_stride, int u_stride, int v_stride, uint32_t dst_stride) { for (int row = 0; row < height; row++) { const uint16_t* y_row = reinterpret_cast( reinterpret_cast(y) + row * y_stride); const uint16_t* u_row = reinterpret_cast( reinterpret_cast(u) + row * u_stride); const uint16_t* v_row = reinterpret_cast( reinterpret_cast(v) + row * v_stride); uint8_t* dst_row = dst + static_cast(row) * dst_stride; const int blocks = width / 6; for (int b = 0; b < blocks; b++) { const int x = b * 6; const uint16_t cb0 = u_row[x/2], cb1 = u_row[x/2+1], cb2 = u_row[x/2+2]; const uint16_t cr0 = v_row[x/2], cr1 = v_row[x/2+1], cr2 = v_row[x/2+2]; const uint16_t y0 = y_row[x], y1 = y_row[x+1], y2 = y_row[x+2]; const uint16_t y3 = y_row[x+3], y4 = y_row[x+4], y5 = y_row[x+5]; auto* w = reinterpret_cast(dst_row + b * 16); w[0] = (cb0 & 0x3FFu) | ((y0 & 0x3FFu) << 10) | ((cr0 & 0x3FFu) << 20); w[1] = (y1 & 0x3FFu) | ((cb1 & 0x3FFu) << 10) | ((y2 & 0x3FFu) << 20); w[2] = (cr1 & 0x3FFu) | ((y3 & 0x3FFu) << 10) | ((cb2 & 0x3FFu) << 20); w[3] = (y4 & 0x3FFu) | ((cr2 & 0x3FFu) << 10) | ((y5 & 0x3FFu) << 20); } } } } // namespace dmf::v210