blob: ca251870ca937cad8c510c5ecc3739e84150544a [file] [log] [blame]
// Copyright 2022 Yuan Tong. All rights reserved.
// SPDX-License-Identifier: BSD-2-Clause
#include "avif/avif.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 ProgressiveTest : public testing::Test {
protected:
static constexpr uint32_t kImageSize = 256;
void SetUp() override {
if (avifCodecName(AVIF_CODEC_CHOICE_AOM, AVIF_CODEC_FLAG_CAN_ENCODE) ==
nullptr) {
GTEST_SKIP() << "ProgressiveTest requires the AOM encoder.";
}
ASSERT_NE(encoder_, nullptr);
encoder_->codecChoice = AVIF_CODEC_CHOICE_AOM;
// The fastest speed that uses AOM_USAGE_GOOD_QUALITY.
encoder_->speed = 6;
ASSERT_NE(decoder_, nullptr);
decoder_->allowProgressive = true;
ASSERT_NE(image_, nullptr);
testutil::FillImageGradient(image_.get());
}
void TestDecode(uint8_t* data, size_t size, uint32_t expect_width,
uint32_t expect_height) {
ASSERT_EQ(avifDecoderSetIOMemory(decoder_.get(), data, size),
AVIF_RESULT_OK);
ASSERT_EQ(avifDecoderParse(decoder_.get()), AVIF_RESULT_OK);
ASSERT_EQ(decoder_->progressiveState, AVIF_PROGRESSIVE_STATE_ACTIVE);
ASSERT_EQ(static_cast<uint32_t>(decoder_->imageCount),
encoder_->extraLayerCount + 1);
for (uint32_t layer = 0; layer < encoder_->extraLayerCount + 1; ++layer) {
ASSERT_EQ(avifDecoderNextImage(decoder_.get()), AVIF_RESULT_OK);
// libavif scales frame automatically.
ASSERT_EQ(decoder_->image->width, expect_width);
ASSERT_EQ(decoder_->image->height, expect_height);
// TODO(wtc): Check avifDecoderNthImageMaxExtent().
}
}
void TestDecode(uint32_t expect_width, uint32_t expect_height) {
TestDecode(encoded_avif_.data, encoded_avif_.size, expect_width,
expect_height);
// TODO(wtc): Check decoder_->image and image_ are similar, and better
// quality layer is more similar.
}
EncoderPtr encoder_{avifEncoderCreate()};
DecoderPtr decoder_{avifDecoderCreate()};
ImagePtr image_ =
testutil::CreateImage(kImageSize, kImageSize, 8, AVIF_PIXEL_FORMAT_YUV444,
AVIF_PLANES_YUV, AVIF_RANGE_FULL);
testutil::AvifRwData encoded_avif_;
};
TEST_F(ProgressiveTest, QualityChange) {
encoder_->extraLayerCount = 1;
encoder_->minQuantizer = 50;
encoder_->maxQuantizer = 50;
ASSERT_EQ(avifEncoderAddImage(encoder_.get(), image_.get(), 1,
AVIF_ADD_IMAGE_FLAG_NONE),
AVIF_RESULT_OK);
encoder_->minQuantizer = 0;
encoder_->maxQuantizer = 0;
ASSERT_EQ(avifEncoderAddImage(encoder_.get(), image_.get(), 1,
AVIF_ADD_IMAGE_FLAG_NONE),
AVIF_RESULT_OK);
ASSERT_EQ(avifEncoderFinish(encoder_.get(), &encoded_avif_), AVIF_RESULT_OK);
TestDecode(kImageSize, kImageSize);
}
// NOTE: This test requires libaom v3.6.0 or later, otherwise the following
// assertion in libaom fails:
// av1/encoder/mcomp.c:1717: av1_full_pixel_search: Assertion
// `ms_params->ms_buffers.ref->stride == ms_params->search_sites->stride'
// failed.
// See https://aomedia.googlesource.com/aom/+/945edd671.
TEST_F(ProgressiveTest, DimensionChange) {
if (avifLibYUVVersion() == 0) {
GTEST_SKIP() << "libyuv not available, skip test.";
}
encoder_->extraLayerCount = 1;
encoder_->minQuantizer = 0;
encoder_->maxQuantizer = 0;
encoder_->scalingMode = {{1, 2}, {1, 2}};
ASSERT_EQ(avifEncoderAddImage(encoder_.get(), image_.get(), 1,
AVIF_ADD_IMAGE_FLAG_NONE),
AVIF_RESULT_OK);
encoder_->scalingMode = {{1, 1}, {1, 1}};
ASSERT_EQ(avifEncoderAddImage(encoder_.get(), image_.get(), 1,
AVIF_ADD_IMAGE_FLAG_NONE),
AVIF_RESULT_OK);
ASSERT_EQ(avifEncoderFinish(encoder_.get(), &encoded_avif_), AVIF_RESULT_OK);
TestDecode(kImageSize, kImageSize);
}
TEST_F(ProgressiveTest, LayeredGrid) {
encoder_->extraLayerCount = 1;
encoder_->minQuantizer = 50;
encoder_->maxQuantizer = 50;
avifImage* image_grid[2] = {image_.get(), image_.get()};
ASSERT_EQ(avifEncoderAddImageGrid(encoder_.get(), 2, 1, image_grid,
AVIF_ADD_IMAGE_FLAG_NONE),
AVIF_RESULT_OK);
encoder_->minQuantizer = 0;
encoder_->maxQuantizer = 0;
ASSERT_EQ(avifEncoderAddImageGrid(encoder_.get(), 2, 1, image_grid,
AVIF_ADD_IMAGE_FLAG_NONE),
AVIF_RESULT_OK);
ASSERT_EQ(avifEncoderFinish(encoder_.get(), &encoded_avif_), AVIF_RESULT_OK);
TestDecode(2 * kImageSize, kImageSize);
}
TEST_F(ProgressiveTest, SameLayers) {
encoder_->extraLayerCount = 3;
for (uint32_t layer = 0; layer < encoder_->extraLayerCount + 1; ++layer) {
ASSERT_EQ(avifEncoderAddImage(encoder_.get(), image_.get(), 1,
AVIF_ADD_IMAGE_FLAG_NONE),
AVIF_RESULT_OK);
}
ASSERT_EQ(avifEncoderFinish(encoder_.get(), &encoded_avif_), AVIF_RESULT_OK);
TestDecode(kImageSize, kImageSize);
}
TEST_F(ProgressiveTest, TooManyLayers) {
encoder_->extraLayerCount = 1;
ASSERT_EQ(avifEncoderAddImage(encoder_.get(), image_.get(), 1,
AVIF_ADD_IMAGE_FLAG_NONE),
AVIF_RESULT_OK);
ASSERT_EQ(avifEncoderAddImage(encoder_.get(), image_.get(), 1,
AVIF_ADD_IMAGE_FLAG_NONE),
AVIF_RESULT_OK);
ASSERT_EQ(avifEncoderAddImage(encoder_.get(), image_.get(), 1,
AVIF_ADD_IMAGE_FLAG_NONE),
AVIF_RESULT_INVALID_ARGUMENT);
}
TEST_F(ProgressiveTest, TooFewLayers) {
encoder_->extraLayerCount = 1;
ASSERT_EQ(avifEncoderAddImage(encoder_.get(), image_.get(), 1,
AVIF_ADD_IMAGE_FLAG_NONE),
AVIF_RESULT_OK);
ASSERT_EQ(avifEncoderFinish(encoder_.get(), &encoded_avif_),
AVIF_RESULT_INVALID_ARGUMENT);
}
// Test progressive decoding with files that use 'idat' (inside the 'meta') box
// instead of 'mdat' to store the image data. Note that for now (as of v1.1.1)
// the decoder waits to have the full meta box available before parsing it, so
// incremental decoding is not really possible and progressive decoding makes
// little sense. But this checks that the files are still processed correctly.
TEST(DecodeProgressiveTest, DecodeIdat) {
const ImagePtr original = testutil::ReadImage(data_path, "draw_points.png");
for (const std::string file_name :
{"draw_points_idat_progressive.avif",
"draw_points_idat_progressive_metasize0.avif"}) {
SCOPED_TRACE(file_name);
const int expected_layer_count = 2;
DecoderPtr decoder(avifDecoderCreate());
decoder->allowProgressive = true;
ASSERT_EQ(avifDecoderSetIOFile(
decoder.get(), (std::string(data_path) + file_name).c_str()),
AVIF_RESULT_OK);
ASSERT_EQ(avifDecoderParse(decoder.get()), AVIF_RESULT_OK);
ASSERT_EQ(decoder->progressiveState, AVIF_PROGRESSIVE_STATE_ACTIVE);
ASSERT_EQ(static_cast<uint32_t>(decoder->imageCount), expected_layer_count);
for (uint32_t layer = 0; layer < expected_layer_count; ++layer) {
ASSERT_EQ(avifDecoderNextImage(decoder.get()), AVIF_RESULT_OK);
ASSERT_EQ(decoder->image->width, original->width);
ASSERT_EQ(decoder->image->height, original->height);
}
ASSERT_EQ(avifDecoderNextImage(decoder.get()),
AVIF_RESULT_NO_IMAGES_REMAINING);
}
}
} // 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();
}