Skip to content

Commit

Permalink
Allow creating images with a different grid for the gain map.
Browse files Browse the repository at this point in the history
The main image can have a different number of cols/rows than
the gain map, or one can be a grid and the other be a single image.
Also allow setting differnet values for gain map metadata version
fields.
Add code in gainmaptest.cc to generate test images. These images
are used in tests and as seeds by the fuzzer.
  • Loading branch information
maryla-uc committed Aug 19, 2024
1 parent 0d3e5e2 commit 9ad2707
Show file tree
Hide file tree
Showing 15 changed files with 286 additions and 46 deletions.
33 changes: 32 additions & 1 deletion include/avif/internal.h
Original file line number Diff line number Diff line change
Expand Up @@ -774,7 +774,7 @@ typedef struct avifSequenceHeader
AVIF_NODISCARD avifBool avifSequenceHeaderParse(avifSequenceHeader * header, const avifROData * sample, avifCodecType codecType);

// ---------------------------------------------------------------------------
// gain maps
// Gain maps

#if defined(AVIF_ENABLE_EXPERIMENTAL_GAIN_MAP)

Expand All @@ -786,6 +786,37 @@ avifResult avifFindMinMaxWithoutOutliers(const float * gainMapF, int numPixels,

#endif // AVIF_ENABLE_EXPERIMENTAL_GAIN_MAP

// ---------------------------------------------------------------------------
// Internal encode
//
// These functions/options give extra flexibility to create non standard images for use in testing.

typedef struct avifEncoderInternalOptions
{
#if defined(AVIF_ENABLE_EXPERIMENTAL_GAIN_MAP)
// Options related to the 'tmap' (tone mapped image) box.
uint8_t tmapVersion; // Value that should be written for the 'version' field (use default if 0)
uint16_t tmapMinimumVersion; // Value that should be written for the 'minimum_version' field (use default if 0)
uint16_t tmapWriterVersion; // Value that should be written for the 'writerVersion' field (use default if 0)
avifBool tmapAddExtraBytes; // Add arbitrary bytes at the end of the box
#endif
char dummy; // Avoid emptry struct error when AVIF_ENABLE_EXPERIMENTAL_GAIN_MAP is off
} avifEncoderInternalOptions;

// Sets extra encoding options.
void avifEncoderSetInternalOptions(avifEncoder * encoder, const avifEncoderInternalOptions * internalOptions);

// Variant of avifEncoderAddImageGrid() that allows creating images where 'gridCols' and 'gridRows' differ for
// the base image and the gain map image.
avifResult avifEncoderAddImageGridInternal(avifEncoder * encoder,
uint32_t gridCols,
uint32_t gridRows,
const avifImage * const * cellImages,
uint32_t gainMapGridCols,
uint32_t gainMapGridRows,
const avifImage * const * gainMapCellImages,
avifAddImageFlags addImageFlags);

#define AVIF_INDEFINITE_DURATION64 UINT64_MAX
#define AVIF_INDEFINITE_DURATION32 UINT32_MAX

Expand Down
127 changes: 99 additions & 28 deletions src/write.c

Large diffs are not rendered by default.

44 changes: 27 additions & 17 deletions tests/data/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -500,8 +500,9 @@ exiftool "-icc_profile<=p3.icc" paris_exif_xmp_icc_gainmap_bigendian.jpg

License: [same as libavif](https://github.com/AOMediaCodec/libavif/blob/main/LICENSE)

Source: generated with a modified libavif at https://github.com/maryla-uc/libavif/tree/weirdgainmaps
by running `./tests/avifgainmaptest --gtest_filter=GainMapTest.CreateGainMapImages ../tests/data/` 
Source: generated by `avifgainmaptest.cc`. To update, set `kUpdateTestImages` to true
in `avifgainmaptest.cc` and run the test:
`./tests/avifgainmaptest --gtest_filter=GainMapTest.CreateTestImages ../tests/data/`

Contains a 4x3 color grid, a 4x3 alpha grid, and a 2x2 gain map grid.

Expand All @@ -511,8 +512,9 @@ Contains a 4x3 color grid, a 4x3 alpha grid, and a 2x2 gain map grid.

License: [same as libavif](https://github.com/AOMediaCodec/libavif/blob/main/LICENSE)

Source: generated with a modified libavif at https://github.com/maryla-uc/libavif/tree/weirdgainmaps
by running `./tests/avifgainmaptest --gtest_filter=GainMapTest.CreateGainMapImages ../tests/data/` 
Source: generated by `avifgainmaptest.cc`. To update, set `kUpdateTestImages` to true
in `avifgainmaptest.cc` and run the test:
`./tests/avifgainmaptest --gtest_filter=GainMapTest.CreateTestImages ../tests/data/`

Contains a single color image, single alpha image, and a 2x2 gain map grid.

Expand All @@ -522,8 +524,9 @@ Contains a single color image, single alpha image, and a 2x2 gain map grid.

License: [same as libavif](https://github.com/AOMediaCodec/libavif/blob/main/LICENSE)

Source: generated with a modified libavif at https://github.com/maryla-uc/libavif/tree/weirdgainmaps
by running `./tests/avifgainmaptest --gtest_filter=GainMapTest.CreateGainMapImages ../tests/data/` 
Source: generated by `avifgainmaptest.cc`. To update, set `kUpdateTestImages` to true
in `avifgainmaptest.cc` and run the test:
`./tests/avifgainmaptest --gtest_filter=GainMapTest.CreateTestImages ../tests/data/`

Contains a 4x3 color grid, a 4x3 alpha grid, and a single gain map image.

Expand All @@ -533,8 +536,9 @@ Contains a 4x3 color grid, a 4x3 alpha grid, and a single gain map image.

License: [same as libavif](https://github.com/AOMediaCodec/libavif/blob/main/LICENSE)

Source: generated with a modified libavif at https://github.com/maryla-uc/libavif/tree/weirdgainmaps
by running `./tests/avifgainmaptest --gtest_filter=GainMapTest.CreateGainMapImages ../tests/data/` 
Source: generated by `avifgainmaptest.cc`. To update, set `kUpdateTestImages` to true
in `avifgainmaptest.cc` and run the test:
`./tests/avifgainmaptest --gtest_filter=GainMapTest.CreateTestImages ../tests/data/`

Contains a gain map with the `version` field set to 99 in the tmap box.
`minimum_version` and `writer_version` are 0.
Expand All @@ -545,8 +549,9 @@ Contains a gain map with the `version` field set to 99 in the tmap box.

License: [same as libavif](https://github.com/AOMediaCodec/libavif/blob/main/LICENSE)

Source: generated with a modified libavif at https://github.com/maryla-uc/libavif/tree/weirdgainmaps
by running `./tests/avifgainmaptest --gtest_filter=GainMapTest.CreateGainMapImages ../tests/data/` 
Source: generated by `avifgainmaptest.cc`. To update, set `kUpdateTestImages` to true
in `avifgainmaptest.cc` and run the test:
`./tests/avifgainmaptest --gtest_filter=GainMapTest.CreateTestImages ../tests/data/`

Contains a gain map with the `minimum_version` field set to 99 in the tmap box.
`version` and `writer_version` are 0.
Expand All @@ -557,8 +562,9 @@ Contains a gain map with the `minimum_version` field set to 99 in the tmap box.

License: [same as libavif](https://github.com/AOMediaCodec/libavif/blob/main/LICENSE)

Source: generated with a modified libavif at https://github.com/maryla-uc/libavif/tree/weirdgainmaps
by running `./tests/avifgainmaptest --gtest_filter=GainMapTest.CreateGainMapImages ../tests/data/` 
Source: generated by `avifgainmaptest.cc`. To update, set `kUpdateTestImages` to true
in `avifgainmaptest.cc` and run the test:
`./tests/avifgainmaptest --gtest_filter=GainMapTest.CreateTestImages ../tests/data/`

Contains a gain map with the `writer_version` field set to 99 in the tmap box,
and some extra unexpected bytes at the end of the gain map metadata.
Expand All @@ -570,8 +576,9 @@ and some extra unexpected bytes at the end of the gain map metadata.

License: [same as libavif](https://github.com/AOMediaCodec/libavif/blob/main/LICENSE)

Source: generated with a modified libavif at https://github.com/maryla-uc/libavif/tree/weirdgainmaps
by running `./tests/avifgainmaptest --gtest_filter=GainMapTest.CreateGainMapImages ../tests/data/` 
Source: generated by `avifgainmaptest.cc`. To update, set `kUpdateTestImages` to true
in `avifgainmaptest.cc` and run the test:
`./tests/avifgainmaptest --gtest_filter=GainMapTest.CreateTestImages ../tests/data/`

Contains a gain map with some extra unexpected bytes at the end of the gain map metadata.
Contains `version`, `minimum_version` and `writer_version` are 0.
Expand Down Expand Up @@ -631,7 +638,8 @@ SDR image with a gain map to allow tone mapping to HDR.
![](seine_sdr_gainmap_big_srgb.avif)

Source : modified version of `seine_sdr_gainmap_srgb.avif` with an upscaled gain map, generated using libavif's API.
See `CreateTestImages` in `avifgainmaptest.cc` (set kUpdateTestImages to update images).
To update, set `kUpdateTestImages` to true in `avifgainmaptest.cc` and run the test:
`./tests/avifgainmaptest --gtest_filter=GainMapTest.CreateTestImages ../tests/data/`

SDR image with a gain map to allow tone mapping to HDR. The gain map's width and height are doubled compared to the base image.
This is an atypical image just for testing. Typically, the gain map would be either the same size or smaller as the base image.
Expand All @@ -643,7 +651,8 @@ This is an atypical image just for testing. Typically, the gain map would be eit
License: [same as libavif](https://github.com/AOMediaCodec/libavif/blob/main/LICENSE)

Source : created from `seine_hdr_srgb.avif` (for the base image) and `seine_sdr_gainmap_srgb.avif` (for the gain map) with libavif's API.
See `CreateTestImages` in `avifgainmaptest.cc` (set kUpdateTestImages to update images).
To update, set `kUpdateTestImages` to true in `avifgainmaptest.cc` and run the test:
`./tests/avifgainmaptest --gtest_filter=GainMapTest.CreateTestImages ../tests/data/`

HDR image with a gain map to allow tone mapping to SDR.

Expand All @@ -654,7 +663,8 @@ HDR image with a gain map to allow tone mapping to SDR.
License: [same as libavif](https://github.com/AOMediaCodec/libavif/blob/main/LICENSE)

Source : modified version of `seine_hdr_gainmap_srgb.avif` with a downscaled gain map, generated using libavif's API.
See `CreateTestImages` in `avifgainmaptest.cc` (set kUpdateTestImages to update images).
To update, set `kUpdateTestImages` to true in `avifgainmaptest.cc` and run the test:
`./tests/avifgainmaptest --gtest_filter=GainMapTest.CreateTestImages ../tests/data/`

SDR image with a gain map to allow tone mapping to HDR. The gain map's width and height are halved compared to the base image.

Expand Down
Binary file modified tests/data/color_grid_alpha_grid_gainmap_nogrid.avif
Binary file not shown.
Binary file modified tests/data/color_grid_gainmap_different_grid.avif
Binary file not shown.
Binary file modified tests/data/color_nogrid_alpha_nogrid_gainmap_grid.avif
Binary file not shown.
Binary file modified tests/data/seine_hdr_gainmap_small_srgb.avif
Binary file not shown.
Binary file modified tests/data/seine_hdr_gainmap_srgb.avif
Binary file not shown.
Binary file modified tests/data/seine_sdr_gainmap_big_srgb.avif
Binary file not shown.
Binary file modified tests/data/seine_sdr_gainmap_srgb.avif
Binary file not shown.
Binary file not shown.
Binary file modified tests/data/unsupported_gainmap_minimum_version.avif
Binary file not shown.
Binary file modified tests/data/unsupported_gainmap_version.avif
Binary file not shown.
Binary file not shown.
128 changes: 128 additions & 0 deletions tests/gtest/avifgainmaptest.cc
Original file line number Diff line number Diff line change
Expand Up @@ -451,6 +451,74 @@ TEST(GainMapTest, EncodeDecodeGrid) {
// .write(reinterpret_cast<char*>(encoded.data), encoded.size);
}

void MakeTestImage(const std::string& path, uint32_t grid_cols,
uint32_t grid_rows, uint32_t cell_width,
uint32_t cell_height, uint32_t gain_map_grid_cols,
uint32_t gain_map_grid_rows, uint32_t gain_map_cell_width,
uint32_t gain_map_cell_height, uint8_t tmap_version = 0,
uint16_t minimum_version = 0, uint16_t writer_version = 0,
bool add_extra_bytes = false) {
std::vector<ImagePtr> cells;
std::vector<const avifImage*> cell_ptrs;
std::vector<ImagePtr> gain_map_cells;
std::vector<const avifImage*> gain_map_ptrs;

avifGainMapMetadata gain_map_metadata =
GetTestGainMapMetadata(/*base_rendition_is_hdr=*/true);
for (uint32_t i = 0; i < grid_cols * grid_rows; ++i) {
ImagePtr image =
testutil::CreateImage(cell_width, cell_height, /*depth=*/10,
AVIF_PIXEL_FORMAT_YUV444, AVIF_PLANES_ALL);
ASSERT_NE(image, nullptr);
image->gainMap = avifGainMapCreate();
image->gainMap->metadata = gain_map_metadata;
image->transferCharacteristics = AVIF_TRANSFER_CHARACTERISTICS_PQ;
image->gainMap->altDepth = 8;
image->gainMap->altPlaneCount = 3;
image->gainMap->altColorPrimaries = AVIF_COLOR_PRIMARIES_SRGB;
image->gainMap->altTransferCharacteristics =
AVIF_TRANSFER_CHARACTERISTICS_SRGB;
image->gainMap->altMatrixCoefficients = AVIF_MATRIX_COEFFICIENTS_BT601;
testutil::FillImageGradient(image.get());

cell_ptrs.push_back(image.get());
cells.push_back(std::move(image));
}
for (uint32_t i = 0; i < gain_map_grid_cols * gain_map_grid_rows; ++i) {
ImagePtr gain_map = testutil::CreateImage(
gain_map_cell_width, gain_map_cell_height, /*depth=*/8,
AVIF_PIXEL_FORMAT_YUV420, AVIF_PLANES_YUV);
ASSERT_NE(gain_map, nullptr);
gain_map->gainMap = avifGainMapCreate();
gain_map->gainMap->metadata = gain_map_metadata;
testutil::FillImageGradient(gain_map.get());
gain_map_ptrs.push_back(gain_map.get());
gain_map_cells.push_back(std::move(gain_map));
}

EncoderPtr encoder(avifEncoderCreate());
ASSERT_NE(encoder, nullptr);
encoder->speed = 10;
avifEncoderInternalOptions internalOptions;
internalOptions.tmapVersion = tmap_version;
internalOptions.tmapMinimumVersion = minimum_version;
internalOptions.tmapWriterVersion = writer_version;
internalOptions.tmapAddExtraBytes = add_extra_bytes;
avifEncoderSetInternalOptions(encoder.get(), &internalOptions);
testutil::AvifRwData encoded;
avifResult result = avifEncoderAddImageGridInternal(
encoder.get(), grid_cols, grid_rows, cell_ptrs.data(), gain_map_grid_cols,
gain_map_grid_rows, gain_map_ptrs.data(), AVIF_ADD_IMAGE_FLAG_SINGLE);
ASSERT_EQ(result, AVIF_RESULT_OK)
<< avifResultToString(result) << " " << encoder->diag.error;
result = avifEncoderFinish(encoder.get(), &encoded);
ASSERT_EQ(result, AVIF_RESULT_OK)
<< avifResultToString(result) << " " << encoder->diag.error;

std::ofstream(path, std::ios::binary)
.write(reinterpret_cast<char*>(encoded.data), encoded.size);
}

TEST(GainMapTest, InvalidGrid) {
std::vector<ImagePtr> cells;
std::vector<const avifImage*> cell_ptrs;
Expand Down Expand Up @@ -1162,6 +1230,66 @@ TEST(GainMapTest, CreateTestImages) {
encoded_small_gainmap.size);
}
}

if (kUpdateTestImages) {
MakeTestImage(
std::string(data_path) + "color_grid_gainmap_different_grid.avif",
/*grid_cols=*/4,
/*grid_rows=*/3,
/*cell_width=*/128, /*cell_heigh=*/200,
/*gain_map_grid_cols=*/2, /*gain_map_grid_rows=*/2,
/*gain_map_cell_width=*/64, /*gain_map_cell_height=*/80);
MakeTestImage(
std::string(data_path) + "color_grid_alpha_grid_gainmap_nogrid.avif",
/*grid_cols=*/4,
/*grid_rows=*/3,
/*cell_width=*/128, /*cell_heigh=*/200,
/*gain_map_grid_cols=*/1, /*gain_map_grid_rows=*/1,
/*gain_map_cell_width=*/64, /*gain_map_cell_height=*/80);
MakeTestImage(
std::string(data_path) + "color_nogrid_alpha_nogrid_gainmap_grid.avif",
/*grid_cols=*/1,
/*grid_rows=*/1,
/*cell_width=*/128, /*cell_heigh=*/200,
/*gain_map_grid_cols=*/2, /*gain_map_grid_rows=*/2,
/*gain_map_cell_width=*/64, /*gain_map_cell_height=*/80);
MakeTestImage(std::string(data_path) + "unsupported_gainmap_version.avif",
/*grid_cols=*/1,
/*grid_rows=*/1,
/*cell_width=*/100, /*cell_heigh=*/100,
/*gain_map_grid_cols=*/1, /*gain_map_grid_rows=*/1,
/*gain_map_cell_width=*/50, /*gain_map_cell_height=*/50,
/*tmap_version=*/99, /*minimum_version=*/0,
/*writer_version=*/0);
MakeTestImage(
std::string(data_path) + "unsupported_gainmap_minimum_version.avif",
/*grid_cols=*/1,
/*grid_rows=*/1,
/*cell_width=*/100, /*cell_heigh=*/100,
/*gain_map_grid_cols=*/1, /*gain_map_grid_rows=*/1,
/*gain_map_cell_width=*/50, /*gain_map_cell_height=*/50,
/*tmap_version=*/0, /*minimum_version=*/99, /*writer_version=*/99);
MakeTestImage(
std::string(data_path) +
"unsupported_gainmap_writer_version_with_extra_bytes.avif",
/*grid_cols=*/1,
/*grid_rows=*/1,
/*cell_width=*/100, /*cell_heigh=*/100,
/*gain_map_grid_cols=*/1, /*gain_map_grid_rows=*/1,
/*gain_map_cell_width=*/50, /*gain_map_cell_height=*/50,
/*tmap_version=*/0, /*minimum_version=*/0, /*writer_version=*/99,
/*add_extra_bytes=*/1);
MakeTestImage(std::string(data_path) +
"supported_gainmap_writer_version_with_extra_bytes.avif",
/*grid_cols=*/1,
/*grid_rows=*/1,
/*cell_width=*/100, /*cell_heigh=*/100,
/*gain_map_grid_cols=*/1, /*gain_map_grid_rows=*/1,
/*gain_map_cell_width=*/50, /*gain_map_cell_height=*/50,
/*tmap_version=*/0, /*minimum_version=*/0,
/*writer_version=*/0,
/*add_extra_bytes=*/1);
}
}

class ToneMapTest
Expand Down

0 comments on commit 9ad2707

Please sign in to comment.