#pragma once #include struct RgbColor { uint8_t r = 0; uint8_t g = 0; uint8_t b = 0; }; inline uint32_t packRgba8(RgbColor color) { constexpr uint8_t alpha = 255; return (alpha << 24) | (color.b << 16) | (color.g << 8) | color.r; } inline uint8_t clampUint8(int32_t v) { return static_cast( v < 0 ? 0 : (v > 255 ? 255 : v) ); } inline uint32_t v210LineStride(uint32_t width) { uint32_t groups = (width + 5) / 6; uint32_t rawBytes = groups * 16; return ((rawBytes + 127) / 128) * 128; } inline void decodeV210Line( const uint8_t* lineData, uint32_t* rgbaLine, uint32_t width) { for (uint32_t x = 0; x < width; ++x) { uint32_t group = x / 6; uint32_t pos = x % 6; const uint32_t baseOffset = group * 16; uint32_t w0; uint32_t w1; uint32_t w2; uint32_t w3; const uint8_t* p = lineData + baseOffset; w0 = static_cast(p[0]) | (static_cast(p[1]) << 8) | (static_cast(p[2]) << 16) | (static_cast(p[3]) << 24); w1 = static_cast(p[4]) | (static_cast(p[5]) << 8) | (static_cast(p[6]) << 16) | (static_cast(p[7]) << 24); w2 = static_cast(p[8]) | (static_cast(p[9]) << 8) | (static_cast(p[10]) << 16) | (static_cast(p[11]) << 24); w3 = static_cast(p[12]) | (static_cast(p[13]) << 8) | (static_cast(p[14]) << 16) | (static_cast(p[15]) << 24); uint32_t cb0 = (w0 >> 0) & 0x3FF; uint32_t y0 = (w0 >> 10) & 0x3FF; uint32_t cr0 = (w0 >> 20) & 0x3FF; uint32_t y1 = (w1 >> 0) & 0x3FF; uint32_t cb2 = (w1 >> 10) & 0x3FF; uint32_t y2 = (w1 >> 20) & 0x3FF; uint32_t cr2 = (w2 >> 0) & 0x3FF; uint32_t y3 = (w2 >> 10) & 0x3FF; uint32_t cb4 = (w2 >> 20) & 0x3FF; uint32_t y4 = (w3 >> 0) & 0x3FF; uint32_t cr4 = (w3 >> 10) & 0x3FF; uint32_t y5 = (w3 >> 20) & 0x3FF; uint32_t y10; uint32_t cb10; uint32_t cr10; switch (pos) { case 0: y10 = y0; cb10 = cb0; cr10 = cr0; break; case 1: y10 = y1; cb10 = cb0; cr10 = cr0; break; case 2: y10 = y2; cb10 = cb2; cr10 = cr2; break; case 3: y10 = y3; cb10 = cb2; cr10 = cr2; break; case 4: y10 = y4; cb10 = cb4; cr10 = cr4; break; case 5: y10 = y5; cb10 = cb4; cr10 = cr4; break; default: y10 = y0; cb10 = cb0; cr10 = cr0; break; } // BT.709 limited-range YCbCr to RGB. v210 stores 10-bit video-range // samples: Y [64, 940], Cb/Cr centered at 512. int32_t yp = (static_cast(y10) - 64) >> 2; int32_t cbp = (static_cast(cb10) - 512) >> 2; int32_t crp = (static_cast(cr10) - 512) >> 2; int32_t r = (298 * yp + 459 * crp + 128) >> 8; int32_t g = (298 * yp - 137 * crp - 55 * cbp + 128) >> 8; int32_t b = (298 * yp + 541 * cbp + 128) >> 8; rgbaLine[x] = packRgba8({clampUint8(r), clampUint8(g), clampUint8(b)}); } } inline void convertV210ToRgba( const uint8_t* v210Data, uint32_t srcWidth, uint32_t srcHeight, uint32_t srcStride, uint32_t* rgbaData, uint32_t dstWidth, uint32_t dstHeight) { for (uint32_t dy = 0; dy < dstHeight; ++dy) { uint32_t sy = dy * srcHeight / dstHeight; const uint8_t* srcLine = v210Data + sy * srcStride; for (uint32_t dx = 0; dx < dstWidth; ++dx) { uint32_t sx = dx * srcWidth / dstWidth; uint32_t group = sx / 6; uint32_t pos = sx % 6; const uint8_t* p = srcLine + group * 16; uint32_t w0 = static_cast(p[0]) | (static_cast(p[1]) << 8) | (static_cast(p[2]) << 16) | (static_cast(p[3]) << 24); uint32_t w1 = static_cast(p[4]) | (static_cast(p[5]) << 8) | (static_cast(p[6]) << 16) | (static_cast(p[7]) << 24); uint32_t w2 = static_cast(p[8]) | (static_cast(p[9]) << 8) | (static_cast(p[10]) << 16) | (static_cast(p[11]) << 24); uint32_t w3 = static_cast(p[12]) | (static_cast(p[13]) << 8) | (static_cast(p[14]) << 16) | (static_cast(p[15]) << 24); uint32_t cb0 = (w0 >> 0) & 0x3FF; uint32_t y0 = (w0 >> 10) & 0x3FF; uint32_t cr0 = (w0 >> 20) & 0x3FF; uint32_t y1 = (w1 >> 0) & 0x3FF; uint32_t cb2 = (w1 >> 10) & 0x3FF; uint32_t y2 = (w1 >> 20) & 0x3FF; uint32_t cr2 = (w2 >> 0) & 0x3FF; uint32_t y3 = (w2 >> 10) & 0x3FF; uint32_t cb4 = (w2 >> 20) & 0x3FF; uint32_t y4 = (w3 >> 0) & 0x3FF; uint32_t cr4 = (w3 >> 10) & 0x3FF; uint32_t y5 = (w3 >> 20) & 0x3FF; uint32_t y10; uint32_t cb10; uint32_t cr10; switch (pos) { case 0: y10 = y0; cb10 = cb0; cr10 = cr0; break; case 1: y10 = y1; cb10 = cb0; cr10 = cr0; break; case 2: y10 = y2; cb10 = cb2; cr10 = cr2; break; case 3: y10 = y3; cb10 = cb2; cr10 = cr2; break; case 4: y10 = y4; cb10 = cb4; cr10 = cr4; break; case 5: y10 = y5; cb10 = cb4; cr10 = cr4; break; default: y10 = y0; cb10 = cb0; cr10 = cr0; break; } // BT.709 limited-range YCbCr to RGB. v210 stores 10-bit video-range // samples: Y [64, 940], Cb/Cr centered at 512. int32_t yp = (static_cast(y10) - 64) >> 2; int32_t cbp = (static_cast(cb10) - 512) >> 2; int32_t crp = (static_cast(cr10) - 512) >> 2; int32_t r = (298 * yp + 459 * crp + 128) >> 8; int32_t g = (298 * yp - 137 * crp - 55 * cbp + 128) >> 8; int32_t b = (298 * yp + 541 * cbp + 128) >> 8; rgbaData[dy * dstWidth + dx] = packRgba8( {clampUint8(r), clampUint8(g), clampUint8(b)}); } } }