LCOV - code coverage report
Current view: top level - frmts/heif - heifdataset.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 245 278 88.1 %
Date: 2024-11-21 22:18:42 Functions: 18 18 100.0 %

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

Generated by: LCOV version 1.14