// 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 libavif { namespace { 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(uint32_t expect_width, uint32_t expect_height) { ASSERT_EQ(avifDecoderSetIOMemory(decoder_.get(), encoded_avif_.data, encoded_avif_.size), AVIF_RESULT_OK); ASSERT_EQ(avifDecoderParse(decoder_.get()), AVIF_RESULT_OK); ASSERT_EQ(decoder_->progressiveState, AVIF_PROGRESSIVE_STATE_ACTIVE); ASSERT_EQ(static_cast(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(). } // TODO(wtc): Check decoder_->image and image_ are similar, and better // quality layer is more similar. } testutil::AvifEncoderPtr encoder_{avifEncoderCreate(), avifEncoderDestroy}; testutil::AvifDecoderPtr decoder_{avifDecoderCreate(), avifDecoderDestroy}; testutil::AvifImagePtr 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); } } // namespace } // namespace libavif