diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 3106260702..0f5d3300a3 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -145,6 +145,7 @@ if(AVIF_ENABLE_GTEST) add_avif_gtest_with_data(avifreadimagetest) add_avif_internal_gtest(avifrgbtest) add_avif_gtest(avifrgbtoyuvtest) + add_avif_gtest(avifrgbtoyuvthreadingtest) add_avif_gtest_with_data(avifscaletest) add_avif_internal_gtest(avifstreamtest) add_avif_internal_gtest(aviftilingtest) diff --git a/tests/gtest/avifrgbtoyuvtest.cc b/tests/gtest/avifrgbtoyuvtest.cc index b9664844ba..d03e5ef1c3 100644 --- a/tests/gtest/avifrgbtoyuvtest.cc +++ b/tests/gtest/avifrgbtoyuvtest.cc @@ -478,7 +478,7 @@ INSTANTIATE_TEST_SUITE_P( // bring any loss in the roundtrip. INSTANTIATE_TEST_SUITE_P(Identity8b, RGBToYUVTest, Combine(/*rgb_depth=*/Values(8), - /*yuv_depth=*/Values(8, 10, 12, 16), + /*yuv_depth=*/Values(8, 12, 16), Values(AVIF_RGB_FORMAT_RGBA), Values(AVIF_PIXEL_FORMAT_YUV444), Values(AVIF_RANGE_FULL), @@ -531,8 +531,7 @@ INSTANTIATE_TEST_SUITE_P( Combine( /*rgb_depth=*/Values(8), /*yuv_depth=*/Values(8), Values(AVIF_RGB_FORMAT_RGBA), - Values(AVIF_PIXEL_FORMAT_YUV444, AVIF_PIXEL_FORMAT_YUV422, - AVIF_PIXEL_FORMAT_YUV420), + Values(AVIF_PIXEL_FORMAT_YUV444, AVIF_PIXEL_FORMAT_YUV420), Values(AVIF_RANGE_FULL), Values(kMatrixCoefficientsBT601), Values(AVIF_CHROMA_DOWNSAMPLING_AUTOMATIC), /*add_noise=*/Values(false), @@ -546,7 +545,7 @@ INSTANTIATE_TEST_SUITE_P( // depth should be lossless. INSTANTIATE_TEST_SUITE_P(MonochromeLossless8b, RGBToYUVTest, Combine(/*rgb_depth=*/Values(8), - /*yuv_depth=*/Values(8, 10, 12, 16), + /*yuv_depth=*/Values(8), Values(AVIF_RGB_FORMAT_RGBA), Values(AVIF_PIXEL_FORMAT_YUV400), Values(AVIF_RANGE_FULL), @@ -558,7 +557,7 @@ INSTANTIATE_TEST_SUITE_P(MonochromeLossless8b, RGBToYUVTest, /*min_psnr=*/Values(99.))); INSTANTIATE_TEST_SUITE_P(MonochromeLossless10b, RGBToYUVTest, Combine(/*rgb_depth=*/Values(10), - /*yuv_depth=*/Values(10, 12, 16), + /*yuv_depth=*/Values(10), Values(AVIF_RGB_FORMAT_RGBA), Values(AVIF_PIXEL_FORMAT_YUV400), Values(AVIF_RANGE_FULL), @@ -570,7 +569,7 @@ INSTANTIATE_TEST_SUITE_P(MonochromeLossless10b, RGBToYUVTest, /*min_psnr=*/Values(99.))); INSTANTIATE_TEST_SUITE_P(MonochromeLossless12b, RGBToYUVTest, Combine(/*rgb_depth=*/Values(12), - /*yuv_depth=*/Values(12, 16), + /*yuv_depth=*/Values(12), Values(AVIF_RGB_FORMAT_RGBA), Values(AVIF_PIXEL_FORMAT_YUV400), Values(AVIF_RANGE_FULL), @@ -599,8 +598,36 @@ INSTANTIATE_TEST_SUITE_P( Combine( /*rgb_depth=*/Values(8), /*yuv_depth=*/Values(8, 10, 12), Values(AVIF_RGB_FORMAT_RGBA), + Values(AVIF_PIXEL_FORMAT_YUV420), Values(AVIF_RANGE_FULL), + Values(kMatrixCoefficientsBT601), + Values(AVIF_CHROMA_DOWNSAMPLING_SHARP_YUV), + /*add_noise=*/Values(true), + /*rgb_step=*/Values(17), + /*max_abs_average_diff=*/Values(1.2), // Sharp YUV introduces some + // color shift. + /*min_psnr=*/Values(34.) // SharpYuv distortion is acceptable. + )); +INSTANTIATE_TEST_SUITE_P( + SharpYuv8BitRanges, RGBToYUVTest, + Combine( + /*rgb_depth=*/Values(8), + /*yuv_depth=*/Values(8), Values(AVIF_RGB_FORMAT_RGBA), Values(AVIF_PIXEL_FORMAT_YUV420), Values(AVIF_RANGE_LIMITED, AVIF_RANGE_FULL), + Values(kMatrixCoefficientsBT601), + Values(AVIF_CHROMA_DOWNSAMPLING_SHARP_YUV), + /*add_noise=*/Values(true), + /*rgb_step=*/Values(17), + /*max_abs_average_diff=*/Values(1.2), // Sharp YUV introduces some + // color shift. + /*min_psnr=*/Values(34.) // SharpYuv distortion is acceptable. + )); +INSTANTIATE_TEST_SUITE_P( + SharpYuv8BitMatrixCoefficients, RGBToYUVTest, + Combine( + /*rgb_depth=*/Values(8), + /*yuv_depth=*/Values(8), Values(AVIF_RGB_FORMAT_RGBA), + Values(AVIF_PIXEL_FORMAT_YUV420), Values(AVIF_RANGE_FULL), Values(kMatrixCoefficientsBT601, kMatrixCoefficientsBT709), Values(AVIF_CHROMA_DOWNSAMPLING_SHARP_YUV), /*add_noise=*/Values(true), @@ -613,9 +640,8 @@ INSTANTIATE_TEST_SUITE_P( SharpYuv10Bit, RGBToYUVTest, Combine( /*rgb_depth=*/Values(10), - /*yuv_depth=*/Values(8, 10, 12), Values(AVIF_RGB_FORMAT_RGBA), - Values(AVIF_PIXEL_FORMAT_YUV420), - Values(AVIF_RANGE_LIMITED, AVIF_RANGE_FULL), + /*yuv_depth=*/Values(10), Values(AVIF_RGB_FORMAT_RGBA), + Values(AVIF_PIXEL_FORMAT_YUV420), Values(AVIF_RANGE_FULL), Values(kMatrixCoefficientsBT601), Values(AVIF_CHROMA_DOWNSAMPLING_SHARP_YUV), /*add_noise=*/Values(true), @@ -629,8 +655,7 @@ INSTANTIATE_TEST_SUITE_P( Combine( /*rgb_depth=*/Values(12), /*yuv_depth=*/Values(8, 10, 12), Values(AVIF_RGB_FORMAT_RGBA), - Values(AVIF_PIXEL_FORMAT_YUV420), - Values(AVIF_RANGE_LIMITED, AVIF_RANGE_FULL), + Values(AVIF_PIXEL_FORMAT_YUV420), Values(AVIF_RANGE_FULL), Values(kMatrixCoefficientsBT601), Values(AVIF_CHROMA_DOWNSAMPLING_SHARP_YUV), /*add_noise=*/Values(true), @@ -645,8 +670,7 @@ INSTANTIATE_TEST_SUITE_P( /*rgb_depth=*/Values(16), // TODO(yguyon): Why max_abs_average_diff>28 if RGB16 to YUV10 full rng? /*yuv_depth=*/Values(8, /*10,*/ 12), Values(AVIF_RGB_FORMAT_RGBA), - Values(AVIF_PIXEL_FORMAT_YUV420), - Values(AVIF_RANGE_LIMITED, AVIF_RANGE_FULL), + Values(AVIF_PIXEL_FORMAT_YUV420), Values(AVIF_RANGE_FULL), Values(kMatrixCoefficientsBT601), Values(AVIF_CHROMA_DOWNSAMPLING_SHARP_YUV), /*add_noise=*/Values(true), @@ -659,14 +683,13 @@ INSTANTIATE_TEST_SUITE_P( // Can be used to print the drift of all RGB to YUV conversion possibilities. // Also used for coverage. INSTANTIATE_TEST_SUITE_P( - All8b, RGBToYUVTest, + All8bTo8b, RGBToYUVTest, Combine(/*rgb_depth=*/Values(8), - /*yuv_depth=*/Values(8, 10, 12, 16), + /*yuv_depth=*/Values(8), Values(AVIF_RGB_FORMAT_RGBA, AVIF_RGB_FORMAT_BGR), Values(AVIF_PIXEL_FORMAT_YUV444, AVIF_PIXEL_FORMAT_YUV422, AVIF_PIXEL_FORMAT_YUV420), - Values(AVIF_RANGE_LIMITED, AVIF_RANGE_FULL), - Values(kMatrixCoefficientsBT601), + Values(AVIF_RANGE_LIMITED), Values(kMatrixCoefficientsBT601), Values(AVIF_CHROMA_DOWNSAMPLING_AUTOMATIC), /*add_noise=*/Bool(), /*rgb_step=*/Values(61), // High or it would be too slow. @@ -676,11 +699,9 @@ INSTANTIATE_TEST_SUITE_P( INSTANTIATE_TEST_SUITE_P( All10b, RGBToYUVTest, Combine(/*rgb_depth=*/Values(10), - /*yuv_depth=*/Values(8, 10, 12, 16), Values(AVIF_RGB_FORMAT_RGBA), - Values(AVIF_PIXEL_FORMAT_YUV444, AVIF_PIXEL_FORMAT_YUV422, - AVIF_PIXEL_FORMAT_YUV420), - Values(AVIF_RANGE_LIMITED, AVIF_RANGE_FULL), - Values(kMatrixCoefficientsBT601), + /*yuv_depth=*/Values(10), Values(AVIF_RGB_FORMAT_RGBA), + Values(AVIF_PIXEL_FORMAT_YUV444, AVIF_PIXEL_FORMAT_YUV420), + Values(AVIF_RANGE_FULL), Values(kMatrixCoefficientsBT601), Values(AVIF_CHROMA_DOWNSAMPLING_AUTOMATIC), /*add_noise=*/Bool(), /*rgb_step=*/Values(211), // High or it would be too slow. @@ -690,11 +711,9 @@ INSTANTIATE_TEST_SUITE_P( INSTANTIATE_TEST_SUITE_P( All12b, RGBToYUVTest, Combine(/*rgb_depth=*/Values(12), - /*yuv_depth=*/Values(8, 10, 12, 16), Values(AVIF_RGB_FORMAT_RGBA), - Values(AVIF_PIXEL_FORMAT_YUV444, AVIF_PIXEL_FORMAT_YUV422, - AVIF_PIXEL_FORMAT_YUV420), - Values(AVIF_RANGE_LIMITED, AVIF_RANGE_FULL), - Values(kMatrixCoefficientsBT601), + /*yuv_depth=*/Values(12), Values(AVIF_RGB_FORMAT_RGBA), + Values(AVIF_PIXEL_FORMAT_YUV444, AVIF_PIXEL_FORMAT_YUV420), + Values(AVIF_RANGE_LIMITED), Values(kMatrixCoefficientsBT601), Values(AVIF_CHROMA_DOWNSAMPLING_AUTOMATIC), /*add_noise=*/Bool(), /*rgb_step=*/Values(809), // High or it would be too slow. @@ -705,10 +724,8 @@ INSTANTIATE_TEST_SUITE_P( All16b, RGBToYUVTest, Combine(/*rgb_depth=*/Values(16), /*yuv_depth=*/Values(16), Values(AVIF_RGB_FORMAT_RGBA), - Values(AVIF_PIXEL_FORMAT_YUV444, AVIF_PIXEL_FORMAT_YUV422, - AVIF_PIXEL_FORMAT_YUV420), - Values(AVIF_RANGE_LIMITED, AVIF_RANGE_FULL), - Values(kMatrixCoefficientsBT601), + Values(AVIF_PIXEL_FORMAT_YUV444, AVIF_PIXEL_FORMAT_YUV420), + Values(AVIF_RANGE_FULL), Values(kMatrixCoefficientsBT601), Values(AVIF_CHROMA_DOWNSAMPLING_AUTOMATIC), /*add_noise=*/Bool(), /*rgb_step=*/Values(16001), // High or it would be too slow. @@ -731,108 +748,5 @@ INSTANTIATE_TEST_SUITE_P( /*max_abs_average_diff=*/Values(10.), /*min_psnr=*/Values(10.))); -// Converts YUV pixels to RGB using one thread and multiple threads and checks -// whether the results of both are identical. -class YUVToRGBThreadingTest - : public testing::TestWithParam> {}; - -TEST_P(YUVToRGBThreadingTest, TestIdentical) { - const int rgb_depth = std::get<0>(GetParam()); - const int yuv_depth = std::get<1>(GetParam()); - const int width = std::get<2>(GetParam()); - const int height = std::get<3>(GetParam()); - const avifRGBFormat rgb_format = std::get<4>(GetParam()); - const avifPixelFormat yuv_format = std::get<5>(GetParam()); - const int maxThreads = std::get<6>(GetParam()); - const bool avoidLibYUV = std::get<7>(GetParam()); - const avifChromaUpsampling chromaUpsampling = std::get<8>(GetParam()); - const bool has_alpha = std::get<9>(GetParam()); - - if (rgb_depth > 8 && rgb_format == AVIF_RGB_FORMAT_RGB_565) { - return; - } - - ImagePtr yuv(avifImageCreate(width, height, yuv_depth, yuv_format)); - ASSERT_NE(yuv, nullptr); - yuv->matrixCoefficients = AVIF_MATRIX_COEFFICIENTS_BT601; - yuv->yuvRange = AVIF_RANGE_FULL; - - // Fill YUVA planes with random values. - srand(0xAABBCCDD); - const int yuv_max = (1 << yuv_depth); - ASSERT_EQ(avifImageAllocatePlanes( - yuv.get(), has_alpha ? AVIF_PLANES_ALL : AVIF_PLANES_YUV), - AVIF_RESULT_OK); - for (int plane = AVIF_CHAN_Y; plane <= AVIF_CHAN_A; ++plane) { - const uint32_t plane_width = avifImagePlaneWidth(yuv.get(), plane); - if (plane_width == 0) continue; - const uint32_t plane_height = avifImagePlaneHeight(yuv.get(), plane); - const uint32_t rowBytes = avifImagePlaneRowBytes(yuv.get(), plane); - uint8_t* row = avifImagePlane(yuv.get(), plane); - for (uint32_t y = 0; y < plane_height; ++y, row += rowBytes) { - for (uint32_t x = 0; x < plane_width; ++x) { - if (yuv_depth == 8) { - row[x] = (uint8_t)(rand() % yuv_max); - } else { - ((uint16_t*)row)[x] = (uint16_t)(rand() % yuv_max); - } - } - } - } - - // Convert to RGB with 1 thread. - testutil::AvifRgbImage rgb(yuv.get(), rgb_depth, rgb_format); - rgb.avoidLibYUV = avoidLibYUV; - rgb.chromaUpsampling = chromaUpsampling; - ASSERT_EQ(avifImageYUVToRGB(yuv.get(), &rgb), AVIF_RESULT_OK); - - // Convert to RGB with multiple threads. - testutil::AvifRgbImage rgb_threaded(yuv.get(), rgb_depth, rgb_format); - rgb_threaded.avoidLibYUV = avoidLibYUV; - rgb_threaded.chromaUpsampling = chromaUpsampling; - rgb_threaded.maxThreads = maxThreads; - ASSERT_EQ(avifImageYUVToRGB(yuv.get(), &rgb_threaded), AVIF_RESULT_OK); - - EXPECT_TRUE(testutil::AreImagesEqual(rgb, rgb_threaded)); -} - -INSTANTIATE_TEST_SUITE_P( - YUVToRGBThreadingTestInstance, YUVToRGBThreadingTest, - Combine(/*rgb_depth=*/Values(8, 16), - /*yuv_depth=*/Values(8, 10), - /*width=*/Values(1, 2, 127, 200), - /*height=*/Values(1, 2, 127, 200), - Values(AVIF_RGB_FORMAT_RGB, AVIF_RGB_FORMAT_RGBA), - Range(AVIF_PIXEL_FORMAT_YUV444, AVIF_PIXEL_FORMAT_COUNT), - // Test an odd and even number for threads. Not adding all possible - // thread values to keep the number of test instances low. - /*threads=*/Values(2, 7), - /*avoidLibYUV=*/Bool(), - Values(AVIF_CHROMA_UPSAMPLING_FASTEST, - AVIF_CHROMA_UPSAMPLING_BILINEAR), - /*has_alpha=*/Bool())); - -// This will generate a large number of test instances and hence it is disabled -// by default. It can be run manually if necessary. -INSTANTIATE_TEST_SUITE_P( - DISABLED_ExhaustiveYUVToRGBThreadingTestInstance, YUVToRGBThreadingTest, - Combine(/*rgb_depth=*/Values(8, 10, 12, 16), - /*yuv_depth=*/Values(8, 10, 12), - /*width=*/Values(1, 2, 127, 200), - /*height=*/Values(1, 2, 127, 200), - Range(AVIF_RGB_FORMAT_RGB, AVIF_RGB_FORMAT_COUNT), - Range(AVIF_PIXEL_FORMAT_YUV444, AVIF_PIXEL_FORMAT_COUNT), - /*threads=*/Range(0, 9), - /*avoidLibYUV=*/Bool(), - Values(AVIF_CHROMA_UPSAMPLING_AUTOMATIC, - AVIF_CHROMA_UPSAMPLING_FASTEST, - AVIF_CHROMA_UPSAMPLING_NEAREST, - AVIF_CHROMA_UPSAMPLING_BILINEAR), - /*has_alpha=*/Bool())); - } // namespace } // namespace avif diff --git a/tests/gtest/avifrgbtoyuvthreadingtest.cc b/tests/gtest/avifrgbtoyuvthreadingtest.cc new file mode 100644 index 0000000000..89588e33c7 --- /dev/null +++ b/tests/gtest/avifrgbtoyuvthreadingtest.cc @@ -0,0 +1,124 @@ +// Copyright 2022 Google LLC +// SPDX-License-Identifier: BSD-2-Clause + +#include +#include +#include +#include + +#include "aviftest_helpers.h" +#include "gtest/gtest.h" + +using ::testing::Bool; +using ::testing::Combine; +using ::testing::Range; +using ::testing::Values; + +namespace avif { +namespace { + +// Converts YUV pixels to RGB using one thread and multiple threads and checks +// whether the results of both are identical. +class YUVToRGBThreadingTest + : public testing::TestWithParam> {}; + +TEST_P(YUVToRGBThreadingTest, TestIdentical) { + const int rgb_depth = std::get<0>(GetParam()); + const int yuv_depth = std::get<1>(GetParam()); + const int width = std::get<2>(GetParam()); + const int height = std::get<3>(GetParam()); + const avifRGBFormat rgb_format = std::get<4>(GetParam()); + const avifPixelFormat yuv_format = std::get<5>(GetParam()); + const int maxThreads = std::get<6>(GetParam()); + const bool avoidLibYUV = std::get<7>(GetParam()); + const avifChromaUpsampling chromaUpsampling = std::get<8>(GetParam()); + const bool has_alpha = std::get<9>(GetParam()); + + if (rgb_depth > 8 && rgb_format == AVIF_RGB_FORMAT_RGB_565) { + return; + } + + ImagePtr yuv(avifImageCreate(width, height, yuv_depth, yuv_format)); + ASSERT_NE(yuv, nullptr); + yuv->matrixCoefficients = AVIF_MATRIX_COEFFICIENTS_BT601; + yuv->yuvRange = AVIF_RANGE_FULL; + + // Fill YUVA planes with random values. + srand(0xAABBCCDD); + const int yuv_max = (1 << yuv_depth); + ASSERT_EQ(avifImageAllocatePlanes( + yuv.get(), has_alpha ? AVIF_PLANES_ALL : AVIF_PLANES_YUV), + AVIF_RESULT_OK); + for (int plane = AVIF_CHAN_Y; plane <= AVIF_CHAN_A; ++plane) { + const uint32_t plane_width = avifImagePlaneWidth(yuv.get(), plane); + if (plane_width == 0) continue; + const uint32_t plane_height = avifImagePlaneHeight(yuv.get(), plane); + const uint32_t rowBytes = avifImagePlaneRowBytes(yuv.get(), plane); + uint8_t* row = avifImagePlane(yuv.get(), plane); + for (uint32_t y = 0; y < plane_height; ++y, row += rowBytes) { + for (uint32_t x = 0; x < plane_width; ++x) { + if (yuv_depth == 8) { + row[x] = (uint8_t)(rand() % yuv_max); + } else { + ((uint16_t*)row)[x] = (uint16_t)(rand() % yuv_max); + } + } + } + } + + // Convert to RGB with 1 thread. + testutil::AvifRgbImage rgb(yuv.get(), rgb_depth, rgb_format); + rgb.avoidLibYUV = avoidLibYUV; + rgb.chromaUpsampling = chromaUpsampling; + ASSERT_EQ(avifImageYUVToRGB(yuv.get(), &rgb), AVIF_RESULT_OK); + + // Convert to RGB with multiple threads. + testutil::AvifRgbImage rgb_threaded(yuv.get(), rgb_depth, rgb_format); + rgb_threaded.avoidLibYUV = avoidLibYUV; + rgb_threaded.chromaUpsampling = chromaUpsampling; + rgb_threaded.maxThreads = maxThreads; + ASSERT_EQ(avifImageYUVToRGB(yuv.get(), &rgb_threaded), AVIF_RESULT_OK); + + EXPECT_TRUE(testutil::AreImagesEqual(rgb, rgb_threaded)); +} + +INSTANTIATE_TEST_SUITE_P( + YUVToRGBThreadingTestInstance, YUVToRGBThreadingTest, + Combine(/*rgb_depth=*/Values(8, 16), + /*yuv_depth=*/Values(8, 10), + /*width=*/Values(1, 2, 127, 200), + /*height=*/Values(1, 2, 127, 200), + Values(AVIF_RGB_FORMAT_RGB, AVIF_RGB_FORMAT_RGBA), + Range(AVIF_PIXEL_FORMAT_YUV444, AVIF_PIXEL_FORMAT_COUNT), + // Test an odd and even number for threads. Not adding all possible + // thread values to keep the number of test instances low. + /*threads=*/Values(2, 7), + /*avoidLibYUV=*/Bool(), + Values(AVIF_CHROMA_UPSAMPLING_FASTEST, + AVIF_CHROMA_UPSAMPLING_BILINEAR), + /*has_alpha=*/Bool())); + +// This will generate a large number of test instances and hence it is disabled +// by default. It can be run manually if necessary. +INSTANTIATE_TEST_SUITE_P( + DISABLED_ExhaustiveYUVToRGBThreadingTestInstance, YUVToRGBThreadingTest, + Combine(/*rgb_depth=*/Values(8, 10, 12, 16), + /*yuv_depth=*/Values(8, 10, 12), + /*width=*/Values(1, 2, 127, 200), + /*height=*/Values(1, 2, 127, 200), + Range(AVIF_RGB_FORMAT_RGB, AVIF_RGB_FORMAT_COUNT), + Range(AVIF_PIXEL_FORMAT_YUV444, AVIF_PIXEL_FORMAT_COUNT), + /*threads=*/Range(0, 9), + /*avoidLibYUV=*/Bool(), + Values(AVIF_CHROMA_UPSAMPLING_AUTOMATIC, + AVIF_CHROMA_UPSAMPLING_FASTEST, + AVIF_CHROMA_UPSAMPLING_NEAREST, + AVIF_CHROMA_UPSAMPLING_BILINEAR), + /*has_alpha=*/Bool())); + +} // namespace +} // namespace avif