LCOV - code coverage report
Current view: top level - frmts/basisu_ktx2 - common.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 125 134 93.3 %
Date: 2024-05-15 17:37:59 Functions: 5 5 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_frmts.h"
      30             : #include "common.h"
      31             : #include "include_basisu_sdk.h"
      32             : 
      33             : #include <algorithm>
      34             : #include <mutex>
      35             : 
      36             : /************************************************************************/
      37             : /*                        GDALInitBasisUTranscoder()                    */
      38             : /************************************************************************/
      39             : 
      40          12 : void GDALInitBasisUTranscoder()
      41             : {
      42             :     static std::once_flag flag;
      43          12 :     std::call_once(flag, basist::basisu_transcoder_init);
      44          12 : }
      45             : 
      46             : /************************************************************************/
      47             : /*                        GDALInitBasisUEncoder()                       */
      48             : /************************************************************************/
      49             : 
      50          78 : void GDALInitBasisUEncoder()
      51             : {
      52             :     static std::once_flag flag;
      53          80 :     std::call_once(flag, []() { basisu::basisu_encoder_init(); });
      54          78 : }
      55             : 
      56             : /************************************************************************/
      57             : /*                           GDALRegister_BASISU_KTX2()                 */
      58             : /*                                                                      */
      59             : /*      This function exists so that when built as a plugin, there      */
      60             : /*      is a function that will register both drivers.                  */
      61             : /************************************************************************/
      62             : 
      63           8 : void GDALRegister_BASISU_KTX2()
      64             : {
      65           8 :     GDALRegister_BASISU();
      66           8 :     GDALRegister_KTX2();
      67           8 : }
      68             : 
      69             : /************************************************************************/
      70             : /*                     GDAL_KTX2_BASISU_CreateCopy()                    */
      71             : /************************************************************************/
      72             : 
      73         110 : bool GDAL_KTX2_BASISU_CreateCopy(const char *pszFilename, GDALDataset *poSrcDS,
      74             :                                  bool bIsKTX2, CSLConstList papszOptions,
      75             :                                  GDALProgressFunc pfnProgress,
      76             :                                  void *pProgressData)
      77             : {
      78         110 :     const int nBands = poSrcDS->GetRasterCount();
      79         110 :     if (nBands == 0 || nBands > 4)
      80             :     {
      81           8 :         CPLError(CE_Failure, CPLE_NotSupported,
      82             :                  "Only band count >= 1 and <= 4 is supported");
      83           8 :         return false;
      84             :     }
      85         102 :     if (poSrcDS->GetRasterBand(1)->GetRasterDataType() != GDT_Byte)
      86             :     {
      87          22 :         CPLError(CE_Failure, CPLE_NotSupported,
      88             :                  "Only Byte data type supported");
      89          22 :         return false;
      90             :     }
      91             : 
      92          80 :     const int nXSize = poSrcDS->GetRasterXSize();
      93          80 :     const int nYSize = poSrcDS->GetRasterYSize();
      94          80 :     void *pSrcData = VSI_MALLOC3_VERBOSE(nXSize, nYSize, nBands);
      95          80 :     if (pSrcData == nullptr)
      96           0 :         return false;
      97             : 
      98         160 :     if (poSrcDS->RasterIO(GF_Read, 0, 0, nXSize, nYSize, pSrcData, nXSize,
      99             :                           nYSize, GDT_Byte, nBands, nullptr, nBands,
     100          80 :                           static_cast<GSpacing>(nBands) * nXSize, 1,
     101          80 :                           nullptr) != CE_None)
     102             :     {
     103           2 :         VSIFree(pSrcData);
     104           2 :         return false;
     105             :     }
     106             : 
     107         156 :     basisu::image img;
     108             :     try
     109             :     {
     110          78 :         img.init(static_cast<const uint8_t *>(pSrcData), nXSize, nYSize,
     111             :                  nBands);
     112          78 :         VSIFree(pSrcData);
     113             :     }
     114           0 :     catch (const std::exception &e)
     115             :     {
     116           0 :         VSIFree(pSrcData);
     117           0 :         CPLError(CE_Failure, CPLE_AppDefined, "%s", e.what());
     118           0 :         return false;
     119             :     }
     120             : 
     121          78 :     GDALInitBasisUEncoder();
     122             : 
     123          78 :     const bool bVerbose = CPLTestBool(CPLGetConfigOption("KTX2_VERBOSE", "NO"));
     124             : 
     125         156 :     basisu::basis_compressor_params params;
     126          78 :     params.m_create_ktx2_file = bIsKTX2;
     127             : 
     128          78 :     params.m_source_images.push_back(img);
     129             : 
     130          78 :     params.m_perceptual = EQUAL(
     131             :         CSLFetchNameValueDef(papszOptions, "COLORSPACE", "PERCEPTUAL_SRGB"),
     132             :         "PERCEPTUAL_SRGB");
     133             : 
     134          78 :     params.m_write_output_basis_files = true;
     135             : 
     136         156 :     std::string osTempFilename;
     137          78 :     bool bUseTempFilename = STARTS_WITH(pszFilename, "/vsi");
     138             : #ifdef _WIN32
     139             :     if (!bUseTempFilename)
     140             :     {
     141             :         bUseTempFilename = !CPLIsASCII(pszFilename, static_cast<size_t>(-1));
     142             :     }
     143             : #endif
     144          78 :     if (bUseTempFilename)
     145             :     {
     146          64 :         osTempFilename = CPLGenerateTempFilename(nullptr);
     147          64 :         CPLDebug("KTX2", "Using temporary file %s", osTempFilename.c_str());
     148          64 :         params.m_out_filename = osTempFilename;
     149             :     }
     150             :     else
     151             :     {
     152          14 :         params.m_out_filename = pszFilename;
     153             :     }
     154             : 
     155          78 :     params.m_uastc = EQUAL(
     156             :         CSLFetchNameValueDef(papszOptions, "COMPRESSION", "ETC1S"), "UASTC");
     157          78 :     if (params.m_uastc)
     158             :     {
     159          12 :         if (bIsKTX2)
     160             :         {
     161           7 :             const char *pszSuperCompression = CSLFetchNameValueDef(
     162             :                 papszOptions, "UASTC_SUPER_COMPRESSION", "ZSTD");
     163           7 :             params.m_ktx2_uastc_supercompression =
     164           7 :                 EQUAL(pszSuperCompression, "ZSTD") ? basist::KTX2_SS_ZSTANDARD
     165             :                                                    : basist::KTX2_SS_NONE;
     166             :         }
     167             : 
     168             :         const int nLevel =
     169          36 :             std::min(std::max(0, atoi(CSLFetchNameValueDef(
     170             :                                      papszOptions, "UASTC_LEVEL", "2"))),
     171          12 :                      static_cast<int>(basisu::TOTAL_PACK_UASTC_LEVELS - 1));
     172             :         static const uint32_t anLevelFlags[] = {
     173             :             basisu::cPackUASTCLevelFastest, basisu::cPackUASTCLevelFaster,
     174             :             basisu::cPackUASTCLevelDefault, basisu::cPackUASTCLevelSlower,
     175             :             basisu::cPackUASTCLevelVerySlow};
     176          12 :         CPL_STATIC_ASSERT(CPL_ARRAYSIZE(anLevelFlags) ==
     177             :                           basisu::TOTAL_PACK_UASTC_LEVELS);
     178          12 :         params.m_pack_uastc_flags &= ~basisu::cPackUASTCLevelMask;
     179          12 :         params.m_pack_uastc_flags |= anLevelFlags[nLevel];
     180             : 
     181             :         const char *pszUASTC_RDO_LEVEL =
     182          12 :             CSLFetchNameValue(papszOptions, "UASTC_RDO_LEVEL");
     183          12 :         if (pszUASTC_RDO_LEVEL)
     184             :         {
     185           4 :             params.m_rdo_uastc_quality_scalar =
     186           4 :                 static_cast<float>(CPLAtof(pszUASTC_RDO_LEVEL));
     187           4 :             params.m_rdo_uastc = true;
     188             :         }
     189             : 
     190          48 :         for (const char *pszOption :
     191             :              {"ETC1S_LEVEL", "ETC1S_QUALITY_LEVEL",
     192          60 :               "ETC1S_MAX_SELECTOR_CLUSTERS", "ETC1S_MAX_SELECTOR_CLUSTERS"})
     193             :         {
     194          48 :             if (CSLFetchNameValue(papszOptions, pszOption) != nullptr)
     195             :             {
     196           0 :                 CPLError(CE_Warning, CPLE_AppDefined,
     197             :                          "%s ignored for COMPRESSION=UASTC", pszOption);
     198             :             }
     199             :         }
     200             :     }
     201             :     else
     202             :     {
     203             :         // CPL_STATIC_ASSERT(basisu::BASISU_MIN_COMPRESSION_LEVEL == 0);
     204          66 :         CPL_STATIC_ASSERT(basisu::BASISU_MAX_COMPRESSION_LEVEL == 6);
     205          66 :         params.m_compression_level =
     206         198 :             std::min(std::max(0, atoi(CSLFetchNameValueDef(
     207             :                                      papszOptions, "ETC1S_LEVEL", "1"))),
     208          66 :                      static_cast<int>(basisu::BASISU_MAX_COMPRESSION_LEVEL));
     209          66 :         CPL_STATIC_ASSERT(basisu::BASISU_QUALITY_MIN == 1);
     210          66 :         CPL_STATIC_ASSERT(basisu::BASISU_QUALITY_MAX == 255);
     211             :         const char *pszQualityLevel =
     212          66 :             CSLFetchNameValue(papszOptions, "ETC1S_QUALITY_LEVEL");
     213          66 :         params.m_quality_level =
     214         132 :             std::min(std::max(static_cast<int>(basisu::BASISU_QUALITY_MIN),
     215          66 :                               atoi(pszQualityLevel ? pszQualityLevel : "128")),
     216         132 :                      static_cast<int>(basisu::BASISU_QUALITY_MAX));
     217          66 :         params.m_max_endpoint_clusters = 0;
     218          66 :         params.m_max_selector_clusters = 0;
     219             : 
     220             :         const char *pszMaxEndpointClusters =
     221          66 :             CSLFetchNameValue(papszOptions, "ETC1S_MAX_ENDPOINTS_CLUSTERS");
     222             :         const char *pszMaxSelectorClusters =
     223          66 :             CSLFetchNameValue(papszOptions, "ETC1S_MAX_SELECTOR_CLUSTERS");
     224          66 :         if (pszQualityLevel == nullptr && (pszMaxEndpointClusters != nullptr ||
     225             :                                            pszMaxSelectorClusters != nullptr))
     226             :         {
     227           8 :             params.m_quality_level = -1;
     228           8 :             if (pszMaxEndpointClusters != nullptr)
     229             :             {
     230           6 :                 params.m_max_endpoint_clusters = atoi(pszMaxEndpointClusters);
     231           6 :                 if (pszMaxSelectorClusters == nullptr)
     232             :                 {
     233           2 :                     CPLError(CE_Failure, CPLE_AppDefined,
     234             :                              "ETC1S_MAX_SELECTOR_CLUSTERS must be set when "
     235             :                              "ETC1S_MAX_ENDPOINTS_CLUSTERS is set");
     236           2 :                     return false;
     237             :                 }
     238             :             }
     239             : 
     240           6 :             if (pszMaxSelectorClusters != nullptr)
     241             :             {
     242           6 :                 params.m_max_selector_clusters = atoi(pszMaxSelectorClusters);
     243           6 :                 if (pszMaxEndpointClusters == nullptr)
     244             :                 {
     245           2 :                     CPLError(CE_Failure, CPLE_AppDefined,
     246             :                              "ETC1S_MAX_ENDPOINTS_CLUSTERS must be set when "
     247             :                              "ETC1S_MAX_SELECTOR_CLUSTERS is set");
     248           2 :                     return false;
     249             :                 }
     250             :             }
     251             :         }
     252             :         else
     253             :         {
     254         116 :             for (const char *pszOption : {"ETC1S_MAX_ENDPOINTS_CLUSTERS",
     255         174 :                                           "ETC1S_MAX_SELECTOR_CLUSTERS"})
     256             :             {
     257         116 :                 if (CSLFetchNameValue(papszOptions, pszOption) != nullptr)
     258             :                 {
     259           4 :                     CPLError(CE_Warning, CPLE_AppDefined,
     260             :                              "%s ignored when ETC1S_QUALITY_LEVEL is specified",
     261             :                              pszOption);
     262             :                 }
     263             :             }
     264             :         }
     265             : 
     266         186 :         for (const char *pszOption : {"UASTC_LEVEL", "UASTC_RDO_LEVEL"})
     267             :         {
     268         124 :             if (CSLFetchNameValue(papszOptions, pszOption) != nullptr)
     269             :             {
     270           0 :                 CPLError(CE_Warning, CPLE_AppDefined,
     271             :                          "%s ignored for COMPRESSION=ETC1S", pszOption);
     272             :             }
     273             :         }
     274             :     }
     275             : 
     276          74 :     if (CPLTestBool(CSLFetchNameValueDef(papszOptions, "MIPMAP", "NO")))
     277             :     {
     278           2 :         params.m_mip_gen = true;
     279           2 :         params.m_mip_srgb = params.m_perceptual;
     280             :     }
     281             : 
     282             :     const int nNumThreads = std::max(
     283          74 :         1, atoi(CSLFetchNameValueDef(
     284             :                papszOptions, "NUM_THREADS",
     285             :                CPLGetConfigOption("GDAL_NUM_THREADS",
     286          74 :                                   CPLSPrintf("%d", CPLGetNumCPUs())))));
     287          74 :     CPLDebug("KTX2", "Using %d threads", nNumThreads);
     288          74 :     if (params.m_uastc)
     289             :     {
     290          12 :         params.m_rdo_uastc_multithreading = nNumThreads > 1;
     291             :     }
     292          74 :     params.m_multithreading = nNumThreads > 1;
     293          74 :     params.m_debug = bVerbose;
     294          74 :     params.m_status_output = bVerbose;
     295          74 :     params.m_compute_stats = bVerbose;
     296             : 
     297         148 :     basisu::job_pool jpool(nNumThreads);
     298          74 :     params.m_pJob_pool = &jpool;
     299             : 
     300         148 :     basisu::basis_compressor comp;
     301          74 :     basisu::enable_debug_printf(bVerbose);
     302             : 
     303          74 :     if (!comp.init(params))
     304             :     {
     305           0 :         CPLError(CE_Failure, CPLE_AppDefined,
     306             :                  "basis_compressor::init() failed");
     307           0 :         return false;
     308             :     }
     309             : 
     310          74 :     const basisu::basis_compressor::error_code result = comp.process();
     311          74 :     if (result != basisu::basis_compressor::cECSuccess)
     312             :     {
     313           6 :         CPLError(CE_Failure, CPLE_AppDefined,
     314             :                  "basis_compressor::process() failed");
     315           6 :         return false;
     316             :     }
     317             : 
     318          68 :     if (!osTempFilename.empty())
     319             :     {
     320          58 :         if (CPLCopyFile(pszFilename, osTempFilename.c_str()) != 0)
     321             :         {
     322          20 :             VSIUnlink(osTempFilename.c_str());
     323          20 :             return false;
     324             :         }
     325          38 :         VSIUnlink(osTempFilename.c_str());
     326             :     }
     327             : 
     328          48 :     if (pfnProgress)
     329          48 :         pfnProgress(1.0, "", pProgressData);
     330             : 
     331          48 :     return true;
     332             : }

Generated by: LCOV version 1.14