Skip to content

Commit aae7ae0

Browse files
committed
Allow creating images with a different grid for the gain map.
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.
1 parent 14d8e3c commit aae7ae0

File tree

7 files changed

+301
-51
lines changed

7 files changed

+301
-51
lines changed

include/avif/internal.h

Lines changed: 32 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -774,7 +774,7 @@ typedef struct avifSequenceHeader
774774
AVIF_NODISCARD avifBool avifSequenceHeaderParse(avifSequenceHeader * header, const avifROData * sample, avifCodecType codecType);
775775

776776
// ---------------------------------------------------------------------------
777-
// gain maps
777+
// Gain maps
778778

779779
#if defined(AVIF_ENABLE_EXPERIMENTAL_GAIN_MAP)
780780

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

787787
#endif // AVIF_ENABLE_EXPERIMENTAL_GAIN_MAP
788788

789+
// ---------------------------------------------------------------------------
790+
// Internal encode
791+
//
792+
// These functions/options give extra flexibility to create non standard images for use in testing.
793+
794+
typedef struct avifEncoderInternalOptions
795+
{
796+
#if defined(AVIF_ENABLE_EXPERIMENTAL_GAIN_MAP)
797+
// Options related to the 'tmap' (tone mapped image) box.
798+
uint8_t tmapVersion; // Value that should be written for the 'version' field (use default if 0)
799+
uint16_t tmapMinimumVersion; // Value that should be written for the 'minimum_version' field (use default if 0)
800+
uint16_t tmapWriterVersion; // Value that should be written for the 'writerVersion' field (use default if 0)
801+
avifBool tmapAddExtraBytes; // Add arbitrary bytes at the end of the box
802+
#endif
803+
char dummy; // Avoid emptry struct error when AVIF_ENABLE_EXPERIMENTAL_GAIN_MAP is off
804+
} avifEncoderInternalOptions;
805+
806+
// Sets extra encoding options.
807+
void avifEncoderSetInternalOptions(avifEncoder * encoder, const avifEncoderInternalOptions * internalOptions);
808+
809+
// Variant of avifEncoderAddImageGrid() that allows creating images where 'gridCols' and 'gridRows' differ for
810+
// the base image and the gain map image.
811+
avifResult avifEncoderAddImageGridInternal(avifEncoder * encoder,
812+
uint32_t gridCols,
813+
uint32_t gridRows,
814+
const avifImage * const * cellImages,
815+
uint32_t gainMapGridCols,
816+
uint32_t gainMapGridRows,
817+
const avifImage * const * gainMapCellImages,
818+
avifAddImageFlags addImageFlags);
819+
789820
#define AVIF_INDEFINITE_DURATION64 UINT64_MAX
790821
#define AVIF_INDEFINITE_DURATION32 UINT32_MAX
791822

src/write.c

Lines changed: 114 additions & 33 deletions
Large diffs are not rendered by default.

tests/data/README.md

Lines changed: 27 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -500,8 +500,9 @@ exiftool "-icc_profile<=p3.icc" paris_exif_xmp_icc_gainmap_bigendian.jpg
500500

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

