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

Generated by: LCOV version 1.14