Convert tests/gtest/avifincrtest to C++ and GTest
diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt
index b625499..d7a7214 100644
--- a/tests/CMakeLists.txt
+++ b/tests/CMakeLists.txt
@@ -42,16 +42,6 @@
################################################################################
# GoogleTest
-add_library(avifincrtest_helpers OBJECT gtest/avifincrtest_helpers.c)
-target_link_libraries(avifincrtest_helpers avif ${AVIF_PLATFORM_LIBRARIES})
-
-add_executable(avifincrtest gtest/avifincrtest.c)
-if(AVIF_LOCAL_LIBGAV1)
- set_target_properties(avifincrtest PROPERTIES LINKER_LANGUAGE "CXX")
-endif()
-target_link_libraries(avifincrtest avifincrtest_helpers)
-add_test(NAME avifincrtest COMMAND avifincrtest ${CMAKE_CURRENT_SOURCE_DIR}/data/sofa_grid1x5_420.avif)
-
if(AVIF_ENABLE_GTEST)
enable_language(CXX)
set(CMAKE_CXX_STANDARD 11)
@@ -81,6 +71,14 @@
target_include_directories(avifgridapitest PRIVATE ${GTEST_INCLUDE_DIRS})
add_test(NAME avifgridapitest COMMAND avifgridapitest)
+ add_library(avifincrtest_helpers OBJECT gtest/avifincrtest_helpers.cc)
+ target_link_libraries(avifincrtest_helpers avif ${AVIF_PLATFORM_LIBRARIES} ${GTEST_LIBRARIES})
+ target_include_directories(avifincrtest_helpers PUBLIC ${GTEST_INCLUDE_DIRS})
+
+ add_executable(avifincrtest gtest/avifincrtest.cc)
+ target_link_libraries(avifincrtest aviftest_helpers avifincrtest_helpers)
+ add_test(NAME avifincrtest COMMAND avifincrtest ${CMAKE_CURRENT_SOURCE_DIR}/data/)
+
add_executable(avifmetadatatest gtest/avifmetadatatest.cc)
target_link_libraries(avifmetadatatest aviftest_helpers ${GTEST_BOTH_LIBRARIES})
target_include_directories(avifmetadatatest PRIVATE ${GTEST_INCLUDE_DIRS})
diff --git a/tests/gtest/avifincrtest.c b/tests/gtest/avifincrtest.c
deleted file mode 100644
index fe04616..0000000
--- a/tests/gtest/avifincrtest.c
+++ /dev/null
@@ -1,151 +0,0 @@
-// Copyright 2022 Google LLC. All rights reserved.
-// SPDX-License-Identifier: BSD-2-Clause
-
-#include "avif/avif.h"
-#include "avifincrtest_helpers.h"
-
-#include <stdint.h>
-#include <stdio.h>
-#include <stdlib.h>
-
-//------------------------------------------------------------------------------
-
-// Reads the file at path into bytes or returns false.
-static avifBool readFile(const char * path, avifRWData * bytes)
-{
- FILE * file;
- avifRWDataFree(bytes);
- file = fopen(path, "rb");
- if (!file) {
- return AVIF_FALSE;
- }
- if (fseek(file, 0, SEEK_END)) {
- fclose(file);
- return AVIF_FALSE;
- }
- avifRWDataRealloc(bytes, ftell(file));
- rewind(file);
- if (fread(bytes->data, bytes->size, 1, file) != 1) {
- avifRWDataFree(bytes);
- fclose(file);
- return AVIF_FALSE;
- }
- fclose(file);
- return AVIF_TRUE;
-}
-
-//------------------------------------------------------------------------------
-
-// Encodes then decodes a window of width*height pixels at the middle of the image.
-// Check that non-incremental and incremental decodings produce the same pixels.
-static avifBool encodeDecodeNonIncrementallyAndIncrementally(const avifImage * image,
- uint32_t width,
- uint32_t height,
- avifBool createAlphaIfNone,
- avifBool flatCells,
- avifBool encodedAvifIsPersistent,
- avifBool giveSizeHint,
- avifBool useNthImageApi)
-{
- avifBool success = AVIF_FALSE;
- avifRWData encodedAvif = { 0 };
- uint32_t cellWidth, cellHeight;
- if (!encodeRectAsIncremental(image, width, height, createAlphaIfNone, flatCells, &encodedAvif, &cellWidth, &cellHeight)) {
- goto cleanup;
- }
- if (!decodeNonIncrementallyAndIncrementally(&encodedAvif, encodedAvifIsPersistent, giveSizeHint, useNthImageApi, cellHeight)) {
- goto cleanup;
- }
- success = AVIF_TRUE;
-cleanup:
- avifRWDataFree(&encodedAvif);
- return success;
-}
-
-//------------------------------------------------------------------------------
-
-int main(int argc, char * argv[])
-{
- int exitCode = EXIT_FAILURE;
- avifRWData encodedAvif = { NULL, 0 };
-
- if (argc != 2) {
- printf("ERROR: bad arguments\n");
- printf("Usage: avifincrtest <AVIF>\n");
- goto cleanup;
- }
- const char * const avifFilePath = argv[1];
-
- if (!readFile(avifFilePath, &encodedAvif)) {
- printf("ERROR: cannot read AVIF: %s\n", avifFilePath);
- goto cleanup;
- }
-
- // First test: decode the input image incrementally and compare it with a non-incrementally decoded reference.
- avifImage * reference = avifImageCreateEmpty();
- if (!reference || !decodeNonIncrementally(&encodedAvif, reference)) {
- goto cleanup;
- }
- // Cell height is hardcoded because there is no API to extract it from an encoded payload.
- if (!decodeIncrementally(&encodedAvif,
- /*isPersistent=*/AVIF_TRUE,
- /*giveSizeHint=*/AVIF_TRUE,
- /*useNthImageApi=*/AVIF_FALSE,
- reference,
- /*cellHeight=*/154)) {
- goto cleanup;
- }
-
- // Second test: encode a bunch of different dimension combinations and decode them incrementally and non-incrementally.
- // Chroma subsampling requires even dimensions. See ISO 23000-22 section 7.3.11.4.2.
- const uint32_t widths[] = { 1, 64, 66 };
- const uint32_t heights[] = { 1, 64, 66 };
- for (uint32_t w = 0; w < sizeof(widths) / sizeof(widths[0]); ++w) {
- for (uint32_t h = 0; h < sizeof(heights) / sizeof(heights[0]); ++h) {
- // avifEncoderAddImageInternal() only accepts grids of one unique cell, or grids where width and height are both at least 64.
- if ((widths[w] >= 64) != (heights[h] >= 64)) {
- continue;
- }
-
- for (avifBool createAlpha = AVIF_FALSE; createAlpha <= AVIF_TRUE; ++createAlpha) {
- for (avifBool flatCells = AVIF_FALSE; flatCells <= AVIF_TRUE; ++flatCells) {
- for (avifBool encodedAvifIsPersistent = AVIF_FALSE; encodedAvifIsPersistent <= AVIF_TRUE; ++encodedAvifIsPersistent) {
- for (avifBool giveSizeHint = AVIF_FALSE; giveSizeHint <= AVIF_TRUE; ++giveSizeHint) {
- for (avifBool useNthImageApi = AVIF_FALSE; useNthImageApi <= AVIF_TRUE; ++useNthImageApi) {
- if (!encodeDecodeNonIncrementallyAndIncrementally(reference,
- widths[w],
- heights[h],
- createAlpha,
- flatCells,
- encodedAvifIsPersistent,
- giveSizeHint,
- useNthImageApi)) {
- goto cleanup;
- }
- }
- }
- }
- }
- }
- }
- }
-
- // Third test: full image.
- for (avifBool flatCells = AVIF_FALSE; flatCells <= AVIF_TRUE; ++flatCells) {
- if (!encodeDecodeNonIncrementallyAndIncrementally(reference,
- reference->width,
- reference->height,
- /*createAlphaIfNone=*/AVIF_TRUE,
- flatCells,
- /*encodedAvifIsPersistent=*/AVIF_TRUE,
- /*giveSizeHint=*/AVIF_TRUE,
- /*useNthImageApi=*/AVIF_FALSE)) {
- goto cleanup;
- }
- }
-
- exitCode = EXIT_SUCCESS;
-cleanup:
- avifRWDataFree(&encodedAvif);
- return exitCode;
-}
diff --git a/tests/gtest/avifincrtest.cc b/tests/gtest/avifincrtest.cc
new file mode 100644
index 0000000..6aae233
--- /dev/null
+++ b/tests/gtest/avifincrtest.cc
@@ -0,0 +1,142 @@
+// Copyright 2022 Google LLC. All rights reserved.
+// SPDX-License-Identifier: BSD-2-Clause
+
+#include "avif/avif.h"
+
+#include <fstream>
+#include <iostream>
+#include <tuple>
+
+#include "avifincrtest_helpers.h"
+#include "aviftest_helpers.h"
+#include "gtest/gtest.h"
+
+using testing::Bool;
+using testing::Combine;
+using testing::Values;
+
+namespace libavif
+{
+namespace
+{
+
+//------------------------------------------------------------------------------
+
+// Used to pass the data folder path to the GoogleTest suites.
+const char * dataPath = nullptr;
+
+// Reads the file with fileName into bytes and returns them.
+testutil::avifRWDataCleaner readFile(const char * fileName)
+{
+ std::ifstream file(std::string(dataPath) + fileName, std::ios::binary | std::ios::ate);
+ testutil::avifRWDataCleaner bytes;
+ avifRWDataRealloc(&bytes, file.good() ? static_cast<size_t>(file.tellg()) : 0);
+ file.seekg(0, std::ios::beg);
+ file.read(reinterpret_cast<char *>(bytes.data), static_cast<std::streamsize>(bytes.size));
+ return bytes;
+}
+
+//------------------------------------------------------------------------------
+
+// Check that non-incremental and incremental decodings of a grid AVIF produce the same pixels.
+TEST(IncrementalTest, Decode)
+{
+ const testutil::avifRWDataCleaner encodedAvif = readFile("sofa_grid1x5_420.avif");
+ ASSERT_NE(encodedAvif.size, 0u);
+ testutil::avifImagePtr reference(avifImageCreateEmpty(), avifImageDestroy);
+ ASSERT_NE(reference, nullptr);
+ testutil::avifDecoderPtr decoder(avifDecoderCreate(), avifDecoderDestroy);
+ ASSERT_NE(decoder, nullptr);
+ ASSERT_EQ(avifDecoderReadMemory(decoder.get(), reference.get(), encodedAvif.data, encodedAvif.size), AVIF_RESULT_OK);
+
+ // Cell height is hardcoded because there is no API to extract it from an encoded payload.
+ testutil::decodeIncrementally(encodedAvif, /*isPersistent=*/true, /*giveSizeHint=*/true, /*useNthImageApi=*/false, *reference, /*cellHeight=*/154);
+}
+
+//------------------------------------------------------------------------------
+
+class IncrementalTest : public testing::TestWithParam<std::tuple</*width=*/uint32_t,
+ /*height=*/uint32_t,
+ /*createAlpha=*/bool,
+ /*flatCells=*/bool,
+ /*encodedAvifIsPersistent=*/bool,
+ /*giveSizeHint=*/bool,
+ /*useNthImageApi=*/bool>>
+{
+};
+
+// Encodes then decodes a window of width*height pixels at the middle of the image.
+// Check that non-incremental and incremental decodings produce the same pixels.
+TEST_P(IncrementalTest, EncodeDecode)
+{
+ const uint32_t width = std::get<0>(GetParam());
+ const uint32_t height = std::get<1>(GetParam());
+ const bool createAlpha = std::get<2>(GetParam());
+ const bool flatCells = std::get<3>(GetParam());
+ const bool encodedAvifIsPersistent = std::get<4>(GetParam());
+ const bool giveSizeHint = std::get<5>(GetParam());
+ const bool useNthImageApi = std::get<6>(GetParam());
+
+ // Load an image. It does not matter that it comes from an AVIF file.
+ testutil::avifImagePtr image(avifImageCreateEmpty(), avifImageDestroy);
+ ASSERT_NE(image, nullptr);
+ const testutil::avifRWDataCleaner imageBytes = readFile("sofa_grid1x5_420.avif");
+ ASSERT_NE(imageBytes.size, 0u);
+ testutil::avifDecoderPtr decoder(avifDecoderCreate(), avifDecoderDestroy);
+ ASSERT_NE(decoder, nullptr);
+ ASSERT_EQ(avifDecoderReadMemory(decoder.get(), image.get(), imageBytes.data, imageBytes.size), AVIF_RESULT_OK);
+
+ // Encode then decode it.
+ testutil::avifRWDataCleaner encodedAvif;
+ uint32_t cellWidth, cellHeight;
+ testutil::encodeRectAsIncremental(*image, width, height, createAlpha, flatCells, &encodedAvif, &cellWidth, &cellHeight);
+ testutil::decodeNonIncrementallyAndIncrementally(encodedAvif, encodedAvifIsPersistent, giveSizeHint, useNthImageApi, cellHeight);
+}
+
+INSTANTIATE_TEST_SUITE_P(WholeImage,
+ IncrementalTest,
+ Combine(/*width=*/Values(1024),
+ /*height=*/Values(770),
+ /*createAlpha=*/Values(true),
+ /*flatCells=*/Bool(),
+ /*encodedAvifIsPersistent=*/Values(true),
+ /*giveSizeHint=*/Values(true),
+ /*useNthImageApi=*/Values(false)));
+
+// avifEncoderAddImageInternal() only accepts grids of one unique cell, or grids where width and height are both at least 64.
+INSTANTIATE_TEST_SUITE_P(SingleCell,
+ IncrementalTest,
+ Combine(/*width=*/Values(1),
+ /*height=*/Values(1),
+ /*createAlpha=*/Bool(),
+ /*flatCells=*/Bool(),
+ /*encodedAvifIsPersistent=*/Bool(),
+ /*giveSizeHint=*/Bool(),
+ /*useNthImageApi=*/Bool()));
+
+// Chroma subsampling requires even dimensions. See ISO 23000-22 section 7.3.11.4.2.
+INSTANTIATE_TEST_SUITE_P(SinglePixel,
+ IncrementalTest,
+ Combine(/*width=*/Values(64, 66),
+ /*height=*/Values(64, 66),
+ /*createAlpha=*/Bool(),
+ /*flatCells=*/Bool(),
+ /*encodedAvifIsPersistent=*/Bool(),
+ /*giveSizeHint=*/Bool(),
+ /*useNthImageApi=*/Bool()));
+
+//------------------------------------------------------------------------------
+
+} // namespace
+} // namespace libavif
+
+int main(int argc, char ** argv)
+{
+ ::testing::InitGoogleTest(&argc, argv);
+ if (argc < 2) {
+ std::cerr << "The path to the test data folder must be provided as an argument" << std::endl;
+ return 1;
+ }
+ libavif::dataPath = argv[1];
+ return RUN_ALL_TESTS();
+}
diff --git a/tests/gtest/avifincrtest_helpers.c b/tests/gtest/avifincrtest_helpers.c
deleted file mode 100644
index b15f740..0000000
--- a/tests/gtest/avifincrtest_helpers.c
+++ /dev/null
@@ -1,423 +0,0 @@
-// Copyright 2022 Google LLC. All rights reserved.
-// SPDX-License-Identifier: BSD-2-Clause
-
-#include "avifincrtest_helpers.h"
-#include "avif/avif.h"
-
-#include <assert.h>
-#include <inttypes.h>
-#include <stdio.h>
-#include <string.h>
-
-//------------------------------------------------------------------------------
-
-// Returns true if the first (top) rowCount rows of image1 and image2 are identical.
-static avifBool comparePartialYUVA(const avifImage * image1, const avifImage * image2, uint32_t rowCount)
-{
- if (rowCount == 0) {
- return AVIF_TRUE;
- }
- if (!image1 || !image2 || (image1->width != image2->width) || (image1->depth != image2->depth) ||
- (image1->yuvFormat != image2->yuvFormat) || (image1->yuvRange != image2->yuvRange)) {
- printf("ERROR: input mismatch\n");
- return AVIF_FALSE;
- }
- if ((image1->height < rowCount) || (image2->height < rowCount)) {
- printf("ERROR: not enough rows to compare\n");
- return AVIF_FALSE;
- }
-
- avifPixelFormatInfo info;
- avifGetPixelFormatInfo(image1->yuvFormat, &info);
- const uint32_t uvWidth = (image1->width + info.chromaShiftX) >> info.chromaShiftX;
- const uint32_t uvHeight = (rowCount + info.chromaShiftY) >> info.chromaShiftY;
- const uint32_t pixelByteCount = (image1->depth > 8) ? sizeof(uint16_t) : sizeof(uint8_t);
-
- for (uint32_t plane = 0; plane < (info.monochrome ? 1 : AVIF_PLANE_COUNT_YUV); ++plane) {
- const uint32_t width = (plane == AVIF_CHAN_Y) ? image1->width : uvWidth;
- const uint32_t widthByteCount = width * pixelByteCount;
- const uint32_t height = (plane == AVIF_CHAN_Y) ? rowCount : uvHeight;
- const uint8_t * data1 = image1->yuvPlanes[plane];
- const uint8_t * data2 = image2->yuvPlanes[plane];
- for (uint32_t y = 0; y < height; ++y) {
- if (memcmp(data1, data2, widthByteCount)) {
- printf("ERROR: different px at row %" PRIu32 ", channel %" PRIu32 "\n", y, plane);
- return AVIF_FALSE;
- }
- data1 += image1->yuvRowBytes[plane];
- data2 += image2->yuvRowBytes[plane];
- }
- }
-
- if (image1->alphaPlane) {
- if (!image2->alphaPlane || (image1->alphaPremultiplied != image2->alphaPremultiplied)) {
- printf("ERROR: input mismatch\n");
- return AVIF_FALSE;
- }
- const uint32_t widthByteCount = image1->width * pixelByteCount;
- const uint8_t * data1 = image1->alphaPlane;
- const uint8_t * data2 = image2->alphaPlane;
- for (uint32_t y = 0; y < rowCount; ++y) {
- if (memcmp(data1, data2, widthByteCount)) {
- printf("ERROR: different px at row %" PRIu32 ", alpha\n", y);
- return AVIF_FALSE;
- }
- data1 += image1->alphaRowBytes;
- data2 += image2->alphaRowBytes;
- }
- }
- return AVIF_TRUE;
-}
-
-// Returns the expected number of decoded rows when availableByteCount out of byteCount were
-// given to the decoder, for an image of height rows, split into cells of cellHeight rows.
-static uint32_t getMinDecodedRowCount(uint32_t height, uint32_t cellHeight, avifBool hasAlpha, size_t availableByteCount, size_t byteCount)
-{
- // The whole image should be available when the full input is.
- if (availableByteCount >= byteCount) {
- return height;
- }
- // All but one cell should be decoded if at most 10 bytes are missing.
- if ((availableByteCount + 10) >= byteCount) {
- return height - cellHeight;
- }
-
- // Subtract the header because decoding it does not output any pixel.
- // Most AVIF headers are below 500 bytes.
- if (availableByteCount <= 500) {
- return 0;
- }
- availableByteCount -= 500;
- byteCount -= 500;
- // Alpha, if any, is assumed to be located before the other planes and to
- // represent at most 50% of the payload.
- if (hasAlpha) {
- if (availableByteCount <= (byteCount / 2)) {
- return 0;
- }
- availableByteCount -= byteCount / 2;
- byteCount -= byteCount / 2;
- }
- // Linearly map the input availability ratio to the decoded row ratio.
- const uint32_t minDecodedCellRowCount = (height / cellHeight) * availableByteCount / byteCount;
- const uint32_t minDecodedPxRowCount = minDecodedCellRowCount * cellHeight;
- // One cell is the incremental decoding granularity.
- // It is unlikely that bytes are evenly distributed among cells. Offset two of them.
- if (minDecodedPxRowCount <= (2 * cellHeight)) {
- return 0;
- }
- return minDecodedPxRowCount - 2 * cellHeight;
-}
-
-//------------------------------------------------------------------------------
-
-typedef struct
-{
- avifROData available;
- size_t fullSize;
-} avifROPartialData;
-
-// Implementation of avifIOReadFunc simulating a stream from an array. See avifIOReadFunc documentation.
-// io->data is expected to point to avifROPartialData.
-static avifResult avifIOPartialRead(struct avifIO * io, uint32_t readFlags, uint64_t offset, size_t size, avifROData * out)
-{
- const avifROPartialData * data = (avifROPartialData *)io->data;
- if ((readFlags != 0) || !data || (data->fullSize < offset)) {
- return AVIF_RESULT_IO_ERROR;
- }
- if (data->fullSize < (offset + size)) {
- size = data->fullSize - offset;
- }
- if (data->available.size < (offset + size)) {
- return AVIF_RESULT_WAITING_ON_IO;
- }
- out->data = data->available.data + offset;
- out->size = size;
- return AVIF_RESULT_OK;
-}
-
-//------------------------------------------------------------------------------
-
-// Encodes the image as a grid of at most gridCols*gridRows cells. Returns AVIF_FALSE in case of error.
-// The cell count is reduced to fit libavif or AVIF format constraints. If impossible, AVIF_TRUE is returned with no encoded output.
-// The final cellWidth and cellHeight are output.
-static avifBool encodeAsGrid(const avifImage * image, uint32_t gridCols, uint32_t gridRows, avifRWData * output, uint32_t * cellWidth, uint32_t * cellHeight)
-{
- avifBool success = AVIF_FALSE;
- avifEncoder * encoder = NULL;
- avifImage ** cellImages = NULL;
- // Chroma subsampling requires even dimensions. See ISO 23000-22 - 7.3.11.4.2
- const avifBool needEvenWidths = ((image->yuvFormat == AVIF_PIXEL_FORMAT_YUV420) || (image->yuvFormat == AVIF_PIXEL_FORMAT_YUV422));
- const avifBool needEvenHeights = (image->yuvFormat == AVIF_PIXEL_FORMAT_YUV420);
-
- if ((gridCols == 0) || (gridRows == 0)) {
- printf("ERROR: Bad grid dimensions\n");
- return AVIF_FALSE;
- }
-
- *cellWidth = image->width / gridCols;
- *cellHeight = image->height / gridRows;
-
- // avifEncoderAddImageGrid() only accepts grids that evenly split the image
- // into cells at least 64 pixels wide and tall.
- while ((gridCols > 1) &&
- (((*cellWidth * gridCols) != image->width) || (*cellWidth < 64) || (needEvenWidths && ((*cellWidth & 1) != 0)))) {
- --gridCols;
- *cellWidth = image->width / gridCols;
- }
- while ((gridRows > 1) &&
- (((*cellHeight * gridRows) != image->height) || (*cellHeight < 64) || (needEvenHeights && ((*cellHeight & 1) != 0)))) {
- --gridRows;
- *cellHeight = image->height / gridRows;
- }
-
- cellImages = avifAlloc(sizeof(avifImage *) * gridCols * gridRows);
- memset(cellImages, 0, sizeof(avifImage *) * gridCols * gridRows);
- for (uint32_t row = 0, iCell = 0; row < gridRows; ++row) {
- for (uint32_t col = 0; col < gridCols; ++col, ++iCell) {
- avifCropRect cell;
- cell.x = col * *cellWidth;
- cell.y = row * *cellHeight;
- cell.width = ((cell.x + *cellWidth) <= image->width) ? *cellWidth : (image->width - cell.x);
- cell.height = ((cell.y + *cellHeight) <= image->height) ? *cellHeight : (image->height - cell.y);
- cellImages[iCell] = avifImageCreateEmpty();
- if (!cellImages[iCell] || (avifImageSetViewRect(cellImages[iCell], image, &cell) != AVIF_RESULT_OK)) {
- printf("ERROR: avifImageCreateEmpty() failed\n");
- goto cleanup;
- }
- }
- }
-
- encoder = avifEncoderCreate();
- if (!encoder) {
- printf("ERROR: avifEncoderCreate() failed\n");
- goto cleanup;
- }
- encoder->speed = AVIF_SPEED_FASTEST;
- if (avifEncoderAddImageGrid(encoder, gridCols, gridRows, (const avifImage * const *)cellImages, AVIF_ADD_IMAGE_FLAG_SINGLE) !=
- AVIF_RESULT_OK) {
- printf("ERROR: avifEncoderAddImageGrid() failed\n");
- goto cleanup;
- }
- if (avifEncoderFinish(encoder, output) != AVIF_RESULT_OK) {
- printf("ERROR: avifEncoderFinish() failed\n");
- goto cleanup;
- }
-
- success = AVIF_TRUE;
-cleanup:
- if (encoder) {
- avifEncoderDestroy(encoder);
- }
- if (cellImages) {
- for (uint32_t i = 0; i < (gridCols * gridRows); ++i) {
- if (cellImages[i]) {
- avifImageDestroy(cellImages[i]);
- }
- }
- avifFree(cellImages);
- }
- return success;
-}
-
-// Encodes the image to be decoded incrementally.
-static avifBool encodeAsIncremental(const avifImage * image, avifBool flatCells, avifRWData * output, uint32_t * cellWidth, uint32_t * cellHeight)
-{
- const uint32_t gridCols = image->width / 64; // 64px is the min cell width.
- const uint32_t gridRows = flatCells ? 1 : (image->height / 64);
- return encodeAsGrid(image, (gridCols > 1) ? gridCols : 1, (gridRows > 1) ? gridRows : 1, output, cellWidth, cellHeight);
-}
-
-avifBool encodeRectAsIncremental(const avifImage * image,
- uint32_t width,
- uint32_t height,
- avifBool createAlphaIfNone,
- avifBool flatCells,
- avifRWData * output,
- uint32_t * cellWidth,
- uint32_t * cellHeight)
-{
- avifBool success = AVIF_FALSE;
- avifImage * subImage = avifImageCreateEmpty();
- if (!subImage) {
- printf("ERROR: avifImageCreateEmpty() failed\n");
- goto cleanup;
- }
- if ((width > image->width) || (height > image->height)) {
- printf("ERROR: Bad dimensions\n");
- goto cleanup;
- }
- avifPixelFormatInfo info;
- avifGetPixelFormatInfo(image->yuvFormat, &info);
- avifCropRect rect;
- rect.x = ((image->width - width) / 2) & ~info.chromaShiftX;
- rect.y = ((image->height - height) / 2) & ~info.chromaShiftX;
- rect.width = width;
- rect.height = height;
- if (avifImageSetViewRect(subImage, image, &rect) != AVIF_RESULT_OK) {
- printf("ERROR: avifImageSetViewRect() failed\n");
- goto cleanup;
- }
- if (createAlphaIfNone && !subImage->alphaPlane) {
- if (!image->yuvPlanes[AVIF_CHAN_Y]) {
- printf("ERROR: No luma plane to simulate an alpha plane\n");
- goto cleanup;
- }
- subImage->alphaPlane = image->yuvPlanes[AVIF_CHAN_Y];
- subImage->alphaRowBytes = image->yuvRowBytes[AVIF_CHAN_Y];
- subImage->alphaPremultiplied = AVIF_FALSE;
- subImage->imageOwnsAlphaPlane = AVIF_FALSE;
- }
- success = encodeAsIncremental(subImage, flatCells, output, cellWidth, cellHeight);
-cleanup:
- if (subImage) {
- avifImageDestroy(subImage);
- }
- return success;
-}
-
-//------------------------------------------------------------------------------
-
-avifBool decodeNonIncrementally(const avifRWData * encodedAvif, avifImage * image)
-{
- avifBool success = AVIF_FALSE;
- avifDecoder * decoder = avifDecoderCreate();
- if (!decoder || avifDecoderReadMemory(decoder, image, encodedAvif->data, encodedAvif->size) != AVIF_RESULT_OK) {
- printf("ERROR: avifDecoderReadMemory() failed\n");
- goto cleanup;
- }
- success = AVIF_TRUE;
-cleanup:
- if (decoder) {
- avifDecoderDestroy(decoder);
- }
- return success;
-}
-
-avifBool decodeIncrementally(const avifRWData * encodedAvif,
- avifBool isPersistent,
- avifBool giveSizeHint,
- avifBool useNthImageApi,
- const avifImage * reference,
- uint32_t cellHeight)
-{
- avifBool success = AVIF_FALSE;
- avifDecoder * decoder = NULL;
- // AVIF cells are at least 64 pixels tall.
- if ((cellHeight == 0) || ((cellHeight > reference->height) && (cellHeight != 64))) {
- printf("ERROR: cell height %" PRIu32 " is invalid\n", cellHeight);
- goto cleanup;
- }
-
- // Emulate a byte-by-byte stream.
- avifROPartialData data = { /*available=*/ { encodedAvif->data, 0 }, /*fullSize=*/encodedAvif->size };
- avifIO io = { 0 };
- io.read = avifIOPartialRead;
- if (giveSizeHint) {
- io.sizeHint = encodedAvif->size;
- }
- io.persistent = isPersistent;
- io.data = &data;
-
- decoder = avifDecoderCreate();
- if (!decoder) {
- printf("ERROR: avifDecoderCreate() failed\n");
- goto cleanup;
- }
- avifDecoderSetIO(decoder, &io);
- decoder->allowIncremental = AVIF_TRUE;
-
- // Parsing is not incremental.
- avifResult parseResult = avifDecoderParse(decoder);
- while (parseResult == AVIF_RESULT_WAITING_ON_IO) {
- if (data.available.size >= data.fullSize) {
- printf("ERROR: avifDecoderParse() returned WAITING_ON_IO instead of OK\n");
- goto cleanup;
- }
- data.available.size = data.available.size + 1;
- parseResult = avifDecoderParse(decoder);
- }
- if (parseResult != AVIF_RESULT_OK) {
- printf("ERROR: avifDecoderParse() failed (%s)\n", avifResultToString(parseResult));
- goto cleanup;
- }
-
- // Decoding is incremental.
- uint32_t previouslyDecodedRowCount = 0;
- avifResult nextImageResult = useNthImageApi ? avifDecoderNthImage(decoder, 0) : avifDecoderNextImage(decoder);
- while (nextImageResult == AVIF_RESULT_WAITING_ON_IO) {
- if (data.available.size >= data.fullSize) {
- printf("ERROR: avifDecoderNextImage() or avifDecoderNthImage(0) returned WAITING_ON_IO instead of OK\n");
- goto cleanup;
- }
- const uint32_t decodedRowCount = avifDecoderDecodedRowCount(decoder);
- if (decodedRowCount < previouslyDecodedRowCount) {
- printf("ERROR: %" PRIu32 " decoded rows decreased to %" PRIu32 "\n", previouslyDecodedRowCount, decodedRowCount);
- goto cleanup;
- }
- const uint32_t minDecodedRowCount =
- getMinDecodedRowCount(reference->height, cellHeight, reference->alphaPlane != NULL, data.available.size, data.fullSize);
- if (decodedRowCount < minDecodedRowCount) {
- printf("ERROR: %" PRIu32 " is fewer than %" PRIu32 " decoded rows\n", decodedRowCount, minDecodedRowCount);
- goto cleanup;
- }
- if (!comparePartialYUVA(reference, decoder->image, decodedRowCount)) {
- goto cleanup;
- }
-
- previouslyDecodedRowCount = decodedRowCount;
- data.available.size = data.available.size + 1;
- nextImageResult = useNthImageApi ? avifDecoderNthImage(decoder, 0) : avifDecoderNextImage(decoder);
- }
- if (nextImageResult != AVIF_RESULT_OK) {
- printf("ERROR: avifDecoderNextImage() or avifDecoderNthImage(0) failed (%s)\n", avifResultToString(nextImageResult));
- goto cleanup;
- }
- if (data.available.size != data.fullSize) {
- printf("ERROR: not all bytes were read\n");
- goto cleanup;
- }
- if (avifDecoderDecodedRowCount(decoder) != decoder->image->height) {
- printf("ERROR: avifDecoderDecodedRowCount() should be decoder->image->height after OK\n");
- goto cleanup;
- }
-
- if (!comparePartialYUVA(reference, decoder->image, reference->height)) {
- goto cleanup;
- }
- success = AVIF_TRUE;
-cleanup:
- if (decoder) {
- avifDecoderDestroy(decoder);
- }
- return success;
-}
-
-avifBool decodeNonIncrementallyAndIncrementally(const avifRWData * encodedAvif,
- avifBool isPersistent,
- avifBool giveSizeHint,
- avifBool useNthImageApi,
- uint32_t cellHeight)
-{
- // TODO(wtc): Remove this assertion when this file is converted to C++.
- assert((useNthImageApi == AVIF_FALSE) || (useNthImageApi == AVIF_TRUE));
- avifBool success = AVIF_FALSE;
- avifImage * reference = avifImageCreateEmpty();
- if (!reference) {
- goto cleanup;
- }
- if (!decodeNonIncrementally(encodedAvif, reference)) {
- goto cleanup;
- }
- if (!decodeIncrementally(encodedAvif, isPersistent, giveSizeHint, useNthImageApi, reference, cellHeight)) {
- goto cleanup;
- }
- success = AVIF_TRUE;
-cleanup:
- if (reference) {
- avifImageDestroy(reference);
- }
- return success;
-}
-
-//------------------------------------------------------------------------------
diff --git a/tests/gtest/avifincrtest_helpers.cc b/tests/gtest/avifincrtest_helpers.cc
new file mode 100644
index 0000000..2078daa
--- /dev/null
+++ b/tests/gtest/avifincrtest_helpers.cc
@@ -0,0 +1,299 @@
+// Copyright 2022 Google LLC. All rights reserved.
+// SPDX-License-Identifier: BSD-2-Clause
+
+#include "avif/avif.h"
+
+#include <algorithm>
+#include <vector>
+
+#include "avifincrtest_helpers.h"
+#include "aviftest_helpers.h"
+#include "gtest/gtest.h"
+
+namespace libavif
+{
+namespace testutil
+{
+namespace
+{
+
+//------------------------------------------------------------------------------
+
+// Verifies that the first (top) rowCount rows of image1 and image2 are identical.
+void comparePartialYUVA(const avifImage & image1, const avifImage & image2, uint32_t rowCount)
+{
+ if (rowCount == 0) {
+ return;
+ }
+ ASSERT_EQ(image1.width, image2.width);
+ ASSERT_GE(image1.height, rowCount);
+ ASSERT_GE(image2.height, rowCount);
+ ASSERT_EQ(image1.depth, image2.depth);
+ ASSERT_EQ(image1.yuvFormat, image2.yuvFormat);
+ ASSERT_EQ(image1.yuvRange, image2.yuvRange);
+
+ avifPixelFormatInfo info;
+ avifGetPixelFormatInfo(image1.yuvFormat, &info);
+ const uint32_t uvWidth = (image1.width + info.chromaShiftX) >> info.chromaShiftX;
+ const uint32_t uvHeight = (rowCount + info.chromaShiftY) >> info.chromaShiftY;
+ const uint32_t pixelByteCount = (image1.depth > 8) ? sizeof(uint16_t) : sizeof(uint8_t);
+
+ for (uint32_t plane = 0; plane < (info.monochrome ? 1 : AVIF_PLANE_COUNT_YUV); ++plane) {
+ const uint32_t width = (plane == AVIF_CHAN_Y) ? image1.width : uvWidth;
+ const uint32_t widthByteCount = width * pixelByteCount;
+ const uint32_t height = (plane == AVIF_CHAN_Y) ? rowCount : uvHeight;
+ const uint8_t * data1 = image1.yuvPlanes[plane];
+ const uint8_t * data2 = image2.yuvPlanes[plane];
+ for (uint32_t y = 0; y < height; ++y) {
+ ASSERT_TRUE(std::equal(data1, data1 + widthByteCount, data2));
+ data1 += image1.yuvRowBytes[plane];
+ data2 += image2.yuvRowBytes[plane];
+ }
+ }
+
+ if (image1.alphaPlane) {
+ ASSERT_NE(image2.alphaPlane, nullptr);
+ ASSERT_EQ(image1.alphaPremultiplied, image2.alphaPremultiplied);
+ const uint32_t widthByteCount = image1.width * pixelByteCount;
+ const uint8_t * data1 = image1.alphaPlane;
+ const uint8_t * data2 = image2.alphaPlane;
+ for (uint32_t y = 0; y < rowCount; ++y) {
+ ASSERT_TRUE(std::equal(data1, data1 + widthByteCount, data2));
+ data1 += image1.alphaRowBytes;
+ data2 += image2.alphaRowBytes;
+ }
+ }
+}
+
+// Returns the expected number of decoded rows when availableByteCount out of byteCount were
+// given to the decoder, for an image of height rows, split into cells of cellHeight rows.
+uint32_t getMinDecodedRowCount(uint32_t height, uint32_t cellHeight, bool hasAlpha, size_t availableByteCount, size_t byteCount)
+{
+ // The whole image should be available when the full input is.
+ if (availableByteCount >= byteCount) {
+ return height;
+ }
+ // All but one cell should be decoded if at most 10 bytes are missing.
+ if ((availableByteCount + 10) >= byteCount) {
+ return height - cellHeight;
+ }
+
+ // Subtract the header because decoding it does not output any pixel.
+ // Most AVIF headers are below 500 bytes.
+ if (availableByteCount <= 500) {
+ return 0;
+ }
+ availableByteCount -= 500;
+ byteCount -= 500;
+ // Alpha, if any, is assumed to be located before the other planes and to
+ // represent at most 50% of the payload.
+ if (hasAlpha) {
+ if (availableByteCount <= (byteCount / 2)) {
+ return 0;
+ }
+ availableByteCount -= byteCount / 2;
+ byteCount -= byteCount / 2;
+ }
+ // Linearly map the input availability ratio to the decoded row ratio.
+ const uint32_t minDecodedCellRowCount = (height / cellHeight) * availableByteCount / byteCount;
+ const uint32_t minDecodedPxRowCount = minDecodedCellRowCount * cellHeight;
+ // One cell is the incremental decoding granularity.
+ // It is unlikely that bytes are evenly distributed among cells. Offset two of them.
+ if (minDecodedPxRowCount <= (2 * cellHeight)) {
+ return 0;
+ }
+ return minDecodedPxRowCount - 2 * cellHeight;
+}
+
+//------------------------------------------------------------------------------
+
+struct avifROPartialData
+{
+ avifROData available;
+ size_t fullSize;
+};
+
+// Implementation of avifIOReadFunc simulating a stream from an array. See avifIOReadFunc documentation.
+// io->data is expected to point to avifROPartialData.
+avifResult avifIOPartialRead(struct avifIO * io, uint32_t readFlags, uint64_t offset, size_t size, avifROData * out)
+{
+ const avifROPartialData * data = (avifROPartialData *)io->data;
+ if ((readFlags != 0) || !data || (data->fullSize < offset)) {
+ return AVIF_RESULT_IO_ERROR;
+ }
+ if (data->fullSize < (offset + size)) {
+ size = data->fullSize - offset;
+ }
+ if (data->available.size < (offset + size)) {
+ return AVIF_RESULT_WAITING_ON_IO;
+ }
+ out->data = data->available.data + offset;
+ out->size = size;
+ return AVIF_RESULT_OK;
+}
+
+//------------------------------------------------------------------------------
+
+// Encodes the image as a grid of at most gridCols*gridRows cells.
+// The cell count is reduced to fit libavif or AVIF format constraints. If impossible, the encoded output is returned empty.
+// The final cellWidth and cellHeight are output.
+void encodeAsGrid(const avifImage & image, uint32_t gridCols, uint32_t gridRows, avifRWData * output, uint32_t * cellWidth, uint32_t * cellHeight)
+{
+ // Chroma subsampling requires even dimensions. See ISO 23000-22 - 7.3.11.4.2
+ const bool needEvenWidths = ((image.yuvFormat == AVIF_PIXEL_FORMAT_YUV420) || (image.yuvFormat == AVIF_PIXEL_FORMAT_YUV422));
+ const bool needEvenHeights = (image.yuvFormat == AVIF_PIXEL_FORMAT_YUV420);
+
+ ASSERT_GT(gridCols * gridRows, 0u);
+ *cellWidth = image.width / gridCols;
+ *cellHeight = image.height / gridRows;
+
+ // avifEncoderAddImageGrid() only accepts grids that evenly split the image into cells at least 64 pixels wide and tall.
+ while ((gridCols > 1) &&
+ (((*cellWidth * gridCols) != image.width) || (*cellWidth < 64) || (needEvenWidths && ((*cellWidth & 1) != 0)))) {
+ --gridCols;
+ *cellWidth = image.width / gridCols;
+ }
+ while ((gridRows > 1) &&
+ (((*cellHeight * gridRows) != image.height) || (*cellHeight < 64) || (needEvenHeights && ((*cellHeight & 1) != 0)))) {
+ --gridRows;
+ *cellHeight = image.height / gridRows;
+ }
+
+ std::vector<testutil::avifImagePtr> cellImages;
+ cellImages.reserve(gridCols * gridRows);
+ for (uint32_t row = 0, iCell = 0; row < gridRows; ++row) {
+ for (uint32_t col = 0; col < gridCols; ++col, ++iCell) {
+ avifCropRect cell;
+ cell.x = col * *cellWidth;
+ cell.y = row * *cellHeight;
+ cell.width = ((cell.x + *cellWidth) <= image.width) ? *cellWidth : (image.width - cell.x);
+ cell.height = ((cell.y + *cellHeight) <= image.height) ? *cellHeight : (image.height - cell.y);
+ cellImages.emplace_back(avifImageCreateEmpty(), avifImageDestroy);
+ ASSERT_NE(cellImages.back(), nullptr);
+ ASSERT_EQ(avifImageSetViewRect(cellImages.back().get(), &image, &cell), AVIF_RESULT_OK);
+ }
+ }
+
+ 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();
+ }
+ ASSERT_EQ(avifEncoderAddImageGrid(encoder.get(), gridCols, gridRows, cellImagePtrs.data(), AVIF_ADD_IMAGE_FLAG_SINGLE), AVIF_RESULT_OK);
+ ASSERT_EQ(avifEncoderFinish(encoder.get(), output), AVIF_RESULT_OK);
+}
+
+// Encodes the image to be decoded incrementally.
+void encodeAsIncremental(const avifImage & image, bool flatCells, avifRWData * output, uint32_t * cellWidth, uint32_t * cellHeight)
+{
+ const uint32_t gridCols = image.width / 64; // 64px is the min cell width.
+ const uint32_t gridRows = flatCells ? 1 : (image.height / 64);
+ encodeAsGrid(image, (gridCols > 1) ? gridCols : 1, (gridRows > 1) ? gridRows : 1, output, cellWidth, cellHeight);
+}
+
+} // namespace
+
+void encodeRectAsIncremental(const avifImage & image,
+ uint32_t width,
+ uint32_t height,
+ bool createAlphaIfNone,
+ bool flatCells,
+ avifRWData * output,
+ uint32_t * cellWidth,
+ uint32_t * cellHeight)
+{
+ avifImagePtr subImage(avifImageCreateEmpty(), avifImageDestroy);
+ ASSERT_NE(subImage, nullptr);
+ ASSERT_LE(width, image.width);
+ ASSERT_LE(height, image.height);
+ avifPixelFormatInfo info;
+ avifGetPixelFormatInfo(image.yuvFormat, &info);
+ const avifCropRect rect {
+ /*x=*/((image.width - width) / 2) & ~info.chromaShiftX, /*y=*/((image.height - height) / 2) & ~info.chromaShiftX, width, height
+ };
+ ASSERT_EQ(avifImageSetViewRect(subImage.get(), &image, &rect), AVIF_RESULT_OK);
+ if (createAlphaIfNone && !subImage->alphaPlane) {
+ ASSERT_NE(image.yuvPlanes[AVIF_CHAN_Y], nullptr) << "No luma plane to simulate an alpha plane";
+ subImage->alphaPlane = image.yuvPlanes[AVIF_CHAN_Y];
+ subImage->alphaRowBytes = image.yuvRowBytes[AVIF_CHAN_Y];
+ subImage->alphaPremultiplied = AVIF_FALSE;
+ subImage->imageOwnsAlphaPlane = AVIF_FALSE;
+ }
+ encodeAsIncremental(*subImage, flatCells, output, cellWidth, cellHeight);
+}
+
+//------------------------------------------------------------------------------
+
+void decodeIncrementally(const avifRWData & encodedAvif,
+ bool isPersistent,
+ bool giveSizeHint,
+ bool useNthImageApi,
+ const avifImage & reference,
+ uint32_t cellHeight)
+{
+ // AVIF cells are at least 64 pixels tall.
+ if (cellHeight != reference.height) {
+ ASSERT_GE(cellHeight, 64u);
+ }
+
+ // Emulate a byte-by-byte stream.
+ avifROPartialData data = { /*available=*/ { encodedAvif.data, 0 }, /*fullSize=*/encodedAvif.size };
+ avifIO io = { /*destroy=*/nullptr, avifIOPartialRead,
+ /*write=*/nullptr, giveSizeHint ? encodedAvif.size : 0,
+ isPersistent, &data };
+
+ testutil::avifDecoderPtr decoder(avifDecoderCreate(), avifDecoderDestroy);
+ ASSERT_NE(decoder, nullptr);
+ avifDecoderSetIO(decoder.get(), &io);
+ decoder->allowIncremental = AVIF_TRUE;
+
+ // Parsing is not incremental.
+ avifResult parseResult = avifDecoderParse(decoder.get());
+ while (parseResult == AVIF_RESULT_WAITING_ON_IO) {
+ ASSERT_LT(data.available.size, data.fullSize) << "avifDecoderParse() returned WAITING_ON_IO instead of OK";
+ data.available.size = data.available.size + 1;
+ parseResult = avifDecoderParse(decoder.get());
+ }
+ ASSERT_EQ(parseResult, AVIF_RESULT_OK);
+
+ // Decoding is incremental.
+ uint32_t previouslyDecodedRowCount = 0;
+ avifResult nextImageResult = useNthImageApi ? avifDecoderNthImage(decoder.get(), 0) : avifDecoderNextImage(decoder.get());
+ while (nextImageResult == AVIF_RESULT_WAITING_ON_IO) {
+ ASSERT_LT(data.available.size, data.fullSize)
+ << (useNthImageApi ? "avifDecoderNthImage(0)" : "avifDecoderNextImage()") << " returned WAITING_ON_IO instead of OK";
+ const uint32_t decodedRowCount = avifDecoderDecodedRowCount(decoder.get());
+ ASSERT_GE(decodedRowCount, previouslyDecodedRowCount);
+ const uint32_t minDecodedRowCount =
+ getMinDecodedRowCount(reference.height, cellHeight, reference.alphaPlane != nullptr, data.available.size, data.fullSize);
+ ASSERT_GE(decodedRowCount, minDecodedRowCount);
+ comparePartialYUVA(reference, *decoder->image, decodedRowCount);
+
+ previouslyDecodedRowCount = decodedRowCount;
+ data.available.size = data.available.size + 1;
+ nextImageResult = useNthImageApi ? avifDecoderNthImage(decoder.get(), 0) : avifDecoderNextImage(decoder.get());
+ }
+ ASSERT_EQ(nextImageResult, AVIF_RESULT_OK);
+ ASSERT_EQ(data.available.size, data.fullSize);
+ ASSERT_EQ(avifDecoderDecodedRowCount(decoder.get()), decoder->image->height);
+
+ comparePartialYUVA(reference, *decoder->image, reference.height);
+}
+
+void decodeNonIncrementallyAndIncrementally(const avifRWData & encodedAvif, bool isPersistent, bool giveSizeHint, bool useNthImageApi, uint32_t cellHeight)
+{
+ avifImagePtr reference(avifImageCreateEmpty(), avifImageDestroy);
+ ASSERT_NE(reference, nullptr);
+ testutil::avifDecoderPtr decoder(avifDecoderCreate(), avifDecoderDestroy);
+ ASSERT_NE(decoder, nullptr);
+ ASSERT_EQ(avifDecoderReadMemory(decoder.get(), reference.get(), encodedAvif.data, encodedAvif.size), AVIF_RESULT_OK);
+
+ decodeIncrementally(encodedAvif, isPersistent, giveSizeHint, useNthImageApi, *reference, cellHeight);
+}
+
+//------------------------------------------------------------------------------
+
+} // namespace testutil
+} // namespace libavif
diff --git a/tests/gtest/avifincrtest_helpers.h b/tests/gtest/avifincrtest_helpers.h
index 82e4061..e0f9986 100644
--- a/tests/gtest/avifincrtest_helpers.h
+++ b/tests/gtest/avifincrtest_helpers.h
@@ -6,44 +6,41 @@
#include "avif/avif.h"
-#ifdef __cplusplus
-extern "C" {
-#endif
+namespace libavif
+{
+namespace testutil
+{
// Encodes a portion of the image to be decoded incrementally.
-avifBool encodeRectAsIncremental(const avifImage * image,
- uint32_t width,
- uint32_t height,
- avifBool createAlphaIfNone,
- avifBool flatCells,
- avifRWData * output,
- uint32_t * cellWidth,
- uint32_t * cellHeight);
-
-// Decodes the data into an image.
-avifBool decodeNonIncrementally(const avifRWData * encodedAvif, avifImage * image);
+void encodeRectAsIncremental(const avifImage & image,
+ uint32_t width,
+ uint32_t height,
+ bool createAlphaIfNone,
+ bool flatCells,
+ avifRWData * output,
+ uint32_t * cellWidth,
+ uint32_t * cellHeight);
// Decodes incrementally the encodedAvif and compares the pixels with the given reference.
// If isPersistent is true, the input encodedAvif is considered as accessible during the whole decoding.
// If giveSizeHint is true, the whole encodedAvif size is given as a hint to the decoder.
// useNthImageApi describes whether the NthImage or NextImage decoder API will be used.
// The cellHeight of all planes of the encodedAvif is given to estimate the incremental granularity.
-avifBool decodeIncrementally(const avifRWData * encodedAvif,
- avifBool isPersistent,
- avifBool giveSizeHint,
- avifBool useNthImageApi,
- const avifImage * reference,
- uint32_t cellHeight);
+void decodeIncrementally(const avifRWData & encodedAvif,
+ bool isPersistent,
+ bool giveSizeHint,
+ bool useNthImageApi,
+ const avifImage & reference,
+ uint32_t cellHeight);
-// Calls decodeIncrementally() with the output of decodeNonIncrementally() as reference.
-avifBool decodeNonIncrementallyAndIncrementally(const avifRWData * encodedAvif,
- avifBool isPersistent,
- avifBool giveSizeHint,
- avifBool useNthImageApi,
- uint32_t cellHeight);
+// Calls decodeIncrementally() with the reference being a regular decoding of encodedAvif.
+void decodeNonIncrementallyAndIncrementally(const avifRWData & encodedAvif,
+ bool isPersistent,
+ bool giveSizeHint,
+ bool useNthImageApi,
+ uint32_t cellHeight);
-#ifdef __cplusplus
-} // extern "C"
-#endif
+} // namespace testutil
+} // namespace libavif
#endif // LIBAVIF_TESTS_AVIFINCRTEST_HELPERS_H_