blob: 004e14f4f8b68a5b5a1af3183c43b605e16402cb [file] [log] [blame]
// Copyright 2022 Google LLC
// SPDX-License-Identifier: BSD-2-Clause
#include <numeric>
#include <string>
#include <vector>
#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, /*ignoreICC=*/true,
/*ignoreExif=*/true, /*ignoreXMP=*/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<uint32_t> 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<const uint16_t*>(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();
}