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

Generated by: LCOV version 1.14