#include "VideoIOFormat.h" #include #include #ifdef min #undef min #endif #ifdef max #undef max #endif namespace { uint16_t Clamp10(int value, int minimum, int maximum) { return static_cast(std::max(minimum, std::min(maximum, value))); } uint32_t MakeV210Word(uint16_t a, uint16_t b, uint16_t c) { return (static_cast(a) & 0x3ffu) | ((static_cast(b) & 0x3ffu) << 10) | ((static_cast(c) & 0x3ffu) << 20); } void StoreWord(std::array& bytes, std::size_t wordIndex, uint32_t word) { const std::size_t offset = wordIndex * 4; bytes[offset + 0] = static_cast(word & 0xffu); bytes[offset + 1] = static_cast((word >> 8) & 0xffu); bytes[offset + 2] = static_cast((word >> 16) & 0xffu); bytes[offset + 3] = static_cast((word >> 24) & 0xffu); } uint32_t LoadWord(const std::array& bytes, std::size_t wordIndex) { const std::size_t offset = wordIndex * 4; return static_cast(bytes[offset + 0]) | (static_cast(bytes[offset + 1]) << 8) | (static_cast(bytes[offset + 2]) << 16) | (static_cast(bytes[offset + 3]) << 24); } uint16_t Component(uint32_t word, unsigned index) { return static_cast((word >> (index * 10)) & 0x3ffu); } } const char* VideoIOPixelFormatName(VideoIOPixelFormat format) { switch (format) { case VideoIOPixelFormat::V210: return "10-bit YUV v210"; case VideoIOPixelFormat::Bgra8: return "8-bit BGRA"; case VideoIOPixelFormat::Uyvy8: default: return "8-bit YUV UYVY"; } } bool VideoIOPixelFormatIsTenBit(VideoIOPixelFormat format) { return format == VideoIOPixelFormat::V210; } VideoIOPixelFormat ChoosePreferredVideoIOFormat(bool tenBitSupported) { return tenBitSupported ? VideoIOPixelFormat::V210 : VideoIOPixelFormat::Uyvy8; } unsigned VideoIOBytesPerPixel(VideoIOPixelFormat format) { switch (format) { case VideoIOPixelFormat::Uyvy8: return 2u; case VideoIOPixelFormat::Bgra8: return 4u; case VideoIOPixelFormat::V210: default: return 0u; } } unsigned VideoIORowBytes(VideoIOPixelFormat format, unsigned frameWidth) { if (format == VideoIOPixelFormat::V210) return MinimumV210RowBytes(frameWidth); return frameWidth * VideoIOBytesPerPixel(format); } unsigned PackedTextureWidthFromRowBytes(unsigned rowBytes) { return (rowBytes + 3u) / 4u; } unsigned MinimumV210RowBytes(unsigned frameWidth) { return ((frameWidth + 5u) / 6u) * 16u; } unsigned ActiveV210WordsForWidth(unsigned frameWidth) { return ((frameWidth + 5u) / 6u) * 4u; } V210CodeValues Rec709RgbToLegalV210(float red, float green, float blue) { red = std::max(0.0f, std::min(1.0f, red)); green = std::max(0.0f, std::min(1.0f, green)); blue = std::max(0.0f, std::min(1.0f, blue)); const float y = 0.2126f * red + 0.7152f * green + 0.0722f * blue; const float cb = (blue - y) / 1.8556f + 0.5f; const float cr = (red - y) / 1.5748f + 0.5f; V210CodeValues values; values.y = Clamp10(static_cast(std::lround(64.0f + y * 876.0f)), 64, 940); values.cb = Clamp10(static_cast(std::lround(64.0f + cb * 896.0f)), 64, 960); values.cr = Clamp10(static_cast(std::lround(64.0f + cr * 896.0f)), 64, 960); return values; } std::array PackV210Block(const V210SixPixelBlock& block) { std::array bytes = {}; StoreWord(bytes, 0, MakeV210Word(block.cb[0], block.y[0], block.cr[0])); StoreWord(bytes, 1, MakeV210Word(block.y[1], block.cb[1], block.y[2])); StoreWord(bytes, 2, MakeV210Word(block.cr[1], block.y[3], block.cb[2])); StoreWord(bytes, 3, MakeV210Word(block.y[4], block.cr[2], block.y[5])); return bytes; } V210SixPixelBlock UnpackV210Block(const std::array& bytes) { const uint32_t word0 = LoadWord(bytes, 0); const uint32_t word1 = LoadWord(bytes, 1); const uint32_t word2 = LoadWord(bytes, 2); const uint32_t word3 = LoadWord(bytes, 3); V210SixPixelBlock block; block.cb[0] = Component(word0, 0); block.y[0] = Component(word0, 1); block.cr[0] = Component(word0, 2); block.y[1] = Component(word1, 0); block.cb[1] = Component(word1, 1); block.y[2] = Component(word1, 2); block.cr[1] = Component(word2, 0); block.y[3] = Component(word2, 1); block.cb[2] = Component(word2, 2); block.y[4] = Component(word3, 0); block.cr[2] = Component(word3, 1); block.y[5] = Component(word3, 2); return block; }