/src/libavif/tests/gtest/avifincrtest_helpers.cc
Line | Count | Source |
1 | | // Copyright 2022 Google LLC |
2 | | // SPDX-License-Identifier: BSD-2-Clause |
3 | | |
4 | | #include "avifincrtest_helpers.h" |
5 | | |
6 | | #include <algorithm> |
7 | | #include <cmath> |
8 | | #include <cstdint> |
9 | | #include <cstring> |
10 | | #include <iostream> |
11 | | #include <memory> |
12 | | #include <vector> |
13 | | |
14 | | #include "avif/avif.h" |
15 | | #include "aviftest_helpers.h" |
16 | | #include "gtest/gtest.h" |
17 | | |
18 | | namespace avif { |
19 | | namespace testutil { |
20 | | namespace { |
21 | | |
22 | | //------------------------------------------------------------------------------ |
23 | | |
24 | | // Verifies that the first (top) row_count rows of image1 and image2 are |
25 | | // identical. |
26 | | void ComparePartialYuva(const avifImage& image1, const avifImage& image2, |
27 | 15.7M | uint32_t row_count) { |
28 | 15.7M | if (row_count == 0) { |
29 | 12.6M | return; |
30 | 12.6M | } |
31 | 15.7M | ASSERT_EQ(image1.width, image2.width); |
32 | 3.07M | ASSERT_GE(image1.height, row_count); |
33 | 3.07M | ASSERT_GE(image2.height, row_count); |
34 | 3.07M | ASSERT_EQ(image1.depth, image2.depth); |
35 | 3.07M | ASSERT_EQ(image1.yuvFormat, image2.yuvFormat); |
36 | 3.07M | ASSERT_EQ(image1.yuvRange, image2.yuvRange); |
37 | | |
38 | 3.07M | avifPixelFormatInfo info; |
39 | 3.07M | avifGetPixelFormatInfo(image1.yuvFormat, &info); |
40 | 3.07M | const uint32_t uv_height = |
41 | 3.07M | info.monochrome ? 0 |
42 | 3.07M | : ((row_count + info.chromaShiftY) >> info.chromaShiftY); |
43 | 3.07M | const size_t pixel_byte_count = |
44 | 3.07M | (image1.depth > 8) ? sizeof(uint16_t) : sizeof(uint8_t); |
45 | | |
46 | 3.07M | if (image1.alphaPlane) { |
47 | 756k | ASSERT_NE(image2.alphaPlane, nullptr); |
48 | 756k | ASSERT_EQ(image1.alphaPremultiplied, image2.alphaPremultiplied); |
49 | 756k | } |
50 | | |
51 | 3.07M | const int last_plane = image1.alphaPlane ? AVIF_CHAN_A : AVIF_CHAN_V; |
52 | 13.0M | for (int plane = AVIF_CHAN_Y; plane <= last_plane; ++plane) { |
53 | 9.96M | const size_t width_byte_count = |
54 | 9.96M | avifImagePlaneWidth(&image1, plane) * pixel_byte_count; |
55 | 9.96M | const uint32_t height = |
56 | 9.96M | (plane == AVIF_CHAN_Y || plane == AVIF_CHAN_A) ? row_count : uv_height; |
57 | 9.96M | const uint8_t* row1 = avifImagePlane(&image1, plane); |
58 | 9.96M | const uint8_t* row2 = avifImagePlane(&image2, plane); |
59 | 9.96M | const uint32_t row1_bytes = avifImagePlaneRowBytes(&image1, plane); |
60 | 9.96M | const uint32_t row2_bytes = avifImagePlaneRowBytes(&image2, plane); |
61 | 1.12G | for (uint32_t y = 0; y < height; ++y) { |
62 | 1.11G | ASSERT_EQ(std::memcmp(row1, row2, width_byte_count), 0); |
63 | 1.11G | row1 += row1_bytes; |
64 | 1.11G | row2 += row2_bytes; |
65 | 1.11G | } |
66 | 9.96M | } |
67 | | |
68 | 3.07M | if (image1.gainMap != nullptr && image1.gainMap->image != nullptr && |
69 | 9.15k | image2.gainMap != nullptr && image2.gainMap->image != nullptr) { |
70 | 9.15k | const uint32_t gain_map_row_count = (uint32_t)roundf( |
71 | 9.15k | (float)row_count / image1.height * image1.gainMap->image->height); |
72 | 9.15k | ComparePartialYuva(*image1.gainMap->image, *image2.gainMap->image, |
73 | 9.15k | gain_map_row_count); |
74 | 9.15k | } |
75 | 3.07M | } |
76 | | |
77 | | // Returns the expected number of decoded rows when available_byte_count out of |
78 | | // byte_count were given to the decoder, for an image of height rows, split into |
79 | | // cells of cell_height rows. |
80 | | uint32_t GetMinDecodedRowCount(uint32_t height, uint32_t cell_height, |
81 | | bool has_alpha, bool has_gain_map, |
82 | | size_t available_byte_count, size_t byte_count, |
83 | 15.7M | bool enable_fine_incremental_check) { |
84 | | // The whole image should be available when the full input is. |
85 | 15.7M | if (available_byte_count >= byte_count) { |
86 | 0 | return height; |
87 | 0 | } |
88 | | |
89 | | // The tests below can be hard to tune for any kind of input, especially |
90 | | // fuzzed grids, where tile ordering is unknown. Early exit in that case. |
91 | 15.7M | if (!enable_fine_incremental_check) return 0; |
92 | | |
93 | | // There is no valid AV1 payload smaller than 10 bytes, so all but one cell |
94 | | // should be decoded if at most 10 bytes are missing. |
95 | 0 | if ((available_byte_count + 10) >= byte_count) { |
96 | 0 | return height - cell_height; |
97 | 0 | } |
98 | | // Subtract the header because decoding it does not output any pixel. |
99 | | // Most AVIF headers are below 500 bytes. |
100 | 0 | if (available_byte_count <= 500) { |
101 | 0 | return 0; |
102 | 0 | } |
103 | 0 | available_byte_count -= 500; |
104 | 0 | byte_count -= 500; |
105 | | // Extra planes (alpha, gain map), if any, are assumed to be located before |
106 | | // the color planes. It's assumed that each extra planes is at most |
107 | | // total_size / (1 + num_extra_planes). |
108 | 0 | const int num_extra_planes = (has_alpha ? 1 : 0) + (has_gain_map ? 1 : 0); |
109 | 0 | const size_t max_size_of_extra_planes = static_cast<size_t>( |
110 | 0 | (byte_count / (num_extra_planes + 1)) * num_extra_planes); |
111 | 0 | if (available_byte_count <= max_size_of_extra_planes) { |
112 | 0 | return 0; |
113 | 0 | } |
114 | 0 | available_byte_count -= max_size_of_extra_planes; |
115 | 0 | byte_count -= max_size_of_extra_planes; |
116 | | // Linearly map the input availability ratio to the decoded row ratio. |
117 | 0 | const uint32_t min_decoded_cell_row_count = static_cast<uint32_t>( |
118 | 0 | (height / cell_height) * available_byte_count / byte_count); |
119 | 0 | const uint32_t min_decoded_px_row_count = |
120 | 0 | min_decoded_cell_row_count * cell_height; |
121 | | // One cell is the incremental decoding granularity. |
122 | | // It is unlikely that bytes are evenly distributed among cells. Offset two of |
123 | | // them. |
124 | 0 | if (min_decoded_px_row_count <= (2 * cell_height)) { |
125 | 0 | return 0; |
126 | 0 | } |
127 | 0 | return min_decoded_px_row_count - 2 * cell_height; |
128 | 0 | } |
129 | | |
130 | | //------------------------------------------------------------------------------ |
131 | | |
132 | | struct PartialData { |
133 | | avifROData available; |
134 | | size_t full_size; |
135 | | |
136 | | // Only used as nonpersistent input. |
137 | | std::unique_ptr<uint8_t[]> nonpersistent_bytes; |
138 | | size_t num_nonpersistent_bytes; |
139 | | }; |
140 | | |
141 | | // Implementation of avifIOReadFunc simulating a stream from an array. See |
142 | | // avifIOReadFunc documentation. io->data is expected to point to PartialData. |
143 | | avifResult PartialRead(struct avifIO* io, uint32_t read_flags, |
144 | 37.2M | uint64_t offset64, size_t size, avifROData* out) { |
145 | 37.2M | PartialData* data = reinterpret_cast<PartialData*>(io->data); |
146 | 37.2M | if ((read_flags != 0) || !data || (data->full_size < offset64)) { |
147 | 0 | return AVIF_RESULT_IO_ERROR; |
148 | 0 | } |
149 | 37.2M | const size_t offset = static_cast<size_t>(offset64); |
150 | | // Use |offset| instead of |offset64| from this point on. |
151 | 37.2M | if (size > (data->full_size - offset)) { |
152 | 5.38k | size = data->full_size - offset; |
153 | 5.38k | } |
154 | 37.2M | if (data->available.size < (offset + size)) { |
155 | 23.3M | return AVIF_RESULT_WAITING_ON_IO; |
156 | 23.3M | } |
157 | 13.8M | if (io->persistent) { |
158 | 6.85M | out->data = data->available.data + offset; |
159 | 6.97M | } else { |
160 | | // Dedicated buffer containing just the available bytes and nothing more. |
161 | 6.97M | std::unique_ptr<uint8_t[]> bytes(new uint8_t[size]); |
162 | 6.97M | std::copy(data->available.data + offset, |
163 | 6.97M | data->available.data + offset + size, bytes.get()); |
164 | 6.97M | out->data = bytes.get(); |
165 | | // Flip the previously returned bytes to make sure the values changed. |
166 | 627M | for (size_t i = 0; i < data->num_nonpersistent_bytes; ++i) { |
167 | 620M | data->nonpersistent_bytes[i] = ~data->nonpersistent_bytes[i]; |
168 | 620M | } |
169 | | // Free the memory to invalidate the old pointer. Only do that after |
170 | | // allocating the new bytes to make sure to have a different pointer. |
171 | 6.97M | data->nonpersistent_bytes = std::move(bytes); |
172 | 6.97M | data->num_nonpersistent_bytes = size; |
173 | 6.97M | } |
174 | 13.8M | out->size = size; |
175 | 13.8M | return AVIF_RESULT_OK; |
176 | 37.2M | } |
177 | | |
178 | | //------------------------------------------------------------------------------ |
179 | | |
180 | | // Encodes the image as a grid of at most grid_cols*grid_rows cells. |
181 | | // The cell count is reduced to fit libavif or AVIF format constraints. If |
182 | | // impossible, the encoded output is returned empty. The final cell_width and |
183 | | // cell_height are output. |
184 | | void EncodeAsGrid(const avifImage& image, uint32_t grid_cols, |
185 | | uint32_t grid_rows, avifRWData* output, uint32_t* cell_width, |
186 | 0 | uint32_t* cell_height) { |
187 | | // Chroma subsampling requires even dimensions. See ISO 23000-22 - 7.3.11.4.2 |
188 | 0 | const bool need_even_widths = |
189 | 0 | ((image.yuvFormat == AVIF_PIXEL_FORMAT_YUV420) || |
190 | 0 | (image.yuvFormat == AVIF_PIXEL_FORMAT_YUV422)); |
191 | 0 | const bool need_even_heights = (image.yuvFormat == AVIF_PIXEL_FORMAT_YUV420); |
192 | |
|
193 | 0 | ASSERT_GT(grid_cols * grid_rows, 0u); |
194 | 0 | *cell_width = image.width / grid_cols; |
195 | 0 | *cell_height = image.height / grid_rows; |
196 | | |
197 | | // avifEncoderAddImageGrid() only accepts grids that evenly split the image |
198 | | // into cells at least 64 pixels wide and tall. |
199 | 0 | while ((grid_cols > 1) && |
200 | 0 | (((*cell_width * grid_cols) != image.width) || (*cell_width < 64) || |
201 | 0 | (need_even_widths && ((*cell_width & 1) != 0)))) { |
202 | 0 | --grid_cols; |
203 | 0 | *cell_width = image.width / grid_cols; |
204 | 0 | } |
205 | 0 | while ((grid_rows > 1) && |
206 | 0 | (((*cell_height * grid_rows) != image.height) || (*cell_height < 64) || |
207 | 0 | (need_even_heights && ((*cell_height & 1) != 0)))) { |
208 | 0 | --grid_rows; |
209 | 0 | *cell_height = image.height / grid_rows; |
210 | 0 | } |
211 | |
|
212 | 0 | std::vector<ImagePtr> cell_images; |
213 | 0 | cell_images.reserve(grid_cols * grid_rows); |
214 | 0 | for (uint32_t row = 0, i_cell = 0; row < grid_rows; ++row) { |
215 | 0 | for (uint32_t col = 0; col < grid_cols; ++col, ++i_cell) { |
216 | 0 | avifCropRect cell; |
217 | 0 | cell.x = col * *cell_width; |
218 | 0 | cell.y = row * *cell_height; |
219 | 0 | cell.width = ((cell.x + *cell_width) <= image.width) |
220 | 0 | ? *cell_width |
221 | 0 | : (image.width - cell.x); |
222 | 0 | cell.height = ((cell.y + *cell_height) <= image.height) |
223 | 0 | ? *cell_height |
224 | 0 | : (image.height - cell.y); |
225 | 0 | cell_images.emplace_back(avifImageCreateEmpty()); |
226 | 0 | ASSERT_NE(cell_images.back(), nullptr); |
227 | 0 | ASSERT_EQ(avifImageSetViewRect(cell_images.back().get(), &image, &cell), |
228 | 0 | AVIF_RESULT_OK); |
229 | 0 | } |
230 | 0 | } |
231 | | |
232 | 0 | EncoderPtr encoder(avifEncoderCreate()); |
233 | 0 | ASSERT_NE(encoder, nullptr); |
234 | 0 | encoder->speed = AVIF_SPEED_FASTEST; |
235 | | // Just here to match libavif API. |
236 | 0 | std::vector<avifImage*> cell_image_ptrs(cell_images.size()); |
237 | 0 | for (size_t i = 0; i < cell_images.size(); ++i) { |
238 | 0 | cell_image_ptrs[i] = cell_images[i].get(); |
239 | 0 | } |
240 | 0 | ASSERT_EQ(avifEncoderAddImageGrid(encoder.get(), grid_cols, grid_rows, |
241 | 0 | cell_image_ptrs.data(), |
242 | 0 | AVIF_ADD_IMAGE_FLAG_SINGLE), |
243 | 0 | AVIF_RESULT_OK); |
244 | 0 | ASSERT_EQ(avifEncoderFinish(encoder.get(), output), AVIF_RESULT_OK); |
245 | 0 | } |
246 | | |
247 | | // Encodes the image to be decoded incrementally. |
248 | | void EncodeAsIncremental(const avifImage& image, bool flat_cells, |
249 | | avifRWData* output, uint32_t* cell_width, |
250 | 0 | uint32_t* cell_height) { |
251 | 0 | const uint32_t grid_cols = image.width / 64; // 64px is the min cell width. |
252 | 0 | const uint32_t grid_rows = flat_cells ? 1 : (image.height / 64); |
253 | 0 | EncodeAsGrid(image, (grid_cols > 1) ? grid_cols : 1, |
254 | 0 | (grid_rows > 1) ? grid_rows : 1, output, cell_width, |
255 | 0 | cell_height); |
256 | 0 | } |
257 | | |
258 | | } // namespace |
259 | | |
260 | | void EncodeRectAsIncremental(const avifImage& image, uint32_t width, |
261 | | uint32_t height, bool create_alpha_if_none, |
262 | | bool flat_cells, avifRWData* output, |
263 | 0 | uint32_t* cell_width, uint32_t* cell_height) { |
264 | 0 | ImagePtr sub_image(avifImageCreateEmpty()); |
265 | 0 | ASSERT_NE(sub_image, nullptr); |
266 | 0 | ASSERT_LE(width, image.width); |
267 | 0 | ASSERT_LE(height, image.height); |
268 | | // Encode the centered rect of dimensions width*height from the image. |
269 | 0 | avifCropRect rect{/*x=*/(image.width - width) / 2, |
270 | 0 | /*y=*/(image.height - height) / 2, width, height}; |
271 | 0 | avifPixelFormatInfo info; |
272 | 0 | avifGetPixelFormatInfo(image.yuvFormat, &info); |
273 | 0 | if (!info.monochrome) { |
274 | | // Use even coordinates in subsampled dimensions. |
275 | 0 | rect.x &= ~info.chromaShiftX; |
276 | 0 | rect.y &= ~info.chromaShiftY; |
277 | 0 | } |
278 | 0 | ASSERT_EQ(avifImageSetViewRect(sub_image.get(), &image, &rect), |
279 | 0 | AVIF_RESULT_OK); |
280 | 0 | if (create_alpha_if_none && !sub_image->alphaPlane) { |
281 | 0 | ASSERT_NE(image.yuvPlanes[AVIF_CHAN_Y], nullptr) |
282 | 0 | << "No luma plane to simulate an alpha plane"; |
283 | 0 | sub_image->alphaPlane = image.yuvPlanes[AVIF_CHAN_Y]; |
284 | 0 | sub_image->alphaRowBytes = image.yuvRowBytes[AVIF_CHAN_Y]; |
285 | 0 | sub_image->alphaPremultiplied = AVIF_FALSE; |
286 | 0 | sub_image->imageOwnsAlphaPlane = AVIF_FALSE; |
287 | 0 | } |
288 | 0 | EncodeAsIncremental(*sub_image, flat_cells, output, cell_width, cell_height); |
289 | 0 | } |
290 | | |
291 | | //------------------------------------------------------------------------------ |
292 | | |
293 | | avifResult DecodeIncrementally(const avifRWData& encoded_avif, |
294 | | avifDecoder* decoder, bool is_persistent, |
295 | | bool give_size_hint, bool use_nth_image_api, |
296 | | const avifImage& reference, uint32_t cell_height, |
297 | | bool enable_fine_incremental_check, |
298 | | bool expect_whole_file_read, |
299 | 10.7k | bool expect_parse_success_from_partial_file) { |
300 | | // AVIF cells are at least 64 pixels tall. |
301 | 10.7k | if (cell_height != reference.height) { |
302 | 492 | AVIF_CHECKERR(cell_height >= 64u, AVIF_RESULT_INVALID_ARGUMENT); |
303 | 492 | } |
304 | | |
305 | | // Emulate a byte-by-byte stream. |
306 | 10.7k | PartialData data = { |
307 | 10.7k | /*available=*/{encoded_avif.data, 0}, /*fullSize=*/encoded_avif.size, |
308 | 10.7k | /*nonpersistent_bytes=*/nullptr, /*num_nonpersistent_bytes=*/0}; |
309 | 10.7k | avifIO io = { |
310 | 10.7k | /*destroy=*/nullptr, PartialRead, |
311 | 10.7k | /*write=*/nullptr, give_size_hint ? encoded_avif.size : 0, |
312 | 10.7k | is_persistent, &data}; |
313 | 10.7k | avifDecoderSetIO(decoder, &io); |
314 | | // Reset the decoder's IO to nullptr before 'io' goes out of scope and becomes |
315 | | // invalid. |
316 | 10.7k | auto cleanup_io_fn = [](avifDecoder* decoder) { |
317 | 10.7k | avifDecoderSetIO(decoder, nullptr); |
318 | 10.7k | }; |
319 | 10.7k | std::unique_ptr<avifDecoder, decltype(cleanup_io_fn)> cleanup_io( |
320 | 10.7k | decoder, cleanup_io_fn); // Call automatically at end of scope. |
321 | | |
322 | 10.7k | decoder->allowIncremental = AVIF_TRUE; |
323 | 10.7k | const size_t step = std::max<size_t>(1, data.full_size / 10000); |
324 | | |
325 | | // Parsing is not incremental. |
326 | 10.7k | avifResult parse_result = avifDecoderParse(decoder); |
327 | 4.39M | while (parse_result == AVIF_RESULT_WAITING_ON_IO) { |
328 | 4.37M | if (data.available.size >= data.full_size) { |
329 | 0 | std::cerr << "avifDecoderParse() returned WAITING_ON_IO instead of OK" |
330 | 0 | << std::endl; |
331 | 0 | return AVIF_RESULT_TRUNCATED_DATA; |
332 | 0 | } |
333 | 4.37M | data.available.size = std::min(data.available.size + step, data.full_size); |
334 | 4.37M | parse_result = avifDecoderParse(decoder); |
335 | 4.37M | } |
336 | 10.7k | if (data.available.size == data.full_size && |
337 | 1.38k | expect_parse_success_from_partial_file) { |
338 | | // Can happen if the data is in 'idat', or if some metadata is at the end of |
339 | | // the file. But ideally this should be avoided. |
340 | 0 | printf( |
341 | 0 | "ERROR: had to provide the whole file for avifDecoderParse() to " |
342 | 0 | "succeed\n"); |
343 | 0 | AVIF_CHECKERR(false, AVIF_RESULT_INVALID_ARGUMENT); |
344 | 0 | } |
345 | 10.7k | AVIF_CHECKRES(parse_result); |
346 | | |
347 | | // Decoding is incremental. |
348 | 10.2k | uint32_t previously_decoded_row_count = 0; |
349 | 10.2k | avifResult next_image_result = use_nth_image_api |
350 | 10.2k | ? avifDecoderNthImage(decoder, 0) |
351 | 10.2k | : avifDecoderNextImage(decoder); |
352 | 15.7M | while (next_image_result == AVIF_RESULT_WAITING_ON_IO) { |
353 | 15.7M | if (data.available.size >= data.full_size) { |
354 | 0 | std::cerr << (use_nth_image_api ? "avifDecoderNthImage(0)" |
355 | 0 | : "avifDecoderNextImage()") |
356 | 0 | << " returned WAITING_ON_IO instead of OK"; |
357 | 0 | AVIF_CHECKERR(false, AVIF_RESULT_INVALID_ARGUMENT); |
358 | 0 | } |
359 | 15.7M | const uint32_t decoded_row_count = avifDecoderDecodedRowCount(decoder); |
360 | 15.7M | if (decoded_row_count < previously_decoded_row_count) { |
361 | 0 | printf("ERROR: decoded row count decreased from %d to %d\n", |
362 | 0 | previously_decoded_row_count, decoded_row_count); |
363 | 0 | AVIF_CHECKERR(false, AVIF_RESULT_INVALID_ARGUMENT); |
364 | 0 | } |
365 | 15.7M | const uint32_t min_decoded_row_count = GetMinDecodedRowCount( |
366 | 15.7M | reference.height, cell_height, reference.alphaPlane != nullptr, |
367 | 15.7M | reference.gainMap != nullptr, data.available.size, data.full_size, |
368 | 15.7M | enable_fine_incremental_check); |
369 | 15.7M | if (decoded_row_count < min_decoded_row_count) { |
370 | 0 | printf( |
371 | 0 | "ERROR: expected to have decoded at least %d rows with %zu available " |
372 | 0 | "bytes, but only %d were decoded\n", |
373 | 0 | min_decoded_row_count, data.available.size, decoded_row_count); |
374 | 0 | AVIF_CHECKERR(false, AVIF_RESULT_INVALID_ARGUMENT); |
375 | 0 | } |
376 | 15.7M | ComparePartialYuva(reference, *decoder->image, decoded_row_count); |
377 | | |
378 | 15.7M | previously_decoded_row_count = decoded_row_count; |
379 | 15.7M | data.available.size = std::min(data.available.size + step, data.full_size); |
380 | 15.7M | next_image_result = use_nth_image_api ? avifDecoderNthImage(decoder, 0) |
381 | 15.7M | : avifDecoderNextImage(decoder); |
382 | 15.7M | } |
383 | 10.2k | AVIF_CHECKRES(next_image_result); |
384 | 10.2k | if (expect_whole_file_read) { |
385 | 6.59k | AVIF_CHECKERR(data.available.size == data.full_size, |
386 | 6.59k | AVIF_RESULT_INVALID_ARGUMENT); |
387 | 6.59k | } |
388 | 10.2k | AVIF_CHECKERR(avifDecoderDecodedRowCount(decoder) == decoder->image->height, |
389 | 10.2k | AVIF_RESULT_INVALID_ARGUMENT); |
390 | | |
391 | 10.2k | ComparePartialYuva(reference, *decoder->image, reference.height); |
392 | 10.2k | return AVIF_RESULT_OK; |
393 | 10.2k | } |
394 | | |
395 | | avifResult DecodeNonIncrementallyAndIncrementally( |
396 | | const avifRWData& encoded_avif, avifDecoder* decoder, bool is_persistent, |
397 | | bool give_size_hint, bool use_nth_image_api, uint32_t cell_height, |
398 | | bool enable_fine_incremental_check, bool expect_whole_file_read, |
399 | 7.64k | bool expect_parse_success_from_partial_file) { |
400 | 7.64k | ImagePtr reference(avifImageCreateEmpty()); |
401 | 7.64k | if (reference == nullptr) return AVIF_RESULT_INVALID_ARGUMENT; |
402 | 7.64k | decoder->allowIncremental = AVIF_FALSE; |
403 | 7.64k | AVIF_CHECKRES(avifDecoderReadMemory(decoder, reference.get(), |
404 | 7.64k | encoded_avif.data, encoded_avif.size)); |
405 | | |
406 | 7.64k | const avifResult result = DecodeIncrementally( |
407 | 7.64k | encoded_avif, decoder, is_persistent, give_size_hint, use_nth_image_api, |
408 | 7.64k | *reference, cell_height, enable_fine_incremental_check, |
409 | 7.64k | expect_whole_file_read, expect_parse_success_from_partial_file); |
410 | 7.64k | return result; |
411 | 7.64k | } |
412 | | |
413 | | //------------------------------------------------------------------------------ |
414 | | |
415 | | } // namespace testutil |
416 | | } // namespace avif |