636644
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.
637645
This is an atypical image just for testing. Typically, the gain map would be either the same size or smaller as the base image.
@@ -643,7 +651,8 @@ This is an atypical image just for testing. Typically, the gain map would be eit
643651
License: [same as libavif](https://github.com/AOMediaCodec/libavif/blob/main/LICENSE)
644652

645653
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.
646-
See `CreateTestImages` in `avifgainmaptest.cc` (set kUpdateTestImages to update images).
654+
To update, set `kUpdateTestImages` to true in `avifgainmaptest.cc` and run the test:
655+
`./tests/avifgainmaptest --gtest_filter=GainMapTest.CreateTestImages ../tests/data/`
647656

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

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

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

659669
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.
660670

0 Bytes
Binary file not shown.
0 Bytes
Binary file not shown.
-14 Bytes
Binary file not shown.

tests/gtest/avifgainmaptest.cc

Lines changed: 128 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -451,6 +451,74 @@ TEST(GainMapTest, EncodeDecodeGrid) {
451451
// .write(reinterpret_cast<char*>(encoded.data), encoded.size);
452452
}
453453

454+
void MakeTestImage(const std::string& path, uint32_t grid_cols,
455+
uint32_t grid_rows, uint32_t cell_width,
456+
uint32_t cell_height, uint32_t gain_map_grid_cols,
457+
uint32_t gain_map_grid_rows, uint32_t gain_map_cell_width,
458+
uint32_t gain_map_cell_height, uint8_t tmap_version = 0,
459+
uint16_t minimum_version = 0, uint16_t writer_version = 0,
460+
bool add_extra_bytes = false) {
461+
std::vector<ImagePtr> cells;
462+
std::vector<const avifImage*> cell_ptrs;
463+
std::vector<ImagePtr> gain_map_cells;
464+
std::vector<const avifImage*> gain_map_ptrs;
465+
466+
avifGainMapMetadata gain_map_metadata =
467+
GetTestGainMapMetadata(/*base_rendition_is_hdr=*/true);
468+
for (uint32_t i = 0; i < grid_cols * grid_rows; ++i) {
469+
ImagePtr image =
470+
testutil::CreateImage(cell_width, cell_height, /*depth=*/10,
471+
AVIF_PIXEL_FORMAT_YUV444, AVIF_PLANES_ALL);
472+
ASSERT_NE(image, nullptr);
473+
image->gainMap = avifGainMapCreate();
474+
image->gainMap->metadata = gain_map_metadata;
475+
image->transferCharacteristics = AVIF_TRANSFER_CHARACTERISTICS_PQ;
476+
image->gainMap->altDepth = 8;
477+
image->gainMap->altPlaneCount = 3;
478+
image->gainMap->altColorPrimaries = AVIF_COLOR_PRIMARIES_SRGB;
479+
image->gainMap->altTransferCharacteristics =
480+
AVIF_TRANSFER_CHARACTERISTICS_SRGB;
481+
image->gainMap->altMatrixCoefficients = AVIF_MATRIX_COEFFICIENTS_BT601;
482+
testutil::FillImageGradient(image.get());
483+
484+
cell_ptrs.push_back(image.get());
485+
cells.push_back(std::move(image));
486+
}
487+
for (uint32_t i = 0; i < gain_map_grid_cols * gain_map_grid_rows; ++i) {
488+
ImagePtr gain_map = testutil::CreateImage(
489+
gain_map_cell_width, gain_map_cell_height, /*depth=*/8,
490+
AVIF_PIXEL_FORMAT_YUV420, AVIF_PLANES_YUV);
491+
ASSERT_NE(gain_map, nullptr);
492+
gain_map->gainMap = avifGainMapCreate();
493+
gain_map->gainMap->metadata = gain_map_metadata;
494+
testutil::FillImageGradient(gain_map.get());
495+
gain_map_ptrs.push_back(gain_map.get());
496+
gain_map_cells.push_back(std::move(gain_map));
497+
}
498+
499+
EncoderPtr encoder(avifEncoderCreate());
500+
ASSERT_NE(encoder, nullptr);
501+
encoder->speed = 10;
502+
avifEncoderInternalOptions internalOptions;
503+
internalOptions.tmapVersion = tmap_version;
504+
internalOptions.tmapMinimumVersion = minimum_version;
505+
internalOptions.tmapWriterVersion = writer_version;
506+
internalOptions.tmapAddExtraBytes = add_extra_bytes;
507+
avifEncoderSetInternalOptions(encoder.get(), &internalOptions);
508+
testutil::AvifRwData encoded;
509+
avifResult result = avifEncoderAddImageGridInternal(
510+
encoder.get(), grid_cols, grid_rows, cell_ptrs.data(), gain_map_grid_cols,
511+
gain_map_grid_rows, gain_map_ptrs.data(), AVIF_ADD_IMAGE_FLAG_SINGLE);
512+
ASSERT_EQ(result, AVIF_RESULT_OK)
513+
<< avifResultToString(result) << " " << encoder->diag.error;
514+
result = avifEncoderFinish(encoder.get(), &encoded);
515+
ASSERT_EQ(result, AVIF_RESULT_OK)
516+
<< avifResultToString(result) << " " << encoder->diag.error;
517+
518+
std::ofstream(path, std::ios::binary)
519+
.write(reinterpret_cast<char*>(encoded.data), encoded.size);
520+
}
521+
454522
TEST(GainMapTest, InvalidGrid) {
455523
std::vector<ImagePtr> cells;
456524
std::vector<const avifImage*> cell_ptrs;
@@ -1162,6 +1230,66 @@ TEST(GainMapTest, CreateTestImages) {
11621230
encoded_small_gainmap.size);
11631231
}
11641232
}
1233+
1234+
if (kUpdateTestImages) {
1235+
MakeTestImage(
1236+
std::string(data_path) + "color_grid_gainmap_different_grid.avif",
1237+
/*grid_cols=*/4,
1238+
/*grid_rows=*/3,
1239+
/*cell_width=*/128, /*cell_heigh=*/200,
1240+
/*gain_map_grid_cols=*/2, /*gain_map_grid_rows=*/2,
1241+
/*gain_map_cell_width=*/64, /*gain_map_cell_height=*/80);
1242+
MakeTestImage(
1243+
std::string(data_path) + "color_grid_alpha_grid_gainmap_nogrid.avif",
1244+
/*grid_cols=*/4,
1245+
/*grid_rows=*/3,
1246+
/*cell_width=*/128, /*cell_heigh=*/200,
1247+
/*gain_map_grid_cols=*/1, /*gain_map_grid_rows=*/1,
1248+
/*gain_map_cell_width=*/64, /*gain_map_cell_height=*/80);
1249+
MakeTestImage(
1250+
std::string(data_path) + "color_nogrid_alpha_nogrid_gainmap_grid.avif",
1251+
/*grid_cols=*/1,
1252+
/*grid_rows=*/1,
1253+
/*cell_width=*/128, /*cell_heigh=*/200,
1254+
/*gain_map_grid_cols=*/2, /*gain_map_grid_rows=*/2,
1255+
/*gain_map_cell_width=*/64, /*gain_map_cell_height=*/80);
1256+
MakeTestImage(std::string(data_path) + "unsupported_gainmap_version.avif",
1257+
/*grid_cols=*/1,
1258+
/*grid_rows=*/1,
1259+
/*cell_width=*/100, /*cell_heigh=*/100,
1260+
/*gain_map_grid_cols=*/1, /*gain_map_grid_rows=*/1,
1261+
/*gain_map_cell_width=*/50, /*gain_map_cell_height=*/50,
1262+
/*tmap_version=*/99, /*minimum_version=*/0,
1263+
/*writer_version=*/0);
1264+
MakeTestImage(
1265+
std::string(data_path) + "unsupported_gainmap_minimum_version.avif",
1266+
/*grid_cols=*/1,
1267+
/*grid_rows=*/1,
1268+
/*cell_width=*/100, /*cell_heigh=*/100,
1269+
/*gain_map_grid_cols=*/1, /*gain_map_grid_rows=*/1,
1270+
/*gain_map_cell_width=*/50, /*gain_map_cell_height=*/50,
1271+
/*tmap_version=*/0, /*minimum_version=*/99, /*writer_version=*/99);
1272+
MakeTestImage(
1273+
std::string(data_path) +
1274+
"unsupported_gainmap_writer_version_with_extra_bytes.avif",
1275+
/*grid_cols=*/1,
1276+
/*grid_rows=*/1,
1277+
/*cell_width=*/100, /*cell_heigh=*/100,
1278+
/*gain_map_grid_cols=*/1, /*gain_map_grid_rows=*/1,
1279+
/*gain_map_cell_width=*/50, /*gain_map_cell_height=*/50,
1280+
/*tmap_version=*/0, /*minimum_version=*/0, /*writer_version=*/99,
1281+
/*add_extra_bytes=*/1);
1282+
MakeTestImage(std::string(data_path) +
1283+
"supported_gainmap_writer_version_with_extra_bytes.avif",
1284+
/*grid_cols=*/1,
1285+
/*grid_rows=*/1,
1286+
/*cell_width=*/100, /*cell_heigh=*/100,
1287+
/*gain_map_grid_cols=*/1, /*gain_map_grid_rows=*/1,
1288+
/*gain_map_cell_width=*/50, /*gain_map_cell_height=*/50,
1289+
/*tmap_version=*/0, /*minimum_version=*/0,
1290+
/*writer_version=*/0,
1291+
/*add_extra_bytes=*/1);
1292+
}
11651293
}
11661294

11671295
class ToneMapTest

0 commit comments

Comments
 (0)