From 18f0c9ad04cfbaeecee3d707200896d9fc48bd17 Mon Sep 17 00:00:00 2001 From: Even Rouault Date: Wed, 21 Aug 2024 22:35:47 +0200 Subject: [PATCH] AVIF: add ICC profile get/set --- doc/source/drivers/raster/avif.rst | 13 +++++++++ frmts/avif/avifdataset.cpp | 43 ++++++++++++++++++++++++++++++ 2 files changed, 56 insertions(+) diff --git a/doc/source/drivers/raster/avif.rst b/doc/source/drivers/raster/avif.rst index e3e887df6935..22d81a6313e5 100644 --- a/doc/source/drivers/raster/avif.rst +++ b/doc/source/drivers/raster/avif.rst @@ -33,6 +33,14 @@ Driver capabilities .. supports_createcopy +Color Profile Metadata +---------------------- + +GDAL can deal with the following color profile +metadata in the COLOR_PROFILE domain: + +- SOURCE_ICC_PROFILE (Base64 encoded ICC profile embedded in file.) + Creation options ---------------- @@ -72,6 +80,11 @@ The following creation options are supported: Number of worker threads for compression. +- .. co:: SOURCE_ICC_PROFILE + + ICC profile encoded in Base64. Can also be + set to empty string to avoid the ICC profile from the source dataset to be used. + - .. co:: WRITE_EXIF_METADATA :choices: YES, NO :default: YES diff --git a/frmts/avif/avifdataset.cpp b/frmts/avif/avifdataset.cpp index e28ac0e257aa..da52c7f91c99 100644 --- a/frmts/avif/avifdataset.cpp +++ b/frmts/avif/avifdataset.cpp @@ -518,6 +518,20 @@ bool GDALAVIFDataset::Init(GDALOpenInfo *poOpenInfo) GDALDataset::SetMetadata(const_cast(apszMD), "xml:XMP"); } + if (m_decoder->image->icc.size > 0) + { + // Escape the profile. + char *pszBase64Profile = + CPLBase64Encode(static_cast(m_decoder->image->icc.size), + m_decoder->image->icc.data); + + // Set ICC profile metadata. + SetMetadataItem("SOURCE_ICC_PROFILE", pszBase64Profile, + "COLOR_PROFILE"); + + CPLFree(pszBase64Profile); + } + // Initialize any PAM information. if (m_decoder->imageCount > 1) { @@ -849,6 +863,25 @@ GDALDataset *GDALAVIFDataset::CreateCopy(const char *pszFilename, } } +#if AVIF_VERSION_MAJOR >= 1 + const char *pszICCProfile = + CSLFetchNameValue(papszOptions, "SOURCE_ICC_PROFILE"); + if (pszICCProfile == nullptr) + { + pszICCProfile = + poSrcDS->GetMetadataItem("SOURCE_ICC_PROFILE", "COLOR_PROFILE"); + } + if (pszICCProfile && pszICCProfile[0] != '\0') + { + char *pEmbedBuffer = CPLStrdup(pszICCProfile); + const GInt32 nEmbedLen = + CPLBase64DecodeInPlace(reinterpret_cast(pEmbedBuffer)); + CPL_IGNORE_RET_VAL(avifImageSetProfileICC( + image, reinterpret_cast(pEmbedBuffer), nEmbedLen)); + CPLFree(pEmbedBuffer); + } +#endif + avifErr = avifEncoderAddImage(encoder, image, 1, AVIF_ADD_IMAGE_FLAG_SINGLE); if (avifErr != AVIF_RESULT_OK) @@ -1039,6 +1072,16 @@ void GDALAVIFDriver::InitMetadata() CPLAddXMLAttributeAndValue(psOption, "default", "YES"); } +#if AVIF_VERSION_MAJOR >= 1 + { + auto psOption = CPLCreateXMLNode(oTree.get(), CXT_Element, "Option"); + CPLAddXMLAttributeAndValue(psOption, "name", "SOURCE_ICC_PROFILE"); + CPLAddXMLAttributeAndValue(psOption, "type", "string"); + CPLAddXMLAttributeAndValue(psOption, "description", + "ICC profile encoded in Base64"); + } +#endif + { auto psOption = CPLCreateXMLNode(oTree.get(), CXT_Element, "Option"); CPLAddXMLAttributeAndValue(psOption, "name", "NBITS");