blob: 8e9d776026e9c1201c9f4df8d9b25bead3ee2b54 [file] [log] [blame]
// Copyright 2023 Google LLC
// SPDX-License-Identifier: BSD-2-Clause
#include <array>
#include <iostream>
#include <ostream>
#include "avif/avif.h"
#include "avif/internal.h"
#include "aviftest_helpers.h"
#include "gtest/gtest.h"
namespace libavif {
namespace {
// Used to pass the data folder path to the GoogleTest suites.
const char* data_path = nullptr;
//------------------------------------------------------------------------------
bool GetIoStatsFromEncode(avifIOStats& encoder_io_stats, int quality,
int qualityAlpha, bool translucent,
int num_frames = 1, bool encode_as_grid = false) {
testutil::AvifImagePtr image =
testutil::ReadImage(data_path, "paris_exif_xmp_icc.jpg");
AVIF_CHECK(image != nullptr);
if (translucent) {
// Make up some alpha from luma.
image->alphaPlane = image->yuvPlanes[AVIF_CHAN_Y];
image->alphaRowBytes = image->yuvRowBytes[AVIF_CHAN_Y];
AVIF_CHECK(!image->imageOwnsAlphaPlane);
}
testutil::AvifEncoderPtr encoder(avifEncoderCreate(), avifEncoderDestroy);
AVIF_CHECK(encoder != nullptr);
encoder->speed = AVIF_SPEED_FASTEST;
encoder->quality = quality;
encoder->qualityAlpha = qualityAlpha;
for (int frame = 0; frame < num_frames; ++frame) {
if (encode_as_grid) {
const avifCropRect left_rect = {0, 0, image->width / 2, image->height};
testutil::AvifImagePtr left_cell(avifImageCreateEmpty(),
avifImageDestroy);
AVIF_CHECK(avifImageSetViewRect(left_cell.get(), image.get(),
&left_rect) == AVIF_RESULT_OK);
const avifCropRect right_rect = {image->width / 2, 0, image->width / 2,
image->height};
testutil::AvifImagePtr right_cell(avifImageCreateEmpty(),
avifImageDestroy);
AVIF_CHECK(avifImageSetViewRect(right_cell.get(), image.get(),
&right_rect) == AVIF_RESULT_OK);
const std::array<avifImage*, 2> pointers = {left_cell.get(),
right_cell.get()};
AVIF_CHECK(avifEncoderAddImageGrid(encoder.get(), /*gridCols=*/2,
/*gridRows=*/1, pointers.data(),
AVIF_ADD_IMAGE_FLAG_NONE) ==
AVIF_RESULT_OK);
} else {
AVIF_CHECK(avifEncoderAddImage(encoder.get(), image.get(),
/*durationInTimescales=*/1,
AVIF_ADD_IMAGE_FLAG_NONE) ==
AVIF_RESULT_OK);
}
// Watermark each frame.
image->yuvPlanes[AVIF_CHAN_Y][0] =
(image->yuvPlanes[AVIF_CHAN_Y][0] + 1u) % 256u;
}
testutil::AvifRwData encoded;
AVIF_CHECK(avifEncoderFinish(encoder.get(), &encoded) == AVIF_RESULT_OK);
encoder_io_stats = encoder->ioStats;
// Make sure decoding gives the same stats.
testutil::AvifDecoderPtr decoder(avifDecoderCreate(), avifDecoderDestroy);
AVIF_CHECK(decoder != nullptr);
AVIF_CHECK(avifDecoderSetIOMemory(decoder.get(), encoded.data,
encoded.size) == AVIF_RESULT_OK);
AVIF_CHECK(avifDecoderParse(decoder.get()) == AVIF_RESULT_OK);
EXPECT_EQ(decoder->ioStats.colorOBUSize, encoder_io_stats.colorOBUSize);
EXPECT_EQ(decoder->ioStats.alphaOBUSize, encoder_io_stats.alphaOBUSize);
return true;
}
//------------------------------------------------------------------------------
TEST(IoStatsTest, ColorObuSize) {
avifIOStats io_stats;
ASSERT_TRUE(GetIoStatsFromEncode(io_stats, AVIF_QUALITY_DEFAULT,
AVIF_QUALITY_DEFAULT,
/*translucent=*/false));
EXPECT_GT(io_stats.colorOBUSize, 0u);
EXPECT_EQ(io_stats.alphaOBUSize, 0u);
}
TEST(IoStatsTest, AlphaObuSize) {
avifIOStats io_stats;
ASSERT_TRUE(GetIoStatsFromEncode(io_stats, AVIF_QUALITY_WORST,
AVIF_QUALITY_WORST, /*translucent=*/true));
EXPECT_GT(io_stats.colorOBUSize, 0u);
EXPECT_GT(io_stats.alphaOBUSize, 0u);
}
// Disabled because segfault happens with some libaom versions (such as 3.5.0)
// but not others (such as 3.6.0). TODO(yguyon): Find the commit that fixed it.
TEST(DISABLED_IoStatsTest, AnimationObuSize) {
avifIOStats io_stats;
ASSERT_TRUE(GetIoStatsFromEncode(io_stats, AVIF_QUALITY_LOSSLESS,
AVIF_QUALITY_LOSSLESS,
/*translucent=*/true));
avifIOStats io_stats_grid;
ASSERT_TRUE(GetIoStatsFromEncode(io_stats_grid, AVIF_QUALITY_LOSSLESS,
AVIF_QUALITY_LOSSLESS, /*translucent=*/true,
/*num_frames=*/3));
EXPECT_GT(io_stats_grid.colorOBUSize, io_stats.colorOBUSize);
EXPECT_GT(io_stats_grid.alphaOBUSize, io_stats.alphaOBUSize);
}
TEST(IoStatsTest, GridObuSize) {
avifIOStats io_stats;
ASSERT_TRUE(GetIoStatsFromEncode(io_stats, AVIF_QUALITY_BEST,
AVIF_QUALITY_BEST, /*translucent=*/true));
avifIOStats io_stats_grid;
ASSERT_TRUE(GetIoStatsFromEncode(io_stats_grid, AVIF_QUALITY_BEST,
AVIF_QUALITY_BEST, /*translucent=*/true,
/*num_frames=*/1, /*encode_as_grid=*/true));
EXPECT_GT(io_stats_grid.colorOBUSize, io_stats.colorOBUSize / 2);
EXPECT_LT(io_stats_grid.colorOBUSize, io_stats.colorOBUSize * 3 / 2);
EXPECT_GT(io_stats_grid.alphaOBUSize, io_stats.alphaOBUSize / 2);
EXPECT_LT(io_stats_grid.alphaOBUSize, io_stats.alphaOBUSize * 3 / 2);
}
TEST(IoStatsTest, GridAnimation) {
avifIOStats io_stats;
// Animated grids are not allowed.
ASSERT_FALSE(GetIoStatsFromEncode(io_stats, AVIF_QUALITY_BEST,
AVIF_QUALITY_BEST, /*translucent=*/false,
/*num_frames=*/3, /*encode_as_grid=*/true));
}
//------------------------------------------------------------------------------
} // 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();
}