| // Copyright 2022 Google LLC. All rights reserved. |
| // SPDX-License-Identifier: BSD-2-Clause |
| |
| #include "avif/avif.h" |
| |
| #include <tuple> |
| #include <vector> |
| |
| #include "aviftest_helpers.h" |
| #include "gtest/gtest.h" |
| |
| using testing::Combine; |
| using testing::Values; |
| using testing::ValuesIn; |
| |
| namespace libavif |
| { |
| namespace |
| { |
| |
| // Pair of cell count and cell size for a single dimension. |
| struct Cell |
| { |
| int count, size; |
| }; |
| |
| class GridApiTest |
| : public testing::TestWithParam<std::tuple</*horizontal=*/Cell, /*vertical=*/Cell, /*bitDepth=*/int, /*yuvFormat=*/avifPixelFormat, /*createAlpha=*/bool, /*expectedSuccess=*/bool>> |
| { |
| }; |
| |
| TEST_P(GridApiTest, EncodeDecode) |
| { |
| const Cell horizontal = std::get<0>(GetParam()); |
| const Cell vertical = std::get<1>(GetParam()); |
| const int bitDepth = std::get<2>(GetParam()); |
| const avifPixelFormat yuvFormat = std::get<3>(GetParam()); |
| const bool createAlpha = std::get<4>(GetParam()); |
| const bool expectedSuccess = std::get<5>(GetParam()); |
| |
| // Construct a grid. |
| std::vector<testutil::avifImagePtr> cellImages; |
| cellImages.reserve(horizontal.count * vertical.count); |
| for (int i = 0; i < horizontal.count * vertical.count; ++i) { |
| cellImages.emplace_back( |
| testutil::createImage(horizontal.size, vertical.size, bitDepth, yuvFormat, createAlpha ? AVIF_PLANES_ALL : AVIF_PLANES_YUV)); |
| ASSERT_NE(cellImages.back(), nullptr); |
| testutil::fillImageGradient(cellImages.back().get()); |
| } |
| |
| // Encode the grid image. |
| testutil::avifEncoderPtr encoder(avifEncoderCreate(), avifEncoderDestroy); |
| ASSERT_NE(encoder, nullptr); |
| encoder->speed = AVIF_SPEED_FASTEST; |
| std::vector<avifImage *> cellImagePtrs(cellImages.size()); // Just here to match libavif API. |
| for (size_t i = 0; i < cellImages.size(); ++i) { |
| cellImagePtrs[i] = cellImages[i].get(); |
| } |
| const avifResult result = |
| avifEncoderAddImageGrid(encoder.get(), horizontal.count, vertical.count, cellImagePtrs.data(), AVIF_ADD_IMAGE_FLAG_SINGLE); |
| |
| if (expectedSuccess) { |
| ASSERT_EQ(result, AVIF_RESULT_OK); |
| testutil::avifRWDataCleaner encodedAvif; |
| ASSERT_EQ(avifEncoderFinish(encoder.get(), &encodedAvif), AVIF_RESULT_OK); |
| |
| // Decode the grid image. |
| testutil::avifImagePtr image(avifImageCreateEmpty(), avifImageDestroy); |
| ASSERT_NE(image, nullptr); |
| testutil::avifDecoderPtr decoder(avifDecoderCreate(), avifDecoderDestroy); |
| ASSERT_NE(decoder, nullptr); |
| ASSERT_EQ(avifDecoderReadMemory(decoder.get(), image.get(), encodedAvif.data, encodedAvif.size), AVIF_RESULT_OK); |
| } else { |
| ASSERT_TRUE(result == AVIF_RESULT_INVALID_IMAGE_GRID || result == AVIF_RESULT_NO_CONTENT); |
| } |
| } |
| |
| // A cell cannot be smaller than 64px in any dimension if there are several cells. |
| // A cell cannot have an odd size in any dimension if there are several cells and chroma subsampling. |
| // Image size must be a multiple of cell size. |
| constexpr Cell kValidCells[] = { { 1, 64 }, { 1, 66 }, { 2, 64 }, { 3, 68 } }; |
| constexpr Cell kInvalidCells[] = { { 0, 0 }, { 0, 1 }, { 1, 0 }, { 2, 1 }, { 2, 2 }, { 2, 3 }, { 2, 63 } }; |
| constexpr int kBitDepths[] = { 8, 10, 12 }; |
| constexpr avifPixelFormat kPixelFormats[] = { AVIF_PIXEL_FORMAT_YUV444, AVIF_PIXEL_FORMAT_YUV422, AVIF_PIXEL_FORMAT_YUV420, AVIF_PIXEL_FORMAT_YUV400 }; |
| |
| INSTANTIATE_TEST_SUITE_P(Valid, |
| GridApiTest, |
| Combine(/*horizontal=*/ValuesIn(kValidCells), |
| /*vertical=*/ValuesIn(kValidCells), |
| ValuesIn(kBitDepths), |
| ValuesIn(kPixelFormats), |
| /*createAlpha=*/Values(false, true), |
| /*expectedSuccess=*/Values(true))); |
| |
| INSTANTIATE_TEST_SUITE_P(InvalidVertically, |
| GridApiTest, |
| Combine(/*horizontal=*/ValuesIn(kValidCells), |
| /*vertical=*/ValuesIn(kInvalidCells), |
| ValuesIn(kBitDepths), |
| ValuesIn(kPixelFormats), |
| /*createAlpha=*/Values(false, true), |
| /*expectedSuccess=*/Values(false))); |
| INSTANTIATE_TEST_SUITE_P(InvalidHorizontally, |
| GridApiTest, |
| Combine(/*horizontal=*/ValuesIn(kInvalidCells), |
| /*vertical=*/ValuesIn(kValidCells), |
| ValuesIn(kBitDepths), |
| ValuesIn(kPixelFormats), |
| /*createAlpha=*/Values(false, true), |
| /*expectedSuccess=*/Values(false))); |
| INSTANTIATE_TEST_SUITE_P(InvalidBoth, |
| GridApiTest, |
| Combine(/*horizontal=*/ValuesIn(kInvalidCells), |
| /*vertical=*/ValuesIn(kInvalidCells), |
| ValuesIn(kBitDepths), |
| ValuesIn(kPixelFormats), |
| /*createAlpha=*/Values(false, true), |
| /*expectedSuccess=*/Values(false))); |
| |
| // Special case depending on the cell count and the chroma subsampling. |
| INSTANTIATE_TEST_SUITE_P(ValidOddHeight, |
| GridApiTest, |
| Combine(/*horizontal=*/Values(Cell { 1, 64 }), |
| /*vertical=*/Values(Cell { 1, 65 }, Cell { 2, 65 }), |
| ValuesIn(kBitDepths), |
| Values(AVIF_PIXEL_FORMAT_YUV444, AVIF_PIXEL_FORMAT_YUV422, AVIF_PIXEL_FORMAT_YUV400), |
| /*createAlpha=*/Values(false, true), |
| /*expectedSuccess=*/Values(true))); |
| INSTANTIATE_TEST_SUITE_P(InvalidOddHeight, |
| GridApiTest, |
| Combine(/*horizontal=*/Values(Cell { 1, 64 }), |
| /*vertical=*/Values(Cell { 2, 65 }), |
| ValuesIn(kBitDepths), |
| Values(AVIF_PIXEL_FORMAT_YUV420), |
| /*createAlpha=*/Values(false, true), |
| /*expectedSuccess=*/Values(false))); |
| |
| // Special case depending on the cell count and the cell size. |
| INSTANTIATE_TEST_SUITE_P(ValidOddDimensions, |
| GridApiTest, |
| Combine(/*horizontal=*/Values(Cell { 1, 1 }), |
| /*vertical=*/Values(Cell { 1, 65 }), |
| ValuesIn(kBitDepths), |
| ValuesIn(kPixelFormats), |
| /*createAlpha=*/Values(false, true), |
| /*expectedSuccess=*/Values(true))); |
| INSTANTIATE_TEST_SUITE_P(InvalidOddDimensions, |
| GridApiTest, |
| Combine(/*horizontal=*/Values(Cell { 2, 1 }), |
| /*vertical=*/Values(Cell { 1, 65 }, Cell { 2, 65 }), |
| ValuesIn(kBitDepths), |
| ValuesIn(kPixelFormats), |
| /*createAlpha=*/Values(false, true), |
| /*expectedSuccess=*/Values(false))); |
| |
| } // namespace |
| } // namespace libavif |