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

Generated by: LCOV version 1.14