LCOV - code coverage report
Current view: top level - frmts/heif - heifdataset.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 249 285 87.4 %
Date: 2025-05-31 00:00:17 Functions: 18 18 100.0 %

          Line data    Source code
       1             : /******************************************************************************
       2             :  *
       3             :  * Project:  HEIF read-only Driver
       4             :  * Author:   Even Rouault <even.rouault at spatialys.com>
       5             :  *
       6             :  ******************************************************************************
       7             :  * Copyright (c) 2020-2025, Even Rouault <even.rouault at spatialys.com>
       8             :  *
       9             :  * SPDX-License-Identifier: MIT
      10             :  ****************************************************************************/
      11             : 
      12             : #include "heifdataset.h"
      13             : 
      14             : #include "cpl_vsi_virtual.h"
      15             : 
      16             : #include <algorithm>
      17             : #include <cinttypes>
      18             : 
      19             : extern "C" void CPL_DLL GDALRegister_HEIF();
      20             : 
      21             : /************************************************************************/
      22             : /*                       GDALHEIFRasterBand                             */
      23             : /************************************************************************/
      24             : 
      25             : class GDALHEIFRasterBand final : public GDALPamRasterBand
      26             : {
      27             :   protected:
      28             :     CPLErr IReadBlock(int, int, void *) override;
      29             : 
      30             :   public:
      31             :     GDALHEIFRasterBand(GDALHEIFDataset *poDSIn, int nBandIn);
      32             : 
      33           6 :     GDALColorInterp GetColorInterpretation() override
      34             :     {
      35           6 :         return static_cast<GDALColorInterp>(GCI_RedBand + nBand - 1);
      36             :     }
      37             : 
      38           7 :     int GetOverviewCount() override
      39             :     {
      40           7 :         GDALHEIFDataset *poGDS = static_cast<GDALHEIFDataset *>(poDS);
      41           7 :         return static_cast<int>(poGDS->m_apoOvrDS.size());
      42             :     }
      43             : 
      44           4 :     GDALRasterBand *GetOverview(int idx) override
      45             :     {
      46           4 :         if (idx < 0 || idx >= GetOverviewCount())
      47           2 :             return nullptr;
      48           2 :         GDALHEIFDataset *poGDS = static_cast<GDALHEIFDataset *>(poDS);
      49           2 :         return poGDS->m_apoOvrDS[idx]->GetRasterBand(nBand);
      50             :     }
      51             : };
      52             : 
      53             : /************************************************************************/
      54             : /*                         GDALHEIFDataset()                            */
      55             : /************************************************************************/
      56             : 
      57          18 : GDALHEIFDataset::GDALHEIFDataset() : m_hCtxt(heif_context_alloc())
      58             : 
      59             : {
      60             : #ifdef HAS_CUSTOM_FILE_READER
      61          18 :     m_oReader.reader_api_version = 1;
      62          18 :     m_oReader.get_position = GetPositionCbk;
      63          18 :     m_oReader.read = ReadCbk;
      64          18 :     m_oReader.seek = SeekCbk;
      65          18 :     m_oReader.wait_for_file_size = WaitForFileSizeCbk;
      66             : #endif
      67          18 : }
      68             : 
      69             : /************************************************************************/
      70             : /*                         ~GDALHEIFDataset()                           */
      71             : /************************************************************************/
      72             : 
      73          36 : GDALHEIFDataset::~GDALHEIFDataset()
      74             : {
      75          18 :     if (m_hCtxt)
      76          18 :         heif_context_free(m_hCtxt);
      77             : #ifdef HAS_CUSTOM_FILE_READER
      78          18 :     if (m_fpL)
      79          11 :         VSIFCloseL(m_fpL);
      80             : #endif
      81             : #ifndef LIBHEIF_SUPPORTS_TILES
      82          18 :     if (m_hImage)
      83           6 :         heif_image_release(m_hImage);
      84             : #endif
      85          18 :     if (m_hImageHandle)
      86          12 :         heif_image_handle_release(m_hImageHandle);
      87          36 : }
      88             : 
      89             : #ifdef HAS_CUSTOM_FILE_READER
      90             : 
      91             : /************************************************************************/
      92             : /*                          GetPositionCbk()                            */
      93             : /************************************************************************/
      94             : 
      95         459 : int64_t GDALHEIFDataset::GetPositionCbk(void *userdata)
      96             : {
      97         459 :     GDALHEIFDataset *poThis = static_cast<GDALHEIFDataset *>(userdata);
      98         459 :     return static_cast<int64_t>(VSIFTellL(poThis->m_fpL));
      99             : }
     100             : 
     101             : /************************************************************************/
     102             : /*                             ReadCbk()                                */
     103             : /************************************************************************/
     104             : 
     105        2374 : int GDALHEIFDataset::ReadCbk(void *data, size_t size, void *userdata)
     106             : {
     107        2374 :     GDALHEIFDataset *poThis = static_cast<GDALHEIFDataset *>(userdata);
     108             : 
     109        2374 :     const uint64_t nCurPos = poThis->m_fpL->Tell();
     110             : 
     111             : #ifdef DEBUG_VERBOSE
     112             :     CPLDebugOnly("HEIF",
     113             :                  "ReadCbk["
     114             :                  "%" PRIu64 ","
     115             :                  "%" PRIu64 "[",
     116             :                  nCurPos, nCurPos + size);
     117             : #endif
     118             : 
     119        2374 :     if (nCurPos >= poThis->m_nAdviseReadStartPos &&
     120        2374 :         nCurPos + size <=
     121        2374 :             poThis->m_nAdviseReadStartPos + poThis->m_nAdviseReadSize)
     122             :     {
     123           0 :         const size_t nRead = poThis->m_fpL->PRead(data, size, nCurPos);
     124           0 :         poThis->m_fpL->Seek(nCurPos + nRead, SEEK_SET);
     125           0 :         return nRead == size ? 0 : -1;
     126             :     }
     127             : 
     128        2374 :     return VSIFReadL(data, 1, size, poThis->m_fpL) == size ? 0 : -1;
     129             : }
     130             : 
     131             : /************************************************************************/
     132             : /*                             SeekCbk()                                */
     133             : /************************************************************************/
     134             : 
     135          49 : int GDALHEIFDataset::SeekCbk(int64_t position, void *userdata)
     136             : {
     137          49 :     GDALHEIFDataset *poThis = static_cast<GDALHEIFDataset *>(userdata);
     138          49 :     return VSIFSeekL(poThis->m_fpL, static_cast<vsi_l_offset>(position),
     139          49 :                      SEEK_SET);
     140             : }
     141             : 
     142             : /************************************************************************/
     143             : /*                         WaitForFileSizeCbk()                         */
     144             : /************************************************************************/
     145             : 
     146             : enum heif_reader_grow_status
     147         486 : GDALHEIFDataset::WaitForFileSizeCbk(int64_t target_size, void *userdata)
     148             : {
     149         486 :     GDALHEIFDataset *poThis = static_cast<GDALHEIFDataset *>(userdata);
     150         486 :     if (target_size > static_cast<int64_t>(poThis->m_nSize))
     151          11 :         return heif_reader_grow_status_size_beyond_eof;
     152         475 :     return heif_reader_grow_status_size_reached;
     153             : }
     154             : 
     155             : /************************************************************************/
     156             : /*                         RequestRangeCbk()                            */
     157             : /************************************************************************/
     158             : 
     159             : #if LIBHEIF_NUMERIC_VERSION >= BUILD_LIBHEIF_VERSION(1, 19, 0)
     160             : /* static */
     161             : struct heif_reader_range_request_result
     162             : GDALHEIFDataset::RequestRangeCbk(uint64_t start_pos, uint64_t end_pos,
     163             :                                  void *userdata)
     164             : {
     165             :     GDALHEIFDataset *poThis = static_cast<GDALHEIFDataset *>(userdata);
     166             : 
     167             :     heif_reader_range_request_result result;
     168             : 
     169             : #ifdef DEBUG_VERBOSE
     170             :     CPLDebugOnly("HEIF",
     171             :                  "RequestRangeCbk["
     172             :                  "%" PRIu64 ","
     173             :                  "%" PRIu64 "[",
     174             :                  start_pos, end_pos);
     175             : #endif
     176             : 
     177             :     // Somewhat arbitrary. Corresponds to the default chunk size of /vsicurl
     178             :     const size_t MINIMUM_RANGE_SIZE = 16384;
     179             : 
     180             :     if (poThis->m_fpL->HasPRead() && end_pos > start_pos + MINIMUM_RANGE_SIZE &&
     181             :         poThis->m_fpL->GetAdviseReadTotalBytesLimit() > 0)
     182             :     {
     183             :         const vsi_l_offset nOffset = start_pos;
     184             :         const size_t nSize = static_cast<size_t>(
     185             :             std::min<uint64_t>(poThis->m_fpL->GetAdviseReadTotalBytesLimit(),
     186             :                                end_pos - start_pos));
     187             :         poThis->m_fpL->AdviseRead(1, &nOffset, &nSize);
     188             :         poThis->m_nAdviseReadStartPos = nOffset;
     189             :         poThis->m_nAdviseReadSize = nSize;
     190             :     }
     191             : 
     192             :     if (end_pos >= poThis->m_nSize)
     193             :     {
     194             :         result.status = heif_reader_grow_status_size_beyond_eof;
     195             :     }
     196             :     else
     197             :     {
     198             :         result.status = heif_reader_grow_status_size_reached;
     199             :     }
     200             : 
     201             :     result.range_end = poThis->m_nSize;
     202             :     result.reader_error_code = 0;
     203             :     result.reader_error_msg = nullptr;
     204             :     return result;
     205             : }
     206             : #endif
     207             : 
     208             : #endif  // HAS_CUSTOM_FILE_READER
     209             : 
     210             : /************************************************************************/
     211             : /*                              Init()                                  */
     212             : /************************************************************************/
     213             : 
     214          16 : bool GDALHEIFDataset::Init(GDALOpenInfo *poOpenInfo)
     215             : {
     216          32 :     CPLString osFilename(poOpenInfo->pszFilename);
     217             : #ifdef HAS_CUSTOM_FILE_READER
     218             :     VSILFILE *fpL;
     219             : #endif
     220          16 :     int iPart = 0;
     221          16 :     if (STARTS_WITH_CI(poOpenInfo->pszFilename, "HEIF:"))
     222             :     {
     223           9 :         const char *pszPartPos = poOpenInfo->pszFilename + strlen("HEIF:");
     224           9 :         const char *pszNextColumn = strchr(pszPartPos, ':');
     225           9 :         if (pszNextColumn == nullptr)
     226           2 :             return false;
     227           7 :         iPart = atoi(pszPartPos);
     228           7 :         if (iPart <= 0)
     229           1 :             return false;
     230           6 :         osFilename = pszNextColumn + 1;
     231             : #ifdef HAS_CUSTOM_FILE_READER
     232           6 :         fpL = VSIFOpenL(osFilename, "rb");
     233           6 :         if (fpL == nullptr)
     234           2 :             return false;
     235             : #endif
     236             :     }
     237             :     else
     238             :     {
     239             : #ifdef HAS_CUSTOM_FILE_READER
     240           7 :         fpL = poOpenInfo->fpL;
     241           7 :         poOpenInfo->fpL = nullptr;
     242             : #endif
     243             :     }
     244             : 
     245             : #ifdef HAS_CUSTOM_FILE_READER
     246             : 
     247             : #if LIBHEIF_NUMERIC_VERSION >= BUILD_LIBHEIF_VERSION(1, 19, 0)
     248             :     m_oReader.reader_api_version = 2;
     249             : #else
     250          11 :     m_oReader.reader_api_version = 1;
     251             : #endif
     252          11 :     m_oReader.get_position = GetPositionCbk;
     253          11 :     m_oReader.read = ReadCbk;
     254          11 :     m_oReader.seek = SeekCbk;
     255          11 :     m_oReader.wait_for_file_size = WaitForFileSizeCbk;
     256             : #if LIBHEIF_NUMERIC_VERSION >= BUILD_LIBHEIF_VERSION(1, 19, 0)
     257             :     m_oReader.request_range = RequestRangeCbk;
     258             : #endif
     259          11 :     m_fpL = fpL;
     260             : 
     261          11 :     VSIFSeekL(m_fpL, 0, SEEK_END);
     262          11 :     m_nSize = VSIFTellL(m_fpL);
     263          11 :     VSIFSeekL(m_fpL, 0, SEEK_SET);
     264             : 
     265             :     auto err =
     266          11 :         heif_context_read_from_reader(m_hCtxt, &m_oReader, this, nullptr);
     267          11 :     if (err.code != heif_error_Ok)
     268             :     {
     269           0 :         CPLError(CE_Failure, CPLE_AppDefined, "%s",
     270           0 :                  err.message ? err.message : "Cannot open file");
     271           0 :         return false;
     272             :     }
     273             : #else
     274             :     auto err =
     275             :         heif_context_read_from_file(m_hCtxt, osFilename.c_str(), nullptr);
     276             :     if (err.code != heif_error_Ok)
     277             :     {
     278             :         CPLError(CE_Failure, CPLE_AppDefined, "%s",
     279             :                  err.message ? err.message : "Cannot open file");
     280             :         return false;
     281             :     }
     282             : #endif
     283             : 
     284             :     const int nSubdatasets =
     285          11 :         heif_context_get_number_of_top_level_images(m_hCtxt);
     286          11 :     if (iPart == 0)
     287             :     {
     288           7 :         if (nSubdatasets > 1)
     289             :         {
     290           2 :             CPLStringList aosSubDS;
     291           3 :             for (int i = 0; i < nSubdatasets; i++)
     292             :             {
     293             :                 aosSubDS.SetNameValue(
     294             :                     CPLSPrintf("SUBDATASET_%d_NAME", i + 1),
     295           2 :                     CPLSPrintf("HEIF:%d:%s", i + 1, poOpenInfo->pszFilename));
     296             :                 aosSubDS.SetNameValue(CPLSPrintf("SUBDATASET_%d_DESC", i + 1),
     297           2 :                                       CPLSPrintf("Subdataset %d", i + 1));
     298             :             }
     299           1 :             GDALDataset::SetMetadata(aosSubDS.List(), "SUBDATASETS");
     300             :         }
     301             :     }
     302           4 :     else if (iPart > nSubdatasets)
     303             :     {
     304           1 :         CPLError(CE_Failure, CPLE_AppDefined,
     305             :                  "Invalid image part number. Maximum allowed is %d",
     306             :                  nSubdatasets);
     307           1 :         return false;
     308             :     }
     309             :     else
     310             :     {
     311           3 :         iPart--;
     312             :     }
     313          20 :     std::vector<heif_item_id> idArray(nSubdatasets);
     314          10 :     heif_context_get_list_of_top_level_image_IDs(m_hCtxt, &idArray[0],
     315             :                                                  nSubdatasets);
     316          10 :     const auto itemId = idArray[iPart];
     317             : 
     318          10 :     err = heif_context_get_image_handle(m_hCtxt, itemId, &m_hImageHandle);
     319          10 :     if (err.code != heif_error_Ok)
     320             :     {
     321           0 :         CPLError(CE_Failure, CPLE_AppDefined, "%s",
     322           0 :                  err.message ? err.message : "Cannot open image");
     323           0 :         return false;
     324             :     }
     325             : 
     326             : #ifdef LIBHEIF_SUPPORTS_TILES
     327             :     err = heif_image_handle_get_image_tiling(m_hImageHandle, true, &m_tiling);
     328             :     if (err.code != heif_error_Ok)
     329             :     {
     330             :         CPLError(CE_Failure, CPLE_AppDefined, "%s",
     331             :                  err.message ? err.message : "Cannot get image tiling");
     332             :         return false;
     333             :     }
     334             : #endif
     335             : 
     336          10 :     nRasterXSize = heif_image_handle_get_width(m_hImageHandle);
     337          10 :     nRasterYSize = heif_image_handle_get_height(m_hImageHandle);
     338             :     const int l_nBands =
     339          10 :         3 + (heif_image_handle_has_alpha_channel(m_hImageHandle) ? 1 : 0);
     340          43 :     for (int i = 0; i < l_nBands; i++)
     341             :     {
     342          33 :         SetBand(i + 1, new GDALHEIFRasterBand(this, i + 1));
     343             :     }
     344             : 
     345          10 :     ReadMetadata();
     346             : 
     347          10 :     OpenThumbnails();
     348             : 
     349          10 :     if (poOpenInfo->nHeaderBytes > 12 &&
     350           7 :         memcmp(poOpenInfo->pabyHeader + 4, "ftypavif", 8) == 0)
     351             :     {
     352           0 :         poDriver = GetGDALDriverManager()->GetDriverByName("AVIF_HEIF");
     353             :     }
     354             : 
     355             :     // Initialize any PAM information.
     356          10 :     if (nSubdatasets > 1)
     357             :     {
     358           4 :         SetSubdatasetName(CPLSPrintf("%d", iPart + 1));
     359           4 :         SetPhysicalFilename(osFilename.c_str());
     360             :     }
     361          10 :     SetDescription(poOpenInfo->pszFilename);
     362          10 :     TryLoadXML(poOpenInfo->GetSiblingFiles());
     363             : 
     364          10 :     return true;
     365             : }
     366             : 
     367             : /************************************************************************/
     368             : /*                         ReadMetadata()                               */
     369             : /************************************************************************/
     370             : 
     371          10 : void GDALHEIFDataset::ReadMetadata()
     372             : {
     373             : #if LIBHEIF_NUMERIC_VERSION >= BUILD_LIBHEIF_VERSION(1, 19, 0)
     374             :     processProperties();
     375             :     ReadUserDescription();
     376             : #endif
     377          20 :     const int nMDBlocks = heif_image_handle_get_number_of_metadata_blocks(
     378          10 :         m_hImageHandle, nullptr);
     379          10 :     if (nMDBlocks <= 0)
     380           7 :         return;
     381             : 
     382           6 :     std::vector<heif_item_id> idsMDBlock(nMDBlocks);
     383           3 :     heif_image_handle_get_list_of_metadata_block_IDs(m_hImageHandle, nullptr,
     384           3 :                                                      &idsMDBlock[0], nMDBlocks);
     385           8 :     for (const auto &id : idsMDBlock)
     386             :     {
     387             :         const char *pszType =
     388           5 :             heif_image_handle_get_metadata_type(m_hImageHandle, id);
     389             :         const size_t nCount =
     390           5 :             heif_image_handle_get_metadata_size(m_hImageHandle, id);
     391           5 :         if (pszType && EQUAL(pszType, "Exif") && nCount > 8 &&
     392             :             nCount < 1024 * 1024)
     393             :         {
     394           2 :             std::vector<GByte> data(nCount);
     395           2 :             heif_image_handle_get_metadata(m_hImageHandle, id, &data[0]);
     396             : 
     397             :             // There are 2 variants
     398             :             // - the one from
     399             :             // https://github.com/nokiatech/heif_conformance/blob/master/conformance_files/C034.heic
     400             :             //   where the TIFF file immediately starts
     401             :             // - the one found in iPhone files (among others), where there
     402             :             //   is first a 4-byte big-endian offset (after those initial 4
     403             :             //   bytes) that points to the TIFF file, with a "Exif\0\0" just
     404             :             //   before
     405           2 :             unsigned nTIFFFileOffset = 0;
     406           4 :             if (memcmp(&data[0], "II\x2a\x00", 4) == 0 ||
     407           2 :                 memcmp(&data[0], "MM\x00\x2a", 4) == 0)
     408             :             {
     409             :                 // do nothing
     410             :             }
     411             :             else
     412             :             {
     413             :                 unsigned nOffset;
     414           2 :                 memcpy(&nOffset, &data[0], 4);
     415           2 :                 CPL_MSBPTR32(&nOffset);
     416           4 :                 if (nOffset < nCount - 8 &&
     417           2 :                     (memcmp(&data[nOffset + 4], "II\x2a\x00", 4) == 0 ||
     418           1 :                      memcmp(&data[nOffset + 4], "MM\x00\x2a", 4) == 0))
     419             :                 {
     420           2 :                     nTIFFFileOffset = nOffset + 4;
     421             :                 }
     422             :                 else
     423             :                 {
     424           0 :                     continue;
     425             :                 }
     426             :             }
     427             : 
     428             :             const CPLString osTempFile(
     429           4 :                 VSIMemGenerateHiddenFilename("heif_exif.tif"));
     430             :             VSILFILE *fpTemp =
     431           2 :                 VSIFileFromMemBuffer(osTempFile, &data[nTIFFFileOffset],
     432           2 :                                      nCount - nTIFFFileOffset, FALSE);
     433           2 :             char **papszMD = nullptr;
     434             : 
     435           3 :             const bool bLittleEndianTIFF = data[nTIFFFileOffset] == 'I' &&
     436           1 :                                            data[nTIFFFileOffset + 1] == 'I';
     437           2 :             const bool bLSBPlatform = CPL_IS_LSB != 0;
     438           2 :             const bool bSwabflag = bLittleEndianTIFF != bLSBPlatform;
     439             : 
     440             :             int nTIFFDirOff;
     441           2 :             memcpy(&nTIFFDirOff, &data[nTIFFFileOffset + 4], 4);
     442           2 :             if (bSwabflag)
     443             :             {
     444           1 :                 CPL_SWAP32PTR(&nTIFFDirOff);
     445             :             }
     446           2 :             int nExifOffset = 0;
     447           2 :             int nInterOffset = 0;
     448           2 :             int nGPSOffset = 0;
     449           2 :             EXIFExtractMetadata(papszMD, fpTemp, nTIFFDirOff, bSwabflag, 0,
     450             :                                 nExifOffset, nInterOffset, nGPSOffset);
     451           2 :             if (nExifOffset > 0)
     452             :             {
     453           2 :                 EXIFExtractMetadata(papszMD, fpTemp, nExifOffset, bSwabflag, 0,
     454             :                                     nExifOffset, nInterOffset, nGPSOffset);
     455             :             }
     456           2 :             if (nGPSOffset > 0)
     457             :             {
     458           1 :                 EXIFExtractMetadata(papszMD, fpTemp, nGPSOffset, bSwabflag, 0,
     459             :                                     nExifOffset, nInterOffset, nGPSOffset);
     460             :             }
     461           2 :             if (nInterOffset > 0)
     462             :             {
     463           0 :                 EXIFExtractMetadata(papszMD, fpTemp, nInterOffset, bSwabflag, 0,
     464             :                                     nExifOffset, nInterOffset, nGPSOffset);
     465             :             }
     466             : 
     467           2 :             if (papszMD)
     468             :             {
     469           2 :                 GDALDataset::SetMetadata(papszMD, "EXIF");
     470           2 :                 CSLDestroy(papszMD);
     471             :             }
     472             : 
     473           2 :             VSIFCloseL(fpTemp);
     474           4 :             VSIUnlink(osTempFile);
     475             :         }
     476           3 :         else if (pszType && EQUAL(pszType, "mime"))
     477             :         {
     478             : #if LIBHEIF_NUMERIC_VERSION >= BUILD_LIBHEIF_VERSION(1, 2, 0)
     479             :             const char *pszContentType =
     480           3 :                 heif_image_handle_get_metadata_content_type(m_hImageHandle, id);
     481           3 :             if (pszContentType &&
     482           3 :                 EQUAL(pszContentType, "application/rdf+xml") &&
     483             : #else
     484             :             if (
     485             : #endif
     486           3 :                 nCount > 0 && nCount < 1024 * 1024)
     487             :             {
     488           6 :                 std::string osXMP;
     489           3 :                 osXMP.resize(nCount);
     490           3 :                 heif_image_handle_get_metadata(m_hImageHandle, id, &osXMP[0]);
     491           3 :                 if (osXMP.find("<?xpacket") != std::string::npos)
     492             :                 {
     493           3 :                     char *apszMDList[2] = {&osXMP[0], nullptr};
     494           3 :                     GDALDataset::SetMetadata(apszMDList, "xml:XMP");
     495             :                 }
     496             :             }
     497             :         }
     498             :     }
     499             : }
     500             : 
     501             : #if LIBHEIF_NUMERIC_VERSION >= BUILD_LIBHEIF_VERSION(1, 19, 0)
     502             : static bool GetPropertyData(heif_context *m_hCtxt, heif_item_id item_id,
     503             :                             heif_property_id prop_id,
     504             :                             std::shared_ptr<std::vector<GByte>> &data)
     505             : {
     506             :     size_t size;
     507             :     heif_error err =
     508             :         heif_item_get_property_raw_size(m_hCtxt, item_id, prop_id, &size);
     509             :     if (err.code != 0)
     510             :     {
     511             :         return false;
     512             :     }
     513             :     if (size == 0)
     514             :     {
     515             :         return false;
     516             :     }
     517             :     data = std::make_shared<std::vector<uint8_t>>(size);
     518             :     err = heif_item_get_property_raw_data(m_hCtxt, item_id, prop_id,
     519             :                                           data->data());
     520             :     if (err.code != 0)
     521             :     {
     522             :         return false;
     523             :     }
     524             :     return true;
     525             : }
     526             : 
     527             : void GDALHEIFDataset::processProperties()
     528             : {
     529             :     constexpr heif_item_property_type TIEP_4CC =
     530             :         (heif_item_property_type)heif_fourcc('t', 'i', 'e', 'p');
     531             :     constexpr heif_item_property_type MTXF_4CC =
     532             :         (heif_item_property_type)heif_fourcc('m', 't', 'x', 'f');
     533             :     constexpr heif_item_property_type MCRS_4CC =
     534             :         (heif_item_property_type)heif_fourcc('m', 'c', 'r', 's');
     535             :     constexpr int MAX_PROPERTIES_REQUIRED = 50;
     536             :     heif_property_id prop_ids[MAX_PROPERTIES_REQUIRED];
     537             :     heif_item_id item_id = heif_image_handle_get_item_id(m_hImageHandle);
     538             :     int num_props = heif_item_get_properties_of_type(
     539             :         m_hCtxt, item_id, heif_item_property_type_invalid, &prop_ids[0],
     540             :         MAX_PROPERTIES_REQUIRED);
     541             :     for (int i = 0; i < num_props; i++)
     542             :     {
     543             :         heif_item_property_type prop_type =
     544             :             heif_item_get_property_type(m_hCtxt, item_id, prop_ids[i]);
     545             :         if (prop_type == TIEP_4CC)
     546             :         {
     547             :             std::shared_ptr<std::vector<uint8_t>> data;
     548             :             if (!GetPropertyData(m_hCtxt, item_id, prop_ids[i], data))
     549             :             {
     550             :                 continue;
     551             :             }
     552             :             geoHEIF.addGCPs(data->data(), data->size());
     553             :         }
     554             :         else if (prop_type == MTXF_4CC)
     555             :         {
     556             :             std::shared_ptr<std::vector<uint8_t>> data;
     557             :             if (!GetPropertyData(m_hCtxt, item_id, prop_ids[i], data))
     558             :             {
     559             :                 continue;
     560             :             }
     561             :             geoHEIF.setModelTransformation(data->data(), data->size());
     562             :         }
     563             :         else if (prop_type == MCRS_4CC)
     564             :         {
     565             :             std::shared_ptr<std::vector<uint8_t>> data;
     566             :             if (!GetPropertyData(m_hCtxt, item_id, prop_ids[i], data))
     567             :             {
     568             :                 continue;
     569             :             }
     570             :             geoHEIF.extractSRS(data->data(), data->size());
     571             :         }
     572             :     }
     573             : }
     574             : 
     575             : /************************************************************************/
     576             : /*                      ReadUserDescription()                           */
     577             : /************************************************************************/
     578             : void GDALHEIFDataset::ReadUserDescription()
     579             : {
     580             :     constexpr int MAX_PROPERTIES = 50;
     581             :     heif_item_id item_id = heif_image_handle_get_item_id(m_hImageHandle);
     582             :     heif_property_id properties[MAX_PROPERTIES];
     583             :     int nProps = heif_item_get_properties_of_type(
     584             :         m_hCtxt, item_id, heif_item_property_type_user_description, properties,
     585             :         MAX_PROPERTIES);
     586             : 
     587             :     heif_property_user_description *user_description = nullptr;
     588             :     for (int i = 0; i < nProps; i++)
     589             :     {
     590             :         heif_error err = heif_item_get_property_user_description(
     591             :             m_hCtxt, item_id, properties[i], &user_description);
     592             :         if (err.code == 0)
     593             :         {
     594             :             std::string domain = "DESCRIPTION";
     595             :             if (strlen(user_description->lang) != 0)
     596             :             {
     597             :                 domain += "_";
     598             :                 domain += user_description->lang;
     599             :             }
     600             :             SetMetadataItem("NAME", user_description->name, domain.c_str());
     601             :             SetMetadataItem("DESCRIPTION", user_description->description,
     602             :                             domain.c_str());
     603             :             if (strlen(user_description->tags) != 0)
     604             :             {
     605             :                 SetMetadataItem("TAGS", user_description->tags, domain.c_str());
     606             :             }
     607             :             heif_property_user_description_release(user_description);
     608             :         }
     609             :     }
     610             : }
     611             : #endif
     612             : 
     613             : /************************************************************************/
     614             : /*                         OpenThumbnails()                             */
     615             : /************************************************************************/
     616             : 
     617          10 : void GDALHEIFDataset::OpenThumbnails()
     618             : {
     619             :     int nThumbnails =
     620          10 :         heif_image_handle_get_number_of_thumbnails(m_hImageHandle);
     621          10 :     if (nThumbnails <= 0)
     622           8 :         return;
     623             : 
     624           2 :     heif_item_id thumbnailId = 0;
     625           2 :     heif_image_handle_get_list_of_thumbnail_IDs(m_hImageHandle, &thumbnailId,
     626             :                                                 1);
     627           2 :     heif_image_handle *hThumbnailHandle = nullptr;
     628           2 :     heif_image_handle_get_thumbnail(m_hImageHandle, thumbnailId,
     629           2 :                                     &hThumbnailHandle);
     630           2 :     if (hThumbnailHandle == nullptr)
     631           0 :         return;
     632             : 
     633             :     const int nThumbnailBands =
     634           2 :         3 + (heif_image_handle_has_alpha_channel(hThumbnailHandle) ? 1 : 0);
     635           2 :     if (nThumbnailBands != nBands)
     636             :     {
     637           0 :         heif_image_handle_release(hThumbnailHandle);
     638           0 :         return;
     639             :     }
     640             : #if LIBHEIF_NUMERIC_VERSION >= BUILD_LIBHEIF_VERSION(1, 4, 0)
     641             :     const int nBits =
     642           2 :         heif_image_handle_get_luma_bits_per_pixel(hThumbnailHandle);
     643           2 :     if (nBits != heif_image_handle_get_luma_bits_per_pixel(m_hImageHandle))
     644             :     {
     645           0 :         heif_image_handle_release(hThumbnailHandle);
     646           0 :         return;
     647             :     }
     648             : #endif
     649             : 
     650           4 :     auto poOvrDS = std::make_unique<GDALHEIFDataset>();
     651           2 :     poOvrDS->m_hImageHandle = hThumbnailHandle;
     652           2 :     poOvrDS->m_bIsThumbnail = true;
     653           2 :     poOvrDS->nRasterXSize = heif_image_handle_get_width(hThumbnailHandle);
     654           2 :     poOvrDS->nRasterYSize = heif_image_handle_get_height(hThumbnailHandle);
     655             : #ifdef LIBHEIF_SUPPORTS_TILES
     656             :     auto err = heif_image_handle_get_image_tiling(hThumbnailHandle, true,
     657             :                                                   &poOvrDS->m_tiling);
     658             :     if (err.code != heif_error_Ok)
     659             :     {
     660             :         CPLError(CE_Failure, CPLE_AppDefined, "%s",
     661             :                  err.message ? err.message : "Cannot get image tiling");
     662             :         heif_image_handle_release(hThumbnailHandle);
     663             :         return;
     664             :     }
     665             : #endif
     666           9 :     for (int i = 0; i < nBands; i++)
     667             :     {
     668           7 :         poOvrDS->SetBand(i + 1, new GDALHEIFRasterBand(poOvrDS.get(), i + 1));
     669             :     }
     670           2 :     m_apoOvrDS.push_back(std::move(poOvrDS));
     671             : }
     672             : 
     673             : /************************************************************************/
     674             : /*                     HEIFDriverIdentify()                             */
     675             : /************************************************************************/
     676             : 
     677          16 : static int HEIFDriverIdentify(GDALOpenInfo *poOpenInfo)
     678             : 
     679             : {
     680          16 :     if (STARTS_WITH_CI(poOpenInfo->pszFilename, "HEIF:"))
     681           9 :         return true;
     682             : 
     683           7 :     if (poOpenInfo->nHeaderBytes < 12 || poOpenInfo->fpL == nullptr)
     684           0 :         return false;
     685             : 
     686             : #if LIBHEIF_NUMERIC_VERSION >= BUILD_LIBHEIF_VERSION(1, 4, 0)
     687             :     const auto res =
     688           7 :         heif_check_filetype(poOpenInfo->pabyHeader, poOpenInfo->nHeaderBytes);
     689           7 :     if (res == heif_filetype_yes_supported)
     690           7 :         return TRUE;
     691           0 :     if (res == heif_filetype_maybe)
     692           0 :         return -1;
     693           0 :     if (res == heif_filetype_yes_unsupported)
     694             :     {
     695           0 :         CPLDebug("HEIF", "HEIF file, but not supported by libheif");
     696             :     }
     697           0 :     return FALSE;
     698             : #else
     699             :     // Simplistic test...
     700             :     const unsigned char abySig1[] = "\x00"
     701             :                                     "\x00"
     702             :                                     "\x00"
     703             :                                     "\x20"
     704             :                                     "ftypheic";
     705             :     const unsigned char abySig2[] = "\x00"
     706             :                                     "\x00"
     707             :                                     "\x00"
     708             :                                     "\x18"
     709             :                                     "ftypheic";
     710             :     const unsigned char abySig3[] = "\x00"
     711             :                                     "\x00"
     712             :                                     "\x00"
     713             :                                     "\x18"
     714             :                                     "ftypmif1"
     715             :                                     "\x00"
     716             :                                     "\x00"
     717             :                                     "\x00"
     718             :                                     "\x00"
     719             :                                     "mif1heic";
     720             :     return (poOpenInfo->nHeaderBytes >= static_cast<int>(sizeof(abySig1)) &&
     721             :             memcmp(poOpenInfo->pabyHeader, abySig1, sizeof(abySig1)) == 0) ||
     722             :            (poOpenInfo->nHeaderBytes >= static_cast<int>(sizeof(abySig2)) &&
     723             :             memcmp(poOpenInfo->pabyHeader, abySig2, sizeof(abySig2)) == 0) ||
     724             :            (poOpenInfo->nHeaderBytes >= static_cast<int>(sizeof(abySig3)) &&
     725             :             memcmp(poOpenInfo->pabyHeader, abySig3, sizeof(abySig3)) == 0);
     726             : #endif
     727             : }
     728             : 
     729             : /************************************************************************/
     730             : /*                            OpenHEIF()                                */
     731             : /************************************************************************/
     732             : 
     733          16 : GDALDataset *GDALHEIFDataset::OpenHEIF(GDALOpenInfo *poOpenInfo)
     734             : {
     735          16 :     if (!HEIFDriverIdentify(poOpenInfo))
     736             :     {
     737           0 :         return nullptr;
     738             :     }
     739          16 :     if (poOpenInfo->eAccess == GA_Update)
     740             :     {
     741           0 :         CPLError(CE_Failure, CPLE_NotSupported,
     742             :                  "Update of existing HEIF file not supported");
     743           0 :         return nullptr;
     744             :     }
     745             : 
     746          32 :     auto poDS = std::make_unique<GDALHEIFDataset>();
     747          16 :     if (!poDS->Init(poOpenInfo))
     748           6 :         return nullptr;
     749             : 
     750          10 :     return poDS.release();
     751             : }
     752             : 
     753             : #if LIBHEIF_NUMERIC_VERSION >= BUILD_LIBHEIF_VERSION(1, 12, 0)
     754             : 
     755             : /************************************************************************/
     756             : /*                     HEIFIdentifyOnlyAVIF()                           */
     757             : /************************************************************************/
     758             : 
     759             : static int HEIFIdentifyOnlyAVIF(GDALOpenInfo *poOpenInfo)
     760             : {
     761             :     if (poOpenInfo->nHeaderBytes < 12 || poOpenInfo->fpL == nullptr)
     762             :         return false;
     763             :     if (memcmp(poOpenInfo->pabyHeader + 4, "ftypavif", 8) == 0)
     764             :         return true;
     765             :     return false;
     766             : }
     767             : 
     768             : /************************************************************************/
     769             : /*                              OpenAVIF()                              */
     770             : /************************************************************************/
     771             : 
     772             : GDALDataset *GDALHEIFDataset::OpenAVIF(GDALOpenInfo *poOpenInfo)
     773             : {
     774             :     if (!HEIFIdentifyOnlyAVIF(poOpenInfo))
     775             :         return nullptr;
     776             :     if (poOpenInfo->eAccess == GA_Update)
     777             :     {
     778             :         CPLError(CE_Failure, CPLE_NotSupported,
     779             :                  "Update of existing AVIF file not supported");
     780             :         return nullptr;
     781             :     }
     782             : 
     783             :     auto poDS = std::make_unique<GDALHEIFDataset>();
     784             :     if (!poDS->Init(poOpenInfo))
     785             :         return nullptr;
     786             : 
     787             :     return poDS.release();
     788             : }
     789             : #endif
     790             : 
     791             : /************************************************************************/
     792             : /*                          GDALHEIFRasterBand()                        */
     793             : /************************************************************************/
     794             : 
     795          40 : GDALHEIFRasterBand::GDALHEIFRasterBand(GDALHEIFDataset *poDSIn, int nBandIn)
     796             : {
     797          40 :     poDS = poDSIn;
     798          40 :     nBand = nBandIn;
     799             : 
     800          40 :     eDataType = GDT_Byte;
     801             : #if LIBHEIF_NUMERIC_VERSION >= BUILD_LIBHEIF_VERSION(1, 4, 0)
     802             :     const int nBits =
     803          40 :         heif_image_handle_get_luma_bits_per_pixel(poDSIn->m_hImageHandle);
     804          40 :     if (nBits > 8)
     805             :     {
     806           7 :         eDataType = GDT_UInt16;
     807             :     }
     808          40 :     if (nBits != 8 && nBits != 16)
     809             :     {
     810           7 :         GDALRasterBand::SetMetadataItem("NBITS", CPLSPrintf("%d", nBits),
     811             :                                         "IMAGE_STRUCTURE");
     812             :     }
     813             : #endif
     814             : 
     815             : #ifdef LIBHEIF_SUPPORTS_TILES
     816             :     nBlockXSize = poDSIn->m_tiling.tile_width;
     817             :     nBlockYSize = poDSIn->m_tiling.tile_height;
     818             : #else
     819          40 :     nBlockXSize = poDS->GetRasterXSize();
     820          40 :     nBlockYSize = 1;
     821             : #endif
     822          40 : }
     823             : 
     824             : /************************************************************************/
     825             : /*                            IReadBlock()                              */
     826             : /************************************************************************/
     827             : #ifdef LIBHEIF_SUPPORTS_TILES
     828             : CPLErr GDALHEIFRasterBand::IReadBlock(int nBlockXOff, int nBlockYOff,
     829             :                                       void *pImage)
     830             : {
     831             :     GDALHEIFDataset *poGDS = static_cast<GDALHEIFDataset *>(poDS);
     832             :     if (poGDS->m_bFailureDecoding)
     833             :         return CE_Failure;
     834             :     const int nBands = poGDS->GetRasterCount();
     835             :     heif_image *hImage = nullptr;
     836             :     struct heif_decoding_options *decode_options =
     837             :         heif_decoding_options_alloc();
     838             : 
     839             :     auto err = heif_image_handle_decode_image_tile(
     840             :         poGDS->m_hImageHandle, &hImage, heif_colorspace_RGB,
     841             :         nBands == 3
     842             :             ? (eDataType == GDT_UInt16 ?
     843             : #if CPL_IS_LSB
     844             :                                        heif_chroma_interleaved_RRGGBB_LE
     845             : #else
     846             :                                        heif_chroma_interleaved_RRGGBB_BE
     847             : #endif
     848             :                                        : heif_chroma_interleaved_RGB)
     849             :             : (eDataType == GDT_UInt16 ?
     850             : #if CPL_IS_LSB
     851             :                                        heif_chroma_interleaved_RRGGBBAA_LE
     852             : #else
     853             :                                        heif_chroma_interleaved_RRGGBBAA_BE
     854             : #endif
     855             :                                        : heif_chroma_interleaved_RGBA),
     856             :         decode_options, nBlockXOff, nBlockYOff);
     857             : 
     858             :     if (err.code != heif_error_Ok)
     859             :     {
     860             :         CPLError(CE_Failure, CPLE_AppDefined, "%s",
     861             :                  err.message ? err.message : "Cannot decode image");
     862             :         poGDS->m_bFailureDecoding = true;
     863             :         heif_decoding_options_free(decode_options);
     864             :         return CE_Failure;
     865             :     }
     866             :     heif_decoding_options_free(decode_options);
     867             :     int nStride = 0;
     868             :     const uint8_t *pSrcData = heif_image_get_plane_readonly(
     869             :         hImage, heif_channel_interleaved, &nStride);
     870             :     if (eDataType == GDT_Byte)
     871             :     {
     872             :         for (int y = 0; y < nBlockYSize; y++)
     873             :         {
     874             :             for (int x = 0; x < nBlockXSize; x++)
     875             :             {
     876             :                 const size_t srcIndex = static_cast<size_t>(y) * nStride +
     877             :                                         static_cast<size_t>(x) * nBands +
     878             :                                         nBand - 1;
     879             :                 const size_t outIndex =
     880             :                     static_cast<size_t>(y) * nBlockXSize + x;
     881             :                 (static_cast<GByte *>(pImage))[outIndex] = pSrcData[srcIndex];
     882             :             }
     883             :         }
     884             :     }
     885             :     else
     886             :     {
     887             :         for (int y = 0; y < nBlockYSize; y++)
     888             :         {
     889             :             for (int x = 0; x < nBlockXSize; x++)
     890             :             {
     891             :                 const size_t srcIndex = static_cast<size_t>(y) * (nStride / 2) +
     892             :                                         static_cast<size_t>(x) * nBands +
     893             :                                         nBand - 1;
     894             :                 const size_t outIndex =
     895             :                     static_cast<size_t>(y) * nBlockXSize + x;
     896             :                 (static_cast<GUInt16 *>(pImage))[outIndex] =
     897             :                     (reinterpret_cast<const GUInt16 *>(pSrcData))[srcIndex];
     898             :             }
     899             :         }
     900             :     }
     901             :     heif_image_release(hImage);
     902             :     return CE_None;
     903             : }
     904             : #else
     905         544 : CPLErr GDALHEIFRasterBand::IReadBlock(int, int nBlockYOff, void *pImage)
     906             : {
     907         544 :     GDALHEIFDataset *poGDS = static_cast<GDALHEIFDataset *>(poDS);
     908         544 :     if (poGDS->m_bFailureDecoding)
     909           0 :         return CE_Failure;
     910         544 :     const int nBands = poGDS->GetRasterCount();
     911         544 :     if (poGDS->m_hImage == nullptr)
     912             :     {
     913             :         auto err = heif_decode_image(
     914           6 :             poGDS->m_hImageHandle, &(poGDS->m_hImage), heif_colorspace_RGB,
     915             :             nBands == 3
     916             :                 ? (
     917             : #if LIBHEIF_NUMERIC_VERSION >= BUILD_LIBHEIF_VERSION(1, 4, 0)
     918           5 :                       eDataType == GDT_UInt16 ?
     919             : #if CPL_IS_LSB
     920             :                                               heif_chroma_interleaved_RRGGBB_LE
     921             : #else
     922             :                                               heif_chroma_interleaved_RRGGBB_BE
     923             : #endif
     924             :                                               :
     925             : #endif
     926             :                                               heif_chroma_interleaved_RGB)
     927             :                 : (
     928             : #if LIBHEIF_NUMERIC_VERSION >= BUILD_LIBHEIF_VERSION(1, 4, 0)
     929           1 :                       eDataType == GDT_UInt16
     930           1 :                           ?
     931             : #if CPL_IS_LSB
     932             :                           heif_chroma_interleaved_RRGGBBAA_LE
     933             : #else
     934             :                           heif_chroma_interleaved_RRGGBBAA_BE
     935             : #endif
     936             :                           :
     937             : #endif
     938             :                           heif_chroma_interleaved_RGBA),
     939          12 :             nullptr);
     940           6 :         if (err.code != heif_error_Ok)
     941             :         {
     942           0 :             CPLError(CE_Failure, CPLE_AppDefined, "%s",
     943           0 :                      err.message ? err.message : "Cannot decode image");
     944           0 :             poGDS->m_bFailureDecoding = true;
     945           0 :             return CE_Failure;
     946             :         }
     947          12 :         const int nBitsPerPixel = heif_image_get_bits_per_pixel(
     948           6 :             poGDS->m_hImage, heif_channel_interleaved);
     949           6 :         if (nBitsPerPixel != nBands * GDALGetDataTypeSize(eDataType))
     950             :         {
     951           0 :             CPLError(CE_Failure, CPLE_AppDefined,
     952             :                      "Unexpected bits_per_pixel = %d value", nBitsPerPixel);
     953           0 :             poGDS->m_bFailureDecoding = true;
     954           0 :             return CE_Failure;
     955             :         }
     956             :     }
     957             : 
     958         544 :     int nStride = 0;
     959        1088 :     const uint8_t *pSrcData = heif_image_get_plane_readonly(
     960         544 :         poGDS->m_hImage, heif_channel_interleaved, &nStride);
     961         544 :     pSrcData += static_cast<size_t>(nBlockYOff) * nStride;
     962         544 :     if (eDataType == GDT_Byte)
     963             :     {
     964       25176 :         for (int i = 0; i < nBlockXSize; i++)
     965       24832 :             (static_cast<GByte *>(pImage))[i] =
     966       24832 :                 pSrcData[nBand - 1 + static_cast<size_t>(i) * nBands];
     967             :     }
     968             :     else
     969             :     {
     970       80200 :         for (int i = 0; i < nBlockXSize; i++)
     971       80000 :             (static_cast<GUInt16 *>(pImage))[i] =
     972             :                 (reinterpret_cast<const GUInt16 *>(
     973       80000 :                     pSrcData))[nBand - 1 + static_cast<size_t>(i) * nBands];
     974             :     }
     975             : 
     976         544 :     return CE_None;
     977             : }
     978             : #endif
     979             : 
     980             : #if LIBHEIF_NUMERIC_VERSION >= BUILD_LIBHEIF_VERSION(1, 19, 0)
     981             : /************************************************************************/
     982             : /*                          GetGeoTransform()                           */
     983             : /************************************************************************/
     984             : 
     985             : CPLErr GDALHEIFDataset::GetGeoTransform(double *padfTransform)
     986             : {
     987             :     return geoHEIF.GetGeoTransform(padfTransform);
     988             : }
     989             : 
     990             : /************************************************************************/
     991             : /*                          GetSpatialRef()                             */
     992             : /************************************************************************/
     993             : const OGRSpatialReference *GDALHEIFDataset::GetSpatialRef() const
     994             : {
     995             :     return geoHEIF.GetSpatialRef();
     996             : }
     997             : 
     998             : int GDALHEIFDataset::GetGCPCount()
     999             : {
    1000             :     return geoHEIF.GetGCPCount();
    1001             : }
    1002             : 
    1003             : const GDAL_GCP *GDALHEIFDataset::GetGCPs()
    1004             : {
    1005             :     return geoHEIF.GetGCPs();
    1006             : }
    1007             : 
    1008             : const OGRSpatialReference *GDALHEIFDataset::GetGCPSpatialRef() const
    1009             : {
    1010             :     return this->GetSpatialRef();
    1011             : }
    1012             : #endif
    1013             : 
    1014             : /************************************************************************/
    1015             : /*                       GDALRegister_HEIF()                            */
    1016             : /************************************************************************/
    1017             : 
    1018          10 : void GDALRegister_HEIF()
    1019             : 
    1020             : {
    1021          10 :     if (!GDAL_CHECK_VERSION("HEIF driver"))
    1022           0 :         return;
    1023             : 
    1024          10 :     if (GDALGetDriverByName(DRIVER_NAME) != nullptr)
    1025           0 :         return;
    1026             : 
    1027          10 :     auto poDM = GetGDALDriverManager();
    1028             :     {
    1029          10 :         GDALDriver *poDriver = new GDALDriver();
    1030          10 :         HEIFDriverSetCommonMetadata(poDriver);
    1031             : 
    1032             : #if LIBHEIF_NUMERIC_VERSION >= BUILD_LIBHEIF_VERSION(1, 12, 0)
    1033             :         if (heif_have_decoder_for_format(heif_compression_AVC))
    1034             :         {
    1035             :             poDriver->SetMetadataItem("SUPPORTS_AVC", "YES", "HEIF");
    1036             :         }
    1037             :         if (heif_have_encoder_for_format(heif_compression_AVC))
    1038             :         {
    1039             :             poDriver->SetMetadataItem("SUPPORTS_AVC_WRITE", "YES", "HEIF");
    1040             :         }
    1041             :         // If the AVIF dedicated driver is not available, register an AVIF driver,
    1042             :         // called AVIF_HEIF, based on libheif, if it has AV1 decoding capabilities.
    1043             :         if (heif_have_decoder_for_format(heif_compression_AV1))
    1044             :         {
    1045             :             poDriver->SetMetadataItem("SUPPORTS_AVIF", "YES", "HEIF");
    1046             :             poDriver->SetMetadataItem("SUPPORTS_AV1", "YES", "HEIF");
    1047             :         }
    1048             :         if (heif_have_encoder_for_format(heif_compression_AV1))
    1049             :         {
    1050             :             poDriver->SetMetadataItem("SUPPORTS_AV1_WRITE", "YES", "HEIF");
    1051             :         }
    1052             :         if (heif_have_decoder_for_format(heif_compression_HEVC))
    1053             :         {
    1054             :             poDriver->SetMetadataItem("SUPPORTS_HEVC", "YES", "HEIF");
    1055             :         }
    1056             :         if (heif_have_encoder_for_format(heif_compression_HEVC))
    1057             :         {
    1058             :             poDriver->SetMetadataItem("SUPPORTS_HEVC_WRITE", "YES", "HEIF");
    1059             :         }
    1060             :         if (heif_have_decoder_for_format(heif_compression_JPEG))
    1061             :         {
    1062             :             poDriver->SetMetadataItem("SUPPORTS_JPEG", "YES", "HEIF");
    1063             :         }
    1064             :         if (heif_have_encoder_for_format(heif_compression_JPEG))
    1065             :         {
    1066             :             poDriver->SetMetadataItem("SUPPORTS_JPEG_WRITE", "YES", "HEIF");
    1067             :         }
    1068             : #if LIBHEIF_NUMERIC_VERSION >= BUILD_LIBHEIF_VERSION(1, 15, 0)
    1069             :         if (heif_have_decoder_for_format(heif_compression_JPEG2000))
    1070             :         {
    1071             :             poDriver->SetMetadataItem("SUPPORTS_JPEG2000", "YES", "HEIF");
    1072             :         }
    1073             :         if (heif_have_encoder_for_format(heif_compression_JPEG2000))
    1074             :         {
    1075             :             poDriver->SetMetadataItem("SUPPORTS_JPEG2000_WRITE", "YES", "HEIF");
    1076             :         }
    1077             : #endif
    1078             : #if LIBHEIF_NUMERIC_VERSION >= BUILD_LIBHEIF_VERSION(1, 18, 0)
    1079             :         if (heif_have_decoder_for_format(heif_compression_HTJ2K))
    1080             :         {
    1081             :             poDriver->SetMetadataItem("SUPPORTS_HTJ2K", "YES", "HEIF");
    1082             :         }
    1083             :         if (heif_have_encoder_for_format(heif_compression_HTJ2K))
    1084             :         {
    1085             :             poDriver->SetMetadataItem("SUPPORTS_HTJ2K_WRITE", "YES", "HEIF");
    1086             :         }
    1087             : #endif
    1088             : #if LIBHEIF_NUMERIC_VERSION >= BUILD_LIBHEIF_VERSION(1, 16, 0)
    1089             :         if (heif_have_decoder_for_format(heif_compression_uncompressed))
    1090             :         {
    1091             :             poDriver->SetMetadataItem("SUPPORTS_UNCOMPRESSED", "YES", "HEIF");
    1092             :         }
    1093             :         if (heif_have_encoder_for_format(heif_compression_uncompressed))
    1094             :         {
    1095             :             poDriver->SetMetadataItem("SUPPORTS_UNCOMPRESSED_WRITE", "YES",
    1096             :                                       "HEIF");
    1097             :         }
    1098             : #endif
    1099             : #if LIBHEIF_NUMERIC_VERSION >= BUILD_LIBHEIF_VERSION(1, 15, 0)
    1100             :         if (heif_have_decoder_for_format(heif_compression_VVC))
    1101             :         {
    1102             :             poDriver->SetMetadataItem("SUPPORTS_VVC", "YES", "HEIF");
    1103             :         }
    1104             :         if (heif_have_encoder_for_format(heif_compression_VVC))
    1105             :         {
    1106             :             poDriver->SetMetadataItem("SUPPORTS_VVC_WRITE", "YES", "HEIF");
    1107             :         }
    1108             : #endif
    1109             : #else
    1110             :         // Anything that old probably supports only HEVC
    1111          10 :         poDriver->SetMetadataItem("SUPPORTS_HEVC", "YES", "HEIF");
    1112             : #endif
    1113             : #ifdef LIBHEIF_SUPPORTS_TILES
    1114             :         poDriver->SetMetadataItem("SUPPORTS_TILES", "YES", "HEIF");
    1115             : #endif
    1116             : #if LIBHEIF_NUMERIC_VERSION >= BUILD_LIBHEIF_VERSION(1, 19, 0)
    1117             :         poDriver->SetMetadataItem("SUPPORTS_GEOHEIF", "YES", "HEIF");
    1118             : #endif
    1119          10 :         poDriver->pfnOpen = GDALHEIFDataset::OpenHEIF;
    1120             : 
    1121             : #ifdef HAS_CUSTOM_FILE_WRITER
    1122          10 :         poDriver->pfnCreateCopy = GDALHEIFDataset::CreateCopy;
    1123             : #endif
    1124          10 :         poDM->RegisterDriver(poDriver);
    1125             :     }
    1126             : 
    1127             : #if LIBHEIF_NUMERIC_VERSION >= BUILD_LIBHEIF_VERSION(1, 12, 0)
    1128             :     // If the AVIF dedicated driver is not available, register an AVIF driver,
    1129             :     // called AVIF_HEIF, based on libheif, if it has AV1 decoding capabilities.
    1130             :     if (heif_have_decoder_for_format(heif_compression_AV1) &&
    1131             :         !poDM->IsKnownDriver("AVIF") && !poDM->IsKnownDriver("AVIF_HEIF"))
    1132             :     {
    1133             :         GDALDriver *poAVIF_HEIFDriver = new GDALDriver();
    1134             :         poAVIF_HEIFDriver->SetMetadataItem(GDAL_DCAP_RASTER, "YES");
    1135             :         poAVIF_HEIFDriver->SetDescription("AVIF_HEIF");
    1136             :         poAVIF_HEIFDriver->SetMetadataItem(
    1137             :             GDAL_DMD_LONGNAME, "AV1 Image File Format (using libheif)");
    1138             :         poAVIF_HEIFDriver->SetMetadataItem(GDAL_DMD_MIMETYPE, "image/avif");
    1139             :         poAVIF_HEIFDriver->SetMetadataItem(GDAL_DMD_HELPTOPIC,
    1140             :                                            "drivers/raster/heif.html");
    1141             :         poAVIF_HEIFDriver->SetMetadataItem(GDAL_DMD_EXTENSION, "avif");
    1142             :         poAVIF_HEIFDriver->SetMetadataItem(GDAL_DCAP_VIRTUALIO, "YES");
    1143             : 
    1144             :         poAVIF_HEIFDriver->pfnOpen = GDALHEIFDataset::OpenAVIF;
    1145             :         poAVIF_HEIFDriver->pfnIdentify = HEIFIdentifyOnlyAVIF;
    1146             : 
    1147             :         poDM->RegisterDriver(poAVIF_HEIFDriver);
    1148             :     }
    1149             : #endif
    1150             : }

Generated by: LCOV version 1.14