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

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

Generated by: LCOV version 1.14