LCOV - code coverage report
Current view: top level - frmts/basisu_ktx2 - ktx2dataset.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 151 178 84.8 %
Date: 2024-05-15 13:15:52 Functions: 12 12 100.0 %

          Line data    Source code
       1             : /******************************************************************************
       2             :  *
       3             :  * Project:  OpenGIS Simple Features Reference Implementation
       4             :  * Purpose:  Implements Basis Universal / KTX2 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 "ktx2drivercore.h"
      33             : 
      34             : #include <algorithm>
      35             : #include <cstdlib>
      36             : #include <limits>
      37             : 
      38             : /************************************************************************/
      39             : /*                            KTX2Dataset                               */
      40             : /************************************************************************/
      41             : 
      42             : class KTX2Dataset final : public GDALPamDataset
      43             : {
      44             :     friend class KTX2RasterBand;
      45             : 
      46             :     basist::ktx2_transcoder m_transcoder{};
      47             :     basist::ktx2_transcoder &m_transcoderRef;
      48             :     bool m_bHasDecodeRun = false;
      49             :     void *m_pEncodedData = nullptr;
      50             :     void *m_pDecodedData = nullptr;
      51             :     uint32_t m_nLineStride = 0;
      52             :     uint32_t m_iLayer = 0;
      53             :     uint32_t m_iFace = 0;
      54             :     uint32_t m_iLevel = 0;
      55             :     std::vector<std::unique_ptr<KTX2Dataset>> m_apoOverviewsDS{};
      56             : 
      57             :     void *GetDecodedData(uint32_t &nLineStride);
      58             : 
      59             :     CPL_DISALLOW_COPY_ASSIGN(KTX2Dataset)
      60             : 
      61             :   public:
      62             :     ~KTX2Dataset() override;
      63             :     KTX2Dataset(uint32_t iLayer, uint32_t iFace, void *pEncodedData);
      64             :     KTX2Dataset(KTX2Dataset *poParent, uint32_t iLevel);
      65             : 
      66             :     static GDALDataset *Open(GDALOpenInfo *poOpenInfo);
      67             :     static GDALDataset *CreateCopy(const char *pszFilename,
      68             :                                    GDALDataset *poSrcDS, int bStrict,
      69             :                                    char **papszOptions,
      70             :                                    GDALProgressFunc pfnProgress,
      71             :                                    void *pProgressData);
      72             : };
      73             : 
      74             : /************************************************************************/
      75             : /*                            KTX2RasterBand                            */
      76             : /************************************************************************/
      77             : 
      78             : class KTX2RasterBand final : public GDALPamRasterBand
      79             : {
      80             :   protected:
      81             :     CPLErr IReadBlock(int nBlockXOff, int nBlockYOff, void *pImage) override;
      82             : 
      83             :   public:
      84             :     KTX2RasterBand(KTX2Dataset *poDSIn, int nBandIn);
      85             : 
      86             :     int GetOverviewCount() override;
      87             :     GDALRasterBand *GetOverview(int nIdx) override;
      88             : };
      89             : 
      90             : /************************************************************************/
      91             : /*                           KTX2Dataset()                              */
      92             : /************************************************************************/
      93             : 
      94          49 : KTX2Dataset::KTX2Dataset(uint32_t iLayer, uint32_t iFace, void *pEncodedData)
      95          49 :     : m_transcoderRef(m_transcoder), m_pEncodedData(pEncodedData),
      96          49 :       m_iLayer(iLayer), m_iFace(iFace)
      97             : {
      98          49 : }
      99             : 
     100             : /************************************************************************/
     101             : /*                           KTX2Dataset()                              */
     102             : /************************************************************************/
     103             : 
     104           7 : KTX2Dataset::KTX2Dataset(KTX2Dataset *poParent, uint32_t iLevel)
     105           7 :     : m_transcoderRef(poParent->m_transcoderRef), m_iLayer(poParent->m_iLayer),
     106           7 :       m_iFace(poParent->m_iFace), m_iLevel(iLevel)
     107             : {
     108             :     basist::ktx2_image_level_info level_info;
     109           7 :     CPL_IGNORE_RET_VAL(m_transcoderRef.get_image_level_info(
     110             :         level_info, m_iLevel, m_iLayer, m_iFace));
     111           7 :     nRasterXSize = static_cast<int>(level_info.m_orig_width);
     112           7 :     nRasterYSize = static_cast<int>(level_info.m_orig_height);
     113           7 : }
     114             : 
     115             : /************************************************************************/
     116             : /*                           ~KTX2Dataset()                             */
     117             : /************************************************************************/
     118             : 
     119         112 : KTX2Dataset::~KTX2Dataset()
     120             : {
     121          56 :     VSIFree(m_pEncodedData);
     122          56 :     VSIFree(m_pDecodedData);
     123         112 : }
     124             : 
     125             : /************************************************************************/
     126             : /*                        GetDecodedData()                              */
     127             : /************************************************************************/
     128             : 
     129        2220 : void *KTX2Dataset::GetDecodedData(uint32_t &nLineStride)
     130             : {
     131        2220 :     if (m_bHasDecodeRun)
     132             :     {
     133        2214 :         nLineStride = m_nLineStride;
     134        2214 :         return m_pDecodedData;
     135             :     }
     136           6 :     m_bHasDecodeRun = true;
     137             : 
     138           6 :     GDALInitBasisUTranscoder();
     139             : 
     140             :     basist::ktx2_image_level_info level_info;
     141           6 :     if (!m_transcoderRef.get_image_level_info(level_info, m_iLevel, m_iLayer,
     142             :                                               m_iFace))
     143             :     {
     144           0 :         CPLError(CE_Failure, CPLE_AppDefined,
     145             :                  "ktx2_transcoder::get_image_level_info() failed!");
     146           0 :         return nullptr;
     147             :     }
     148             : 
     149           6 :     if (!m_transcoderRef.start_transcoding())
     150             :     {
     151           0 :         CPLError(CE_Failure, CPLE_AppDefined,
     152             :                  "ktx2_transcoder::start_transcoding() failed!");
     153           0 :         return nullptr;
     154             :     }
     155             : 
     156           6 :     m_pDecodedData = VSI_MALLOC3_VERBOSE(level_info.m_orig_width,
     157             :                                          level_info.m_orig_height, 4);
     158           6 :     if (m_pDecodedData == nullptr)
     159           0 :         return nullptr;
     160             : 
     161           6 :     constexpr basist::transcoder_texture_format transcoder_tex_fmt =
     162             :         basist::transcoder_texture_format::cTFRGBA32;
     163           6 :     if (!m_transcoderRef.transcode_image_level(
     164             :             m_iLevel, m_iLayer, m_iFace, m_pDecodedData,
     165           6 :             level_info.m_orig_width * level_info.m_orig_height * 4,
     166             :             transcoder_tex_fmt))
     167             :     {
     168           0 :         CPLError(CE_Failure, CPLE_AppDefined,
     169             :                  "ktx2_transcoder::transcode_image_level() failed!");
     170           0 :         VSIFree(m_pDecodedData);
     171           0 :         m_pDecodedData = nullptr;
     172           0 :         return nullptr;
     173             :     }
     174             : 
     175           6 :     m_nLineStride = level_info.m_orig_width * 4;
     176           6 :     nLineStride = m_nLineStride;
     177           6 :     return m_pDecodedData;
     178             : }
     179             : 
     180             : /************************************************************************/
     181             : /*                           KTX2RasterBand()                           */
     182             : /************************************************************************/
     183             : 
     184         204 : KTX2RasterBand::KTX2RasterBand(KTX2Dataset *poDSIn, int nBandIn)
     185             : {
     186         204 :     poDS = poDSIn;
     187         204 :     nBand = nBandIn;
     188         204 :     nRasterXSize = poDSIn->GetRasterXSize();
     189         204 :     nRasterYSize = poDSIn->GetRasterYSize();
     190         204 :     nBlockXSize = nRasterXSize;
     191         204 :     nBlockYSize = 1;
     192         204 :     eDataType = GDT_Byte;
     193         204 :     SetColorInterpretation(
     194         204 :         static_cast<GDALColorInterp>(GCI_RedBand + nBandIn - 1));
     195         204 : }
     196             : 
     197             : /************************************************************************/
     198             : /*                             IReadBlock()                             */
     199             : /************************************************************************/
     200             : 
     201        2220 : CPLErr KTX2RasterBand::IReadBlock(int /*nBlockXOff*/, int nBlockYOff,
     202             :                                   void *pImage)
     203             : {
     204        2220 :     auto poGDS = cpl::down_cast<KTX2Dataset *>(poDS);
     205        2220 :     uint32_t nLineStride = 0;
     206        2220 :     void *decoded_data = poGDS->GetDecodedData(nLineStride);
     207        2220 :     if (decoded_data == nullptr)
     208           0 :         return CE_Failure;
     209             : 
     210        2220 :     GDALCopyWords(static_cast<GByte *>(decoded_data) +
     211        2220 :                       nBlockYOff * nLineStride + nBand - 1,
     212             :                   GDT_Byte, 4, pImage, GDT_Byte, 1, nBlockXSize);
     213        2220 :     return CE_None;
     214             : }
     215             : 
     216             : /************************************************************************/
     217             : /*                           GetOverviewCount()                         */
     218             : /************************************************************************/
     219             : 
     220           6 : int KTX2RasterBand::GetOverviewCount()
     221             : {
     222           6 :     auto poGDS = cpl::down_cast<KTX2Dataset *>(poDS);
     223           6 :     return static_cast<int>(poGDS->m_apoOverviewsDS.size());
     224             : }
     225             : 
     226             : /************************************************************************/
     227             : /*                             GetOverview()                            */
     228             : /************************************************************************/
     229             : 
     230           3 : GDALRasterBand *KTX2RasterBand::GetOverview(int nIdx)
     231             : {
     232           3 :     if (nIdx < 0 || nIdx >= GetOverviewCount())
     233           2 :         return nullptr;
     234           1 :     auto poGDS = cpl::down_cast<KTX2Dataset *>(poDS);
     235           1 :     return poGDS->m_apoOverviewsDS[nIdx]->GetRasterBand(nBand);
     236             : }
     237             : 
     238             : /************************************************************************/
     239             : /*                                Open()                                */
     240             : /************************************************************************/
     241             : 
     242          53 : GDALDataset *KTX2Dataset::Open(GDALOpenInfo *poOpenInfo)
     243             : {
     244          53 :     if (!KTX2DriverIdentify(poOpenInfo) || poOpenInfo->eAccess == GA_Update)
     245           0 :         return nullptr;
     246             : 
     247          53 :     VSILFILE *fpL = nullptr;
     248          53 :     uint32_t nLayer = static_cast<uint32_t>(-1);
     249          53 :     uint32_t nFace = static_cast<uint32_t>(-1);
     250          53 :     if (STARTS_WITH(poOpenInfo->pszFilename, "KTX2:"))
     251             :     {
     252             :         const CPLStringList aosTokens(CSLTokenizeString2(
     253           8 :             poOpenInfo->pszFilename, ":", CSLT_HONOURSTRINGS));
     254           8 :         if (aosTokens.size() != 4)
     255           3 :             return nullptr;
     256           5 :         fpL = VSIFOpenL(aosTokens[1], "rb");
     257           5 :         if (fpL == nullptr)
     258             :         {
     259           1 :             CPLError(CE_Failure, CPLE_FileIO, "Cannot open %s", aosTokens[1]);
     260           1 :             return nullptr;
     261             :         }
     262           4 :         nLayer = static_cast<uint32_t>(atoi(aosTokens[2]));
     263           4 :         nFace = static_cast<uint32_t>(atoi(aosTokens[3]));
     264             :     }
     265          49 :     GIntBig nMaxSize = std::strtoull(
     266          49 :         CPLGetConfigOption("KTX2_MAX_FILE_SIZE", "0"), nullptr, 10);
     267          49 :     constexpr GIntBig KTX2_LIMIT = std::numeric_limits<uint32_t>::max();
     268          49 :     if (nMaxSize == 0 || nMaxSize > KTX2_LIMIT)
     269          49 :         nMaxSize = KTX2_LIMIT;
     270          49 :     GByte *pabyRet = nullptr;
     271          49 :     vsi_l_offset nSizeLarge = 0;
     272          49 :     int nRet = VSIIngestFile(fpL ? fpL : poOpenInfo->fpL, nullptr, &pabyRet,
     273             :                              &nSizeLarge, nMaxSize);
     274          49 :     if (fpL != nullptr)
     275           4 :         VSIFCloseL(fpL);
     276          49 :     if (!nRet)
     277             :     {
     278           0 :         return nullptr;
     279             :     }
     280          49 :     const uint32_t nSize = static_cast<uint32_t>(nSizeLarge);
     281             : 
     282             :     auto poDS = std::make_unique<KTX2Dataset>(
     283          49 :         nLayer != static_cast<uint32_t>(-1) ? nLayer : 0,
     284         147 :         nFace != static_cast<uint32_t>(-1) ? nFace : 0, pabyRet);
     285          49 :     auto &transcoder = poDS->m_transcoder;
     286          49 :     const bool bInit = transcoder.init(pabyRet, nSize);
     287          49 :     if (!bInit)
     288             :     {
     289           0 :         if (nSize >= sizeof(basist::ktx2_header))
     290             :         {
     291             : #define DEBUG_u32(x)                                                           \
     292             :     CPLDebug("KTX2", #x " = %u",                                               \
     293             :              static_cast<uint32_t>(transcoder.get_header().m_##x))
     294           0 :             DEBUG_u32(vk_format);
     295           0 :             DEBUG_u32(type_size);
     296           0 :             DEBUG_u32(pixel_width);
     297           0 :             DEBUG_u32(pixel_height);
     298           0 :             DEBUG_u32(pixel_depth);
     299           0 :             DEBUG_u32(layer_count);
     300           0 :             DEBUG_u32(face_count);
     301           0 :             DEBUG_u32(level_count);
     302           0 :             DEBUG_u32(supercompression_scheme);
     303           0 :             DEBUG_u32(dfd_byte_offset);
     304           0 :             DEBUG_u32(dfd_byte_length);
     305             :         }
     306           0 :         CPLError(CE_Failure, CPLE_AppDefined,
     307             :                  "ktx2_transcoder::init() failed! "
     308             :                  "File either uses an unsupported feature or is invalid");
     309           0 :         return nullptr;
     310             :     }
     311             : 
     312             :     const uint32_t nLayers =
     313          49 :         std::max(1U, transcoder.get_layers());  // get_layers() may return 0
     314          49 :     const uint32_t nFaces = transcoder.get_faces();
     315          49 :     CPLDebug("KTX2", "levels = %u, faces = %u, layers = %u",
     316             :              transcoder.get_levels(), nFaces, nLayers);
     317             : 
     318          49 :     switch (transcoder.get_format())
     319             :     {
     320          34 :         case basist::basis_tex_format::cETC1S:
     321          34 :             poDS->SetMetadataItem("COMPRESSION", "ETC1S", "IMAGE_STRUCTURE");
     322          34 :             break;
     323          15 :         case basist::basis_tex_format::cUASTC4x4:
     324          15 :             poDS->SetMetadataItem("COMPRESSION", "UASTC", "IMAGE_STRUCTURE");
     325          15 :             break;
     326             :     }
     327             : 
     328          49 :     if (nLayer == static_cast<uint32_t>(-1) && (nFaces >= 2 || nLayers >= 2))
     329             :     {
     330           2 :         CPLStringList aosSubdatasets;
     331           1 :         int nSubDS = 1;
     332           3 :         for (uint32_t iLayer = 0; iLayer < nLayers; ++iLayer)
     333             :         {
     334           4 :             for (uint32_t iFace = 0; iFace < nFaces; ++iFace)
     335             :             {
     336             :                 aosSubdatasets.SetNameValue(
     337             :                     CPLSPrintf("SUBDATASET_%d_NAME", nSubDS),
     338             :                     CPLSPrintf("KTX2:\"%s\":%u:%u", poOpenInfo->pszFilename,
     339           2 :                                iLayer, iFace));
     340             :                 aosSubdatasets.SetNameValue(
     341             :                     CPLSPrintf("SUBDATASET_%d_DESC", nSubDS),
     342             :                     CPLSPrintf("Layer %u, face %u of %s", iLayer, iFace,
     343           2 :                                poOpenInfo->pszFilename));
     344           2 :                 nSubDS++;
     345             :             }
     346             :         }
     347           1 :         poDS->nRasterXSize = 0;
     348           1 :         poDS->nRasterYSize = 0;
     349           1 :         poDS->SetMetadata(aosSubdatasets.List(), "SUBDATASETS");
     350             : 
     351           1 :         poDS->SetPamFlags(poDS->GetPamFlags() & ~GPF_DIRTY);
     352             : 
     353           1 :         return poDS.release();
     354             :     }
     355          48 :     else if (nLayer != static_cast<uint32_t>(-1) && nLayer >= nLayers)
     356             :     {
     357           1 :         CPLError(CE_Failure, CPLE_AppDefined, "Invalid layer number: %u",
     358             :                  nLayer);
     359           1 :         return nullptr;
     360             :     }
     361          47 :     else if (nFace != static_cast<uint32_t>(-1) && nFace >= nFaces)
     362             :     {
     363           1 :         CPLError(CE_Failure, CPLE_AppDefined, "Invalid face number: %u", nFace);
     364           1 :         return nullptr;
     365             :     }
     366             : 
     367          46 :     poDS->nRasterXSize = transcoder.get_width();
     368          46 :     poDS->nRasterYSize = transcoder.get_height();
     369             : 
     370          46 :     const int l_nBands = 3 + (transcoder.get_has_alpha() ? 1 : 0);
     371         222 :     for (int i = 1; i <= l_nBands; ++i)
     372             :     {
     373         176 :         poDS->SetBand(i, new KTX2RasterBand(poDS.get(), i));
     374             :     }
     375             : 
     376          99 :     for (uint32_t level_index = 0; level_index < transcoder.get_levels();
     377             :          ++level_index)
     378             :     {
     379             :         basist::ktx2_image_level_info level_info;
     380          53 :         uint32_t layer_index = 0;
     381          53 :         uint32_t face_index = 0;
     382          53 :         if (transcoder.get_image_level_info(level_info, level_index,
     383             :                                             layer_index, face_index))
     384             :         {
     385          53 :             CPLDebug(
     386             :                 "KTX2",
     387             :                 "level %u: width=%u, orig_width=%u, height=%u, orig_height=%u",
     388             :                 level_index, level_info.m_width, level_info.m_orig_width,
     389             :                 level_info.m_height, level_info.m_orig_height);
     390             : 
     391          53 :             if (level_index > 0)
     392             :             {
     393             :                 auto poOverviewDS =
     394          14 :                     std::make_unique<KTX2Dataset>(poDS.get(), level_index);
     395          35 :                 for (int i = 1; i <= l_nBands; ++i)
     396             :                 {
     397          56 :                     poOverviewDS->SetBand(
     398          28 :                         i, new KTX2RasterBand(poOverviewDS.get(), i));
     399             :                 }
     400           7 :                 poDS->m_apoOverviewsDS.emplace_back(std::move(poOverviewDS));
     401             :             }
     402             :         }
     403             :     }
     404             : 
     405          46 :     poDS->SetPamFlags(poDS->GetPamFlags() & ~GPF_DIRTY);
     406             : 
     407             :     // Initialize any PAM information.
     408          46 :     poDS->SetDescription(poOpenInfo->pszFilename);
     409          46 :     poDS->TryLoadXML(poOpenInfo->GetSiblingFiles());
     410             : 
     411          46 :     return poDS.release();
     412             : }
     413             : 
     414             : /************************************************************************/
     415             : /*                            CreateCopy()                              */
     416             : /************************************************************************/
     417             : 
     418          56 : GDALDataset *KTX2Dataset::CreateCopy(const char *pszFilename,
     419             :                                      GDALDataset *poSrcDS, int /*bStrict*/,
     420             :                                      char **papszOptions,
     421             :                                      GDALProgressFunc pfnProgress,
     422             :                                      void *pProgressData)
     423             : {
     424          56 :     if (!GDAL_KTX2_BASISU_CreateCopy(pszFilename, poSrcDS,
     425             :                                      true,  // bIsKTX2
     426             :                                      papszOptions, pfnProgress, pProgressData))
     427             :     {
     428          31 :         return nullptr;
     429             :     }
     430          50 :     GDALOpenInfo oOpenInfo(pszFilename, GA_ReadOnly);
     431          25 :     return Open(&oOpenInfo);
     432             : }
     433             : 
     434             : /************************************************************************/
     435             : /*                         GDALRegister_KTX2()                          */
     436             : /************************************************************************/
     437             : 
     438           8 : void GDALRegister_KTX2()
     439             : {
     440           8 :     if (GDALGetDriverByName(KTX2_DRIVER_NAME) != nullptr)
     441           0 :         return;
     442             : 
     443           8 :     GDALDriver *poDriver = new GDALDriver();
     444           8 :     KTX2DriverSetCommonMetadata(poDriver);
     445             : 
     446           8 :     poDriver->pfnOpen = KTX2Dataset::Open;
     447           8 :     poDriver->pfnCreateCopy = KTX2Dataset::CreateCopy;
     448             : 
     449           8 :     GetGDALDriverManager()->RegisterDriver(poDriver);
     450             : }

Generated by: LCOV version 1.14