Add an API lossless test
diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 4511bd0..a2a5767 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt
@@ -118,6 +118,11 @@ target_include_directories(avifiostatstest PRIVATE ${GTEST_INCLUDE_DIRS}) add_test(NAME avifiostatstest COMMAND avifiostatstest ${CMAKE_CURRENT_SOURCE_DIR}/data/) + add_executable(aviflosslesstest gtest/aviflosslesstest.cc) + target_link_libraries(aviflosslesstest aviftest_helpers ${GTEST_BOTH_LIBRARIES}) + target_include_directories(aviflosslesstest PRIVATE ${GTEST_INCLUDE_DIRS}) + add_test(NAME aviflosslesstest COMMAND aviflosslesstest ${CMAKE_CURRENT_SOURCE_DIR}/data/) + add_executable(avifmetadatatest gtest/avifmetadatatest.cc) target_link_libraries(avifmetadatatest aviftest_helpers ${GTEST_LIBRARIES}) target_include_directories(avifmetadatatest PRIVATE ${GTEST_INCLUDE_DIRS}) @@ -167,7 +172,9 @@ # These tests are supported with aom being the encoder and decoder. If aom is unavailable, # these tests are disabled because other codecs may not implement all the necessary features. # For example, SVT-AV1 requires 4:2:0 images with even dimensions of at least 64x64 px. - set_tests_properties(avifallocationtest avifgridapitest avifmetadatatest avifincrtest PROPERTIES DISABLED True) + set_tests_properties( + avifallocationtest avifgridapitest avifincrtest aviflosslesstest avifmetadatatest PROPERTIES DISABLED True + ) message(STATUS "Some tests are disabled because aom is unavailable for encoding or decoding.") endif()
diff --git a/tests/gtest/aviflosslesstest.cc b/tests/gtest/aviflosslesstest.cc new file mode 100644 index 0000000..82c446c --- /dev/null +++ b/tests/gtest/aviflosslesstest.cc
@@ -0,0 +1,97 @@ +// 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 : + {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, + /*ignoreICC=*/false, /*ignoreExif=*/false, + /*ignoreXMP=*/false, image.get(), + /*outDepth=*/nullptr, /*sourceTiming=*/nullptr, + /*frameIter=*/nullptr); + 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) { + 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(); +}