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

Generated by: LCOV version 1.14