Coverage Report

Created: 2026-08-13 06:43

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/libavif/tests/gtest/avif_fuzztest_helpers.cc
Line
Count
Source
1
// Copyright 2022 Google LLC
2
// SPDX-License-Identifier: BSD-2-Clause
3
4
#include "avif_fuzztest_helpers.h"
5
6
#include <algorithm>
7
#include <cassert>
8
#include <cstdint>
9
#include <cstdlib>
10
#include <iostream>
11
#include <utility>
12
#include <vector>
13
14
#include "avif/avif.h"
15
#include "avifutil.h"
16
#include "fuzztest/fuzztest.h"
17
#include "gtest/gtest.h"
18
19
namespace avif {
20
namespace testutil {
21
namespace {
22
23
//------------------------------------------------------------------------------
24
25
ImagePtr CreateAvifImage(size_t width, size_t height, int depth,
26
                         avifPixelFormat pixel_format, bool has_alpha,
27
91.2k
                         const uint8_t* samples) {
28
91.2k
  ImagePtr image(avifImageCreate(static_cast<uint32_t>(width),
29
91.2k
                                 static_cast<uint32_t>(height), depth,
30
91.2k
                                 pixel_format));
31
91.2k
  if (image.get() == nullptr) {
32
0
    return image;
33
0
  }
34
91.2k
  if (avifImageAllocatePlanes(image.get(),
35
91.2k
                              has_alpha ? AVIF_PLANES_ALL : AVIF_PLANES_YUV) !=
36
91.2k
      AVIF_RESULT_OK) {
37
0
    return nullptr;
38
0
  }
39
40
91.2k
  for (avifChannelIndex c :
41
365k
       {AVIF_CHAN_Y, AVIF_CHAN_U, AVIF_CHAN_V, AVIF_CHAN_A}) {
42
365k
    const size_t plane_height = avifImagePlaneHeight(image.get(), c);
43
365k
    uint8_t* row = avifImagePlane(image.get(), c);
44
365k
    const uint32_t row_bytes = avifImagePlaneRowBytes(image.get(), c);
45
365k
    assert(row_bytes == avifImagePlaneWidth(image.get(), c) *
46
365k
                            (avifImageUsesU16(image.get()) ? 2 : 1));
47
14.3M
    for (size_t y = 0; y < plane_height; ++y) {
48
14.0M
      std::copy(samples, samples + row_bytes, row);
49
14.0M
      row += row_bytes;
50
14.0M
      samples += row_bytes;
51
14.0M
    }
52
365k
  }
53
91.2k
  return image;
54
91.2k
}
55
56
}  // namespace
57
58
ImagePtr CreateAvifImage8b(size_t width, size_t height,
59
                           avifPixelFormat pixel_format, bool has_alpha,
60
27.9k
                           const std::vector<uint8_t>& samples) {
61
27.9k
  return CreateAvifImage(width, height, 8, pixel_format, has_alpha,
62
27.9k
                         samples.data());
63
27.9k
}
64
65
ImagePtr CreateAvifImage16b(size_t width, size_t height, int depth,
66
                            avifPixelFormat pixel_format, bool has_alpha,
67
18.7k
                            const std::vector<uint16_t>& samples) {
68
18.7k
  return CreateAvifImage(width, height, depth, pixel_format, has_alpha,
69
18.7k
                         reinterpret_cast<const uint8_t*>(samples.data()));
70
18.7k
}
71
72
std::vector<ImagePtr> CreateAvifAnim8b(size_t num_frames, size_t width,
73
                                       size_t height,
74
                                       avifPixelFormat pixel_format,
75
                                       bool has_alpha,
76
10.8k
                                       const std::vector<uint8_t>& samples) {
77
10.8k
  std::vector<ImagePtr> frames;
78
10.8k
  frames.reserve(num_frames);
79
10.8k
  const uint8_t* frame_samples = samples.data();
80
42.9k
  for (size_t i = 0; i < num_frames; ++i) {
81
32.0k
    frames.push_back(CreateAvifImage(width, height, 8, pixel_format, has_alpha,
82
32.0k
                                     frame_samples));
83
32.0k
    frame_samples +=
84
32.0k
        GetNumSamples(/*num_frames=*/1, width, height, pixel_format, has_alpha);
85
32.0k
  }
86
10.8k
  return frames;
87
10.8k
}
88
89
std::vector<ImagePtr> CreateAvifAnim16b(size_t num_frames, size_t width,
90
                                        size_t height, int depth,
91
                                        avifPixelFormat pixel_format,
92
                                        bool has_alpha,
93
4.58k
                                        const std::vector<uint16_t>& samples) {
94
4.58k
  std::vector<ImagePtr> frames;
95
4.58k
  frames.reserve(num_frames);
96
4.58k
  const uint16_t* frame_samples = samples.data();
97
17.0k
  for (size_t i = 0; i < num_frames; ++i) {
98
12.4k
    frames.push_back(
99
12.4k
        CreateAvifImage(width, height, depth, pixel_format, has_alpha,
100
12.4k
                        reinterpret_cast<const uint8_t*>(frame_samples)));
101
12.4k
    frame_samples +=
102
12.4k
        GetNumSamples(/*num_frames=*/1, width, height, pixel_format, has_alpha);
103
12.4k
  }
104
4.58k
  return frames;
105
4.58k
}
106
107
EncoderPtr CreateAvifEncoder(avifCodecChoice codec_choice, int max_threads,
108
                             int min_quantizer, int max_quantizer,
109
                             int min_quantizer_alpha, int max_quantizer_alpha,
110
                             int tile_rows_log2, int tile_cols_log2,
111
54.7k
                             int speed) {
112
54.7k
  EncoderPtr encoder(avifEncoderCreate());
113
54.7k
  if (encoder.get() == nullptr) {
114
0
    return encoder;
115
0
  }
116
54.7k
  encoder->codecChoice = codec_choice;
117
54.7k
  encoder->maxThreads = max_threads;
118
  // minQuantizer must be at most maxQuantizer.
119
54.7k
  encoder->minQuantizer = std::min(min_quantizer, max_quantizer);
120
54.7k
  encoder->maxQuantizer = std::max(min_quantizer, max_quantizer);
121
54.7k
  encoder->minQuantizerAlpha =
122
54.7k
      std::min(min_quantizer_alpha, max_quantizer_alpha);
123
54.7k
  encoder->maxQuantizerAlpha =
124
54.7k
      std::max(min_quantizer_alpha, max_quantizer_alpha);
125
54.7k
  encoder->tileRowsLog2 = tile_rows_log2;
126
54.7k
  encoder->tileColsLog2 = tile_cols_log2;
127
54.7k
  encoder->speed = speed;
128
54.7k
  return encoder;
129
54.7k
}
130
131
DecoderPtr CreateAvifDecoder(avifCodecChoice codec_choice, int max_threads,
132
                             avifDecoderSource requested_source,
133
                             bool allow_progressive, bool allow_incremental,
134
                             bool ignore_exif, bool ignore_xmp,
135
                             uint32_t image_size_limit,
136
                             uint32_t image_dimension_limit,
137
                             uint32_t image_count_limit,
138
82.5k
                             avifStrictFlags strict_flags) {
139
82.5k
  DecoderPtr decoder(avifDecoderCreate());
140
82.5k
  if (decoder.get() == nullptr) {
141
0
    return decoder;
142
0
  }
143
82.5k
  decoder->codecChoice = codec_choice;
144
82.5k
  decoder->maxThreads = max_threads;
145
82.5k
  decoder->requestedSource = requested_source;
146
82.5k
  decoder->allowProgressive = allow_progressive;
147
82.5k
  decoder->allowIncremental = allow_incremental;
148
82.5k
  decoder->ignoreExif = ignore_exif;
149
82.5k
  decoder->ignoreXMP = ignore_xmp;
150
82.5k
  decoder->imageSizeLimit = image_size_limit;
151
82.5k
  decoder->imageDimensionLimit = image_dimension_limit;
152
82.5k
  decoder->imageCountLimit = image_count_limit;
153
82.5k
  decoder->strictFlags = strict_flags;
154
82.5k
  return decoder;
155
82.5k
}
156
157
0
ImagePtr AvifImageToUniquePtr(avifImage* image) { return ImagePtr(image); }
158
159
DecoderPtr AddGainMapOptionsToDecoder(
160
82.5k
    DecoderPtr decoder, avifImageContentTypeFlags image_content_to_decode) {
161
82.5k
  decoder->imageContentToDecode = image_content_to_decode;
162
82.5k
  return decoder;
163
82.5k
}
164
165
ImagePtr AddGainMapToImage(
166
    ImagePtr image, ImagePtr gain_map,
167
    const std::pair<avifSignedFraction, avifSignedFraction>& gain_map_min_max0,
168
    const std::pair<avifSignedFraction, avifSignedFraction>& gain_map_min_max1,
169
    const std::pair<avifSignedFraction, avifSignedFraction>& gain_map_min_max2,
170
    uint32_t gain_map_gamma_n0, uint32_t gain_map_gamma_n1,
171
    uint32_t gain_map_gamma_n2, uint32_t gain_map_gamma_d0,
172
    uint32_t gain_map_gamma_d1, uint32_t gain_map_gamma_d2,
173
    int32_t base_offset_n0, int32_t base_offset_n1, int32_t base_offset_n2,
174
    uint32_t base_offset_d0, uint32_t base_offset_d1, uint32_t base_offset_d2,
175
    int32_t alternate_offset_n0, int32_t alternate_offset_n1,
176
    int32_t alternate_offset_n2, uint32_t alternate_offset_d0,
177
    uint32_t alternate_offset_d1, uint32_t alternate_offset_d2,
178
    uint32_t base_hdr_headroom_n, uint32_t base_hdr_headroom_d,
179
    uint32_t alternate_hdr_headroom_n, uint32_t alternate_hdr_headroom_d,
180
3.35k
    bool use_base_color_space) {
181
3.35k
  image->gainMap = avifGainMapCreate();
182
3.35k
  image->gainMap->image = gain_map.release();
183
184
3.35k
  image->gainMap->gainMapMin[0] = gain_map_min_max0.first;
185
3.35k
  image->gainMap->gainMapMin[1] = gain_map_min_max1.first;
186
3.35k
  image->gainMap->gainMapMin[2] = gain_map_min_max2.first;
187
188
3.35k
  image->gainMap->gainMapMax[0] = gain_map_min_max0.second;
189
3.35k
  image->gainMap->gainMapMax[1] = gain_map_min_max1.second;
190
3.35k
  image->gainMap->gainMapMax[2] = gain_map_min_max2.second;
191
192
3.35k
  image->gainMap->gainMapGamma[0] = {gain_map_gamma_n0, gain_map_gamma_d0};
193
3.35k
  image->gainMap->gainMapGamma[1] = {gain_map_gamma_n1, gain_map_gamma_d1};
194
3.35k
  image->gainMap->gainMapGamma[2] = {gain_map_gamma_n2, gain_map_gamma_d2};
195
196
3.35k
  image->gainMap->baseOffset[0] = {base_offset_n0, base_offset_d0};
197
3.35k
  image->gainMap->baseOffset[1] = {base_offset_n1, base_offset_d1};
198
3.35k
  image->gainMap->baseOffset[2] = {base_offset_n2, base_offset_d2};
199
200
3.35k
  image->gainMap->alternateOffset[0] = {alternate_offset_n0,
201
3.35k
                                        alternate_offset_d0};
202
3.35k
  image->gainMap->alternateOffset[1] = {alternate_offset_n1,
203
3.35k
                                        alternate_offset_d1};
204
3.35k
  image->gainMap->alternateOffset[2] = {alternate_offset_n2,
205
3.35k
                                        alternate_offset_d2};
206
207
3.35k
  image->gainMap->baseHdrHeadroom = {base_hdr_headroom_n, base_hdr_headroom_d};
208
3.35k
  image->gainMap->alternateHdrHeadroom = {alternate_hdr_headroom_n,
209
3.35k
                                          alternate_hdr_headroom_d};
210
3.35k
  image->gainMap->useBaseColorSpace = use_base_color_space;
211
212
3.35k
  return image;
213
3.35k
}
214
215
//------------------------------------------------------------------------------
216
217
size_t GetNumSamples(size_t num_frames, size_t width, size_t height,
218
233k
                     avifPixelFormat pixel_format, bool has_alpha) {
219
233k
  const size_t num_luma_samples = width * height;
220
221
233k
  avifPixelFormatInfo pixel_format_info;
222
233k
  avifGetPixelFormatInfo(pixel_format, &pixel_format_info);
223
233k
  size_t num_chroma_samples = 0;
224
233k
  if (!pixel_format_info.monochrome) {
225
122k
    num_chroma_samples = 2 *
226
122k
                         ((width + pixel_format_info.chromaShiftX) >>
227
122k
                          pixel_format_info.chromaShiftX) *
228
122k
                         ((height + pixel_format_info.chromaShiftY) >>
229
122k
                          pixel_format_info.chromaShiftY);
230
122k
  }
231
232
233k
  size_t num_alpha_samples = 0;
233
233k
  if (has_alpha) {
234
72.2k
    num_alpha_samples = width * height;
235
72.2k
  }
236
237
233k
  return num_frames *
238
233k
         (num_luma_samples + num_chroma_samples + num_alpha_samples);
239
233k
}
240
241
//------------------------------------------------------------------------------
242
243
41.2k
std::vector<std::string> GetSeedDataDirs() {
244
41.2k
  const char* var = std::getenv("TEST_DATA_DIRS");
245
41.2k
  std::vector<std::string> res;
246
41.2k
  if (var == nullptr || *var == 0) return res;
247
41.2k
  const char* var_start = var;
248
1.97M
  while (true) {
249
1.97M
    if (*var == 0 || *var == ';') {
250
41.2k
      res.push_back(std::string(var_start, var - var_start));
251
41.2k
      if (*var == 0) break;
252
0
      var_start = var + 1;
253
0
    }
254
1.93M
    ++var;
255
1.93M
  }
256
41.2k
  return res;
257
41.2k
}
258
259
std::vector<std::string> GetTestImagesContents(
260
11
    size_t max_file_size, const std::vector<avifAppFileFormat>& image_formats) {
261
  // Use an environment variable to get the test data directory because
262
  // fuzztest seeds are created before the main() function is called, so the
263
  // test has no chance to parse command line arguments.
264
11
  const std::vector<std::string> test_data_dirs = GetSeedDataDirs();
265
11
  if (test_data_dirs.empty()) {
266
    // Only a warning because this can happen when running the binary with
267
    // --list_fuzz_tests (such as with gtest_discover_tests() in cmake).
268
0
    std::cerr << "WARNING: TEST_DATA_DIRS env variable not set, unable to read "
269
0
                 "seed files\n";
270
0
    return {};
271
0
  }
272
273
11
  std::vector<std::string> seeds;
274
11
  for (const std::string& test_data_dir : test_data_dirs) {
275
11
    std::cout << "Reading seeds from " << test_data_dir
276
11
              << " (non recursively)\n";
277
11
    auto tuple_vector = fuzztest::ReadFilesFromDirectory(test_data_dir);
278
11
    seeds.reserve(tuple_vector.size());
279
1.12k
    for (auto& [file_content] : tuple_vector) {
280
1.12k
      if (file_content.size() > max_file_size) continue;
281
1.08k
      if (!image_formats.empty()) {
282
1.08k
        const avifAppFileFormat format = avifGuessBufferFileFormat(
283
1.08k
            reinterpret_cast<const uint8_t*>(file_content.data()),
284
1.08k
            file_content.size());
285
1.08k
        if (std::find(image_formats.begin(), image_formats.end(), format) ==
286
1.08k
            image_formats.end()) {
287
407
          continue;
288
407
        }
289
1.08k
      }
290
291
682
      seeds.push_back(std::move(file_content));
292
682
    }
293
11
  }
294
11
  if (seeds.empty()) {
295
0
    std::cerr << "ERROR: no files found that match the given file size and "
296
0
                 "format criteria\n";
297
0
    std::abort();
298
0
  }
299
11
  std::cout << "Returning " << seeds.size() << " seed images\n";
300
11
  return seeds;
301
11
}
302
303
//------------------------------------------------------------------------------
304
305
}  // namespace testutil
306
}  // namespace avif