blob: 5d72c360b6a12d7d2450712a4aafa8f85ffb143c [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 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<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().
}
// 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