// Copyright 2022 Google LLC // SPDX-License-Identifier: BSD-2-Clause #include #include #include "avif/avif.h" #include "aviftest_helpers.h" #include "gtest/gtest.h" #include "y4m.h" using testing::Combine; using testing::Values; namespace libavif { namespace { class Y4mTest : public testing::TestWithParam< std::tuple> {}; TEST_P(Y4mTest, EncodeDecode) { const int width = std::get<0>(GetParam()); const int height = std::get<1>(GetParam()); const int bit_depth = std::get<2>(GetParam()); const avifPixelFormat yuv_format = std::get<3>(GetParam()); const avifRange yuv_range = std::get<4>(GetParam()); const bool create_alpha = std::get<5>(GetParam()); std::ostringstream file_path; file_path << testing::TempDir() << "avify4mtest_encodedecode_" << width << "_" << height << "_" << bit_depth << "_" << yuv_format << "_" << yuv_range << "_" << create_alpha; testutil::AvifImagePtr image = testutil::CreateImage( width, height, bit_depth, yuv_format, create_alpha ? AVIF_PLANES_ALL : AVIF_PLANES_YUV, yuv_range); ASSERT_NE(image, nullptr); const uint32_t yuva[] = { (yuv_range == AVIF_RANGE_LIMITED) ? (235u << (bit_depth - 8)) : ((1u << bit_depth) - 1), (yuv_range == AVIF_RANGE_LIMITED) ? (240u << (bit_depth - 8)) : ((1u << bit_depth) - 1), (yuv_range == AVIF_RANGE_LIMITED) ? (240u << (bit_depth - 8)) : ((1u << bit_depth) - 1), (1u << bit_depth) - 1}; testutil::FillImagePlain(image.get(), yuva); ASSERT_TRUE(y4mWrite(file_path.str().c_str(), image.get())); testutil::AvifImagePtr decoded(avifImageCreateEmpty(), avifImageDestroy); ASSERT_NE(decoded, nullptr); ASSERT_TRUE(y4mRead(file_path.str().c_str(), decoded.get(), /*sourceTiming=*/nullptr, /*iter=*/nullptr)); EXPECT_TRUE(testutil::AreImagesEqual(*image, *decoded)); } TEST_P(Y4mTest, OutOfRange) { const int width = std::get<0>(GetParam()); const int height = std::get<1>(GetParam()); const int bit_depth = std::get<2>(GetParam()); const avifPixelFormat yuv_format = std::get<3>(GetParam()); const avifRange yuv_range = std::get<4>(GetParam()); const bool create_alpha = std::get<5>(GetParam()); std::ostringstream file_path; file_path << testing::TempDir() << "avify4mtest_outofrange_" << width << "_" << height << "_" << bit_depth << "_" << yuv_format << "_" << yuv_range << "_" << create_alpha; testutil::AvifImagePtr image = testutil::CreateImage( width, height, bit_depth, yuv_format, create_alpha ? AVIF_PLANES_ALL : AVIF_PLANES_YUV, yuv_range); ASSERT_NE(image, nullptr); // Insert values that may be out-of-range on purpose compared to the specified // bit_depth and yuv_range. const uint32_t yuva8[] = {255, 0, 255, 255}; const uint32_t yuva16[] = {0, (1u << 16) - 1u, 0, (1u << 16) - 1u}; testutil::FillImagePlain(image.get(), avifImageUsesU16(image.get()) ? yuva16 : yuva8); ASSERT_TRUE(y4mWrite(file_path.str().c_str(), image.get())); // y4mRead() should clamp the values to respect the specified depth in order // to avoid computation with unexpected sample values. However, it does not // respect the limited ("video") range because the libavif API just passes // that tag along, it is ignored by the compression algorithm. testutil::AvifImagePtr decoded(avifImageCreateEmpty(), avifImageDestroy); ASSERT_NE(decoded, nullptr); ASSERT_TRUE(y4mRead(file_path.str().c_str(), decoded.get(), /*sourceTiming=*/nullptr, /*iter=*/nullptr)); // Pass it through the libavif API to make sure reading a bad y4m does not // trigger undefined behavior. const testutil::AvifRwData encoded = testutil::Encode(decoded.get()); EXPECT_NE(testutil::Decode(encoded.data, encoded.size), nullptr); } INSTANTIATE_TEST_SUITE_P( OpaqueCombinations, Y4mTest, Combine(/*width=*/Values(1, 2, 3), /*height=*/Values(1, 2, 3), /*depths=*/Values(8, 10, 12), Values(AVIF_PIXEL_FORMAT_YUV444, AVIF_PIXEL_FORMAT_YUV422, AVIF_PIXEL_FORMAT_YUV420, AVIF_PIXEL_FORMAT_YUV400), Values(AVIF_RANGE_LIMITED, AVIF_RANGE_FULL), /*create_alpha=*/Values(false))); // Writing alpha is currently only supported in 8bpc YUV444. INSTANTIATE_TEST_SUITE_P(AlphaCombinations, Y4mTest, Combine(/*width=*/Values(1, 2, 3), /*height=*/Values(1, 2, 3), /*depths=*/Values(8), Values(AVIF_PIXEL_FORMAT_YUV444), Values(AVIF_RANGE_LIMITED, AVIF_RANGE_FULL), /*create_alpha=*/Values(true))); } // namespace } // namespace libavif