Files
SimpleRemoter/test/unit/screen/RGB565Test.cpp
2026-04-19 22:55:21 +02:00

598 lines
19 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.
// RGB565Test.cpp - Phase 4: RGB565压缩单元测试
// 测试 BGRA <-> RGB565 颜色空间转换
#include <gtest/gtest.h>
#include <vector>
#include <cstdint>
#include <cstring>
#include <random>
#include <cmath>
// ============================================
// RGB565 颜色空间转换实现
// 来源: client/ScreenCapture.h, server/ScreenSpyDlg.cpp
// ============================================
// RGB565 格式说明:
// 16位: RRRRRGGG GGGBBBBB
// R: 5位 (0-31)
// G: 6位 (0-63)
// B: 5位 (0-31)
class RGB565Converter {
public:
// ============================================
// BGRA -> RGB565 转换 (标量版本)
// ============================================
static void ConvertBGRAtoRGB565_Scalar(
const uint8_t* src,
uint16_t* dst,
size_t pixelCount
) {
for (size_t i = 0; i < pixelCount; i++) {
// BGRA 格式: B=0, G=1, R=2, A=3
uint8_t b = src[i * 4 + 0];
uint8_t g = src[i * 4 + 1];
uint8_t r = src[i * 4 + 2];
// A 通道被忽略
// RGB565: RRRRRGGG GGGBBBBB
dst[i] = static_cast<uint16_t>(
((r >> 3) << 11) | // R: 高5位 -> 位11-15
((g >> 2) << 5) | // G: 高6位 -> 位5-10
(b >> 3) // B: 高5位 -> 位0-4
);
}
}
// ============================================
// RGB565 -> BGRA 转换
// 位复制填充低位以提高精度
// ============================================
static void ConvertRGB565ToBGRA(
const uint16_t* src,
uint8_t* dst,
size_t pixelCount
) {
for (size_t i = 0; i < pixelCount; i++) {
uint16_t c = src[i];
// 提取各通道
uint8_t r5 = (c >> 11) & 0x1F; // 5位红色
uint8_t g6 = (c >> 5) & 0x3F; // 6位绿色
uint8_t b5 = c & 0x1F; // 5位蓝色
// 扩展到8位使用位复制填充低位
// 例如: 5位 11111 -> 8位 11111111 (通过 (x << 3) | (x >> 2))
dst[i * 4 + 0] = (b5 << 3) | (b5 >> 2); // B: 5->8位
dst[i * 4 + 1] = (g6 << 2) | (g6 >> 4); // G: 6->8位
dst[i * 4 + 2] = (r5 << 3) | (r5 >> 2); // R: 5->8位
dst[i * 4 + 3] = 0xFF; // A: 不透明
}
}
// ============================================
// 单像素转换辅助函数
// ============================================
static uint16_t BGRAToRGB565(uint8_t b, uint8_t g, uint8_t r) {
return static_cast<uint16_t>(
((r >> 3) << 11) |
((g >> 2) << 5) |
(b >> 3)
);
}
static void RGB565ToBGRA(uint16_t c, uint8_t& b, uint8_t& g, uint8_t& r, uint8_t& a) {
uint8_t r5 = (c >> 11) & 0x1F;
uint8_t g6 = (c >> 5) & 0x3F;
uint8_t b5 = c & 0x1F;
b = (b5 << 3) | (b5 >> 2);
g = (g6 << 2) | (g6 >> 4);
r = (r5 << 3) | (r5 >> 2);
a = 0xFF;
}
// ============================================
// 计算量化误差
// ============================================
static int CalculateError(uint8_t original, uint8_t converted) {
return std::abs(static_cast<int>(original) - static_cast<int>(converted));
}
// 计算最大理论误差
// 5位通道: 量化+位填充可能产生最大误差 7
// 6位通道: 量化+位填充可能产生最大误差 3
static int MaxError5Bit() { return 7; }
static int MaxError6Bit() { return 3; }
};
// ============================================
// 测试夹具
// ============================================
class RGB565Test : public ::testing::Test {
protected:
// 创建BGRA像素数组
static std::vector<uint8_t> CreateBGRAPixels(size_t count, uint8_t b, uint8_t g, uint8_t r, uint8_t a = 0xFF) {
std::vector<uint8_t> pixels(count * 4);
for (size_t i = 0; i < count; i++) {
pixels[i * 4 + 0] = b;
pixels[i * 4 + 1] = g;
pixels[i * 4 + 2] = r;
pixels[i * 4 + 3] = a;
}
return pixels;
}
};
// ============================================
// 基础转换测试
// ============================================
TEST_F(RGB565Test, SinglePixel_Black) {
uint16_t result = RGB565Converter::BGRAToRGB565(0, 0, 0);
EXPECT_EQ(result, 0x0000);
}
TEST_F(RGB565Test, SinglePixel_White) {
uint16_t result = RGB565Converter::BGRAToRGB565(255, 255, 255);
// R=31<<11=0xF800, G=63<<5=0x07E0, B=31=0x001F
// 合计: 0xFFFF
EXPECT_EQ(result, 0xFFFF);
}
TEST_F(RGB565Test, SinglePixel_Red) {
uint16_t result = RGB565Converter::BGRAToRGB565(0, 0, 255);
// R=31<<11=0xF800, G=0, B=0
EXPECT_EQ(result, 0xF800);
}
TEST_F(RGB565Test, SinglePixel_Green) {
uint16_t result = RGB565Converter::BGRAToRGB565(0, 255, 0);
// R=0, G=63<<5=0x07E0, B=0
EXPECT_EQ(result, 0x07E0);
}
TEST_F(RGB565Test, SinglePixel_Blue) {
uint16_t result = RGB565Converter::BGRAToRGB565(255, 0, 0);
// R=0, G=0, B=31=0x001F
EXPECT_EQ(result, 0x001F);
}
// ============================================
// 反向转换测试
// ============================================
TEST_F(RGB565Test, Reverse_Black) {
uint8_t b, g, r, a;
RGB565Converter::RGB565ToBGRA(0x0000, b, g, r, a);
EXPECT_EQ(b, 0);
EXPECT_EQ(g, 0);
EXPECT_EQ(r, 0);
EXPECT_EQ(a, 255);
}
TEST_F(RGB565Test, Reverse_White) {
uint8_t b, g, r, a;
RGB565Converter::RGB565ToBGRA(0xFFFF, b, g, r, a);
EXPECT_EQ(b, 255);
EXPECT_EQ(g, 255);
EXPECT_EQ(r, 255);
EXPECT_EQ(a, 255);
}
TEST_F(RGB565Test, Reverse_Red) {
uint8_t b, g, r, a;
RGB565Converter::RGB565ToBGRA(0xF800, b, g, r, a);
EXPECT_EQ(b, 0);
EXPECT_EQ(g, 0);
EXPECT_EQ(r, 255);
EXPECT_EQ(a, 255);
}
TEST_F(RGB565Test, Reverse_Green) {
uint8_t b, g, r, a;
RGB565Converter::RGB565ToBGRA(0x07E0, b, g, r, a);
EXPECT_EQ(b, 0);
EXPECT_EQ(g, 255);
EXPECT_EQ(r, 0);
EXPECT_EQ(a, 255);
}
TEST_F(RGB565Test, Reverse_Blue) {
uint8_t b, g, r, a;
RGB565Converter::RGB565ToBGRA(0x001F, b, g, r, a);
EXPECT_EQ(b, 255);
EXPECT_EQ(g, 0);
EXPECT_EQ(r, 0);
EXPECT_EQ(a, 255);
}
// ============================================
// 往返转换测试
// ============================================
TEST_F(RGB565Test, RoundTrip_ExactColors) {
// 测试能精确表示的颜色 (只有 0 和 255 能完美往返)
// 位填充公式: (x << 3) | (x >> 2) 只有 x=0 -> 0, x=31 -> 255 是精确的
struct TestCase {
uint8_t b, g, r;
} testCases[] = {
{0, 0, 0}, // 黑色 - 精确
{255, 255, 255}, // 白色 - 精确
{0, 0, 255}, // 红色 - 精确
{0, 255, 0}, // 绿色 - 精确
{255, 0, 0}, // 蓝色 - 精确
{255, 255, 0}, // 青色 - 精确
{0, 255, 255}, // 黄色 - 精确
{255, 0, 255}, // 品红 - 精确
};
for (const auto& tc : testCases) {
uint16_t rgb565 = RGB565Converter::BGRAToRGB565(tc.b, tc.g, tc.r);
uint8_t b, g, r, a;
RGB565Converter::RGB565ToBGRA(rgb565, b, g, r, a);
// 能精确表示的颜色应该完美还原
EXPECT_EQ(b, tc.b) << "B channel mismatch for (" << (int)tc.r << "," << (int)tc.g << "," << (int)tc.b << ")";
EXPECT_EQ(g, tc.g) << "G channel mismatch for (" << (int)tc.r << "," << (int)tc.g << "," << (int)tc.b << ")";
EXPECT_EQ(r, tc.r) << "R channel mismatch for (" << (int)tc.r << "," << (int)tc.g << "," << (int)tc.b << ")";
}
}
TEST_F(RGB565Test, RoundTrip_QuantizationError) {
// 测试所有颜色的量化误差在允许范围内
std::mt19937 rng(12345);
std::uniform_int_distribution<int> dist(0, 255);
for (int i = 0; i < 1000; i++) {
uint8_t origB = dist(rng);
uint8_t origG = dist(rng);
uint8_t origR = dist(rng);
uint16_t rgb565 = RGB565Converter::BGRAToRGB565(origB, origG, origR);
uint8_t b, g, r, a;
RGB565Converter::RGB565ToBGRA(rgb565, b, g, r, a);
// 验证误差在允许范围内
EXPECT_LE(RGB565Converter::CalculateError(origB, b), RGB565Converter::MaxError5Bit());
EXPECT_LE(RGB565Converter::CalculateError(origG, g), RGB565Converter::MaxError6Bit());
EXPECT_LE(RGB565Converter::CalculateError(origR, r), RGB565Converter::MaxError5Bit());
}
}
// ============================================
// 批量转换测试
// ============================================
TEST_F(RGB565Test, BatchConvert_SinglePixel) {
auto bgra = CreateBGRAPixels(1, 128, 64, 192);
std::vector<uint16_t> rgb565(1);
RGB565Converter::ConvertBGRAtoRGB565_Scalar(bgra.data(), rgb565.data(), 1);
// 验证转换结果
uint16_t expected = RGB565Converter::BGRAToRGB565(128, 64, 192);
EXPECT_EQ(rgb565[0], expected);
}
TEST_F(RGB565Test, BatchConvert_MultiplePixels) {
const size_t COUNT = 100;
auto bgra = CreateBGRAPixels(COUNT, 100, 150, 200);
std::vector<uint16_t> rgb565(COUNT);
RGB565Converter::ConvertBGRAtoRGB565_Scalar(bgra.data(), rgb565.data(), COUNT);
uint16_t expected = RGB565Converter::BGRAToRGB565(100, 150, 200);
for (size_t i = 0; i < COUNT; i++) {
EXPECT_EQ(rgb565[i], expected);
}
}
TEST_F(RGB565Test, BatchReverse_MultiplePixels) {
const size_t COUNT = 100;
std::vector<uint16_t> rgb565(COUNT, 0xF800); // 红色
std::vector<uint8_t> bgra(COUNT * 4);
RGB565Converter::ConvertRGB565ToBGRA(rgb565.data(), bgra.data(), COUNT);
for (size_t i = 0; i < COUNT; i++) {
EXPECT_EQ(bgra[i * 4 + 0], 0); // B
EXPECT_EQ(bgra[i * 4 + 1], 0); // G
EXPECT_EQ(bgra[i * 4 + 2], 255); // R
EXPECT_EQ(bgra[i * 4 + 3], 255); // A
}
}
TEST_F(RGB565Test, BatchRoundTrip) {
const size_t COUNT = 1000;
std::mt19937 rng(42);
std::uniform_int_distribution<int> dist(0, 255);
// 创建随机BGRA数据
std::vector<uint8_t> original(COUNT * 4);
for (size_t i = 0; i < COUNT * 4; i++) {
original[i] = (i % 4 == 3) ? 255 : dist(rng);
}
// 转换到 RGB565
std::vector<uint16_t> rgb565(COUNT);
RGB565Converter::ConvertBGRAtoRGB565_Scalar(original.data(), rgb565.data(), COUNT);
// 转换回 BGRA
std::vector<uint8_t> reconstructed(COUNT * 4);
RGB565Converter::ConvertRGB565ToBGRA(rgb565.data(), reconstructed.data(), COUNT);
// 验证所有像素误差在允许范围内
for (size_t i = 0; i < COUNT; i++) {
EXPECT_LE(RGB565Converter::CalculateError(original[i * 4 + 0], reconstructed[i * 4 + 0]),
RGB565Converter::MaxError5Bit()) << "Pixel " << i << " B";
EXPECT_LE(RGB565Converter::CalculateError(original[i * 4 + 1], reconstructed[i * 4 + 1]),
RGB565Converter::MaxError6Bit()) << "Pixel " << i << " G";
EXPECT_LE(RGB565Converter::CalculateError(original[i * 4 + 2], reconstructed[i * 4 + 2]),
RGB565Converter::MaxError5Bit()) << "Pixel " << i << " R";
EXPECT_EQ(reconstructed[i * 4 + 3], 255) << "Pixel " << i << " A";
}
}
// ============================================
// 边界值测试
// ============================================
TEST_F(RGB565Test, Boundary_AllZeros) {
uint16_t result = RGB565Converter::BGRAToRGB565(0, 0, 0);
EXPECT_EQ(result, 0);
}
TEST_F(RGB565Test, Boundary_AllOnes) {
uint16_t result = RGB565Converter::BGRAToRGB565(255, 255, 255);
EXPECT_EQ(result, 0xFFFF);
}
TEST_F(RGB565Test, Boundary_ChannelMax) {
// 单通道最大值
EXPECT_EQ(RGB565Converter::BGRAToRGB565(255, 0, 0), 0x001F); // B
EXPECT_EQ(RGB565Converter::BGRAToRGB565(0, 255, 0), 0x07E0); // G
EXPECT_EQ(RGB565Converter::BGRAToRGB565(0, 0, 255), 0xF800); // R
}
TEST_F(RGB565Test, Boundary_ChannelMin) {
// 单通道最小值(其他为最大)
EXPECT_EQ(RGB565Converter::BGRAToRGB565(0, 255, 255), 0xFFE0); // 无B
EXPECT_EQ(RGB565Converter::BGRAToRGB565(255, 0, 255), 0xF81F); // 无G
EXPECT_EQ(RGB565Converter::BGRAToRGB565(255, 255, 0), 0x07FF); // 无R
}
// ============================================
// 位填充测试
// ============================================
TEST_F(RGB565Test, BitFilling_5BitExpansion) {
// 测试5位扩展到8位的位填充策略
// 5位值 11111 (31) -> 8位值 11111111 (255)
// 公式: (x << 3) | (x >> 2)
// 最大值: 31 -> 255
uint8_t expanded = (31 << 3) | (31 >> 2);
EXPECT_EQ(expanded, 255);
// 最小值: 0 -> 0
expanded = (0 << 3) | (0 >> 2);
EXPECT_EQ(expanded, 0);
// 中间值: 16 -> 132 (10000 -> 10000100)
expanded = (16 << 3) | (16 >> 2);
EXPECT_EQ(expanded, 132);
}
TEST_F(RGB565Test, BitFilling_6BitExpansion) {
// 测试6位扩展到8位的位填充策略
// 6位值 111111 (63) -> 8位值 11111111 (255)
// 公式: (x << 2) | (x >> 4)
// 最大值: 63 -> 255
uint8_t expanded = (63 << 2) | (63 >> 4);
EXPECT_EQ(expanded, 255);
// 最小值: 0 -> 0
expanded = (0 << 2) | (0 >> 4);
EXPECT_EQ(expanded, 0);
// 中间值: 32 -> 130 (100000 -> 10000010)
expanded = (32 << 2) | (32 >> 4);
EXPECT_EQ(expanded, 130);
}
// ============================================
// Alpha通道测试
// ============================================
TEST_F(RGB565Test, Alpha_Ignored) {
// 不同alpha值应该产生相同的RGB565
auto bgra1 = CreateBGRAPixels(1, 100, 100, 100, 255);
auto bgra2 = CreateBGRAPixels(1, 100, 100, 100, 128);
auto bgra3 = CreateBGRAPixels(1, 100, 100, 100, 0);
std::vector<uint16_t> rgb565_1(1), rgb565_2(1), rgb565_3(1);
RGB565Converter::ConvertBGRAtoRGB565_Scalar(bgra1.data(), rgb565_1.data(), 1);
RGB565Converter::ConvertBGRAtoRGB565_Scalar(bgra2.data(), rgb565_2.data(), 1);
RGB565Converter::ConvertBGRAtoRGB565_Scalar(bgra3.data(), rgb565_3.data(), 1);
EXPECT_EQ(rgb565_1[0], rgb565_2[0]);
EXPECT_EQ(rgb565_2[0], rgb565_3[0]);
}
TEST_F(RGB565Test, Alpha_RestoredToOpaque) {
// 反向转换时Alpha应该恢复为255
std::vector<uint16_t> rgb565 = {0x1234, 0x5678, 0x9ABC};
std::vector<uint8_t> bgra(3 * 4);
RGB565Converter::ConvertRGB565ToBGRA(rgb565.data(), bgra.data(), 3);
for (size_t i = 0; i < 3; i++) {
EXPECT_EQ(bgra[i * 4 + 3], 255);
}
}
// ============================================
// 压缩率测试
// ============================================
TEST_F(RGB565Test, CompressionRatio) {
// BGRA: 4字节/像素
// RGB565: 2字节/像素
// 压缩率: 50%
const size_t PIXEL_COUNT = 1920 * 1080;
size_t bgraSize = PIXEL_COUNT * 4;
size_t rgb565Size = PIXEL_COUNT * 2;
EXPECT_EQ(rgb565Size, bgraSize / 2);
EXPECT_DOUBLE_EQ(static_cast<double>(rgb565Size) / bgraSize, 0.5);
}
// ============================================
// 特殊颜色测试
// ============================================
TEST_F(RGB565Test, CommonColors) {
struct ColorTest {
const char* name;
uint8_t r, g, b;
uint16_t expectedRGB565;
} colors[] = {
{"Black", 0, 0, 0, 0x0000},
{"White", 255, 255, 255, 0xFFFF},
{"Red", 255, 0, 0, 0xF800},
{"Green", 0, 255, 0, 0x07E0},
{"Blue", 0, 0, 255, 0x001F},
{"Yellow", 255, 255, 0, 0xFFE0},
{"Cyan", 0, 255, 255, 0x07FF},
{"Magenta", 255, 0, 255, 0xF81F},
};
for (const auto& c : colors) {
uint16_t result = RGB565Converter::BGRAToRGB565(c.b, c.g, c.r);
EXPECT_EQ(result, c.expectedRGB565) << "Color: " << c.name;
}
}
TEST_F(RGB565Test, GrayScales) {
// 测试灰度值转换
for (int gray = 0; gray <= 255; gray += 17) {
uint8_t g = static_cast<uint8_t>(gray);
uint16_t rgb565 = RGB565Converter::BGRAToRGB565(g, g, g);
uint8_t b, gr, r, a;
RGB565Converter::RGB565ToBGRA(rgb565, b, gr, r, a);
// 灰度值在量化误差范围内应该保持一致
EXPECT_NEAR(b, g, 8);
EXPECT_NEAR(gr, g, 4);
EXPECT_NEAR(r, g, 8);
}
}
// ============================================
// 参数化测试
// ============================================
class RGB565ChannelTest : public ::testing::TestWithParam<int> {};
TEST_P(RGB565ChannelTest, ChannelValueRange) {
int value = GetParam();
uint8_t v = static_cast<uint8_t>(value);
// 测试R通道
{
uint16_t rgb565 = RGB565Converter::BGRAToRGB565(0, 0, v);
uint8_t b, g, r, a;
RGB565Converter::RGB565ToBGRA(rgb565, b, g, r, a);
EXPECT_LE(RGB565Converter::CalculateError(v, r), RGB565Converter::MaxError5Bit());
}
// 测试G通道
{
uint16_t rgb565 = RGB565Converter::BGRAToRGB565(0, v, 0);
uint8_t b, g, r, a;
RGB565Converter::RGB565ToBGRA(rgb565, b, g, r, a);
EXPECT_LE(RGB565Converter::CalculateError(v, g), RGB565Converter::MaxError6Bit());
}
// 测试B通道
{
uint16_t rgb565 = RGB565Converter::BGRAToRGB565(v, 0, 0);
uint8_t b, g, r, a;
RGB565Converter::RGB565ToBGRA(rgb565, b, g, r, a);
EXPECT_LE(RGB565Converter::CalculateError(v, b), RGB565Converter::MaxError5Bit());
}
}
INSTANTIATE_TEST_SUITE_P(
AllValues,
RGB565ChannelTest,
::testing::Range(0, 256, 1) // 测试0-255所有值
);
// ============================================
// 大数据量测试
// ============================================
TEST_F(RGB565Test, LargeFrame_1080p) {
const size_t WIDTH = 1920, HEIGHT = 1080;
const size_t COUNT = WIDTH * HEIGHT;
// 创建渐变图像
std::vector<uint8_t> bgra(COUNT * 4);
for (size_t y = 0; y < HEIGHT; y++) {
for (size_t x = 0; x < WIDTH; x++) {
size_t idx = (y * WIDTH + x) * 4;
bgra[idx + 0] = static_cast<uint8_t>(x * 255 / WIDTH);
bgra[idx + 1] = static_cast<uint8_t>(y * 255 / HEIGHT);
bgra[idx + 2] = static_cast<uint8_t>((x + y) * 127 / (WIDTH + HEIGHT));
bgra[idx + 3] = 255;
}
}
// 转换
std::vector<uint16_t> rgb565(COUNT);
RGB565Converter::ConvertBGRAtoRGB565_Scalar(bgra.data(), rgb565.data(), COUNT);
// 验证大小
EXPECT_EQ(rgb565.size() * sizeof(uint16_t), COUNT * 2);
// 抽样验证
for (size_t i = 0; i < COUNT; i += COUNT / 100) {
uint16_t expected = RGB565Converter::BGRAToRGB565(bgra[i * 4 + 0], bgra[i * 4 + 1], bgra[i * 4 + 2]);
EXPECT_EQ(rgb565[i], expected) << "Mismatch at pixel " << i;
}
}
// ============================================
// 端序测试
// ============================================
TEST_F(RGB565Test, Endianness_LittleEndian) {
// RGB565 应该以小端存储
uint16_t rgb565 = 0x1234;
uint8_t* bytes = reinterpret_cast<uint8_t*>(&rgb565);
// 在小端系统上: bytes[0] = 0x34, bytes[1] = 0x12
EXPECT_EQ(bytes[0], 0x34);
EXPECT_EQ(bytes[1], 0x12);
}
// ============================================
// 错误处理测试
// ============================================
TEST_F(RGB565Test, ZeroPixelCount) {
std::vector<uint8_t> bgra(4);
std::vector<uint16_t> rgb565(1);
// 零像素不应崩溃
RGB565Converter::ConvertBGRAtoRGB565_Scalar(bgra.data(), rgb565.data(), 0);
RGB565Converter::ConvertRGB565ToBGRA(rgb565.data(), bgra.data(), 0);
}