// RGB565Test.cpp - Phase 4: RGB565压缩单元测试 // 测试 BGRA <-> RGB565 颜色空间转换 #include #include #include #include #include #include // ============================================ // 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( ((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( ((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(original) - static_cast(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 CreateBGRAPixels(size_t count, uint8_t b, uint8_t g, uint8_t r, uint8_t a = 0xFF) { std::vector 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 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 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 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 rgb565(COUNT, 0xF800); // 红色 std::vector 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 dist(0, 255); // 创建随机BGRA数据 std::vector original(COUNT * 4); for (size_t i = 0; i < COUNT * 4; i++) { original[i] = (i % 4 == 3) ? 255 : dist(rng); } // 转换到 RGB565 std::vector rgb565(COUNT); RGB565Converter::ConvertBGRAtoRGB565_Scalar(original.data(), rgb565.data(), COUNT); // 转换回 BGRA std::vector 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 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 rgb565 = {0x1234, 0x5678, 0x9ABC}; std::vector 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(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(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 {}; TEST_P(RGB565ChannelTest, ChannelValueRange) { int value = GetParam(); uint8_t v = static_cast(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 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(x * 255 / WIDTH); bgra[idx + 1] = static_cast(y * 255 / HEIGHT); bgra[idx + 2] = static_cast((x + y) * 127 / (WIDTH + HEIGHT)); bgra[idx + 3] = 255; } } // 转换 std::vector 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(&rgb565); // 在小端系统上: bytes[0] = 0x34, bytes[1] = 0x12 EXPECT_EQ(bytes[0], 0x34); EXPECT_EQ(bytes[1], 0x12); } // ============================================ // 错误处理测试 // ============================================ TEST_F(RGB565Test, ZeroPixelCount) { std::vector bgra(4); std::vector rgb565(1); // 零像素不应崩溃 RGB565Converter::ConvertBGRAtoRGB565_Scalar(bgra.data(), rgb565.data(), 0); RGB565Converter::ConvertRGB565ToBGRA(rgb565.data(), bgra.data(), 0); }