LCOV - code coverage report
Current view: top level - frmts/basisu_ktx2 - basisudataset.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 143 156 91.7 %
Date: 2025-10-01 17:07:58 Functions: 12 12 100.0 %

          Line data    Source code
       1             : /******************************************************************************
       2             :  *
       3             :  * Project:  OpenGIS Simple Features Reference Implementation
       4             :  * Purpose:  Implements Basis Universal / BASISU driver.
       5             :  * Author:   Even Rouault, <even dot rouault at spatialys.com>
       6             :  *
       7             :  ******************************************************************************
       8             :  * Copyright (c) 2022, Even Rouault <even dot rouault at spatialys.com>
       9             :  *
      10             :  * SPDX-License-Identifier: MIT
      11             :  ****************************************************************************/
      12             : 
      13             : #include "gdal_frmts.h"
      14             : #include "gdal_pam.h"
      15             : #include "common.h"
      16             : #include "include_basisu_sdk.h"
      17             : #include "basisudrivercore.h"
      18             : 
      19             : #include <algorithm>
      20             : #include <cstdlib>
      21             : #include <limits>
      22             : 
      23             : /************************************************************************/
      24             : /*                           BASISUDataset                              */
      25             : /************************************************************************/
      26             : 
      27             : class BASISUDataset final : public GDALPamDataset
      28             : {
      29             :     friend class BASISURasterBand;
      30             : 
      31             :     basist::basisu_transcoder m_transcoder{};
      32             :     basist::basisu_transcoder &m_transcoderRef;
      33             :     bool m_bHasDecodeRun = false;
      34             :     void *m_pEncodedData = nullptr;
      35             :     uint32_t m_nEncodedDataSize = 0;
      36             :     void *m_pDecodedData = nullptr;
      37             :     uint32_t m_nLineStride = 0;
      38             :     BASISUDataset *m_poParent = nullptr;
      39             :     uint32_t m_iImageIdx = 0;
      40             :     uint32_t m_iLevel = 0;
      41             :     std::vector<std::unique_ptr<BASISUDataset>> m_apoOverviewsDS{};
      42             : 
      43             :     void *GetDecodedData(uint32_t &nLineStride);
      44             : 
      45             :     CPL_DISALLOW_COPY_ASSIGN(BASISUDataset)
      46             : 
      47             :   public:
      48             :     ~BASISUDataset() override;
      49             :     BASISUDataset(uint32_t iImageIdx, void *pEncodedData,
      50             :                   uint32_t nEncodedDataSize);
      51             :     BASISUDataset(BASISUDataset *poParent, uint32_t iLevel);
      52             : 
      53             :     static GDALDataset *Open(GDALOpenInfo *poOpenInfo);
      54             :     static GDALDataset *CreateCopy(const char *pszFilename,
      55             :                                    GDALDataset *poSrcDS, int bStrict,
      56             :                                    char **papszOptions,
      57             :                                    GDALProgressFunc pfnProgress,
      58             :                                    void *pProgressData);
      59             : };
      60             : 
      61             : /************************************************************************/
      62             : /*                          BASISURasterBand                            */
      63             : /************************************************************************/
      64             : 
      65             : class BASISURasterBand final : public GDALPamRasterBand
      66             : {
      67             :   protected:
      68             :     CPLErr IReadBlock(int nBlockXOff, int nBlockYOff, void *pImage) override;
      69             : 
      70             :   public:
      71             :     BASISURasterBand(BASISUDataset *poDSIn, int nBandIn);
      72             : 
      73             :     int GetOverviewCount() override;
      74             :     GDALRasterBand *GetOverview(int nIdx) override;
      75             : };
      76             : 
      77             : /************************************************************************/
      78             : /*                           BASISUDataset()                            */
      79             : /************************************************************************/
      80             : 
      81          44 : BASISUDataset::BASISUDataset(uint32_t iImageIdx, void *pEncodedData,
      82          44 :                              uint32_t nEncodedDataSize)
      83          44 :     : m_transcoderRef(m_transcoder), m_pEncodedData(pEncodedData),
      84          44 :       m_nEncodedDataSize(nEncodedDataSize), m_iImageIdx(iImageIdx)
      85             : {
      86          44 : }
      87             : 
      88             : /************************************************************************/
      89             : /*                           BASISUDataset()                            */
      90             : /************************************************************************/
      91             : 
      92           7 : BASISUDataset::BASISUDataset(BASISUDataset *poParent, uint32_t iLevel)
      93           7 :     : m_transcoderRef(poParent->m_transcoderRef), m_poParent(poParent),
      94           7 :       m_iImageIdx(poParent->m_iImageIdx), m_iLevel(iLevel)
      95             : {
      96             :     basist::basisu_image_level_info level_info;
      97           7 :     CPL_IGNORE_RET_VAL(m_transcoderRef.get_image_level_info(
      98           7 :         m_poParent->m_pEncodedData, m_poParent->m_nEncodedDataSize, level_info,
      99             :         m_iImageIdx, m_iLevel));
     100           7 :     nRasterXSize = static_cast<int>(level_info.m_orig_width);
     101           7 :     nRasterYSize = static_cast<int>(level_info.m_orig_height);
     102           7 : }
     103             : 
     104             : /************************************************************************/
     105             : /*                           ~BASISUDataset()                           */
     106             : /************************************************************************/
     107             : 
     108         102 : BASISUDataset::~BASISUDataset()
     109             : {
     110          51 :     VSIFree(m_pEncodedData);
     111          51 :     VSIFree(m_pDecodedData);
     112         102 : }
     113             : 
     114             : /************************************************************************/
     115             : /*                        GetDecodedData()                              */
     116             : /************************************************************************/
     117             : 
     118        2220 : void *BASISUDataset::GetDecodedData(uint32_t &nLineStride)
     119             : {
     120        2220 :     if (m_bHasDecodeRun)
     121             :     {
     122        2214 :         nLineStride = m_nLineStride;
     123        2214 :         return m_pDecodedData;
     124             :     }
     125           6 :     m_bHasDecodeRun = true;
     126             : 
     127           6 :     GDALInitBasisUTranscoder();
     128             : 
     129             :     basist::basisu_image_level_info level_info;
     130           6 :     const auto poRefDS = m_poParent ? m_poParent : this;
     131           6 :     CPL_IGNORE_RET_VAL(m_transcoderRef.get_image_level_info(
     132           6 :         poRefDS->m_pEncodedData, poRefDS->m_nEncodedDataSize, level_info,
     133             :         m_iImageIdx, m_iLevel));
     134             : 
     135           6 :     if (!m_transcoderRef.start_transcoding(poRefDS->m_pEncodedData,
     136             :                                            poRefDS->m_nEncodedDataSize))
     137             :     {
     138           0 :         CPLError(CE_Failure, CPLE_AppDefined,
     139             :                  "basisu_transcoder::start_transcoding() failed!");
     140           0 :         return nullptr;
     141             :     }
     142             : 
     143           6 :     m_pDecodedData = VSI_MALLOC3_VERBOSE(level_info.m_orig_width,
     144             :                                          level_info.m_orig_height, 4);
     145           6 :     if (m_pDecodedData == nullptr)
     146           0 :         return nullptr;
     147             : 
     148           6 :     constexpr basist::transcoder_texture_format transcoder_tex_fmt =
     149             :         basist::transcoder_texture_format::cTFRGBA32;
     150           6 :     if (!m_transcoderRef.transcode_image_level(
     151           6 :             poRefDS->m_pEncodedData, poRefDS->m_nEncodedDataSize, m_iImageIdx,
     152             :             m_iLevel, m_pDecodedData,
     153           6 :             level_info.m_orig_width * level_info.m_orig_height * 4,
     154             :             transcoder_tex_fmt))
     155             :     {
     156           0 :         CPLError(CE_Failure, CPLE_AppDefined,
     157             :                  "basisu_transcoder::transcode_image_level() failed!");
     158           0 :         VSIFree(m_pDecodedData);
     159           0 :         m_pDecodedData = nullptr;
     160           0 :         return nullptr;
     161             :     }
     162             : 
     163           6 :     m_nLineStride = level_info.m_orig_width * 4;
     164           6 :     nLineStride = m_nLineStride;
     165           6 :     return m_pDecodedData;
     166             : }
     167             : 
     168             : /************************************************************************/
     169             : /*                           BASISURasterBand()                         */
     170             : /************************************************************************/
     171             : 
     172         188 : BASISURasterBand::BASISURasterBand(BASISUDataset *poDSIn, int nBandIn)
     173             : {
     174         188 :     poDS = poDSIn;
     175         188 :     nBand = nBandIn;
     176         188 :     nRasterXSize = poDSIn->GetRasterXSize();
     177         188 :     nRasterYSize = poDSIn->GetRasterYSize();
     178         188 :     nBlockXSize = nRasterXSize;
     179         188 :     nBlockYSize = 1;
     180         188 :     eDataType = GDT_Byte;
     181         188 :     SetColorInterpretation(
     182         188 :         static_cast<GDALColorInterp>(GCI_RedBand + nBandIn - 1));
     183         188 : }
     184             : 
     185             : /************************************************************************/
     186             : /*                             IReadBlock()                             */
     187             : /************************************************************************/
     188             : 
     189        2220 : CPLErr BASISURasterBand::IReadBlock(int /*nBlockXOff*/, int nBlockYOff,
     190             :                                     void *pImage)
     191             : {
     192        2220 :     auto poGDS = cpl::down_cast<BASISUDataset *>(poDS);
     193        2220 :     uint32_t nLineStride = 0;
     194        2220 :     void *decoded_data = poGDS->GetDecodedData(nLineStride);
     195        2220 :     if (decoded_data == nullptr)
     196           0 :         return CE_Failure;
     197             : 
     198        2220 :     GDALCopyWords(static_cast<GByte *>(decoded_data) +
     199        2220 :                       nBlockYOff * nLineStride + nBand - 1,
     200             :                   GDT_Byte, 4, pImage, GDT_Byte, 1, nBlockXSize);
     201        2220 :     return CE_None;
     202             : }
     203             : 
     204             : /************************************************************************/
     205             : /*                           GetOverviewCount()                         */
     206             : /************************************************************************/
     207             : 
     208           6 : int BASISURasterBand::GetOverviewCount()
     209             : {
     210           6 :     auto poGDS = cpl::down_cast<BASISUDataset *>(poDS);
     211           6 :     return static_cast<int>(poGDS->m_apoOverviewsDS.size());
     212             : }
     213             : 
     214             : /************************************************************************/
     215             : /*                             GetOverview()                            */
     216             : /************************************************************************/
     217             : 
     218           3 : GDALRasterBand *BASISURasterBand::GetOverview(int nIdx)
     219             : {
     220           3 :     if (nIdx < 0 || nIdx >= GetOverviewCount())
     221           2 :         return nullptr;
     222           1 :     auto poGDS = cpl::down_cast<BASISUDataset *>(poDS);
     223           1 :     return poGDS->m_apoOverviewsDS[nIdx]->GetRasterBand(nBand);
     224             : }
     225             : 
     226             : /************************************************************************/
     227             : /*                                Open()                                */
     228             : /************************************************************************/
     229             : 
     230          47 : GDALDataset *BASISUDataset::Open(GDALOpenInfo *poOpenInfo)
     231             : {
     232          47 :     if (!BASISUDriverIdentify(poOpenInfo) || poOpenInfo->eAccess == GA_Update)
     233           0 :         return nullptr;
     234             : 
     235          47 :     VSILFILE *fpL = nullptr;
     236          47 :     uint32_t nImageIdx = static_cast<uint32_t>(-1);
     237          47 :     if (STARTS_WITH(poOpenInfo->pszFilename, "BASISU:"))
     238             :     {
     239             :         const CPLStringList aosTokens(CSLTokenizeString2(
     240           6 :             poOpenInfo->pszFilename, ":", CSLT_HONOURSTRINGS));
     241           6 :         if (aosTokens.size() != 3)
     242           2 :             return nullptr;
     243           4 :         fpL = VSIFOpenL(aosTokens[1], "rb");
     244           4 :         if (fpL == nullptr)
     245             :         {
     246           1 :             CPLError(CE_Failure, CPLE_FileIO, "Cannot open %s", aosTokens[1]);
     247           1 :             return nullptr;
     248             :         }
     249           3 :         nImageIdx = static_cast<uint32_t>(atoi(aosTokens[2]));
     250             :     }
     251          44 :     GIntBig nMaxSize = std::strtoull(
     252          44 :         CPLGetConfigOption("BASISU_MAX_FILE_SIZE", "0"), nullptr, 10);
     253          44 :     constexpr GIntBig BASISU_LIMIT = std::numeric_limits<uint32_t>::max();
     254          44 :     if (nMaxSize == 0 || nMaxSize > BASISU_LIMIT)
     255          44 :         nMaxSize = BASISU_LIMIT;
     256          44 :     GByte *pabyRet = nullptr;
     257          44 :     vsi_l_offset nSizeLarge = 0;
     258          44 :     int nRet = VSIIngestFile(fpL ? fpL : poOpenInfo->fpL, nullptr, &pabyRet,
     259             :                              &nSizeLarge, nMaxSize);
     260          44 :     if (fpL != nullptr)
     261           3 :         VSIFCloseL(fpL);
     262          44 :     if (!nRet)
     263             :     {
     264           0 :         return nullptr;
     265             :     }
     266          44 :     const uint32_t nSize = static_cast<uint32_t>(nSizeLarge);
     267             : 
     268             :     auto poDS = std::make_unique<BASISUDataset>(
     269          88 :         nImageIdx != static_cast<uint32_t>(-1) ? nImageIdx : 0, pabyRet, nSize);
     270          44 :     auto &transcoder = poDS->m_transcoder;
     271          88 :     basist::basisu_file_info file_info;
     272          44 :     if (!transcoder.get_file_info(pabyRet, nSize, file_info))
     273             :     {
     274           0 :         CPLError(CE_Failure, CPLE_AppDefined,
     275             :                  "basisu_transcoder::get_file_info() failed! "
     276             :                  "File either uses an unsupported feature or is invalid");
     277           0 :         return nullptr;
     278             :     }
     279          44 :     if (nImageIdx == static_cast<uint32_t>(-1) && file_info.m_total_images > 1)
     280             :     {
     281           2 :         CPLStringList aosSubdatasets;
     282           3 :         for (uint32_t iImageIdx = 0; iImageIdx < file_info.m_total_images;
     283             :              ++iImageIdx)
     284             :         {
     285             :             aosSubdatasets.SetNameValue(
     286             :                 CPLSPrintf("SUBDATASET_%d_NAME", iImageIdx + 1),
     287             :                 CPLSPrintf("BASISU:\"%s\":%u", poOpenInfo->pszFilename,
     288           2 :                            iImageIdx));
     289             :             aosSubdatasets.SetNameValue(
     290             :                 CPLSPrintf("SUBDATASET_%d_DESC", iImageIdx + 1),
     291             :                 CPLSPrintf("Image %u of %s", iImageIdx,
     292           2 :                            poOpenInfo->pszFilename));
     293             :         }
     294           1 :         poDS->nRasterXSize = 0;
     295           1 :         poDS->nRasterYSize = 0;
     296           1 :         poDS->SetMetadata(aosSubdatasets.List(), "SUBDATASETS");
     297             : 
     298           1 :         poDS->SetPamFlags(poDS->GetPamFlags() & ~GPF_DIRTY);
     299             : 
     300           1 :         return poDS.release();
     301             :     }
     302             : 
     303             :     basist::basisu_image_info image_info;
     304          43 :     if (!transcoder.get_image_info(pabyRet, nSize, image_info,
     305          43 :                                    poDS->m_iImageIdx))
     306             :     {
     307           1 :         CPLError(CE_Failure, CPLE_AppDefined,
     308             :                  "basisu_transcoder::get_image_info() failed");
     309           1 :         return nullptr;
     310             :     }
     311          42 :     poDS->nRasterXSize = static_cast<int>(image_info.m_orig_width);
     312          42 :     poDS->nRasterYSize = static_cast<int>(image_info.m_orig_height);
     313             : 
     314          42 :     switch (file_info.m_tex_format)
     315             :     {
     316          31 :         case basist::basis_tex_format::cETC1S:
     317          31 :             poDS->SetMetadataItem("COMPRESSION", "ETC1S", "IMAGE_STRUCTURE");
     318          31 :             break;
     319          11 :         case basist::basis_tex_format::cUASTC4x4:
     320          11 :             poDS->SetMetadataItem("COMPRESSION", "UASTC", "IMAGE_STRUCTURE");
     321          11 :             break;
     322             :     }
     323             : 
     324          42 :     const int l_nBands = 3 + (image_info.m_alpha_flag ? 1 : 0);
     325         202 :     for (int i = 1; i <= l_nBands; ++i)
     326             :     {
     327         160 :         poDS->SetBand(i, new BASISURasterBand(poDS.get(), i));
     328             :     }
     329             : 
     330          42 :     const uint32_t nLevels = file_info.m_image_mipmap_levels[poDS->m_iImageIdx];
     331          49 :     for (uint32_t level_index = 1; level_index < nLevels; ++level_index)
     332             :     {
     333             :         basist::basisu_image_level_info level_info;
     334           7 :         if (transcoder.get_image_level_info(pabyRet, nSize, level_info,
     335           7 :                                             poDS->m_iImageIdx, level_index))
     336             :         {
     337             :             auto poOverviewDS =
     338          14 :                 std::make_unique<BASISUDataset>(poDS.get(), level_index);
     339          35 :             for (int i = 1; i <= l_nBands; ++i)
     340             :             {
     341          56 :                 poOverviewDS->SetBand(
     342          28 :                     i, new BASISURasterBand(poOverviewDS.get(), i));
     343             :             }
     344           7 :             poDS->m_apoOverviewsDS.emplace_back(std::move(poOverviewDS));
     345             :         }
     346             :     }
     347             : 
     348          42 :     poDS->SetPamFlags(poDS->GetPamFlags() & ~GPF_DIRTY);
     349             : 
     350             :     // Initialize any PAM information.
     351          42 :     poDS->SetDescription(poOpenInfo->pszFilename);
     352          42 :     poDS->TryLoadXML(poOpenInfo->GetSiblingFiles());
     353             : 
     354          42 :     return poDS.release();
     355             : }
     356             : 
     357             : /************************************************************************/
     358             : /*                            CreateCopy()                              */
     359             : /************************************************************************/
     360             : 
     361          54 : GDALDataset *BASISUDataset::CreateCopy(const char *pszFilename,
     362             :                                        GDALDataset *poSrcDS, int /*bStrict*/,
     363             :                                        char **papszOptions,
     364             :                                        GDALProgressFunc pfnProgress,
     365             :                                        void *pProgressData)
     366             : {
     367          54 :     if (!GDAL_KTX2_BASISU_CreateCopy(pszFilename, poSrcDS,
     368             :                                      false,  // bIsKTX2
     369             :                                      papszOptions, pfnProgress, pProgressData))
     370             :     {
     371          31 :         return nullptr;
     372             :     }
     373          46 :     GDALOpenInfo oOpenInfo(pszFilename, GA_ReadOnly);
     374          23 :     return Open(&oOpenInfo);
     375             : }
     376             : 
     377             : /************************************************************************/
     378             : /*                        GDALRegister_BASISU()                         */
     379             : /************************************************************************/
     380             : 
     381          11 : void GDALRegister_BASISU()
     382             : {
     383          11 :     if (GDALGetDriverByName(BASISU_DRIVER_NAME) != nullptr)
     384           0 :         return;
     385             : 
     386          11 :     GDALDriver *poDriver = new GDALDriver();
     387          11 :     BASISUDriverSetCommonMetadata(poDriver);
     388             : 
     389          11 :     poDriver->pfnOpen = BASISUDataset::Open;
     390          11 :     poDriver->pfnCreateCopy = BASISUDataset::CreateCopy;
     391             : 
     392          11 :     GetGDALDriverManager()->RegisterDriver(poDriver);
     393             : }

Generated by: LCOV version 1.14