Fix setting of cicp values in avifgainmaputil combine and convert. (#3110)
CICP must be set *before* calling ReadImage() so that the RGB->YUV
conversion uses the right matrix coefficient for PNG/JPEG files.
Also explicitly set the default matrix coefficients when not specified.
For Avif files, we overwrite the CICP after reading the file in ReadImage()
(keeps the previous behavior).
Fix --cicp-input flag not being used in tonemap command.
Fix memory leak when in 'combine' command when using with a file that already has a gain map.
Fix memory leak in 'tonemap' and 'swapbase' command with RGB pixels.
Fixes #3102 and #2869
diff --git a/CHANGELOG.md b/CHANGELOG.md
index ac49f96..658ee95 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -15,6 +15,8 @@
* Update libxml2.cmd/LocalLibXml2.cmake: v2.15.2
* Update libyuv.cmd/LocalLibyuv.cmake: 6067afde5 (1922)
* Support long path names in Windows
+* Fix cicp management and memory leaks in avifgainmaputil
+ https://github.com/AOMediaCodec/libavif/issues/3102.
### Removed since 1.4.0
diff --git a/apps/avifgainmaputil/combine_command.cc b/apps/avifgainmaputil/combine_command.cc
index d32d036..654316d 100644
--- a/apps/avifgainmaputil/combine_command.cc
+++ b/apps/avifgainmaputil/combine_command.cc
@@ -3,6 +3,7 @@
#include "combine_command.h"
+#include <cassert>
#include <cmath>
#include "avif/avif_cxx.h"
@@ -88,29 +89,21 @@
if (base_image == nullptr || alternate_image == nullptr) {
return AVIF_RESULT_OUT_OF_MEMORY;
}
- avifResult result =
- ReadImage(base_image.get(), arg_base_filename_, pixel_format,
- arg_image_read_.depth, arg_image_read_.ignore_profile);
- if (result != AVIF_RESULT_OK) {
- std::cout << "Failed to read base image: " << avifResultToString(result)
- << "\n";
- return result;
- }
if (arg_base_cicp_.provenance() == argparse::Provenance::SPECIFIED) {
base_image->colorPrimaries = arg_base_cicp_.value().color_primaries;
base_image->transferCharacteristics =
arg_base_cicp_.value().transfer_characteristics;
base_image->matrixCoefficients = arg_base_cicp_.value().matrix_coefficients;
}
-
- result =
- ReadImage(alternate_image.get(), arg_alternate_filename_, pixel_format,
- arg_image_read_.depth, arg_image_read_.ignore_profile);
+ avifResult result = ReadImage(
+ base_image.get(), arg_base_filename_, pixel_format, arg_image_read_.depth,
+ arg_image_read_.ignore_profile, /*ignore_gain_map=*/true);
if (result != AVIF_RESULT_OK) {
- std::cout << "Failed to read alternate image: "
- << avifResultToString(result) << "\n";
+ std::cout << "Failed to read base image: " << avifResultToString(result)
+ << "\n";
return result;
}
+
if (arg_alternate_cicp_.provenance() == argparse::Provenance::SPECIFIED) {
alternate_image->colorPrimaries =
arg_alternate_cicp_.value().color_primaries;
@@ -119,6 +112,14 @@
alternate_image->matrixCoefficients =
arg_alternate_cicp_.value().matrix_coefficients;
}
+ result =
+ ReadImage(alternate_image.get(), arg_alternate_filename_, pixel_format,
+ arg_image_read_.depth, arg_image_read_.ignore_profile);
+ if (result != AVIF_RESULT_OK) {
+ std::cout << "Failed to read alternate image: "
+ << avifResultToString(result) << "\n";
+ return result;
+ }
const uint32_t downscaling = std::max<int>(1, arg_downscaling_);
const uint32_t rounding = downscaling / 2;
@@ -129,6 +130,10 @@
std::cout << "Creating a gain map of size " << gain_map_width << " x "
<< gain_map_height << "\n";
+ // Because base_image is read with ignore_gain_map=true, there is no
+ // preexisting gain map. Otherwise, overwriting the pointer would cause a
+ // memory leak.
+ assert(base_image->gainMap == nullptr);
base_image->gainMap = avifGainMapCreate();
base_image->gainMap->image =
avifImageCreate(gain_map_width, gain_map_height, arg_gain_map_depth_,
diff --git a/apps/avifgainmaputil/convert_command.cc b/apps/avifgainmaputil/convert_command.cc
index 58a7690..06bb949 100644
--- a/apps/avifgainmaputil/convert_command.cc
+++ b/apps/avifgainmaputil/convert_command.cc
@@ -52,32 +52,19 @@
return AVIF_RESULT_OUT_OF_MEMORY;
}
- const avifAppFileFormat file_format = avifReadImage(
- arg_input_filename_.value().c_str(),
- AVIF_APP_FILE_FORMAT_UNKNOWN /* guess format */, pixel_format,
- arg_image_read_.depth, AVIF_CHROMA_DOWNSAMPLING_AUTOMATIC,
- arg_image_read_.ignore_profile,
- /*ignoreExif=*/false,
- /*ignoreXMP=*/false,
- /*ignoreGainMap=*/false, AVIF_DEFAULT_IMAGE_SIZE_LIMIT, image.get(),
- /*outDepth=*/nullptr,
- /*sourceTiming=*/nullptr,
- /*frameIter=*/nullptr);
- if (file_format == AVIF_APP_FILE_FORMAT_UNKNOWN) {
- std::cout << "Failed to decode image: " << arg_input_filename_;
- return AVIF_RESULT_INVALID_ARGUMENT;
- }
if (arg_cicp_.provenance() == argparse::Provenance::SPECIFIED) {
image->colorPrimaries = arg_cicp_.value().color_primaries;
image->transferCharacteristics = arg_cicp_.value().transfer_characteristics;
image->matrixCoefficients = arg_cicp_.value().matrix_coefficients;
- } else if (image->icc.size == 0 &&
- image->colorPrimaries == AVIF_COLOR_PRIMARIES_UNSPECIFIED &&
- image->transferCharacteristics ==
- AVIF_COLOR_PRIMARIES_UNSPECIFIED) {
- // If there is no ICC and no CICP, assume sRGB by default.
- image->colorPrimaries = AVIF_COLOR_PRIMARIES_SRGB;
- image->transferCharacteristics = AVIF_TRANSFER_CHARACTERISTICS_SRGB;
+ }
+
+ avifResult result =
+ ReadImage(image.get(), arg_input_filename_.value(), pixel_format,
+ arg_image_read_.depth, arg_image_read_.ignore_profile,
+ /*ignore_gain_map*/ false);
+ if (result != AVIF_RESULT_OK) {
+ std::cout << "Failed to decode image: " << arg_input_filename_;
+ return result;
}
if (image->gainMap && image->gainMap->altICC.size == 0) {
@@ -110,8 +97,7 @@
if (new_base == nullptr) {
return AVIF_RESULT_OUT_OF_MEMORY;
}
- const avifResult result =
- ChangeBase(*image, depth, image->yuvFormat, new_base.get());
+ result = ChangeBase(*image, depth, image->yuvFormat, new_base.get());
if (result != AVIF_RESULT_OK) {
return result;
}
@@ -126,10 +112,9 @@
encoder->qualityAlpha = arg_image_encode_.quality_alpha;
encoder->qualityGainMap = arg_gain_map_quality_;
encoder->speed = arg_image_encode_.speed;
- const avifResult result =
- WriteAvifGrid(image.get(), arg_image_encode_.grid.value().grid_cols,
- arg_image_encode_.grid.value().grid_rows, encoder.get(),
- arg_output_filename_);
+ result = WriteAvifGrid(image.get(), arg_image_encode_.grid.value().grid_cols,
+ arg_image_encode_.grid.value().grid_rows,
+ encoder.get(), arg_output_filename_);
if (result != AVIF_RESULT_OK) {
std::cout << "Failed to encode image: " << avifResultToString(result)
<< " (" << encoder->diag.error << ")\n";
diff --git a/apps/avifgainmaputil/imageio.cc b/apps/avifgainmaputil/imageio.cc
index 9a7e4e8..1686f55 100644
--- a/apps/avifgainmaputil/imageio.cc
+++ b/apps/avifgainmaputil/imageio.cc
@@ -155,7 +155,7 @@
avifResult ReadImage(avifImage* image, const std::string& input_filename,
avifPixelFormat requested_format, uint32_t requested_depth,
- bool ignore_profile) {
+ bool ignore_profile, bool ignore_gain_map) {
avifAppFileFormat input_format = avifGuessFileFormat(input_filename.c_str());
if (input_format == AVIF_APP_FILE_FORMAT_UNKNOWN) {
std::cerr << "Cannot determine input format: " << input_filename;
@@ -165,10 +165,21 @@
if (decoder == nullptr) {
return AVIF_RESULT_OUT_OF_MEMORY;
}
+ if (!ignore_gain_map) {
+ decoder->imageContentToDecode |= AVIF_IMAGE_CONTENT_GAIN_MAP;
+ }
avifResult result = ReadAvif(decoder.get(), input_filename, ignore_profile);
if (result != AVIF_RESULT_OK) {
return result;
}
+ if (ignore_gain_map && decoder->image->gainMap) {
+ avifGainMapDestroy(decoder->image->gainMap);
+ decoder->image->gainMap = nullptr;
+ }
+ const avifColorPrimaries in_primaries = image->colorPrimaries;
+ const avifTransferCharacteristics in_transfer =
+ image->transferCharacteristics;
+ const avifMatrixCoefficients in_matrix = image->matrixCoefficients;
if (decoder->image->imageOwnsYUVPlanes &&
(decoder->image->alphaPlane == nullptr ||
decoder->image->imageOwnsAlphaPlane)) {
@@ -179,26 +190,37 @@
return result;
}
}
+ if (in_primaries != AVIF_COLOR_PRIMARIES_UNSPECIFIED ||
+ in_transfer != AVIF_TRANSFER_CHARACTERISTICS_UNSPECIFIED ||
+ in_matrix != AVIF_MATRIX_COEFFICIENTS_UNSPECIFIED) {
+ image->colorPrimaries = in_primaries;
+ image->transferCharacteristics = in_transfer;
+ image->matrixCoefficients = in_matrix;
+ }
} else {
const avifAppFileFormat file_format = avifReadImage(
input_filename.c_str(), AVIF_APP_FILE_FORMAT_UNKNOWN /* guess format */,
requested_format, static_cast<int>(requested_depth),
AVIF_CHROMA_DOWNSAMPLING_AUTOMATIC, ignore_profile,
- /*ignoreExif=*/false, /*ignoreXMP=*/false,
- /*ignoreGainMap=*/true, AVIF_DEFAULT_IMAGE_SIZE_LIMIT, image,
+ /*ignoreExif=*/false, /*ignoreXMP=*/false, ignore_gain_map,
+ AVIF_DEFAULT_IMAGE_SIZE_LIMIT, image,
/*outDepth=*/nullptr,
/*sourceTiming=*/nullptr, /*frameIter=*/nullptr);
if (file_format == AVIF_APP_FILE_FORMAT_UNKNOWN) {
std::cout << "Failed to decode image: " << input_filename;
return AVIF_RESULT_INVALID_ARGUMENT;
}
- if (image->icc.size == 0) {
- // Assume sRGB by default.
- if (image->colorPrimaries == AVIF_COLOR_PRIMARIES_UNSPECIFIED &&
- image->transferCharacteristics == AVIF_COLOR_PRIMARIES_UNSPECIFIED) {
- image->colorPrimaries = AVIF_COLOR_PRIMARIES_SRGB;
- image->transferCharacteristics = AVIF_TRANSFER_CHARACTERISTICS_SRGB;
- }
+ // Assume sRGB by default.
+ if (image->icc.size == 0 &&
+ image->colorPrimaries == AVIF_COLOR_PRIMARIES_UNSPECIFIED &&
+ image->transferCharacteristics == AVIF_COLOR_PRIMARIES_UNSPECIFIED) {
+ image->colorPrimaries = AVIF_COLOR_PRIMARIES_SRGB;
+ image->transferCharacteristics = AVIF_TRANSFER_CHARACTERISTICS_SRGB;
+ }
+ if (image->matrixCoefficients == AVIF_MATRIX_COEFFICIENTS_UNSPECIFIED) {
+ // Explicitly set the default matrix coefficient, see
+ // avifCalcYUVCoefficients().
+ image->matrixCoefficients = AVIF_MATRIX_COEFFICIENTS_BT601;
}
}
return AVIF_RESULT_OK;
diff --git a/apps/avifgainmaputil/imageio.h b/apps/avifgainmaputil/imageio.h
index 441e5d7..c3d494b 100644
--- a/apps/avifgainmaputil/imageio.h
+++ b/apps/avifgainmaputil/imageio.h
@@ -19,7 +19,7 @@
// Reads an image in any of the supported formats. Ignores any gain map.
avifResult ReadImage(avifImage* image, const std::string& input_filename,
avifPixelFormat requested_format, uint32_t requested_depth,
- bool ignore_profile);
+ bool ignore_profile, bool ignore_gain_map = true);
// Reads an image in avif format given a pre-configured encoder.
avifResult WriteAvif(const avifImage* image, avifEncoder* encoder,
diff --git a/apps/avifgainmaputil/swapbase_command.cc b/apps/avifgainmaputil/swapbase_command.cc
index e1d1e3b..e6e44ec 100644
--- a/apps/avifgainmaputil/swapbase_command.cc
+++ b/apps/avifgainmaputil/swapbase_command.cc
@@ -54,6 +54,7 @@
}
avifRGBImage swapped_rgb;
+ RGBImageCleanup rgb_cleanup(&swapped_rgb);
avifRGBImageSetDefaults(&swapped_rgb, swapped);
avifContentLightLevelInformationBox clli = image.gainMap->altCLLI;
diff --git a/apps/avifgainmaputil/tonemap_command.cc b/apps/avifgainmaputil/tonemap_command.cc
index c5e5520..1f2da42 100644
--- a/apps/avifgainmaputil/tonemap_command.cc
+++ b/apps/avifgainmaputil/tonemap_command.cc
@@ -69,6 +69,13 @@
if (result != AVIF_RESULT_OK) {
return result;
}
+ if (arg_input_cicp_.provenance() == argparse::Provenance::SPECIFIED) {
+ decoder->image->colorPrimaries = arg_input_cicp_.value().color_primaries;
+ decoder->image->transferCharacteristics =
+ arg_input_cicp_.value().transfer_characteristics;
+ decoder->image->matrixCoefficients =
+ arg_input_cicp_.value().matrix_coefficients;
+ }
avifImage* image = decoder->image;
if (image->gainMap == nullptr || image->gainMap->image == nullptr) {
@@ -115,10 +122,9 @@
image->gainMap->altMatrixCoefficients};
}
if (cicp.color_primaries == AVIF_COLOR_PRIMARIES_UNSPECIFIED) {
- // TODO(maryla): for now avifImageApplyGainMap always uses the primaries of
- // the base image, but it should take into account the metadata's
- // useBaseColorSpace property.
- cicp.color_primaries = image->colorPrimaries;
+ cicp.color_primaries = image->gainMap->useBaseColorSpace
+ ? image->colorPrimaries
+ : image->gainMap->altColorPrimaries;
}
if (cicp.transfer_characteristics ==
AVIF_TRANSFER_CHARACTERISTICS_UNSPECIFIED) {
@@ -181,7 +187,13 @@
if (tone_mapped == nullptr) {
return AVIF_RESULT_OUT_OF_MEMORY;
}
+ tone_mapped->colorPrimaries = cicp.color_primaries;
+ tone_mapped->transferCharacteristics = cicp.transfer_characteristics;
+ tone_mapped->matrixCoefficients = cicp.matrix_coefficients;
+ tone_mapped->clli = clli_box;
+
avifRGBImage tone_mapped_rgb;
+ RGBImageCleanup rgb_cleanup(&tone_mapped_rgb);
avifRGBImageSetDefaults(&tone_mapped_rgb, tone_mapped.get());
avifDiagnostics diag;
result = avifImageApplyGainMap(
@@ -200,11 +212,6 @@
return result;
}
- tone_mapped->clli = clli_box;
- tone_mapped->transferCharacteristics = cicp.transfer_characteristics;
- tone_mapped->colorPrimaries = cicp.color_primaries;
- tone_mapped->matrixCoefficients = cicp.matrix_coefficients;
-
return WriteImage(tone_mapped.get(), arg_image_encode_.grid.value().grid_cols,
arg_image_encode_.grid.value().grid_rows,
arg_output_filename_, arg_image_encode_.quality,
diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt
index dee2119..bb1199b 100644
--- a/tests/CMakeLists.txt
+++ b/tests/CMakeLists.txt
@@ -267,10 +267,9 @@
add_cmd_test(test_cmd_stdin ${CMAKE_CURRENT_SOURCE_DIR}/data)
add_cmd_test(test_cmd_targetsize ${CMAKE_CURRENT_SOURCE_DIR}/data)
add_cmd_test(test_cmd_transform ${CMAKE_CURRENT_SOURCE_DIR}/data)
+ add_cmd_test(test_cmd_avifgainmaputil ${CMAKE_CURRENT_SOURCE_DIR}/data)
- if(AVIF_ENABLE_JPEG_GAIN_MAP_CONVERSION)
- add_cmd_test(test_cmd_avifgainmaputil ${CMAKE_CURRENT_SOURCE_DIR}/data)
- else()
+ if(NOT AVIF_ENABLE_JPEG_GAIN_MAP_CONVERSION)
set_tests_properties(test_cmd_stdin PROPERTIES DISABLED True)
endif()
@@ -361,6 +360,7 @@
avifrangetest
aviftunetest
avify4mtest
+ test_cmd_avifgainmaputil
PROPERTIES DISABLED True
)
diff --git a/tests/data/README.md b/tests/data/README.md
index 25fc03c..1ad798b 100644
--- a/tests/data/README.md
+++ b/tests/data/README.md
@@ -823,6 +823,18 @@
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.
+### File [seine_sdr_gainmap_srgb_icc.avif](seine_sdr_gainmap_srgb_icc.avif)
+
+
+
+License: [same as libavif](https://github.com/AOMediaCodec/libavif/blob/main/LICENSE)
+
+Source : created by running the following command with avifenc compiled with libxml enabled:
+
+```bash
+avifenc seine_sdr_gainmap_srgb.jpg seine_sdr_gainmap_srgb_icc.avif --qcolor 90 --qgain-map 90
+```
+
## Files colors*_hdr_*.avif and colors*_sdr_srgb.avif

diff --git a/tests/data/seine_sdr_gainmap_srgb_icc.avif b/tests/data/seine_sdr_gainmap_srgb_icc.avif
new file mode 100644
index 0000000..a020ba6
--- /dev/null
+++ b/tests/data/seine_sdr_gainmap_srgb_icc.avif
Binary files differ
diff --git a/tests/test_cmd_avifgainmaputil.sh b/tests/test_cmd_avifgainmaputil.sh
index d339aac..9a30acb 100755
--- a/tests/test_cmd_avifgainmaputil.sh
+++ b/tests/test_cmd_avifgainmaputil.sh
@@ -15,9 +15,9 @@
# Input file paths.
INPUT_AVIF_GAINMAP_SDR="${TESTDATA_DIR}/seine_sdr_gainmap_srgb.avif"
INPUT_AVIF_GAINMAP_HDR="${TESTDATA_DIR}/seine_hdr_gainmap_srgb.avif"
-INPUT_AVIF_GAINMAP_HDR2020="${TESTDATA_DIR}/seine_hdr_rec2020.avif"
-INPUT_JPEG_AVIF_GAINMAP_SDR="${TESTDATA_DIR}/seine_sdr_gainmap_srgb.jpg"
-AVIF_GAINMAP_SDR_WITH_ICC="seine_sdr_gainmap_srgb_icc.jpg" # generated below
+INPUT_AVIF_HDR2020="${TESTDATA_DIR}/seine_hdr_rec2020.avif"
+INPUT_JPEG_GAINMAP_SDR="${TESTDATA_DIR}/seine_sdr_gainmap_srgb.jpg"
+INPUT_AVIF_GAINMAP_SDR_WITH_ICC="${TESTDATA_DIR}/seine_sdr_gainmap_srgb_icc.avif"
# Output file names.
AVIF_OUTPUT="avif_test_cmd_avifgainmaputil_output.avif"
JPEG_OUTPUT="avif_test_cmd_avifgainmaputil_output.jpg"
@@ -25,16 +25,11 @@
# Cleanup
cleanup() {
- pushd ${TMP_DIR}
- rm -- "${AVIF_OUTPUT}" "${JPEG_OUTPUT}" "${PNG_OUTPUT}" "${AVIF_GAINMAP_SDR_WITH_ICC}"
- popd
+ rm -f -r "${TMP_DIR}"
}
trap cleanup EXIT
pushd ${TMP_DIR}
- # generate another input file used in tests below
- "${AVIFENC}" "${INPUT_JPEG_AVIF_GAINMAP_SDR}" "${AVIF_GAINMAP_SDR_WITH_ICC}" --qcolor 90 --qgain-map 90
-
"${AVIFGAINMAPUTIL}" help
"${AVIFGAINMAPUTIL}" printmetadata "${INPUT_AVIF_GAINMAP_SDR}"
@@ -45,33 +40,63 @@
"${AVIFGAINMAPUTIL}" combine "${INPUT_AVIF_GAINMAP_SDR}" "${INPUT_AVIF_GAINMAP_HDR}" "${AVIF_OUTPUT}" \
-q 50 --downscaling 2 --yuv-gain-map 400
- "${AVIFGAINMAPUTIL}" combine "${INPUT_JPEG_AVIF_GAINMAP_SDR}" "${INPUT_AVIF_GAINMAP_HDR}" "${AVIF_OUTPUT}" \
+ "${AVIFGAINMAPUTIL}" combine "${INPUT_JPEG_GAINMAP_SDR}" "${INPUT_AVIF_GAINMAP_HDR}" "${AVIF_OUTPUT}" \
-q 50 --qgain-map 90 && exit 1 # should fail because icc profiles are not supported
- "${AVIFGAINMAPUTIL}" combine "${INPUT_JPEG_AVIF_GAINMAP_SDR}" "${INPUT_AVIF_GAINMAP_HDR}" "${AVIF_OUTPUT}" \
+ "${AVIFGAINMAPUTIL}" combine "${INPUT_JPEG_GAINMAP_SDR}" "${INPUT_AVIF_GAINMAP_HDR}" "${AVIF_OUTPUT}" \
-q 50 --qgain-map 90 --ignore-profile
- "${AVIFGAINMAPUTIL}" combine "${INPUT_AVIF_GAINMAP_SDR}" "${INPUT_AVIF_GAINMAP_HDR2020}" "${AVIF_OUTPUT}" \
+ "${AVIFGAINMAPUTIL}" combine "${INPUT_AVIF_GAINMAP_SDR}" "${INPUT_AVIF_HDR2020}" "${AVIF_OUTPUT}" \
-q 50 --downscaling 2 --yuv-gain-map 400 --grid 2x2
"${AVIFGAINMAPUTIL}" combine "${INPUT_AVIF_GAINMAP_HDR}" "${INPUT_AVIF_GAINMAP_SDR}" "${AVIF_OUTPUT}" \
-q 90 --qgain-map 90
"${AVIFGAINMAPUTIL}" tonemap "${AVIF_OUTPUT}" "${PNG_OUTPUT}" --headroom 0
"${AVIFGAINMAPUTIL}" tonemap "${INPUT_AVIF_GAINMAP_SDR}" "${PNG_OUTPUT}" --headroom 0 --clli 400,500
- "${ARE_IMAGES_EQUAL}" "${PNG_OUTPUT}" "${INPUT_JPEG_AVIF_GAINMAP_SDR}" 0 40
+ "${ARE_IMAGES_EQUAL}" "${PNG_OUTPUT}" "${INPUT_JPEG_GAINMAP_SDR}" 0 40
+
+ # Test combine with overriden cicp values. Matrix coefficient 0 (identity) makes it obvious if there is an issue.
+ "${AVIFGAINMAPUTIL}" combine "${INPUT_JPEG_GAINMAP_SDR}" "${INPUT_AVIF_HDR2020}" "${AVIF_OUTPUT}" \
+ -q 100 --qgain-map 100 --cicp-base 1/13/0 --ignore-profile
+ # Tone map to SDR and compare with original SDR.
+ "${AVIFGAINMAPUTIL}" tonemap "${AVIF_OUTPUT}" "${PNG_OUTPUT}" --headroom 0
+ "${ARE_IMAGES_EQUAL}" "${PNG_OUTPUT}" "${INPUT_JPEG_GAINMAP_SDR}" 0 99
+ # Tone map to HDR and compare with original HDR.
+ "${AVIFGAINMAPUTIL}" tonemap "${AVIF_OUTPUT}" "${AVIF_OUTPUT}.tonemapped.png" --headroom 2
+ # are_images_equal doesn't support AVIF so we convert to PNG.
+ "${AVIFDEC}" "${INPUT_AVIF_HDR2020}" "input_avif_hdr2020.png"
+ "${ARE_IMAGES_EQUAL}" "${AVIF_OUTPUT}.tonemapped.png" "input_avif_hdr2020.png" 0 60 # A bit of loss from going through gainmap
+
+ # Same as above but HDR base.
+ "${AVIFGAINMAPUTIL}" combine "${INPUT_AVIF_HDR2020}" "${INPUT_JPEG_GAINMAP_SDR}" "${AVIF_OUTPUT}" \
+ -q 100 --qgain-map 100 --cicp-alternate 1/13/0 --ignore-profile
+ "${AVIFGAINMAPUTIL}" tonemap "${AVIF_OUTPUT}" "${PNG_OUTPUT}" --headroom 0
+ "${ARE_IMAGES_EQUAL}" "${PNG_OUTPUT}" "${INPUT_JPEG_GAINMAP_SDR}" 0 50 # A bit of loss from going through gainmap
+ "${AVIFGAINMAPUTIL}" tonemap "${AVIF_OUTPUT}" "${AVIF_OUTPUT}.tonemapped.png" --headroom 2
+ # are_images_equal doesn't support AVIF so we convert to PNG.
+ "${AVIFDEC}" "${INPUT_AVIF_HDR2020}" "input_avif_hdr2020.png"
+ "${ARE_IMAGES_EQUAL}" "${AVIF_OUTPUT}.tonemapped.png" "input_avif_hdr2020.png" 0 90
"${AVIFGAINMAPUTIL}" swapbase "${INPUT_AVIF_GAINMAP_SDR}" "${AVIF_OUTPUT}" --qcolor 90 --qgain-map 90
# should fail because icc profiles are not supported
- "${AVIFGAINMAPUTIL}" swapbase "${AVIF_GAINMAP_SDR_WITH_ICC}" "${AVIF_OUTPUT}" --qcolor 90 --qgain-map 90 && exit 1
- "${AVIFGAINMAPUTIL}" swapbase "${AVIF_GAINMAP_SDR_WITH_ICC}" "${AVIF_OUTPUT}" --qcolor 90 --qgain-map 90 --ignore-profile
+ "${AVIFGAINMAPUTIL}" swapbase "${INPUT_AVIF_GAINMAP_SDR_WITH_ICC}" "${AVIF_OUTPUT}" --qcolor 90 --qgain-map 90 && exit 1
+ "${AVIFGAINMAPUTIL}" swapbase "${INPUT_AVIF_GAINMAP_SDR_WITH_ICC}" "${AVIF_OUTPUT}" --qcolor 90 --qgain-map 90 --ignore-profile
- "${AVIFGAINMAPUTIL}" convert "${INPUT_JPEG_AVIF_GAINMAP_SDR}" "${AVIF_OUTPUT}"
+ # Also test the are_images_equal binary itself with some gain maps
+ "${ARE_IMAGES_EQUAL}" "${INPUT_JPEG_GAINMAP_SDR}" "${INPUT_JPEG_GAINMAP_SDR}" 0 40 0
+ "${ARE_IMAGES_EQUAL}" "${INPUT_JPEG_GAINMAP_SDR}" "${INPUT_JPEG_GAINMAP_SDR}" 0 40 1
+
+ # Check if avifgainmaputil was built with libxml2.
+ # If it was not, the 'convert' command will fail with an error message
+ # containing "libxml2".
+ if "${AVIFGAINMAPUTIL}" convert "${INPUT_JPEG_GAINMAP_SDR}" "${AVIF_OUTPUT}" 2>&1 | grep -q "libxml2"; then
+ echo "avifgainmaputil was built without libxml2, skipping convert tests."
+ popd
+ exit 0
+ fi
+ "${AVIFGAINMAPUTIL}" convert "${INPUT_JPEG_GAINMAP_SDR}" "${AVIF_OUTPUT}"
# should fail because icc profiles are not supported
- "${AVIFGAINMAPUTIL}" convert "${INPUT_JPEG_AVIF_GAINMAP_SDR}" "${AVIF_OUTPUT}" --swap-base && exit 1
- "${AVIFGAINMAPUTIL}" convert "${INPUT_JPEG_AVIF_GAINMAP_SDR}" "${AVIF_OUTPUT}" --swap-base --ignore-profile \
+ "${AVIFGAINMAPUTIL}" convert "${INPUT_JPEG_GAINMAP_SDR}" "${AVIF_OUTPUT}" --swap-base && exit 1
+ "${AVIFGAINMAPUTIL}" convert "${INPUT_JPEG_GAINMAP_SDR}" "${AVIF_OUTPUT}" --swap-base --ignore-profile \
--cicp 2/3/4
-
- # also test the are_images_equal binary itself with some gain maps
- "${ARE_IMAGES_EQUAL}" "${INPUT_JPEG_AVIF_GAINMAP_SDR}" "${INPUT_JPEG_AVIF_GAINMAP_SDR}" 0 40 0
- "${ARE_IMAGES_EQUAL}" "${INPUT_JPEG_AVIF_GAINMAP_SDR}" "${INPUT_JPEG_AVIF_GAINMAP_SDR}" 0 40 1
popd
exit 0