blob: ccdaa94f99ab30e6bfb37df92b2091ab6d8e3d3d [file] [log] [blame]
// Copyright 2024 Google LLC
// SPDX-License-Identifier: BSD-2-Clause
#include <cstring>
#include "avif/avif.h"
#include "avif/avif_cxx.h"
#include "avif/internal.h"
#include "aviftest_helpers.h"
#include "gtest/gtest.h"
namespace avif {
namespace {
//------------------------------------------------------------------------------
// Used to pass the data folder path to the GoogleTest suites.
const char* data_path = nullptr;
class SampleTransformTest
: public testing::TestWithParam<
std::tuple<avifSampleTransformRecipe, avifPixelFormat,
/*create_alpha=*/bool, /*quality=*/int>> {};
//------------------------------------------------------------------------------
TEST_P(SampleTransformTest, Avif16bit) {
const avifSampleTransformRecipe recipe = std::get<0>(GetParam());
const avifPixelFormat yuv_format = std::get<1>(GetParam());
const bool create_alpha = std::get<2>(GetParam());
const int quality = std::get<3>(GetParam());
const ImagePtr image = testutil::ReadImage(
data_path, "weld_16bit.png", yuv_format, /*requested_depth=*/16);
ASSERT_NE(image, nullptr);
if (create_alpha && !image->alphaPlane) {
// Simulate alpha plane with a view on luma.
image->alphaPlane = image->yuvPlanes[AVIF_CHAN_Y];
image->alphaRowBytes = image->yuvRowBytes[AVIF_CHAN_Y];
image->imageOwnsAlphaPlane = false;
}
EncoderPtr encoder(avifEncoderCreate());
ASSERT_NE(encoder, nullptr);
encoder->speed = AVIF_SPEED_FASTEST;
encoder->quality = quality;
encoder->qualityAlpha = quality;
encoder->sampleTransformRecipe = recipe;
testutil::AvifRwData encoded;
ASSERT_EQ(avifEncoderWrite(encoder.get(), image.get(), &encoded),
AVIF_RESULT_OK);
const ImagePtr decoded = testutil::Decode(encoded.data, encoded.size);
ASSERT_NE(decoded, nullptr);
ASSERT_EQ(image->depth, decoded->depth);
ASSERT_EQ(image->width, decoded->width);
ASSERT_EQ(image->height, decoded->height);
EXPECT_GE(testutil::GetPsnr(*image, *decoded),
(quality == AVIF_QUALITY_LOSSLESS) ? 99.0 : 15.0);
// Replace all 'sato' box types by "zzzz" garbage. This simulates an old
// decoder that does not recognize the Sample Transform feature.
for (size_t i = 0; i + 4 <= encoded.size; ++i) {
if (!std::memcmp(&encoded.data[i], "sato", 4)) {
std::memcpy(&encoded.data[i], "zzzz", 4);
}
}
const ImagePtr decoded_no_sato = testutil::Decode(encoded.data, encoded.size);
ASSERT_NE(decoded_no_sato, nullptr);
// Only the most significant bits of each sample can be retrieved.
// They should be encoded losslessly no matter the quantizer settings.
ImagePtr image_no_sato = testutil::CreateImage(
static_cast<int>(image->width), static_cast<int>(image->height),
static_cast<int>(decoded_no_sato->depth), image->yuvFormat,
image->alphaPlane ? AVIF_PLANES_ALL : AVIF_PLANES_YUV, image->yuvRange);
ASSERT_NE(image_no_sato, nullptr);
const uint32_t shift =
recipe == AVIF_SAMPLE_TRANSFORM_BIT_DEPTH_EXTENSION_8B_8B ? 8 : 4;
const avifImage* inputImage = image.get();
// Postfix notation.
const avifSampleTransformToken tokens[] = {
{AVIF_SAMPLE_TRANSFORM_INPUT_IMAGE_ITEM_INDEX, 0,
/*inputImageItemIndex=*/1},
{AVIF_SAMPLE_TRANSFORM_CONSTANT, 1 << shift, 0},
{AVIF_SAMPLE_TRANSFORM_DIVIDE, 0, 0}};
ASSERT_EQ(avifImageApplyOperations(image_no_sato.get(),
AVIF_SAMPLE_TRANSFORM_BIT_DEPTH_32, 3,
tokens, 1, &inputImage, AVIF_PLANES_ALL),
AVIF_RESULT_OK);
EXPECT_TRUE(testutil::AreImagesEqual(*image_no_sato, *decoded_no_sato));
}
//------------------------------------------------------------------------------
INSTANTIATE_TEST_SUITE_P(
Formats, SampleTransformTest,
testing::Combine(
testing::Values(AVIF_SAMPLE_TRANSFORM_BIT_DEPTH_EXTENSION_8B_8B),
testing::Values(AVIF_PIXEL_FORMAT_YUV444, AVIF_PIXEL_FORMAT_YUV420,
AVIF_PIXEL_FORMAT_YUV400),
/*create_alpha=*/testing::Values(false),
/*quality=*/
testing::Values(AVIF_QUALITY_DEFAULT)));
INSTANTIATE_TEST_SUITE_P(
BitDepthExtensions, SampleTransformTest,
testing::Combine(
testing::Values(AVIF_SAMPLE_TRANSFORM_BIT_DEPTH_EXTENSION_8B_8B,
AVIF_SAMPLE_TRANSFORM_BIT_DEPTH_EXTENSION_12B_4B),
testing::Values(AVIF_PIXEL_FORMAT_YUV444),
/*create_alpha=*/testing::Values(false),
/*quality=*/
testing::Values(AVIF_QUALITY_LOSSLESS)));
INSTANTIATE_TEST_SUITE_P(
Alpha, SampleTransformTest,
testing::Combine(
testing::Values(AVIF_SAMPLE_TRANSFORM_BIT_DEPTH_EXTENSION_8B_8B),
testing::Values(AVIF_PIXEL_FORMAT_YUV444),
/*create_alpha=*/testing::Values(true),
/*quality=*/
testing::Values(AVIF_QUALITY_LOSSLESS)));
// TODO(yguyon): Test grids with bit depth extensions.
} // namespace
} // namespace avif
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;
}
avif::data_path = argv[1];
return RUN_ALL_TESTS();
}