// Copyright 2022 Google LLC // SPDX-License-Identifier: BSD-2-Clause #include #include #include #include "avif/avif.h" #include "avifpng.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; //------------------------------------------------------------------------------ // Reads samples with an expected bit depth. testutil::AvifImagePtr ReadImageLosslessBitDepth(const std::string& path, uint32_t bit_depth) { testutil::AvifImagePtr image(avifImageCreateEmpty(), avifImageDestroy); if (!image) return {nullptr, nullptr}; // Lossless. image->yuvRange = AVIF_RANGE_FULL; image->matrixCoefficients = AVIF_MATRIX_COEFFICIENTS_IDENTITY; uint32_t output_depth; if (avifReadImage(path.c_str(), AVIF_PIXEL_FORMAT_YUV444, bit_depth, AVIF_CHROMA_DOWNSAMPLING_AUTOMATIC, /*ignoreColorProfile=*/true, /*ignoreExif=*/true, /*ignoreXMP=*/true, /*allowChangingCicp=*/true, image.get(), &output_depth, /*sourceTiming=*/nullptr, /*frameIter=*/nullptr) == AVIF_APP_FILE_FORMAT_UNKNOWN) { return {nullptr, nullptr}; } EXPECT_EQ(output_depth, bit_depth); EXPECT_EQ(image->depth, bit_depth); return image; } // Returns the ratio of sample values present in the channel over all possible // values for the sample bit depth. double GetSampleValueSpread(const avifImage& image, avifChannelIndex channel) { std::vector is_value_used(size_t{1} << image.depth, 0); const uint8_t* row = image.yuvPlanes[channel]; for (uint32_t y = 0; y < avifImagePlaneHeight(&image, channel); ++y) { for (uint32_t x = 0; x < avifImagePlaneWidth(&image, channel); ++x) { const uint32_t value = avifImageUsesU16(&image) ? reinterpret_cast(row)[x] : row[x]; is_value_used[value] = 1; } row += avifImagePlaneRowBytes(&image, channel); } const double num_used_values = std::accumulate(is_value_used.begin(), is_value_used.end(), 0u); return num_used_values / is_value_used.size(); } //------------------------------------------------------------------------------ TEST(BitDepthTest, Png16b) { // Read 16-bit image. testutil::AvifImagePtr original = ReadImageLosslessBitDepth(std::string(data_path) + "weld_16bit.png", 16); ASSERT_NE(original, nullptr); // More than 75% of possible sample values are present in each channel. // This is a "true" 16-bit image, not 8-bit samples shifted left by 8 bits. EXPECT_GT(GetSampleValueSpread(*original, AVIF_CHAN_Y), 0.75); EXPECT_GT(GetSampleValueSpread(*original, AVIF_CHAN_U), 0.75); EXPECT_GT(GetSampleValueSpread(*original, AVIF_CHAN_V), 0.75); // Write and read it as a 16-bit PNG. const std::string file_path = testing::TempDir() + "avifpng16bittest_weld_16bit.png"; ASSERT_TRUE(avifPNGWrite(file_path.c_str(), original.get(), original->depth, AVIF_CHROMA_UPSAMPLING_AUTOMATIC, /*compressionLevel=*/0)); testutil::AvifImagePtr image = ReadImageLosslessBitDepth(file_path, original->depth); ASSERT_NE(image, nullptr); // They should match exactly. EXPECT_TRUE(testutil::AreImagesEqual(*original, *image)); } //------------------------------------------------------------------------------ } // 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(); }