// Copyright 2023 Google LLC // SPDX-License-Identifier: BSD-2-Clause #include "avif/avif.h" #include "aviftest_helpers.h" #include "avifutil.h" #include "gtest/gtest.h" namespace libavif { namespace { // Used to pass the data folder path to the GoogleTest suites. const char* data_path = nullptr; // Tests encode/decode round trips under different matrix coefficients. TEST(BasicTest, EncodeDecodeMatrixCoefficients) { for (const auto& file_name : {"paris_icc_exif_xmp.png", "paris_exif_xmp_icc.jpg"}) { // Read a ground truth image with default matrix coefficients. const std::string file_path = std::string(data_path) + file_name; const testutil::AvifImagePtr ground_truth_image = testutil::ReadImage(data_path, file_name); for (auto matrix_coefficient : { #if defined(AVIF_ENABLE_EXPERIMENTAL_YCGCO_R) AVIF_MATRIX_COEFFICIENTS_YCGCO_RE, AVIF_MATRIX_COEFFICIENTS_YCGCO_RO, #endif AVIF_MATRIX_COEFFICIENTS_IDENTITY, AVIF_MATRIX_COEFFICIENTS_YCGCO }) { // Read a ground truth image but ask for certain matrix coefficients. testutil::AvifImagePtr image(avifImageCreateEmpty(), avifImageDestroy); ASSERT_NE(image, nullptr); image->matrixCoefficients = (avifMatrixCoefficients)matrix_coefficient; const avifAppFileFormat file_format = avifReadImage( file_path.c_str(), /*requestedFormat=*/AVIF_PIXEL_FORMAT_NONE, /*requestedDepth=*/0, /*chromaDownsampling=*/AVIF_CHROMA_DOWNSAMPLING_AUTOMATIC, /*ignoreColorProfile=*/false, /*ignoreExif=*/false, /*ignoreXMP=*/false, /*allowChangingCicp=*/true, image.get(), /*outDepth=*/nullptr, /*sourceTiming=*/nullptr, /*frameIter=*/nullptr); #if defined(AVIF_ENABLE_EXPERIMENTAL_YCGCO_R) if (matrix_coefficient == AVIF_MATRIX_COEFFICIENTS_YCGCO_RO) { // AVIF_MATRIX_COEFFICIENTS_YCGCO_RO does not work because the input // depth is not odd. ASSERT_EQ(file_format, AVIF_APP_FILE_FORMAT_UNKNOWN); continue; } #endif ASSERT_NE(file_format, AVIF_APP_FILE_FORMAT_UNKNOWN); // Encode. testutil::AvifEncoderPtr encoder(avifEncoderCreate(), avifEncoderDestroy); ASSERT_NE(encoder, nullptr); encoder->speed = AVIF_SPEED_FASTEST; encoder->quality = AVIF_QUALITY_LOSSLESS; encoder->qualityAlpha = AVIF_QUALITY_LOSSLESS; testutil::AvifRwData encoded; avifResult result = avifEncoderWrite(encoder.get(), image.get(), &encoded); ASSERT_EQ(result, AVIF_RESULT_OK) << avifResultToString(result); // Decode to RAM. testutil::AvifImagePtr decoded(avifImageCreateEmpty(), avifImageDestroy); ASSERT_NE(decoded, nullptr); testutil::AvifDecoderPtr decoder(avifDecoderCreate(), avifDecoderDestroy); ASSERT_NE(decoder, nullptr); result = avifDecoderReadMemory(decoder.get(), decoded.get(), encoded.data, encoded.size); ASSERT_EQ(result, AVIF_RESULT_OK) << avifResultToString(result); // Convert to a temporary PNG and read back, to have default matrix // coefficients. const std::string temp_dir = testing::TempDir(); const std::string temp_file = "decoded_default.png"; const std::string tmp_path = temp_dir + temp_file; ASSERT_TRUE(testutil::WriteImage(decoded.get(), tmp_path.c_str())); const testutil::AvifImagePtr decoded_default = testutil::ReadImage(temp_dir.c_str(), temp_file.c_str()); // Verify that the ground truth and decoded images are the same. const bool are_images_equal = testutil::AreImagesEqual(*ground_truth_image, *decoded_default); if (matrix_coefficient == AVIF_MATRIX_COEFFICIENTS_IDENTITY #if defined(AVIF_ENABLE_EXPERIMENTAL_YCGCO_R) || matrix_coefficient == AVIF_MATRIX_COEFFICIENTS_YCGCO_RE #endif ) { ASSERT_TRUE(are_images_equal); } else { // AVIF_MATRIX_COEFFICIENTS_YCGCO is not lossless because it does not // expand the input bit range for YCgCo values. ASSERT_FALSE(are_images_equal); } } } } } // namespace } // namespace libavif int main(int argc, char** argv) { ::testing::InitGoogleTest(&argc, argv); if (argc != 2) { std::cerr << "There must be exactly one argument containing the path to " "the test data folder" << std::endl; return 1; } libavif::data_path = argv[1]; return RUN_ALL_TESTS(); }