LCOV - code coverage report
Current view: top level - frmts/heif - heifdataset.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 238 270 88.1 %
Date: 2024-05-14 13:00:50 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             :  * Permission is hereby granted, free of charge, to any person obtaining a
      10             :  * copy of this software and associated documentation files (the "Software"),
      11             :  * to deal in the Software without restriction, including without limitation
      12             :  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
      13             :  * and/or sell copies of the Software, and to permit persons to whom the
      14             :  * Software is furnished to do so, subject to the following conditions:
      15             :  *
      16             :  * The above copyright notice and this permission notice shall be included
      17             :  * in all copies or substantial portions of the Software.
      18             :  *
      19             :  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
      20             :  * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
      21             :  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
      22             :  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
      23             :  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
      24             :  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
      25             :  * DEALINGS IN THE SOFTWARE.
      26             :  ****************************************************************************/
      27             : 
      28             : #include "gdal_pam.h"
      29             : #include "ogr_spatialref.h"
      30             : 
      31             : #include "include_libheif.h"
      32             : 
      33             : #include "heifdrivercore.h"
      34             : 
      35             : #include <vector>
      36             : 
      37             : extern "C" void CPL_DLL GDALRegister_HEIF();
      38             : 
      39             : // g++ -fPIC -std=c++11 frmts/heif/heifdataset.cpp -Iport -Igcore -Iogr
      40             : // -Iogr/ogrsf_frmts -I$HOME/heif/install-ubuntu-18.04/include
      41             : // -L$HOME/heif/install-ubuntu-18.04/lib -lheif -shared -o gdal_HEIF.so -L.
      42             : // -lgdal
      43             : 
      44             : /************************************************************************/
      45             : /*                        GDALHEIFDataset                               */
      46             : /************************************************************************/
      47             : 
      48             : class GDALHEIFDataset final : public GDALPamDataset
      49             : {
      50             :     friend class GDALHEIFRasterBand;
      51             : 
      52             :     heif_context *m_hCtxt = nullptr;
      53             :     heif_image_handle *m_hImageHandle = nullptr;
      54             :     heif_image *m_hImage = nullptr;
      55             :     bool m_bFailureDecoding = false;
      56             :     std::vector<std::unique_ptr<GDALHEIFDataset>> m_apoOvrDS{};
      57             :     bool m_bIsThumbnail = false;
      58             : 
      59             : #ifdef HAS_CUSTOM_FILE_READER
      60             :     heif_reader m_oReader{};
      61             :     VSILFILE *m_fpL = nullptr;
      62             :     vsi_l_offset m_nSize = 0;
      63             : 
      64             :     static int64_t GetPositionCbk(void *userdata);
      65             :     static int ReadCbk(void *data, size_t size, void *userdata);
      66             :     static int SeekCbk(int64_t position, void *userdata);
      67             :     static enum heif_reader_grow_status WaitForFileSizeCbk(int64_t target_size,
      68             :                                                            void *userdata);
      69             : #endif
      70             : 
      71             :     bool Init(GDALOpenInfo *poOpenInfo);
      72             :     void ReadMetadata();
      73             :     void OpenThumbnails();
      74             : 
      75             :   public:
      76             :     GDALHEIFDataset();
      77             :     ~GDALHEIFDataset();
      78             : 
      79             :     static GDALDataset *Open(GDALOpenInfo *poOpenInfo);
      80             : };
      81             : 
      82             : /************************************************************************/
      83             : /*                       GDALHEIFRasterBand                             */
      84             : /************************************************************************/
      85             : 
      86             : class GDALHEIFRasterBand final : public GDALPamRasterBand
      87             : {
      88             :   protected:
      89             :     CPLErr IReadBlock(int, int, void *) override;
      90             : 
      91             :   public:
      92             :     GDALHEIFRasterBand(GDALHEIFDataset *poDSIn, int nBandIn);
      93             : 
      94           6 :     GDALColorInterp GetColorInterpretation() override
      95             :     {
      96           6 :         return static_cast<GDALColorInterp>(GCI_RedBand + nBand - 1);
      97             :     }
      98             : 
      99           7 :     int GetOverviewCount() override
     100             :     {
     101           7 :         GDALHEIFDataset *poGDS = static_cast<GDALHEIFDataset *>(poDS);
     102           7 :         return static_cast<int>(poGDS->m_apoOvrDS.size());
     103             :     }
     104             : 
     105           4 :     GDALRasterBand *GetOverview(int idx) override
     106             :     {
     107           4 :         if (idx < 0 || idx >= GetOverviewCount())
     108           2 :             return nullptr;
     109           2 :         GDALHEIFDataset *poGDS = static_cast<GDALHEIFDataset *>(poDS);
     110           2 :         return poGDS->m_apoOvrDS[idx]->GetRasterBand(nBand);
     111             :     }
     112             : };
     113             : 
     114             : /************************************************************************/
     115             : /*                         GDALHEIFDataset()                            */
     116             : /************************************************************************/
     117             : 
     118          17 : GDALHEIFDataset::GDALHEIFDataset() : m_hCtxt(heif_context_alloc())
     119             : 
     120             : {
     121             : #ifdef HAS_CUSTOM_FILE_READER
     122          17 :     m_oReader.reader_api_version = 1;
     123          17 :     m_oReader.get_position = GetPositionCbk;
     124          17 :     m_oReader.read = ReadCbk;
     125          17 :     m_oReader.seek = SeekCbk;
     126          17 :     m_oReader.wait_for_file_size = WaitForFileSizeCbk;
     127             : #endif
     128          17 : }
     129             : 
     130             : /************************************************************************/
     131             : /*                         ~GDALHEIFDataset()                           */
     132             : /************************************************************************/
     133             : 
     134          34 : GDALHEIFDataset::~GDALHEIFDataset()
     135             : {
     136          17 :     if (m_hCtxt)
     137          17 :         heif_context_free(m_hCtxt);
     138             : #ifdef HAS_CUSTOM_FILE_READER
     139          17 :     if (m_fpL)
     140          10 :         VSIFCloseL(m_fpL);
     141             : #endif
     142          17 :     if (m_hImage)
     143           5 :         heif_image_release(m_hImage);
     144          17 :     if (m_hImageHandle)
     145          11 :         heif_image_handle_release(m_hImageHandle);
     146          34 : }
     147             : 
     148             : #ifdef HAS_CUSTOM_FILE_READER
     149             : 
     150             : /************************************************************************/
     151             : /*                          GetPositionCbk()                            */
     152             : /************************************************************************/
     153             : 
     154         414 : int64_t GDALHEIFDataset::GetPositionCbk(void *userdata)
     155             : {
     156         414 :     GDALHEIFDataset *poThis = static_cast<GDALHEIFDataset *>(userdata);
     157         414 :     return static_cast<int64_t>(VSIFTellL(poThis->m_fpL));
     158             : }
     159             : 
     160             : /************************************************************************/
     161             : /*                             ReadCbk()                                */
     162             : /************************************************************************/
     163             : 
     164        2128 : int GDALHEIFDataset::ReadCbk(void *data, size_t size, void *userdata)
     165             : {
     166        2128 :     GDALHEIFDataset *poThis = static_cast<GDALHEIFDataset *>(userdata);
     167        2128 :     return VSIFReadL(data, size, 1, poThis->m_fpL) == 1 ? 0 : -1;
     168             : }
     169             : 
     170             : /************************************************************************/
     171             : /*                             SeekCbk()                                */
     172             : /************************************************************************/
     173             : 
     174          44 : int GDALHEIFDataset::SeekCbk(int64_t position, void *userdata)
     175             : {
     176          44 :     GDALHEIFDataset *poThis = static_cast<GDALHEIFDataset *>(userdata);
     177          44 :     return VSIFSeekL(poThis->m_fpL, static_cast<vsi_l_offset>(position),
     178          44 :                      SEEK_SET);
     179             : }
     180             : 
     181             : /************************************************************************/
     182             : /*                         WaitForFileSizeCbk()                         */
     183             : /************************************************************************/
     184             : 
     185             : enum heif_reader_grow_status
     186         438 : GDALHEIFDataset::WaitForFileSizeCbk(int64_t target_size, void *userdata)
     187             : {
     188         438 :     GDALHEIFDataset *poThis = static_cast<GDALHEIFDataset *>(userdata);
     189         438 :     if (target_size > static_cast<int64_t>(poThis->m_nSize))
     190          10 :         return heif_reader_grow_status_size_beyond_eof;
     191         428 :     return heif_reader_grow_status_size_reached;
     192             : }
     193             : 
     194             : #endif
     195             : 
     196             : /************************************************************************/
     197             : /*                              Init()                                  */
     198             : /************************************************************************/
     199             : 
     200          15 : bool GDALHEIFDataset::Init(GDALOpenInfo *poOpenInfo)
     201             : {
     202          30 :     CPLString osFilename(poOpenInfo->pszFilename);
     203             : #ifdef HAS_CUSTOM_FILE_READER
     204             :     VSILFILE *fpL;
     205             : #endif
     206          15 :     int iPart = 0;
     207          15 :     if (STARTS_WITH_CI(poOpenInfo->pszFilename, "HEIF:"))
     208             :     {
     209           8 :         const char *pszPartPos = poOpenInfo->pszFilename + strlen("HEIF:");
     210           8 :         const char *pszNextColumn = strchr(pszPartPos, ':');
     211           8 :         if (pszNextColumn == nullptr)
     212           2 :             return false;
     213           6 :         iPart = atoi(pszPartPos);
     214           6 :         if (iPart <= 0)
     215           1 :             return false;
     216           5 :         osFilename = pszNextColumn + 1;
     217             : #ifdef HAS_CUSTOM_FILE_READER
     218           5 :         fpL = VSIFOpenL(osFilename, "rb");
     219           5 :         if (fpL == nullptr)
     220           2 :             return false;
     221             : #endif
     222             :     }
     223             :     else
     224             :     {
     225             : #ifdef HAS_CUSTOM_FILE_READER
     226           7 :         fpL = poOpenInfo->fpL;
     227           7 :         poOpenInfo->fpL = nullptr;
     228             : #endif
     229             :     }
     230             : 
     231             : #ifdef HAS_CUSTOM_FILE_READER
     232          10 :     m_oReader.reader_api_version = 1;
     233          10 :     m_oReader.get_position = GetPositionCbk;
     234          10 :     m_oReader.read = ReadCbk;
     235          10 :     m_oReader.seek = SeekCbk;
     236          10 :     m_oReader.wait_for_file_size = WaitForFileSizeCbk;
     237          10 :     m_fpL = fpL;
     238             : 
     239          10 :     VSIFSeekL(m_fpL, 0, SEEK_END);
     240          10 :     m_nSize = VSIFTellL(m_fpL);
     241          10 :     VSIFSeekL(m_fpL, 0, SEEK_SET);
     242             : 
     243             :     auto err =
     244          10 :         heif_context_read_from_reader(m_hCtxt, &m_oReader, this, nullptr);
     245          10 :     if (err.code != heif_error_Ok)
     246             :     {
     247           0 :         CPLError(CE_Failure, CPLE_AppDefined, "%s",
     248           0 :                  err.message ? err.message : "Cannot open file");
     249           0 :         return false;
     250             :     }
     251             : #else
     252             :     auto err =
     253             :         heif_context_read_from_file(m_hCtxt, osFilename.c_str(), nullptr);
     254             :     if (err.code != heif_error_Ok)
     255             :     {
     256             :         CPLError(CE_Failure, CPLE_AppDefined, "%s",
     257             :                  err.message ? err.message : "Cannot open file");
     258             :         return false;
     259             :     }
     260             : #endif
     261             : 
     262             :     const int nSubdatasets =
     263          10 :         heif_context_get_number_of_top_level_images(m_hCtxt);
     264          10 :     if (iPart == 0)
     265             :     {
     266           7 :         if (nSubdatasets > 1)
     267             :         {
     268           2 :             CPLStringList aosSubDS;
     269           3 :             for (int i = 0; i < nSubdatasets; i++)
     270             :             {
     271             :                 aosSubDS.SetNameValue(
     272             :                     CPLSPrintf("SUBDATASET_%d_NAME", i + 1),
     273           2 :                     CPLSPrintf("HEIF:%d:%s", i + 1, poOpenInfo->pszFilename));
     274             :                 aosSubDS.SetNameValue(CPLSPrintf("SUBDATASET_%d_DESC", i + 1),
     275           2 :                                       CPLSPrintf("Subdataset %d", i + 1));
     276             :             }
     277           1 :             GDALDataset::SetMetadata(aosSubDS.List(), "SUBDATASETS");
     278             :         }
     279             :     }
     280           3 :     else if (iPart > nSubdatasets)
     281             :     {
     282           1 :         CPLError(CE_Failure, CPLE_AppDefined,
     283             :                  "Invalid image part number. Maximum allowed is %d",
     284             :                  nSubdatasets);
     285           1 :         return false;
     286             :     }
     287             :     else
     288             :     {
     289           2 :         iPart--;
     290             :     }
     291          18 :     std::vector<heif_item_id> idArray(nSubdatasets);
     292           9 :     heif_context_get_list_of_top_level_image_IDs(m_hCtxt, &idArray[0],
     293             :                                                  nSubdatasets);
     294           9 :     const auto itemId = idArray[iPart];
     295             : 
     296           9 :     err = heif_context_get_image_handle(m_hCtxt, itemId, &m_hImageHandle);
     297           9 :     if (err.code != heif_error_Ok)
     298             :     {
     299           0 :         CPLError(CE_Failure, CPLE_AppDefined, "%s",
     300           0 :                  err.message ? err.message : "Cannot open image");
     301           0 :         return false;
     302             :     }
     303             : 
     304           9 :     nRasterXSize = heif_image_handle_get_width(m_hImageHandle);
     305           9 :     nRasterYSize = heif_image_handle_get_height(m_hImageHandle);
     306             :     const int l_nBands =
     307           9 :         3 + (heif_image_handle_has_alpha_channel(m_hImageHandle) ? 1 : 0);
     308          39 :     for (int i = 0; i < l_nBands; i++)
     309             :     {
     310          30 :         SetBand(i + 1, new GDALHEIFRasterBand(this, i + 1));
     311             :     }
     312             : 
     313           9 :     ReadMetadata();
     314             : 
     315           9 :     OpenThumbnails();
     316             : 
     317             :     // Initialize any PAM information.
     318           9 :     SetDescription(poOpenInfo->pszFilename);
     319           9 :     TryLoadXML(poOpenInfo->GetSiblingFiles());
     320             : 
     321           9 :     return true;
     322             : }
     323             : 
     324             : /************************************************************************/
     325             : /*                         ReadMetadata()                               */
     326             : /************************************************************************/
     327             : 
     328           9 : void GDALHEIFDataset::ReadMetadata()
     329             : {
     330          18 :     const int nMDBlocks = heif_image_handle_get_number_of_metadata_blocks(
     331           9 :         m_hImageHandle, nullptr);
     332           9 :     if (nMDBlocks <= 0)
     333           6 :         return;
     334             : 
     335           6 :     std::vector<heif_item_id> idsMDBlock(nMDBlocks);
     336           3 :     heif_image_handle_get_list_of_metadata_block_IDs(m_hImageHandle, nullptr,
     337           3 :                                                      &idsMDBlock[0], nMDBlocks);
     338           8 :     for (const auto &id : idsMDBlock)
     339             :     {
     340             :         const char *pszType =
     341           5 :             heif_image_handle_get_metadata_type(m_hImageHandle, id);
     342             :         const size_t nCount =
     343           5 :             heif_image_handle_get_metadata_size(m_hImageHandle, id);
     344           5 :         if (pszType && EQUAL(pszType, "Exif") && nCount > 8 &&
     345             :             nCount < 1024 * 1024)
     346             :         {
     347           2 :             std::vector<GByte> data(nCount);
     348           2 :             heif_image_handle_get_metadata(m_hImageHandle, id, &data[0]);
     349             : 
     350             :             // There are 2 variants
     351             :             // - the one from
     352             :             // https://github.com/nokiatech/heif_conformance/blob/master/conformance_files/C034.heic
     353             :             //   where the TIFF file immediately starts
     354             :             // - the one found in iPhone files (among others), where there
     355             :             //   is first a 4-byte big-endian offset (after those initial 4
     356             :             //   bytes) that points to the TIFF file, with a "Exif\0\0" just
     357             :             //   before
     358           2 :             unsigned nTIFFFileOffset = 0;
     359           4 :             if (memcmp(&data[0], "II\x2a\x00", 4) == 0 ||
     360           2 :                 memcmp(&data[0], "MM\x00\x2a", 4) == 0)
     361             :             {
     362             :                 // do nothing
     363             :             }
     364             :             else
     365             :             {
     366             :                 unsigned nOffset;
     367           2 :                 memcpy(&nOffset, &data[0], 4);
     368           2 :                 CPL_MSBPTR32(&nOffset);
     369           4 :                 if (nOffset < nCount - 8 &&
     370           2 :                     (memcmp(&data[nOffset + 4], "II\x2a\x00", 4) == 0 ||
     371           1 :                      memcmp(&data[nOffset + 4], "MM\x00\x2a", 4) == 0))
     372             :                 {
     373           2 :                     nTIFFFileOffset = nOffset + 4;
     374             :                 }
     375             :                 else
     376             :                 {
     377           0 :                     continue;
     378             :                 }
     379             :             }
     380             : 
     381           4 :             CPLString osTempFile;
     382           2 :             osTempFile.Printf("/vsimem/heif_exif_%p.tif", this);
     383             :             VSILFILE *fpTemp =
     384           2 :                 VSIFileFromMemBuffer(osTempFile, &data[nTIFFFileOffset],
     385           2 :                                      nCount - nTIFFFileOffset, FALSE);
     386           2 :             char **papszMD = nullptr;
     387             : 
     388           3 :             const bool bLittleEndianTIFF = data[nTIFFFileOffset] == 'I' &&
     389           1 :                                            data[nTIFFFileOffset + 1] == 'I';
     390           2 :             const bool bLSBPlatform = CPL_IS_LSB != 0;
     391           2 :             const bool bSwabflag = bLittleEndianTIFF != bLSBPlatform;
     392             : 
     393             :             int nTIFFDirOff;
     394           2 :             memcpy(&nTIFFDirOff, &data[nTIFFFileOffset + 4], 4);
     395           2 :             if (bSwabflag)
     396             :             {
     397           1 :                 CPL_SWAP32PTR(&nTIFFDirOff);
     398             :             }
     399           2 :             int nExifOffset = 0;
     400           2 :             int nInterOffset = 0;
     401           2 :             int nGPSOffset = 0;
     402           2 :             EXIFExtractMetadata(papszMD, fpTemp, nTIFFDirOff, bSwabflag, 0,
     403             :                                 nExifOffset, nInterOffset, nGPSOffset);
     404           2 :             if (nExifOffset > 0)
     405             :             {
     406           2 :                 EXIFExtractMetadata(papszMD, fpTemp, nExifOffset, bSwabflag, 0,
     407             :                                     nExifOffset, nInterOffset, nGPSOffset);
     408             :             }
     409           2 :             if (nGPSOffset > 0)
     410             :             {
     411           1 :                 EXIFExtractMetadata(papszMD, fpTemp, nGPSOffset, bSwabflag, 0,
     412             :                                     nExifOffset, nInterOffset, nGPSOffset);
     413             :             }
     414           2 :             if (nInterOffset > 0)
     415             :             {
     416           0 :                 EXIFExtractMetadata(papszMD, fpTemp, nInterOffset, bSwabflag, 0,
     417             :                                     nExifOffset, nInterOffset, nGPSOffset);
     418             :             }
     419             : 
     420           2 :             if (papszMD)
     421             :             {
     422           2 :                 GDALDataset::SetMetadata(papszMD, "EXIF");
     423           2 :                 CSLDestroy(papszMD);
     424             :             }
     425             : 
     426           2 :             VSIFCloseL(fpTemp);
     427           4 :             VSIUnlink(osTempFile);
     428             :         }
     429           3 :         else if (pszType && EQUAL(pszType, "mime"))
     430             :         {
     431             : #if LIBHEIF_NUMERIC_VERSION >= BUILD_LIBHEIF_VERSION(1, 2, 0)
     432             :             const char *pszContentType =
     433           3 :                 heif_image_handle_get_metadata_content_type(m_hImageHandle, id);
     434           3 :             if (pszContentType &&
     435           3 :                 EQUAL(pszContentType, "application/rdf+xml") &&
     436             : #else
     437             :             if (
     438             : #endif
     439           3 :                 nCount > 0 && nCount < 1024 * 1024)
     440             :             {
     441           6 :                 std::string osXMP;
     442           3 :                 osXMP.resize(nCount);
     443           3 :                 heif_image_handle_get_metadata(m_hImageHandle, id, &osXMP[0]);
     444           3 :                 if (osXMP.find("<?xpacket") != std::string::npos)
     445             :                 {
     446           3 :                     char *apszMDList[2] = {&osXMP[0], nullptr};
     447           3 :                     GDALDataset::SetMetadata(apszMDList, "xml:XMP");
     448             :                 }
     449             :             }
     450             :         }
     451             :     }
     452             : }
     453             : 
     454             : /************************************************************************/
     455             : /*                         OpenThumbnails()                             */
     456             : /************************************************************************/
     457             : 
     458           9 : void GDALHEIFDataset::OpenThumbnails()
     459             : {
     460             :     int nThumbnails =
     461           9 :         heif_image_handle_get_number_of_thumbnails(m_hImageHandle);
     462           9 :     if (nThumbnails <= 0)
     463           7 :         return;
     464             : 
     465           2 :     heif_item_id thumbnailId = 0;
     466           2 :     heif_image_handle_get_list_of_thumbnail_IDs(m_hImageHandle, &thumbnailId,
     467             :                                                 1);
     468           2 :     heif_image_handle *hThumbnailHandle = nullptr;
     469           2 :     heif_image_handle_get_thumbnail(m_hImageHandle, thumbnailId,
     470           2 :                                     &hThumbnailHandle);
     471           2 :     if (hThumbnailHandle == nullptr)
     472           0 :         return;
     473             : 
     474             :     const int nThumbnailBands =
     475           2 :         3 + (heif_image_handle_has_alpha_channel(hThumbnailHandle) ? 1 : 0);
     476           2 :     if (nThumbnailBands != nBands)
     477             :     {
     478           0 :         heif_image_handle_release(hThumbnailHandle);
     479           0 :         return;
     480             :     }
     481             : #if LIBHEIF_NUMERIC_VERSION >= BUILD_LIBHEIF_VERSION(1, 4, 0)
     482             :     const int nBits =
     483           2 :         heif_image_handle_get_luma_bits_per_pixel(hThumbnailHandle);
     484           2 :     if (nBits != heif_image_handle_get_luma_bits_per_pixel(m_hImageHandle))
     485             :     {
     486           0 :         heif_image_handle_release(hThumbnailHandle);
     487           0 :         return;
     488             :     }
     489             : #endif
     490             : 
     491           4 :     auto poOvrDS = std::make_unique<GDALHEIFDataset>();
     492           2 :     poOvrDS->m_hImageHandle = hThumbnailHandle;
     493           2 :     poOvrDS->m_bIsThumbnail = true;
     494           2 :     poOvrDS->nRasterXSize = heif_image_handle_get_width(hThumbnailHandle);
     495           2 :     poOvrDS->nRasterYSize = heif_image_handle_get_height(hThumbnailHandle);
     496           9 :     for (int i = 0; i < nBands; i++)
     497             :     {
     498           7 :         poOvrDS->SetBand(i + 1, new GDALHEIFRasterBand(poOvrDS.get(), i + 1));
     499             :     }
     500           2 :     m_apoOvrDS.push_back(std::move(poOvrDS));
     501             : }
     502             : 
     503             : /************************************************************************/
     504             : /*                     HEIFDriverIdentify()                             */
     505             : /************************************************************************/
     506             : 
     507          15 : static int HEIFDriverIdentify(GDALOpenInfo *poOpenInfo)
     508             : 
     509             : {
     510          15 :     if (STARTS_WITH_CI(poOpenInfo->pszFilename, "HEIF:"))
     511           8 :         return true;
     512             : 
     513           7 :     if (poOpenInfo->nHeaderBytes < 12 || poOpenInfo->fpL == nullptr)
     514           0 :         return false;
     515             : #if LIBHEIF_NUMERIC_VERSION >= BUILD_LIBHEIF_VERSION(1, 4, 0)
     516             :     const auto res =
     517           7 :         heif_check_filetype(poOpenInfo->pabyHeader, poOpenInfo->nHeaderBytes);
     518           7 :     if (res == heif_filetype_yes_supported)
     519           7 :         return TRUE;
     520           0 :     if (res == heif_filetype_maybe)
     521           0 :         return -1;
     522           0 :     if (res == heif_filetype_yes_unsupported)
     523             :     {
     524           0 :         CPLDebug("HEIF", "HEIF file, but not supported by libheif");
     525             :     }
     526           0 :     return FALSE;
     527             : #else
     528             :     // Simplistic test...
     529             :     const unsigned char abySig1[] = "\x00"
     530             :                                     "\x00"
     531             :                                     "\x00"
     532             :                                     "\x20"
     533             :                                     "ftypheic";
     534             :     const unsigned char abySig2[] = "\x00"
     535             :                                     "\x00"
     536             :                                     "\x00"
     537             :                                     "\x18"
     538             :                                     "ftypheic";
     539             :     const unsigned char abySig3[] = "\x00"
     540             :                                     "\x00"
     541             :                                     "\x00"
     542             :                                     "\x18"
     543             :                                     "ftypmif1"
     544             :                                     "\x00"
     545             :                                     "\x00"
     546             :                                     "\x00"
     547             :                                     "\x00"
     548             :                                     "mif1heic";
     549             :     return (poOpenInfo->nHeaderBytes >= static_cast<int>(sizeof(abySig1)) &&
     550             :             memcmp(poOpenInfo->pabyHeader, abySig1, sizeof(abySig1)) == 0) ||
     551             :            (poOpenInfo->nHeaderBytes >= static_cast<int>(sizeof(abySig2)) &&
     552             :             memcmp(poOpenInfo->pabyHeader, abySig2, sizeof(abySig2)) == 0) ||
     553             :            (poOpenInfo->nHeaderBytes >= static_cast<int>(sizeof(abySig3)) &&
     554             :             memcmp(poOpenInfo->pabyHeader, abySig3, sizeof(abySig3)) == 0);
     555             : #endif
     556             : }
     557             : 
     558             : /************************************************************************/
     559             : /*                              Open()                                  */
     560             : /************************************************************************/
     561             : 
     562          15 : GDALDataset *GDALHEIFDataset::Open(GDALOpenInfo *poOpenInfo)
     563             : {
     564          15 :     if (!HEIFDriverIdentify(poOpenInfo))
     565           0 :         return nullptr;
     566          15 :     if (poOpenInfo->eAccess == GA_Update)
     567             :     {
     568           0 :         CPLError(CE_Failure, CPLE_NotSupported,
     569             :                  "Update of existing HEIF file not supported");
     570           0 :         return nullptr;
     571             :     }
     572             : 
     573          30 :     auto poDS = std::make_unique<GDALHEIFDataset>();
     574          15 :     if (!poDS->Init(poOpenInfo))
     575           6 :         return nullptr;
     576             : 
     577           9 :     return poDS.release();
     578             : }
     579             : 
     580             : /************************************************************************/
     581             : /*                          GDALHEIFRasterBand()                        */
     582             : /************************************************************************/
     583             : 
     584          37 : GDALHEIFRasterBand::GDALHEIFRasterBand(GDALHEIFDataset *poDSIn, int nBandIn)
     585             : {
     586          37 :     poDS = poDSIn;
     587          37 :     nBand = nBandIn;
     588             : 
     589          37 :     eDataType = GDT_Byte;
     590             : #if LIBHEIF_NUMERIC_VERSION >= BUILD_LIBHEIF_VERSION(1, 4, 0)
     591             :     const int nBits =
     592          37 :         heif_image_handle_get_luma_bits_per_pixel(poDSIn->m_hImageHandle);
     593          37 :     if (nBits > 8)
     594             :     {
     595           7 :         eDataType = GDT_UInt16;
     596             :     }
     597          37 :     if (nBits != 8 && nBits != 16)
     598             :     {
     599           7 :         GDALRasterBand::SetMetadataItem("NBITS", CPLSPrintf("%d", nBits),
     600             :                                         "IMAGE_STRUCTURE");
     601             :     }
     602             : #endif
     603          37 :     nBlockXSize = poDS->GetRasterXSize();
     604          37 :     nBlockYSize = 1;
     605          37 : }
     606             : 
     607             : /************************************************************************/
     608             : /*                            IReadBlock()                              */
     609             : /************************************************************************/
     610             : 
     611         480 : CPLErr GDALHEIFRasterBand::IReadBlock(int, int nBlockYOff, void *pImage)
     612             : {
     613         480 :     GDALHEIFDataset *poGDS = static_cast<GDALHEIFDataset *>(poDS);
     614         480 :     if (poGDS->m_bFailureDecoding)
     615           0 :         return CE_Failure;
     616         480 :     const int nBands = poGDS->GetRasterCount();
     617         480 :     if (poGDS->m_hImage == nullptr)
     618             :     {
     619             :         auto err = heif_decode_image(
     620           5 :             poGDS->m_hImageHandle, &(poGDS->m_hImage), heif_colorspace_RGB,
     621             :             nBands == 3
     622             :                 ? (
     623             : #if LIBHEIF_NUMERIC_VERSION >= BUILD_LIBHEIF_VERSION(1, 4, 0)
     624           4 :                       eDataType == GDT_UInt16 ?
     625             : #if CPL_IS_LSB
     626             :                                               heif_chroma_interleaved_RRGGBB_LE
     627             : #else
     628             :                                               heif_chroma_interleaved_RRGGBB_BE
     629             : #endif
     630             :                                               :
     631             : #endif
     632             :                                               heif_chroma_interleaved_RGB)
     633             :                 : (
     634             : #if LIBHEIF_NUMERIC_VERSION >= BUILD_LIBHEIF_VERSION(1, 4, 0)
     635           1 :                       eDataType == GDT_UInt16
     636           1 :                           ?
     637             : #if CPL_IS_LSB
     638             :                           heif_chroma_interleaved_RRGGBBAA_LE
     639             : #else
     640             :                           heif_chroma_interleaved_RRGGBBAA_BE
     641             : #endif
     642             :                           :
     643             : #endif
     644             :                           heif_chroma_interleaved_RGBA),
     645          10 :             nullptr);
     646           5 :         if (err.code != heif_error_Ok)
     647             :         {
     648           0 :             CPLError(CE_Failure, CPLE_AppDefined, "%s",
     649           0 :                      err.message ? err.message : "Cannot decode image");
     650           0 :             poGDS->m_bFailureDecoding = true;
     651           0 :             return CE_Failure;
     652             :         }
     653          10 :         const int nBitsPerPixel = heif_image_get_bits_per_pixel(
     654           5 :             poGDS->m_hImage, heif_channel_interleaved);
     655           5 :         if (nBitsPerPixel != nBands * GDALGetDataTypeSize(eDataType))
     656             :         {
     657           0 :             CPLError(CE_Failure, CPLE_AppDefined,
     658             :                      "Unexpected bits_per_pixel = %d value", nBitsPerPixel);
     659           0 :             poGDS->m_bFailureDecoding = true;
     660           0 :             return CE_Failure;
     661             :         }
     662             :     }
     663             : 
     664         480 :     int nStride = 0;
     665         960 :     const uint8_t *pSrcData = heif_image_get_plane_readonly(
     666         480 :         poGDS->m_hImage, heif_channel_interleaved, &nStride);
     667         480 :     pSrcData += nBlockYOff * nStride;
     668         480 :     if (eDataType == GDT_Byte)
     669             :     {
     670       21016 :         for (int i = 0; i < nBlockXSize; i++)
     671       20736 :             (static_cast<GByte *>(pImage))[i] =
     672       20736 :                 pSrcData[nBand - 1 + i * nBands];
     673             :     }
     674             :     else
     675             :     {
     676       80200 :         for (int i = 0; i < nBlockXSize; i++)
     677       80000 :             (static_cast<GUInt16 *>(pImage))[i] =
     678             :                 (reinterpret_cast<const GUInt16 *>(
     679       80000 :                     pSrcData))[nBand - 1 + i * nBands];
     680             :     }
     681             : 
     682         480 :     return CE_None;
     683             : }
     684             : 
     685             : /************************************************************************/
     686             : /*                       GDALRegister_HEIF()                            */
     687             : /************************************************************************/
     688             : 
     689           6 : void GDALRegister_HEIF()
     690             : 
     691             : {
     692           6 :     if (!GDAL_CHECK_VERSION("HEIF driver"))
     693           0 :         return;
     694             : 
     695           6 :     if (GDALGetDriverByName(DRIVER_NAME) != nullptr)
     696           0 :         return;
     697             : 
     698           6 :     GDALDriver *poDriver = new GDALDriver();
     699           6 :     HEIFDriverSetCommonMetadata(poDriver);
     700             : 
     701           6 :     poDriver->pfnOpen = GDALHEIFDataset::Open;
     702             : 
     703           6 :     GetGDALDriverManager()->RegisterDriver(poDriver);
     704             : }

Generated by: LCOV version 1.14