LCOV - code coverage report
Current view: top level - alg - gdalpansharpen.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 619 748 82.8 %
Date: 2026-07-24 18:27:47 Functions: 40 63 63.5 %

          Line data    Source code
       1             : /******************************************************************************
       2             :  *
       3             :  * Project:  GDAL Pansharpening module
       4             :  * Purpose:  Implementation of pansharpening.
       5             :  * Author:   Even Rouault <even.rouault at spatialys.com>
       6             :  *
       7             :  ******************************************************************************
       8             :  * Copyright (c) 2015, Even Rouault <even.rouault at spatialys.com>
       9             :  * Copyright (c) 2015, Airbus DS Geo SA (weighted Brovey algorithm)
      10             :  *
      11             :  * SPDX-License-Identifier: MIT
      12             :  ****************************************************************************/
      13             : 
      14             : #include "cpl_port.h"
      15             : #include "cpl_worker_thread_pool.h"
      16             : #include "gdalpansharpen.h"
      17             : 
      18             : #include <algorithm>
      19             : #include <array>
      20             : #include <cstddef>
      21             : #include <cstdio>
      22             : #include <cstdlib>
      23             : #include <cstring>
      24             : #include <limits>
      25             : #include <new>
      26             : 
      27             : #include "cpl_conv.h"
      28             : #include "cpl_error.h"
      29             : #include "cpl_float.h"
      30             : #include "cpl_multiproc.h"
      31             : #include "cpl_vsi.h"
      32             : #include "../frmts/mem/memdataset.h"
      33             : #include "../frmts/vrt/vrtdataset.h"
      34             : #include "gdal_priv.h"
      35             : #include "gdal_priv_templates.hpp"
      36             : #include "gdal_thread_pool.h"
      37             : // #include "gdalsse_priv.h"
      38             : 
      39             : // Limit types to practical use cases.
      40             : #define LIMIT_TYPES 1
      41             : 
      42             : /************************************************************************/
      43             : /*                    GDALCreatePansharpenOptions()                     */
      44             : /************************************************************************/
      45             : 
      46             : /** Create pansharpening options.
      47             :  *
      48             :  * @return a newly allocated pansharpening option structure that must be freed
      49             :  * with GDALDestroyPansharpenOptions().
      50             :  *
      51             :  */
      52             : 
      53         147 : GDALPansharpenOptions *GDALCreatePansharpenOptions()
      54             : {
      55             :     GDALPansharpenOptions *psOptions = static_cast<GDALPansharpenOptions *>(
      56         147 :         CPLCalloc(1, sizeof(GDALPansharpenOptions)));
      57         147 :     psOptions->ePansharpenAlg = GDAL_PSH_WEIGHTED_BROVEY;
      58         147 :     psOptions->eResampleAlg = GRIORA_Cubic;
      59         147 :     return psOptions;
      60             : }
      61             : 
      62             : /************************************************************************/
      63             : /*                    GDALDestroyPansharpenOptions()                    */
      64             : /************************************************************************/
      65             : 
      66             : /** Destroy pansharpening options.
      67             :  *
      68             :  * @param psOptions a pansharpening option structure allocated with
      69             :  * GDALCreatePansharpenOptions()
      70             :  *
      71             :  */
      72             : 
      73         148 : void GDALDestroyPansharpenOptions(GDALPansharpenOptions *psOptions)
      74             : {
      75         148 :     if (psOptions == nullptr)
      76           1 :         return;
      77         147 :     CPLFree(psOptions->padfWeights);
      78         147 :     CPLFree(psOptions->pahInputSpectralBands);
      79         147 :     CPLFree(psOptions->panOutPansharpenedBands);
      80         147 :     CPLFree(psOptions);
      81             : }
      82             : 
      83             : /************************************************************************/
      84             : /*                     GDALClonePansharpenOptions()                     */
      85             : /************************************************************************/
      86             : 
      87             : /** Clone pansharpening options.
      88             :  *
      89             :  * @param psOptions a pansharpening option structure allocated with
      90             :  * GDALCreatePansharpenOptions()
      91             :  * @return a newly allocated pansharpening option structure that must be freed
      92             :  * with GDALDestroyPansharpenOptions().
      93             :  *
      94             :  */
      95             : 
      96             : GDALPansharpenOptions *
      97          76 : GDALClonePansharpenOptions(const GDALPansharpenOptions *psOptions)
      98             : {
      99          76 :     GDALPansharpenOptions *psNewOptions = GDALCreatePansharpenOptions();
     100          76 :     psNewOptions->ePansharpenAlg = psOptions->ePansharpenAlg;
     101          76 :     psNewOptions->eResampleAlg = psOptions->eResampleAlg;
     102          76 :     psNewOptions->nBitDepth = psOptions->nBitDepth;
     103          76 :     psNewOptions->nWeightCount = psOptions->nWeightCount;
     104          76 :     if (psOptions->padfWeights)
     105             :     {
     106          76 :         psNewOptions->padfWeights = static_cast<double *>(
     107          76 :             CPLMalloc(sizeof(double) * psOptions->nWeightCount));
     108          76 :         memcpy(psNewOptions->padfWeights, psOptions->padfWeights,
     109          76 :                sizeof(double) * psOptions->nWeightCount);
     110             :     }
     111          76 :     psNewOptions->hPanchroBand = psOptions->hPanchroBand;
     112          76 :     psNewOptions->nInputSpectralBands = psOptions->nInputSpectralBands;
     113          76 :     if (psOptions->pahInputSpectralBands)
     114             :     {
     115          76 :         const size_t nSize =
     116          76 :             sizeof(GDALRasterBandH) * psOptions->nInputSpectralBands;
     117          76 :         psNewOptions->pahInputSpectralBands =
     118          76 :             static_cast<GDALRasterBandH *>(CPLMalloc(nSize));
     119          76 :         memcpy(psNewOptions->pahInputSpectralBands,
     120          76 :                psOptions->pahInputSpectralBands, nSize);
     121             :     }
     122          76 :     psNewOptions->nOutPansharpenedBands = psOptions->nOutPansharpenedBands;
     123          76 :     if (psOptions->panOutPansharpenedBands)
     124             :     {
     125          75 :         psNewOptions->panOutPansharpenedBands = static_cast<int *>(
     126          75 :             CPLMalloc(sizeof(int) * psOptions->nOutPansharpenedBands));
     127          75 :         memcpy(psNewOptions->panOutPansharpenedBands,
     128          75 :                psOptions->panOutPansharpenedBands,
     129          75 :                sizeof(int) * psOptions->nOutPansharpenedBands);
     130             :     }
     131          76 :     psNewOptions->bHasNoData = psOptions->bHasNoData;
     132          76 :     psNewOptions->dfNoData = psOptions->dfNoData;
     133          76 :     psNewOptions->nThreads = psOptions->nThreads;
     134          76 :     return psNewOptions;
     135             : }
     136             : 
     137             : /************************************************************************/
     138             : /*                      GDALPansharpenOperation()                       */
     139             : /************************************************************************/
     140             : 
     141             : /** Pansharpening operation constructor.
     142             :  *
     143             :  * The object is ready to be used after Initialize() has been called.
     144             :  */
     145             : GDALPansharpenOperation::GDALPansharpenOperation() = default;
     146             : 
     147             : /************************************************************************/
     148             : /*                      ~GDALPansharpenOperation()                      */
     149             : /************************************************************************/
     150             : 
     151             : /** Pansharpening operation destructor.
     152             :  */
     153             : 
     154          74 : GDALPansharpenOperation::~GDALPansharpenOperation()
     155             : {
     156          74 :     GDALDestroyPansharpenOptions(psOptions);
     157          83 :     for (size_t i = 0; i < aVDS.size(); i++)
     158           9 :         delete aVDS[i];
     159          74 :     delete poThreadPool;
     160          74 : }
     161             : 
     162             : /************************************************************************/
     163             : /*                             Initialize()                             */
     164             : /************************************************************************/
     165             : 
     166             : /** Initialize the pansharpening operation.
     167             :  *
     168             :  * @param psOptionsIn pansharpening options. Must not be NULL.
     169             :  *
     170             :  * @return CE_None in case of success, CE_Failure in case of failure.
     171             :  */
     172             : CPLErr
     173          74 : GDALPansharpenOperation::Initialize(const GDALPansharpenOptions *psOptionsIn)
     174             : {
     175          74 :     if (psOptionsIn->hPanchroBand == nullptr)
     176             :     {
     177           0 :         CPLError(CE_Failure, CPLE_AppDefined, "hPanchroBand not set");
     178           0 :         return CE_Failure;
     179             :     }
     180          74 :     if (psOptionsIn->nInputSpectralBands <= 0)
     181             :     {
     182           0 :         CPLError(CE_Failure, CPLE_AppDefined,
     183             :                  "No input spectral bands defined");
     184           0 :         return CE_Failure;
     185             :     }
     186          74 :     if (psOptionsIn->padfWeights == nullptr ||
     187          74 :         psOptionsIn->nWeightCount != psOptionsIn->nInputSpectralBands)
     188             :     {
     189           0 :         CPLError(CE_Failure, CPLE_AppDefined,
     190             :                  "No weights defined, or not the same number as input "
     191             :                  "spectral bands");
     192           0 :         return CE_Failure;
     193             :     }
     194             : 
     195          74 :     auto poPanchroBand = GDALRasterBand::FromHandle(psOptionsIn->hPanchroBand);
     196          74 :     auto poPanchroDS = poPanchroBand->GetDataset();
     197          74 :     if (poPanchroDS == nullptr)
     198             :     {
     199           0 :         CPLError(CE_Failure, CPLE_AppDefined,
     200             :                  "Cannot retrieve dataset associated with hPanchroBand");
     201           0 :         return CE_Failure;
     202             :     }
     203             :     // Make sure that the band is really a first level child of the owning dataset
     204          74 :     if (poPanchroDS->GetRasterBand(poPanchroBand->GetBand()) != poPanchroBand)
     205             :     {
     206           0 :         CPLError(CE_Failure, CPLE_AppDefined,
     207             :                  "poPanchroDS->GetRasterBand(poPanchroBand->GetBand()) != "
     208             :                  "poPanchroBand");
     209           0 :         return CE_Failure;
     210             :     }
     211          74 :     GDALGeoTransform panchroGT;
     212          74 :     if (poPanchroDS->GetGeoTransform(panchroGT) != CE_None)
     213             :     {
     214           0 :         CPLError(CE_Failure, CPLE_AppDefined,
     215             :                  "Panchromatic band has no associated geotransform");
     216           0 :         return CE_Failure;
     217             :     }
     218             : 
     219          74 :     GDALRasterBandH hRefBand = psOptionsIn->pahInputSpectralBands[0];
     220          74 :     auto poRefBand = GDALRasterBand::FromHandle(hRefBand);
     221          74 :     auto poRefBandDS = poRefBand->GetDataset();
     222          74 :     if (poRefBandDS == nullptr)
     223             :     {
     224           0 :         CPLError(
     225             :             CE_Failure, CPLE_AppDefined,
     226             :             "Cannot retrieve dataset associated with ahInputSpectralBands[0]");
     227           0 :         return CE_Failure;
     228             :     }
     229             :     // Make sure that the band is really a first level child of the owning dataset
     230          74 :     if (poRefBandDS->GetRasterBand(poRefBand->GetBand()) != poRefBand)
     231             :     {
     232           0 :         CPLError(
     233             :             CE_Failure, CPLE_AppDefined,
     234             :             "poRefBandDS->GetRasterBand(poRefBand->GetBand()) != poRefBand");
     235           0 :         return CE_Failure;
     236             :     }
     237             : 
     238          74 :     GDALGeoTransform refMSGT;
     239          74 :     if (poRefBandDS->GetGeoTransform(refMSGT) != CE_None)
     240             :     {
     241           0 :         CPLError(CE_Failure, CPLE_AppDefined,
     242             :                  "ahInputSpectralBands[0] band has no associated geotransform");
     243           0 :         return CE_Failure;
     244             :     }
     245             : 
     246          74 :     GDALGeoTransform invMSGT;
     247          74 :     if (!refMSGT.GetInverse(invMSGT))
     248             :     {
     249           0 :         CPLError(CE_Failure, CPLE_AppDefined,
     250             :                  "ahInputSpectralBands[0] geotransform is not invertible");
     251           0 :         return CE_Failure;
     252             :     }
     253             : 
     254             :     // Do InvMSGT * PanchroGT multiplication
     255          74 :     m_panToMSGT[1] = invMSGT[1] * panchroGT[1] + invMSGT[2] * panchroGT[4];
     256          74 :     m_panToMSGT[2] = invMSGT[1] * panchroGT[2] + invMSGT[2] * panchroGT[5];
     257         148 :     m_panToMSGT[0] =
     258          74 :         invMSGT[1] * panchroGT[0] + invMSGT[2] * panchroGT[3] + invMSGT[0];
     259          74 :     m_panToMSGT[4] = invMSGT[4] * panchroGT[1] + invMSGT[5] * panchroGT[4];
     260          74 :     m_panToMSGT[5] = invMSGT[4] * panchroGT[2] + invMSGT[5] * panchroGT[5];
     261         148 :     m_panToMSGT[3] =
     262          74 :         invMSGT[4] * panchroGT[0] + invMSGT[5] * panchroGT[3] + invMSGT[3];
     263             : #if 0
     264             :     CPLDebug("GDAL", "m_panToMSGT[] = %g %g %g %g %g %g",
     265             :            m_panToMSGT[0], m_panToMSGT[1], m_panToMSGT[2],
     266             :            m_panToMSGT[3], m_panToMSGT[4], m_panToMSGT[5]);
     267             : #endif
     268          74 :     if (std::fabs(m_panToMSGT[2]) > 1e-10 || std::fabs(m_panToMSGT[4]) > 1e-10)
     269             :     {
     270           0 :         CPLError(CE_Failure, CPLE_NotSupported,
     271             :                  "Composition of panchromatic and multispectral geotransform "
     272             :                  "has rotational terms");
     273           0 :         return CE_Failure;
     274             :     }
     275             : 
     276          74 :     bool bSameDataset = psOptionsIn->nInputSpectralBands > 1;
     277          74 :     if (bSameDataset)
     278          55 :         anInputBands.push_back(GDALGetBandNumber(hRefBand));
     279         178 :     for (int i = 1; i < psOptionsIn->nInputSpectralBands; i++)
     280             :     {
     281         105 :         GDALRasterBandH hBand = psOptionsIn->pahInputSpectralBands[i];
     282         209 :         if (GDALGetRasterBandXSize(hBand) != GDALGetRasterBandXSize(hRefBand) ||
     283         104 :             GDALGetRasterBandYSize(hBand) != GDALGetRasterBandYSize(hRefBand))
     284             :         {
     285           1 :             CPLError(CE_Failure, CPLE_AppDefined,
     286             :                      "Dimensions of input spectral band %d different from "
     287             :                      "first spectral band",
     288             :                      i + 1);
     289           1 :             return CE_Failure;
     290             :         }
     291             : 
     292         104 :         auto poBand = GDALRasterBand::FromHandle(hBand);
     293         104 :         auto poBandDS = poBand->GetDataset();
     294         104 :         if (poBandDS == nullptr)
     295             :         {
     296           0 :             CPLError(CE_Failure, CPLE_AppDefined,
     297             :                      "Cannot retrieve dataset associated with "
     298             :                      "input spectral band %d",
     299             :                      i + 1);
     300           0 :             return CE_Failure;
     301             :         }
     302             :         // Make sure that the band is really a first level child of the owning dataset
     303         104 :         if (poBandDS->GetRasterBand(poBand->GetBand()) != poBand)
     304             :         {
     305           0 :             CPLError(CE_Failure, CPLE_AppDefined,
     306             :                      "poBandDS->GetRasterBand(poBand->GetBand()) != poBand");
     307           0 :             return CE_Failure;
     308             :         }
     309             : 
     310         104 :         GDALGeoTransform MSGT;
     311         104 :         if (poBandDS->GetGeoTransform(MSGT) != CE_None)
     312             :         {
     313           0 :             CPLError(
     314             :                 CE_Failure, CPLE_AppDefined,
     315             :                 "input spectral band %d band has no associated geotransform",
     316             :                 i + 1);
     317           0 :             return CE_Failure;
     318             :         }
     319         104 :         if (MSGT != refMSGT)
     320             :         {
     321           0 :             CPLError(CE_Failure, CPLE_AppDefined,
     322             :                      "input spectral band %d has a different "
     323             :                      "geotransform than the first spectral band",
     324             :                      i + 1);
     325           0 :             return CE_Failure;
     326             :         }
     327             : 
     328         104 :         if (bSameDataset)
     329             :         {
     330         101 :             if (GDALGetBandDataset(hBand) != GDALGetBandDataset(hRefBand))
     331             :             {
     332           5 :                 anInputBands.resize(0);
     333           5 :                 bSameDataset = false;
     334             :             }
     335             :             else
     336             :             {
     337          96 :                 anInputBands.push_back(GDALGetBandNumber(hBand));
     338             :             }
     339             :         }
     340             :     }
     341          73 :     if (psOptionsIn->nOutPansharpenedBands == 0)
     342             :     {
     343           1 :         CPLError(CE_Warning, CPLE_AppDefined,
     344             :                  "No output pansharpened band defined");
     345             :     }
     346         244 :     for (int i = 0; i < psOptionsIn->nOutPansharpenedBands; i++)
     347             :     {
     348         171 :         if (psOptionsIn->panOutPansharpenedBands[i] < 0 ||
     349         171 :             psOptionsIn->panOutPansharpenedBands[i] >=
     350         171 :                 psOptionsIn->nInputSpectralBands)
     351             :         {
     352           0 :             CPLError(CE_Failure, CPLE_AppDefined,
     353             :                      "Invalid value panOutPansharpenedBands[%d] = %d", i,
     354           0 :                      psOptionsIn->panOutPansharpenedBands[i]);
     355           0 :             return CE_Failure;
     356             :         }
     357             :     }
     358             : 
     359          73 :     GDALDataType eWorkDataType = poPanchroBand->GetRasterDataType();
     360          73 :     if (psOptionsIn->nBitDepth)
     361             :     {
     362          13 :         if (psOptionsIn->nBitDepth < 0 || psOptionsIn->nBitDepth > 31 ||
     363          13 :             (eWorkDataType == GDT_UInt8 && psOptionsIn->nBitDepth > 8) ||
     364           3 :             (eWorkDataType == GDT_UInt16 && psOptionsIn->nBitDepth > 16))
     365             :         {
     366           0 :             CPLError(CE_Failure, CPLE_AppDefined,
     367             :                      "Invalid value nBitDepth = %d for type %s",
     368           0 :                      psOptionsIn->nBitDepth,
     369             :                      GDALGetDataTypeName(eWorkDataType));
     370           0 :             return CE_Failure;
     371             :         }
     372             :     }
     373             : 
     374          73 :     psOptions = GDALClonePansharpenOptions(psOptionsIn);
     375          73 :     if (psOptions->nBitDepth == GDALGetDataTypeSizeBits(eWorkDataType))
     376           7 :         psOptions->nBitDepth = 0;
     377          73 :     if (psOptions->nBitDepth &&
     378           3 :         !(eWorkDataType == GDT_UInt8 || eWorkDataType == GDT_UInt16 ||
     379             :           eWorkDataType == GDT_UInt32 || eWorkDataType == GDT_UInt64))
     380             :     {
     381           0 :         CPLError(CE_Warning, CPLE_AppDefined,
     382           0 :                  "Ignoring nBitDepth = %d for type %s", psOptions->nBitDepth,
     383             :                  GDALGetDataTypeName(eWorkDataType));
     384           0 :         psOptions->nBitDepth = 0;
     385             :     }
     386             : 
     387             :     // Detect negative weights.
     388         250 :     for (int i = 0; i < psOptions->nInputSpectralBands; i++)
     389             :     {
     390         177 :         if (psOptions->padfWeights[i] < 0.0)
     391             :         {
     392           0 :             bPositiveWeights = FALSE;
     393           0 :             break;
     394             :         }
     395             :     }
     396             : 
     397         250 :     for (int i = 0; i < psOptions->nInputSpectralBands; i++)
     398             :     {
     399         354 :         aMSBands.push_back(
     400         177 :             GDALRasterBand::FromHandle(psOptions->pahInputSpectralBands[i]));
     401             :     }
     402             : 
     403          73 :     if (psOptions->bHasNoData)
     404             :     {
     405           9 :         bool bNeedToWrapInVRT = false;
     406          29 :         for (int i = 0; i < psOptions->nInputSpectralBands; i++)
     407             :         {
     408             :             GDALRasterBand *poBand =
     409          20 :                 GDALRasterBand::FromHandle(psOptions->pahInputSpectralBands[i]);
     410          20 :             int bHasNoData = FALSE;
     411          20 :             double dfNoData = poBand->GetNoDataValue(&bHasNoData);
     412          20 :             if (!bHasNoData || dfNoData != psOptions->dfNoData)
     413          17 :                 bNeedToWrapInVRT = true;
     414             :         }
     415             : 
     416           9 :         if (bNeedToWrapInVRT)
     417             :         {
     418             :             // Wrap spectral bands in a VRT if they don't have the nodata value.
     419           8 :             VRTDataset *poVDS = nullptr;
     420          25 :             for (int i = 0; i < psOptions->nInputSpectralBands; i++)
     421             :             {
     422          17 :                 GDALRasterBand *poSrcBand = aMSBands[i];
     423          17 :                 int iVRTBand = 1;
     424          17 :                 if (anInputBands.empty() || i == 0)
     425             :                 {
     426           9 :                     poVDS = new VRTDataset(poSrcBand->GetXSize(),
     427           9 :                                            poSrcBand->GetYSize());
     428           9 :                     aVDS.push_back(poVDS);
     429             :                 }
     430          17 :                 if (!anInputBands.empty())
     431             :                 {
     432          13 :                     anInputBands[i] = i + 1;
     433          13 :                     iVRTBand = i + 1;
     434             :                 }
     435          17 :                 poVDS->AddBand(poSrcBand->GetRasterDataType(), nullptr);
     436             :                 VRTSourcedRasterBand *poVRTBand =
     437           0 :                     dynamic_cast<VRTSourcedRasterBand *>(
     438          17 :                         poVDS->GetRasterBand(iVRTBand));
     439          17 :                 if (poVRTBand == nullptr)
     440           0 :                     return CE_Failure;
     441          17 :                 aMSBands[i] = poVRTBand;
     442          17 :                 poVRTBand->SetNoDataValue(psOptions->dfNoData);
     443          34 :                 const char *pszNBITS = poSrcBand->GetMetadataItem(
     444          17 :                     GDALMD_NBITS, GDAL_MDD_IMAGE_STRUCTURE);
     445          17 :                 if (pszNBITS)
     446           0 :                     poVRTBand->SetMetadataItem(GDALMD_NBITS, pszNBITS,
     447           0 :                                                GDAL_MDD_IMAGE_STRUCTURE);
     448             : 
     449          17 :                 VRTSimpleSource *poSimpleSource = new VRTSimpleSource();
     450          68 :                 poVRTBand->ConfigureSource(
     451             :                     poSimpleSource, poSrcBand, FALSE, 0, 0,
     452          17 :                     poSrcBand->GetXSize(), poSrcBand->GetYSize(), 0, 0,
     453          17 :                     poSrcBand->GetXSize(), poSrcBand->GetYSize());
     454          17 :                 poVRTBand->AddSource(poSimpleSource);
     455             :             }
     456             :         }
     457             :     }
     458             : 
     459             :     // Setup thread pool.
     460          73 :     int nThreads = psOptions->nThreads;
     461          73 :     if (nThreads == -1)
     462          27 :         nThreads = CPLGetNumCPUs();
     463          46 :     else if (nThreads == 0)
     464             :     {
     465          46 :         nThreads = GDALGetNumThreads(GDAL_DEFAULT_MAX_THREAD_COUNT,
     466             :                                      /* bDefaultAllCPUs = */ false);
     467             :     }
     468          73 :     if (nThreads > 1)
     469             :     {
     470          27 :         CPLDebug("PANSHARPEN", "Using %d threads", nThreads);
     471          54 :         poThreadPool = new (std::nothrow) CPLWorkerThreadPool();
     472             :         // coverity[tainted_data]
     473          54 :         if (poThreadPool == nullptr ||
     474          27 :             !poThreadPool->Setup(nThreads, nullptr, nullptr))
     475             :         {
     476           0 :             delete poThreadPool;
     477           0 :             poThreadPool = nullptr;
     478             :         }
     479             :     }
     480             : 
     481          73 :     GDALRIOResampleAlg eResampleAlg = psOptions->eResampleAlg;
     482          73 :     if (eResampleAlg != GRIORA_NearestNeighbour)
     483             :     {
     484          73 :         const char *pszResampling =
     485         146 :             (eResampleAlg == GRIORA_Bilinear)      ? "BILINEAR"
     486          73 :             : (eResampleAlg == GRIORA_Cubic)       ? "CUBIC"
     487           0 :             : (eResampleAlg == GRIORA_CubicSpline) ? "CUBICSPLINE"
     488           0 :             : (eResampleAlg == GRIORA_Lanczos)     ? "LANCZOS"
     489           0 :             : (eResampleAlg == GRIORA_Average)     ? "AVERAGE"
     490           0 :             : (eResampleAlg == GRIORA_RMS)         ? "RMS"
     491           0 :             : (eResampleAlg == GRIORA_Mode)        ? "MODE"
     492           0 :             : (eResampleAlg == GRIORA_Gauss)       ? "GAUSS"
     493             :                                                    : "UNKNOWN";
     494             : 
     495          73 :         GDALGetResampleFunction(pszResampling, &nKernelRadius);
     496             :     }
     497             : 
     498          73 :     return CE_None;
     499             : }
     500             : 
     501             : /************************************************************************/
     502             : /*                      WeightedBroveyWithNoData()                      */
     503             : /************************************************************************/
     504             : 
     505             : template <class WorkDataType, class OutDataType>
     506           8 : void GDALPansharpenOperation::WeightedBroveyWithNoData(
     507             :     const WorkDataType *pPanBuffer,
     508             :     const WorkDataType *pUpsampledSpectralBuffer, OutDataType *pDataBuf,
     509             :     size_t nValues, size_t nBandValues, WorkDataType nMaxValue) const
     510             : {
     511             :     WorkDataType noData, validValue;
     512           8 :     GDALCopyWord(psOptions->dfNoData, noData);
     513             : 
     514             :     if constexpr (!(std::numeric_limits<WorkDataType>::is_integer))
     515           0 :         validValue = static_cast<WorkDataType>(noData + 1e-5);
     516           8 :     else if (noData == std::numeric_limits<WorkDataType>::min())
     517           4 :         validValue = std::numeric_limits<WorkDataType>::min() + 1;
     518             :     else
     519           4 :         validValue = noData - 1;
     520             : 
     521     1600010 :     for (size_t j = 0; j < nValues; j++)
     522             :     {
     523     1600000 :         double dfPseudoPanchro = 0.0;
     524     5760000 :         for (int i = 0; i < psOptions->nInputSpectralBands; i++)
     525             :         {
     526     4160000 :             WorkDataType nSpectralVal =
     527     4160000 :                 pUpsampledSpectralBuffer[i * nBandValues + j];
     528     4160000 :             if (nSpectralVal == noData)
     529             :             {
     530           0 :                 dfPseudoPanchro = 0.0;
     531           0 :                 break;
     532             :             }
     533     4160000 :             dfPseudoPanchro += psOptions->padfWeights[i] * nSpectralVal;
     534             :         }
     535     1600000 :         if (dfPseudoPanchro != 0.0 && pPanBuffer[j] != noData)
     536             :         {
     537     1597480 :             const double dfFactor = pPanBuffer[j] / dfPseudoPanchro;
     538     5751080 :             for (int i = 0; i < psOptions->nOutPansharpenedBands; i++)
     539             :             {
     540     4153600 :                 WorkDataType nRawValue = pUpsampledSpectralBuffer
     541     4153600 :                     [psOptions->panOutPansharpenedBands[i] * nBandValues + j];
     542             :                 WorkDataType nPansharpenedValue;
     543     4153600 :                 GDALCopyWord(nRawValue * dfFactor, nPansharpenedValue);
     544     4153600 :                 if (nMaxValue != 0 && nPansharpenedValue > nMaxValue)
     545           0 :                     nPansharpenedValue = nMaxValue;
     546             :                 // We don't want a valid value to be mapped to NoData.
     547     4153600 :                 if (nPansharpenedValue == noData)
     548        5030 :                     nPansharpenedValue = validValue;
     549     4153600 :                 GDALCopyWord(nPansharpenedValue, pDataBuf[i * nBandValues + j]);
     550     1597480 :             }
     551             :         }
     552             :         else
     553             :         {
     554        8920 :             for (int i = 0; i < psOptions->nOutPansharpenedBands; i++)
     555             :             {
     556        6404 :                 GDALCopyWord(noData, pDataBuf[i * nBandValues + j]);
     557             :             }
     558             :         }
     559             :     }
     560           8 : }
     561             : 
     562             : /************************************************************************/
     563             : /*                           ComputeFactor()                            */
     564             : /************************************************************************/
     565             : 
     566             : template <class T>
     567    23965280 : static inline double ComputeFactor(T panValue, double dfPseudoPanchro)
     568             : {
     569    23965280 :     if (dfPseudoPanchro == 0.0)
     570        4163 :         return 0.0;
     571             : 
     572    23961110 :     return panValue / dfPseudoPanchro;
     573             : }
     574             : 
     575             : /************************************************************************/
     576             : /*                           ClampAndRound()                            */
     577             : /************************************************************************/
     578             : 
     579     2000458 : template <class T> static inline T ClampAndRound(double dfVal, T nMaxValue)
     580             : {
     581     2000458 :     if (dfVal > nMaxValue)
     582          50 :         return nMaxValue;
     583             :     else
     584     2000404 :         return static_cast<T>(dfVal + 0.5);
     585             : }
     586             : 
     587             : /************************************************************************/
     588             : /*                           WeightedBrovey()                           */
     589             : /************************************************************************/
     590             : 
     591             : template <class WorkDataType, class OutDataType, int bHasBitDepth>
     592         160 : void GDALPansharpenOperation::WeightedBrovey3(
     593             :     const WorkDataType *pPanBuffer,
     594             :     const WorkDataType *pUpsampledSpectralBuffer, OutDataType *pDataBuf,
     595             :     size_t nValues, size_t nBandValues, WorkDataType nMaxValue) const
     596             : {
     597         160 :     if (psOptions->bHasNoData)
     598             :     {
     599           8 :         WeightedBroveyWithNoData<WorkDataType, OutDataType>(
     600             :             pPanBuffer, pUpsampledSpectralBuffer, pDataBuf, nValues,
     601             :             nBandValues, nMaxValue);
     602           8 :         return;
     603             :     }
     604             : 
     605    22965062 :     for (size_t j = 0; j < nValues; j++)
     606             :     {
     607    22964900 :         double dfFactor = 0.0;
     608             :         // if( pPanBuffer[j] == 0 )
     609             :         //     dfFactor = 1.0;
     610             :         // else
     611             :         {
     612    22964900 :             double dfPseudoPanchro = 0.0;
     613   100638300 :             for (int i = 0; i < psOptions->nInputSpectralBands; i++)
     614    77673400 :                 dfPseudoPanchro +=
     615    77673400 :                     psOptions->padfWeights[i] *
     616    77673400 :                     pUpsampledSpectralBuffer[i * nBandValues + j];
     617    22964900 :             dfFactor = ComputeFactor(pPanBuffer[j], dfPseudoPanchro);
     618             :         }
     619             : 
     620    96132200 :         for (int i = 0; i < psOptions->nOutPansharpenedBands; i++)
     621             :         {
     622    73167300 :             WorkDataType nRawValue =
     623    73167300 :                 pUpsampledSpectralBuffer[psOptions->panOutPansharpenedBands[i] *
     624    73167300 :                                              nBandValues +
     625             :                                          j];
     626             :             WorkDataType nPansharpenedValue;
     627    73167300 :             GDALCopyWord(nRawValue * dfFactor, nPansharpenedValue);
     628             :             if constexpr (bHasBitDepth)
     629             :             {
     630           0 :                 if (nPansharpenedValue > nMaxValue)
     631           0 :                     nPansharpenedValue = nMaxValue;
     632             :             }
     633    73167300 :             GDALCopyWord(nPansharpenedValue, pDataBuf[i * nBandValues + j]);
     634             :         }
     635             :     }
     636             : }
     637             : 
     638             : /* We restrict to 64bit processors because they are guaranteed to have SSE2 */
     639             : /* Could possibly be used too on 32bit, but we would need to check at runtime */
     640             : #if defined(__x86_64) || defined(_M_X64) || defined(USE_NEON_OPTIMIZATIONS)
     641             : 
     642             : #define USE_SSE2
     643             : #include "gdalsse_priv.h"
     644             : 
     645             : template <class T, int NINPUT, int NOUTPUT>
     646          41 : size_t GDALPansharpenOperation::WeightedBroveyPositiveWeightsInternal(
     647             :     const T *pPanBuffer, const T *pUpsampledSpectralBuffer, T *pDataBuf,
     648             :     size_t nValues, size_t nBandValues, T nMaxValue) const
     649             : {
     650             :     static_assert(NINPUT == 3 || NINPUT == 4);
     651             :     static_assert(NOUTPUT == 3 || NOUTPUT == 4);
     652          41 :     const XMMReg4Double w0 =
     653          41 :         XMMReg4Double::Load1ValHighAndLow(psOptions->padfWeights + 0);
     654          41 :     const XMMReg4Double w1 =
     655          41 :         XMMReg4Double::Load1ValHighAndLow(psOptions->padfWeights + 1);
     656          41 :     const XMMReg4Double w2 =
     657          41 :         XMMReg4Double::Load1ValHighAndLow(psOptions->padfWeights + 2);
     658          41 :     [[maybe_unused]] const XMMReg4Double w3 =
     659             :         (NINPUT == 3)
     660             :             ? XMMReg4Double::Zero()
     661           8 :             : XMMReg4Double::Load1ValHighAndLow(psOptions->padfWeights + 3);
     662             : 
     663          41 :     const XMMReg4Double zero = XMMReg4Double::Zero();
     664          41 :     double dfMaxValue = nMaxValue;
     665          41 :     const XMMReg4Double maxValue =
     666             :         XMMReg4Double::Load1ValHighAndLow(&dfMaxValue);
     667             : 
     668          41 :     size_t j = 0;  // Used after for.
     669     1344937 :     for (; j + 3 < nValues; j += 4)
     670             :     {
     671     1344896 :         XMMReg4Double pseudoPanchro = zero;
     672             : 
     673     1344896 :         XMMReg4Double val0 = XMMReg4Double::Load4Val(pUpsampledSpectralBuffer +
     674      784896 :                                                      0 * nBandValues + j);
     675     1344896 :         XMMReg4Double val1 = XMMReg4Double::Load4Val(pUpsampledSpectralBuffer +
     676     1344896 :                                                      1 * nBandValues + j);
     677     1344896 :         XMMReg4Double val2 = XMMReg4Double::Load4Val(pUpsampledSpectralBuffer +
     678     1344896 :                                                      2 * nBandValues + j);
     679     1344896 :         XMMReg4Double val3;
     680             :         if constexpr (NINPUT == 4 || NOUTPUT == 4)
     681             :         {
     682      523264 :             val3 = XMMReg4Double::Load4Val(pUpsampledSpectralBuffer +
     683      523264 :                                            3 * nBandValues + j);
     684             :         }
     685             : 
     686     1344896 :         pseudoPanchro += w0 * val0;
     687     1344896 :         pseudoPanchro += w1 * val1;
     688     1344896 :         pseudoPanchro += w2 * val2;
     689             :         if constexpr (NINPUT == 4)
     690      523264 :             pseudoPanchro += w3 * val3;
     691             : 
     692             :         /* Little trick to avoid use of ternary operator due to one of the
     693             :          * branch being zero */
     694     1344896 :         XMMReg4Double factor = XMMReg4Double::And(
     695             :             XMMReg4Double::NotEquals(pseudoPanchro, zero),
     696      784896 :             XMMReg4Double::Load4Val(pPanBuffer + j) / pseudoPanchro);
     697             : 
     698     1344896 :         val0 = XMMReg4Double::Min(val0 * factor, maxValue);
     699     1344896 :         val1 = XMMReg4Double::Min(val1 * factor, maxValue);
     700     1344896 :         val2 = XMMReg4Double::Min(val2 * factor, maxValue);
     701             :         if constexpr (NOUTPUT == 4)
     702             :         {
     703      261632 :             val3 = XMMReg4Double::Min(val3 * factor, maxValue);
     704             :         }
     705     1344896 :         val0.Store4Val(pDataBuf + 0 * nBandValues + j);
     706     1344896 :         val1.Store4Val(pDataBuf + 1 * nBandValues + j);
     707     1344896 :         val2.Store4Val(pDataBuf + 2 * nBandValues + j);
     708             :         if constexpr (NOUTPUT == 4)
     709             :         {
     710      261632 :             val3.Store4Val(pDataBuf + 3 * nBandValues + j);
     711             :         }
     712             :     }
     713          41 :     return j;
     714             : }
     715             : 
     716             : #else
     717             : 
     718             : template <class T, int NINPUT, int NOUTPUT>
     719             : size_t GDALPansharpenOperation::WeightedBroveyPositiveWeightsInternal(
     720             :     const T *pPanBuffer, const T *pUpsampledSpectralBuffer, T *pDataBuf,
     721             :     size_t nValues, size_t nBandValues, T nMaxValue) const
     722             : {
     723             :     static_assert(NINPUT == 3 || NINPUT == 4);
     724             :     const double dfw0 = psOptions->padfWeights[0];
     725             :     const double dfw1 = psOptions->padfWeights[1];
     726             :     const double dfw2 = psOptions->padfWeights[2];
     727             :     // cppcheck-suppress knownConditionTrueFalse
     728             :     [[maybe_unused]] const double dfw3 =
     729             :         (NINPUT == 3) ? 0 : psOptions->padfWeights[3];
     730             :     size_t j = 0;  // Used after for.
     731             :     for (; j + 1 < nValues; j += 2)
     732             :     {
     733             :         double dfFactor = 0.0;
     734             :         double dfFactor2 = 0.0;
     735             :         double dfPseudoPanchro = 0.0;
     736             :         double dfPseudoPanchro2 = 0.0;
     737             : 
     738             :         dfPseudoPanchro += dfw0 * pUpsampledSpectralBuffer[j];
     739             :         dfPseudoPanchro2 += dfw0 * pUpsampledSpectralBuffer[j + 1];
     740             : 
     741             :         dfPseudoPanchro += dfw1 * pUpsampledSpectralBuffer[nBandValues + j];
     742             :         dfPseudoPanchro2 +=
     743             :             dfw1 * pUpsampledSpectralBuffer[nBandValues + j + 1];
     744             : 
     745             :         dfPseudoPanchro += dfw2 * pUpsampledSpectralBuffer[2 * nBandValues + j];
     746             :         dfPseudoPanchro2 +=
     747             :             dfw2 * pUpsampledSpectralBuffer[2 * nBandValues + j + 1];
     748             : 
     749             :         if constexpr (NINPUT == 4)
     750             :         {
     751             :             dfPseudoPanchro +=
     752             :                 dfw3 * pUpsampledSpectralBuffer[3 * nBandValues + j];
     753             :             dfPseudoPanchro2 +=
     754             :                 dfw3 * pUpsampledSpectralBuffer[3 * nBandValues + j + 1];
     755             :         }
     756             : 
     757             :         dfFactor = ComputeFactor(pPanBuffer[j], dfPseudoPanchro);
     758             :         dfFactor2 = ComputeFactor(pPanBuffer[j + 1], dfPseudoPanchro2);
     759             : 
     760             :         for (int i = 0; i < NOUTPUT; i++)
     761             :         {
     762             :             T nRawValue = pUpsampledSpectralBuffer[i * nBandValues + j];
     763             :             double dfTmp = nRawValue * dfFactor;
     764             :             pDataBuf[i * nBandValues + j] = ClampAndRound(dfTmp, nMaxValue);
     765             : 
     766             :             T nRawValue2 = pUpsampledSpectralBuffer[i * nBandValues + j + 1];
     767             :             double dfTmp2 = nRawValue2 * dfFactor2;
     768             :             pDataBuf[i * nBandValues + j + 1] =
     769             :                 ClampAndRound(dfTmp2, nMaxValue);
     770             :         }
     771             :     }
     772             :     return j;
     773             : }
     774             : #endif
     775             : 
     776             : template <class T>
     777          64 : void GDALPansharpenOperation::WeightedBroveyPositiveWeights(
     778             :     const T *pPanBuffer, const T *pUpsampledSpectralBuffer, T *pDataBuf,
     779             :     size_t nValues, size_t nBandValues, T nMaxValue) const
     780             : {
     781          64 :     if (psOptions->bHasNoData)
     782             :     {
     783           0 :         WeightedBroveyWithNoData<T, T>(pPanBuffer, pUpsampledSpectralBuffer,
     784             :                                        pDataBuf, nValues, nBandValues,
     785             :                                        nMaxValue);
     786           0 :         return;
     787             :     }
     788             : 
     789          64 :     if (nMaxValue == 0)
     790          58 :         nMaxValue = cpl::NumericLimits<T>::max();
     791             :     size_t j;
     792          64 :     if (psOptions->nInputSpectralBands == 3 &&
     793          33 :         psOptions->nOutPansharpenedBands == 3 &&
     794          33 :         psOptions->panOutPansharpenedBands[0] == 0 &&
     795          33 :         psOptions->panOutPansharpenedBands[1] == 1 &&
     796          33 :         psOptions->panOutPansharpenedBands[2] == 2)
     797             :     {
     798          33 :         j = WeightedBroveyPositiveWeightsInternal<T, 3, 3>(
     799             :             pPanBuffer, pUpsampledSpectralBuffer, pDataBuf, nValues,
     800             :             nBandValues, nMaxValue);
     801             :     }
     802          31 :     else if (psOptions->nInputSpectralBands == 4 &&
     803           8 :              psOptions->nOutPansharpenedBands == 4 &&
     804           4 :              psOptions->panOutPansharpenedBands[0] == 0 &&
     805           4 :              psOptions->panOutPansharpenedBands[1] == 1 &&
     806           4 :              psOptions->panOutPansharpenedBands[2] == 2 &&
     807           4 :              psOptions->panOutPansharpenedBands[3] == 3)
     808             :     {
     809           4 :         j = WeightedBroveyPositiveWeightsInternal<T, 4, 4>(
     810             :             pPanBuffer, pUpsampledSpectralBuffer, pDataBuf, nValues,
     811             :             nBandValues, nMaxValue);
     812             :     }
     813          27 :     else if (psOptions->nInputSpectralBands == 4 &&
     814           4 :              psOptions->nOutPansharpenedBands == 3 &&
     815           4 :              psOptions->panOutPansharpenedBands[0] == 0 &&
     816           4 :              psOptions->panOutPansharpenedBands[1] == 1 &&
     817           4 :              psOptions->panOutPansharpenedBands[2] == 2)
     818             :     {
     819           4 :         j = WeightedBroveyPositiveWeightsInternal<T, 4, 3>(
     820             :             pPanBuffer, pUpsampledSpectralBuffer, pDataBuf, nValues,
     821             :             nBandValues, nMaxValue);
     822             :     }
     823             :     else
     824             :     {
     825      500230 :         for (j = 0; j + 1 < nValues; j += 2)
     826             :         {
     827      500207 :             double dfFactor = 0.0;
     828      500207 :             double dfFactor2 = 0.0;
     829      500207 :             double dfPseudoPanchro = 0.0;
     830      500207 :             double dfPseudoPanchro2 = 0.0;
     831     1500418 :             for (int i = 0; i < psOptions->nInputSpectralBands; i++)
     832             :             {
     833     1000204 :                 dfPseudoPanchro +=
     834     1000204 :                     psOptions->padfWeights[i] *
     835     1000204 :                     pUpsampledSpectralBuffer[i * nBandValues + j];
     836     1000204 :                 dfPseudoPanchro2 +=
     837     1000204 :                     psOptions->padfWeights[i] *
     838     1000204 :                     pUpsampledSpectralBuffer[i * nBandValues + j + 1];
     839             :             }
     840             : 
     841      500207 :             dfFactor = ComputeFactor(pPanBuffer[j], dfPseudoPanchro);
     842      500207 :             dfFactor2 = ComputeFactor(pPanBuffer[j + 1], dfPseudoPanchro2);
     843             : 
     844     1500418 :             for (int i = 0; i < psOptions->nOutPansharpenedBands; i++)
     845             :             {
     846     1000204 :                 const T nRawValue = pUpsampledSpectralBuffer
     847     1000204 :                     [psOptions->panOutPansharpenedBands[i] * nBandValues + j];
     848     1000204 :                 const double dfTmp = nRawValue * dfFactor;
     849     1000204 :                 pDataBuf[i * nBandValues + j] = ClampAndRound(dfTmp, nMaxValue);
     850             : 
     851     1000204 :                 const T nRawValue2 = pUpsampledSpectralBuffer
     852     1000204 :                     [psOptions->panOutPansharpenedBands[i] * nBandValues + j +
     853             :                      1];
     854     1000204 :                 const double dfTmp2 = nRawValue2 * dfFactor2;
     855     1000204 :                 pDataBuf[i * nBandValues + j + 1] =
     856     1000204 :                     ClampAndRound(dfTmp2, nMaxValue);
     857             :             }
     858             :         }
     859             :     }
     860          77 :     for (; j < nValues; j++)
     861             :     {
     862          13 :         double dfFactor = 0.0;
     863          13 :         double dfPseudoPanchro = 0.0;
     864          54 :         for (int i = 0; i < psOptions->nInputSpectralBands; i++)
     865          41 :             dfPseudoPanchro += psOptions->padfWeights[i] *
     866          41 :                                pUpsampledSpectralBuffer[i * nBandValues + j];
     867          13 :         dfFactor = ComputeFactor(pPanBuffer[j], dfPseudoPanchro);
     868             : 
     869          53 :         for (int i = 0; i < psOptions->nOutPansharpenedBands; i++)
     870             :         {
     871          40 :             T nRawValue =
     872          40 :                 pUpsampledSpectralBuffer[psOptions->panOutPansharpenedBands[i] *
     873          40 :                                              nBandValues +
     874             :                                          j];
     875          40 :             double dfTmp = nRawValue * dfFactor;
     876          40 :             pDataBuf[i * nBandValues + j] = ClampAndRound(dfTmp, nMaxValue);
     877             :         }
     878             :     }
     879             : }
     880             : 
     881             : template <class WorkDataType, class OutDataType>
     882         154 : void GDALPansharpenOperation::WeightedBrovey(
     883             :     const WorkDataType *pPanBuffer,
     884             :     const WorkDataType *pUpsampledSpectralBuffer, OutDataType *pDataBuf,
     885             :     size_t nValues, size_t nBandValues, WorkDataType nMaxValue) const
     886             : {
     887         154 :     if (nMaxValue == 0)
     888         154 :         WeightedBrovey3<WorkDataType, OutDataType, FALSE>(
     889             :             pPanBuffer, pUpsampledSpectralBuffer, pDataBuf, nValues,
     890             :             nBandValues, 0);
     891             :     else
     892             :     {
     893           0 :         WeightedBrovey3<WorkDataType, OutDataType, TRUE>(
     894             :             pPanBuffer, pUpsampledSpectralBuffer, pDataBuf, nValues,
     895             :             nBandValues, nMaxValue);
     896             :     }
     897         154 : }
     898             : 
     899             : template <class T>
     900          64 : void GDALPansharpenOperation::WeightedBroveyGByteOrUInt16(
     901             :     const T *pPanBuffer, const T *pUpsampledSpectralBuffer, T *pDataBuf,
     902             :     size_t nValues, size_t nBandValues, T nMaxValue) const
     903             : {
     904          64 :     if (bPositiveWeights)
     905             :     {
     906          64 :         WeightedBroveyPositiveWeights(pPanBuffer, pUpsampledSpectralBuffer,
     907             :                                       pDataBuf, nValues, nBandValues,
     908             :                                       nMaxValue);
     909             :     }
     910           0 :     else if (nMaxValue == 0)
     911             :     {
     912           0 :         WeightedBrovey3<T, T, FALSE>(pPanBuffer, pUpsampledSpectralBuffer,
     913             :                                      pDataBuf, nValues, nBandValues, 0);
     914             :     }
     915             :     else
     916             :     {
     917           0 :         WeightedBrovey3<T, T, TRUE>(pPanBuffer, pUpsampledSpectralBuffer,
     918             :                                     pDataBuf, nValues, nBandValues, nMaxValue);
     919             :     }
     920          64 : }
     921             : 
     922             : template <>
     923          49 : void GDALPansharpenOperation::WeightedBrovey<GByte, GByte>(
     924             :     const GByte *pPanBuffer, const GByte *pUpsampledSpectralBuffer,
     925             :     GByte *pDataBuf, size_t nValues, size_t nBandValues, GByte nMaxValue) const
     926             : {
     927          49 :     WeightedBroveyGByteOrUInt16(pPanBuffer, pUpsampledSpectralBuffer, pDataBuf,
     928             :                                 nValues, nBandValues, nMaxValue);
     929          49 : }
     930             : 
     931             : template <>
     932          15 : void GDALPansharpenOperation::WeightedBrovey<GUInt16, GUInt16>(
     933             :     const GUInt16 *pPanBuffer, const GUInt16 *pUpsampledSpectralBuffer,
     934             :     GUInt16 *pDataBuf, size_t nValues, size_t nBandValues,
     935             :     GUInt16 nMaxValue) const
     936             : {
     937          15 :     WeightedBroveyGByteOrUInt16(pPanBuffer, pUpsampledSpectralBuffer, pDataBuf,
     938             :                                 nValues, nBandValues, nMaxValue);
     939          15 : }
     940             : 
     941             : template <class WorkDataType>
     942         218 : CPLErr GDALPansharpenOperation::WeightedBrovey(
     943             :     const WorkDataType *pPanBuffer,
     944             :     const WorkDataType *pUpsampledSpectralBuffer, void *pDataBuf,
     945             :     GDALDataType eBufDataType, size_t nValues, size_t nBandValues,
     946             :     WorkDataType nMaxValue) const
     947             : {
     948         218 :     switch (eBufDataType)
     949             :     {
     950          50 :         case GDT_UInt8:
     951          50 :             WeightedBrovey(pPanBuffer, pUpsampledSpectralBuffer,
     952             :                            static_cast<GByte *>(pDataBuf), nValues, nBandValues,
     953             :                            nMaxValue);
     954          50 :             break;
     955             : 
     956          16 :         case GDT_UInt16:
     957          16 :             WeightedBrovey(pPanBuffer, pUpsampledSpectralBuffer,
     958             :                            static_cast<GUInt16 *>(pDataBuf), nValues,
     959             :                            nBandValues, nMaxValue);
     960          16 :             break;
     961             : 
     962             : #ifndef LIMIT_TYPES
     963             :         case GDT_Int8:
     964             :             WeightedBrovey(pPanBuffer, pUpsampledSpectralBuffer,
     965             :                            static_cast<GInt8 *>(pDataBuf), nValues, nBandValues,
     966             :                            nMaxValue);
     967             :             break;
     968             : 
     969             :         case GDT_Int16:
     970             :             WeightedBrovey(pPanBuffer, pUpsampledSpectralBuffer,
     971             :                            static_cast<GInt16 *>(pDataBuf), nValues,
     972             :                            nBandValues, nMaxValue);
     973             :             break;
     974             : 
     975             :         case GDT_UInt32:
     976             :             WeightedBrovey(pPanBuffer, pUpsampledSpectralBuffer,
     977             :                            static_cast<GUInt32 *>(pDataBuf), nValues,
     978             :                            nBandValues, nMaxValue);
     979             :             break;
     980             : 
     981             :         case GDT_Int32:
     982             :             WeightedBrovey(pPanBuffer, pUpsampledSpectralBuffer,
     983             :                            static_cast<GInt32 *>(pDataBuf), nValues,
     984             :                            nBandValues, nMaxValue);
     985             :             break;
     986             : 
     987             :         case GDT_UInt64:
     988             :             WeightedBrovey(pPanBuffer, pUpsampledSpectralBuffer,
     989             :                            static_cast<std::uint64_t *>(pDataBuf), nValues,
     990             :                            nBandValues, nMaxValue);
     991             :             break;
     992             : 
     993             :         case GDT_Int64:
     994             :             WeightedBrovey(pPanBuffer, pUpsampledSpectralBuffer,
     995             :                            static_cast<std::int64_t *>(pDataBuf), nValues,
     996             :                            nBandValues, nMaxValue);
     997             :             break;
     998             : 
     999             :         case GDT_Float16:
    1000             :             WeightedBrovey(pPanBuffer, pUpsampledSpectralBuffer,
    1001             :                            static_cast<GFloat16 *>(pDataBuf), nValues,
    1002             :                            nBandValues, nMaxValue);
    1003             :             break;
    1004             : 
    1005             :         case GDT_Float32:
    1006             :             WeightedBrovey(pPanBuffer, pUpsampledSpectralBuffer,
    1007             :                            static_cast<float *>(pDataBuf), nValues, nBandValues,
    1008             :                            nMaxValue);
    1009             :             break;
    1010             : #endif
    1011             : 
    1012         152 :         case GDT_Float64:
    1013         152 :             WeightedBrovey(pPanBuffer, pUpsampledSpectralBuffer,
    1014             :                            static_cast<double *>(pDataBuf), nValues,
    1015             :                            nBandValues, nMaxValue);
    1016         152 :             break;
    1017             : 
    1018           0 :         default:
    1019           0 :             CPLError(CE_Failure, CPLE_NotSupported,
    1020             :                      "eBufDataType not supported");
    1021           0 :             return CE_Failure;
    1022             :             break;
    1023             :     }
    1024             : 
    1025         218 :     return CE_None;
    1026             : }
    1027             : 
    1028             : template <class WorkDataType>
    1029           6 : CPLErr GDALPansharpenOperation::WeightedBrovey(
    1030             :     const WorkDataType *pPanBuffer,
    1031             :     const WorkDataType *pUpsampledSpectralBuffer, void *pDataBuf,
    1032             :     GDALDataType eBufDataType, size_t nValues, size_t nBandValues) const
    1033             : {
    1034           6 :     switch (eBufDataType)
    1035             :     {
    1036           6 :         case GDT_UInt8:
    1037           6 :             WeightedBrovey3<WorkDataType, GByte, FALSE>(
    1038             :                 pPanBuffer, pUpsampledSpectralBuffer,
    1039             :                 static_cast<GByte *>(pDataBuf), nValues, nBandValues, 0);
    1040           6 :             break;
    1041             : 
    1042           0 :         case GDT_UInt16:
    1043           0 :             WeightedBrovey3<WorkDataType, GUInt16, FALSE>(
    1044             :                 pPanBuffer, pUpsampledSpectralBuffer,
    1045             :                 static_cast<GUInt16 *>(pDataBuf), nValues, nBandValues, 0);
    1046           0 :             break;
    1047             : 
    1048             : #ifndef LIMIT_TYPES
    1049             :         case GDT_Int8:
    1050             :             WeightedBrovey3<WorkDataType, GInt8, FALSE>(
    1051             :                 pPanBuffer, pUpsampledSpectralBuffer,
    1052             :                 static_cast<GInt8 *>(pDataBuf), nValues, nBandValues, 0);
    1053             :             break;
    1054             : 
    1055             :         case GDT_Int16:
    1056             :             WeightedBrovey3<WorkDataType, GInt16, FALSE>(
    1057             :                 pPanBuffer, pUpsampledSpectralBuffer,
    1058             :                 static_cast<GInt16 *>(pDataBuf), nValues, nBandValues, 0);
    1059             :             break;
    1060             : 
    1061             :         case GDT_UInt32:
    1062             :             WeightedBrovey3<WorkDataType, GUInt32, FALSE>(
    1063             :                 pPanBuffer, pUpsampledSpectralBuffer,
    1064             :                 static_cast<GUInt32 *>(pDataBuf), nValues, nBandValues, 0);
    1065             :             break;
    1066             : 
    1067             :         case GDT_Int32:
    1068             :             WeightedBrovey3<WorkDataType, GInt32, FALSE>(
    1069             :                 pPanBuffer, pUpsampledSpectralBuffer,
    1070             :                 static_cast<GInt32 *>(pDataBuf), nValues, nBandValues, 0);
    1071             :             break;
    1072             : 
    1073             :         case GDT_UInt64:
    1074             :             WeightedBrovey3<WorkDataType, std::uint64_t, FALSE>(
    1075             :                 pPanBuffer, pUpsampledSpectralBuffer,
    1076             :                 static_cast<std::uint64_t *>(pDataBuf), nValues, nBandValues,
    1077             :                 0);
    1078             :             break;
    1079             : 
    1080             :         case GDT_Int64:
    1081             :             WeightedBrovey3<WorkDataType, std::int64_t, FALSE>(
    1082             :                 pPanBuffer, pUpsampledSpectralBuffer,
    1083             :                 static_cast<std::int64_t *>(pDataBuf), nValues, nBandValues, 0);
    1084             :             break;
    1085             : 
    1086             :         case GDT_Float16:
    1087             :             WeightedBrovey3<WorkDataType, GFloat16, FALSE>(
    1088             :                 pPanBuffer, pUpsampledSpectralBuffer,
    1089             :                 static_cast<GFloat16 *>(pDataBuf), nValues, nBandValues, 0);
    1090             :             break;
    1091             : 
    1092             :         case GDT_Float32:
    1093             :             WeightedBrovey3<WorkDataType, float, FALSE>(
    1094             :                 pPanBuffer, pUpsampledSpectralBuffer,
    1095             :                 static_cast<float *>(pDataBuf), nValues, nBandValues, 0);
    1096             :             break;
    1097             : #endif
    1098             : 
    1099           0 :         case GDT_Float64:
    1100           0 :             WeightedBrovey3<WorkDataType, double, FALSE>(
    1101             :                 pPanBuffer, pUpsampledSpectralBuffer,
    1102             :                 static_cast<double *>(pDataBuf), nValues, nBandValues, 0);
    1103           0 :             break;
    1104             : 
    1105           0 :         default:
    1106           0 :             CPLError(CE_Failure, CPLE_NotSupported,
    1107             :                      "eBufDataType not supported");
    1108           0 :             return CE_Failure;
    1109             :             break;
    1110             :     }
    1111             : 
    1112           6 :     return CE_None;
    1113             : }
    1114             : 
    1115             : /************************************************************************/
    1116             : /*                            ClampValues()                             */
    1117             : /************************************************************************/
    1118             : 
    1119             : template <class T>
    1120           2 : static void ClampValues(T *panBuffer, size_t nValues, T nMaxVal)
    1121             : {
    1122          34 :     for (size_t i = 0; i < nValues; i++)
    1123             :     {
    1124          32 :         if (panBuffer[i] > nMaxVal)
    1125           8 :             panBuffer[i] = nMaxVal;
    1126             :     }
    1127           2 : }
    1128             : 
    1129             : /************************************************************************/
    1130             : /*                           ProcessRegion()                            */
    1131             : /************************************************************************/
    1132             : 
    1133             : /** Executes a pansharpening operation on a rectangular region of the
    1134             :  * resulting dataset.
    1135             :  *
    1136             :  * The window is expressed with respect to the dimensions of the panchromatic
    1137             :  * band.
    1138             :  *
    1139             :  * Spectral bands are upsampled and merged with the panchromatic band according
    1140             :  * to the select algorithm and options.
    1141             :  *
    1142             :  * @param nXOff pixel offset.
    1143             :  * @param nYOff pixel offset.
    1144             :  * @param nXSize width of the pansharpened region to compute.
    1145             :  * @param nYSize height of the pansharpened region to compute.
    1146             :  * @param pDataBuf output buffer. Must be nXSize * nYSize *
    1147             :  *                 GDALGetDataTypeSizeBytes(eBufDataType) *
    1148             :  *                 psOptions->nOutPansharpenedBands large.
    1149             :  *                 It begins with all values of the first output band, followed
    1150             :  *                 by values of the second output band, etc...
    1151             :  * @param eBufDataType data type of the output buffer
    1152             :  *
    1153             :  * @return CE_None in case of success, CE_Failure in case of failure.
    1154             :  *
    1155             :  */
    1156          92 : CPLErr GDALPansharpenOperation::ProcessRegion(int nXOff, int nYOff, int nXSize,
    1157             :                                               int nYSize, void *pDataBuf,
    1158             :                                               GDALDataType eBufDataType)
    1159             : {
    1160          92 :     if (psOptions == nullptr)
    1161           0 :         return CE_Failure;
    1162             : 
    1163             :     // TODO: Avoid allocating buffers each time.
    1164             :     GDALRasterBand *poPanchroBand =
    1165          92 :         GDALRasterBand::FromHandle(psOptions->hPanchroBand);
    1166          92 :     GDALDataType eWorkDataType = poPanchroBand->GetRasterDataType();
    1167             : #ifdef LIMIT_TYPES
    1168          92 :     if (eWorkDataType != GDT_UInt8 && eWorkDataType != GDT_UInt16)
    1169           6 :         eWorkDataType = GDT_Float64;
    1170             : #endif
    1171          92 :     const int nDataTypeSize = GDALGetDataTypeSizeBytes(eWorkDataType);
    1172          92 :     GByte *pUpsampledSpectralBuffer = static_cast<GByte *>(VSI_MALLOC3_VERBOSE(
    1173             :         nXSize, nYSize,
    1174             :         cpl::fits_on<int>(psOptions->nInputSpectralBands * nDataTypeSize)));
    1175             :     GByte *pPanBuffer = static_cast<GByte *>(
    1176          92 :         VSI_MALLOC3_VERBOSE(nXSize, nYSize, nDataTypeSize));
    1177          92 :     if (pUpsampledSpectralBuffer == nullptr || pPanBuffer == nullptr)
    1178             :     {
    1179           0 :         VSIFree(pUpsampledSpectralBuffer);
    1180           0 :         VSIFree(pPanBuffer);
    1181           0 :         return CE_Failure;
    1182             :     }
    1183             : 
    1184          92 :     CPLErr eErr = poPanchroBand->RasterIO(GF_Read, nXOff, nYOff, nXSize, nYSize,
    1185             :                                           pPanBuffer, nXSize, nYSize,
    1186             :                                           eWorkDataType, 0, 0, nullptr);
    1187          92 :     if (eErr != CE_None)
    1188             :     {
    1189           0 :         VSIFree(pUpsampledSpectralBuffer);
    1190           0 :         VSIFree(pPanBuffer);
    1191           0 :         return CE_Failure;
    1192             :     }
    1193             : 
    1194          92 :     int nTasks = 0;
    1195          92 :     if (poThreadPool)
    1196             :     {
    1197          45 :         nTasks = poThreadPool->GetThreadCount();
    1198          45 :         if (nTasks > nYSize)
    1199           1 :             nTasks = nYSize;
    1200             :     }
    1201             : 
    1202             :     GDALRasterIOExtraArg sExtraArg;
    1203          92 :     INIT_RASTERIO_EXTRA_ARG(sExtraArg);
    1204          92 :     const GDALRIOResampleAlg eResampleAlg = psOptions->eResampleAlg;
    1205             :     // cppcheck-suppress redundantAssignment
    1206          92 :     sExtraArg.eResampleAlg = eResampleAlg;
    1207          92 :     sExtraArg.bFloatingPointWindowValidity = TRUE;
    1208          92 :     sExtraArg.dfXOff = m_panToMSGT[0] + nXOff * m_panToMSGT[1];
    1209          92 :     sExtraArg.dfYOff = m_panToMSGT[3] + nYOff * m_panToMSGT[5];
    1210          92 :     sExtraArg.dfXSize = nXSize * m_panToMSGT[1];
    1211          92 :     sExtraArg.dfYSize = nYSize * m_panToMSGT[5];
    1212          92 :     if (sExtraArg.dfXOff + sExtraArg.dfXSize > aMSBands[0]->GetXSize())
    1213           3 :         sExtraArg.dfXSize = aMSBands[0]->GetXSize() - sExtraArg.dfXOff;
    1214          92 :     if (sExtraArg.dfYOff + sExtraArg.dfYSize > aMSBands[0]->GetYSize())
    1215           3 :         sExtraArg.dfYSize = aMSBands[0]->GetYSize() - sExtraArg.dfYOff;
    1216          92 :     int nSpectralXOff = static_cast<int>(sExtraArg.dfXOff);
    1217          92 :     int nSpectralYOff = static_cast<int>(sExtraArg.dfYOff);
    1218          92 :     int nSpectralXSize = static_cast<int>(0.49999 + sExtraArg.dfXSize);
    1219          92 :     int nSpectralYSize = static_cast<int>(0.49999 + sExtraArg.dfYSize);
    1220          92 :     if (nSpectralXOff + nSpectralXSize > aMSBands[0]->GetXSize())
    1221           0 :         nSpectralXSize = aMSBands[0]->GetXSize() - nSpectralXOff;
    1222          92 :     if (nSpectralYOff + nSpectralYSize > aMSBands[0]->GetYSize())
    1223           0 :         nSpectralYSize = aMSBands[0]->GetYSize() - nSpectralYOff;
    1224          92 :     if (nSpectralXSize == 0)
    1225          10 :         nSpectralXSize = 1;
    1226          92 :     if (nSpectralYSize == 0)
    1227          11 :         nSpectralYSize = 1;
    1228             : 
    1229             :     // When upsampling, extract the multispectral data at
    1230             :     // full resolution in a temp buffer, and then do the upsampling.
    1231          92 :     if (nSpectralXSize < nXSize && nSpectralYSize < nYSize &&
    1232          82 :         eResampleAlg != GRIORA_NearestNeighbour && nYSize > 1)
    1233             :     {
    1234             :         // Take some margin to take into account the radius of the
    1235             :         // resampling kernel.
    1236          82 :         int nXOffExtract = nSpectralXOff - nKernelRadius;
    1237          82 :         int nYOffExtract = nSpectralYOff - nKernelRadius;
    1238          82 :         int nXSizeExtract = nSpectralXSize + 1 + 2 * nKernelRadius;
    1239          82 :         int nYSizeExtract = nSpectralYSize + 1 + 2 * nKernelRadius;
    1240          82 :         if (nXOffExtract < 0)
    1241             :         {
    1242          74 :             nXSizeExtract += nXOffExtract;
    1243          74 :             nXOffExtract = 0;
    1244             :         }
    1245          82 :         if (nYOffExtract < 0)
    1246             :         {
    1247          67 :             nYSizeExtract += nYOffExtract;
    1248          67 :             nYOffExtract = 0;
    1249             :         }
    1250          82 :         if (nXOffExtract + nXSizeExtract > aMSBands[0]->GetXSize())
    1251          75 :             nXSizeExtract = aMSBands[0]->GetXSize() - nXOffExtract;
    1252          82 :         if (nYOffExtract + nYSizeExtract > aMSBands[0]->GetYSize())
    1253          68 :             nYSizeExtract = aMSBands[0]->GetYSize() - nYOffExtract;
    1254             : 
    1255          82 :         GByte *pSpectralBuffer = static_cast<GByte *>(VSI_MALLOC3_VERBOSE(
    1256             :             nXSizeExtract, nYSizeExtract,
    1257             :             cpl::fits_on<int>(psOptions->nInputSpectralBands * nDataTypeSize)));
    1258          82 :         if (pSpectralBuffer == nullptr)
    1259             :         {
    1260           0 :             VSIFree(pUpsampledSpectralBuffer);
    1261           0 :             VSIFree(pPanBuffer);
    1262           0 :             return CE_Failure;
    1263             :         }
    1264             : 
    1265          82 :         if (!anInputBands.empty())
    1266             :         {
    1267             :             // Use dataset RasterIO when possible.
    1268         146 :             eErr = aMSBands[0]->GetDataset()->RasterIO(
    1269             :                 GF_Read, nXOffExtract, nYOffExtract, nXSizeExtract,
    1270             :                 nYSizeExtract, pSpectralBuffer, nXSizeExtract, nYSizeExtract,
    1271          73 :                 eWorkDataType, static_cast<int>(anInputBands.size()),
    1272          73 :                 &anInputBands[0], 0, 0, 0, nullptr);
    1273             :         }
    1274             :         else
    1275             :         {
    1276           9 :             for (int i = 0;
    1277          22 :                  eErr == CE_None && i < psOptions->nInputSpectralBands; i++)
    1278             :             {
    1279          26 :                 eErr = aMSBands[i]->RasterIO(
    1280             :                     GF_Read, nXOffExtract, nYOffExtract, nXSizeExtract,
    1281             :                     nYSizeExtract,
    1282          13 :                     pSpectralBuffer + static_cast<size_t>(i) * nXSizeExtract *
    1283          13 :                                           nYSizeExtract * nDataTypeSize,
    1284             :                     nXSizeExtract, nYSizeExtract, eWorkDataType, 0, 0, nullptr);
    1285             :             }
    1286             :         }
    1287          82 :         if (eErr != CE_None)
    1288             :         {
    1289           0 :             VSIFree(pSpectralBuffer);
    1290           0 :             VSIFree(pUpsampledSpectralBuffer);
    1291           0 :             VSIFree(pPanBuffer);
    1292           0 :             return CE_Failure;
    1293             :         }
    1294             : 
    1295             :         // Create a MEM dataset that wraps the input buffer.
    1296          82 :         auto poMEMDS = MEMDataset::Create("", nXSizeExtract, nYSizeExtract, 0,
    1297             :                                           eWorkDataType, nullptr);
    1298             : 
    1299         324 :         for (int i = 0; i < psOptions->nInputSpectralBands; i++)
    1300             :         {
    1301         242 :             GByte *pabyBuffer =
    1302         242 :                 pSpectralBuffer + static_cast<size_t>(i) * nDataTypeSize *
    1303         242 :                                       nXSizeExtract * nYSizeExtract;
    1304         242 :             GDALRasterBandH hMEMBand = MEMCreateRasterBandEx(
    1305             :                 poMEMDS, i + 1, pabyBuffer, eWorkDataType, 0, 0, false);
    1306         242 :             poMEMDS->AddMEMBand(hMEMBand);
    1307             : 
    1308         242 :             const char *pszNBITS = aMSBands[i]->GetMetadataItem(
    1309         242 :                 GDALMD_NBITS, GDAL_MDD_IMAGE_STRUCTURE);
    1310         242 :             if (pszNBITS)
    1311           4 :                 poMEMDS->GetRasterBand(i + 1)->SetMetadataItem(
    1312           4 :                     GDALMD_NBITS, pszNBITS, GDAL_MDD_IMAGE_STRUCTURE);
    1313             : 
    1314         242 :             if (psOptions->bHasNoData)
    1315          13 :                 poMEMDS->GetRasterBand(i + 1)->SetNoDataValue(
    1316          13 :                     psOptions->dfNoData);
    1317             :         }
    1318             : 
    1319          82 :         if (nTasks <= 1)
    1320             :         {
    1321          37 :             nSpectralXOff -= nXOffExtract;
    1322          37 :             nSpectralYOff -= nYOffExtract;
    1323          37 :             sExtraArg.dfXOff -= nXOffExtract;
    1324          37 :             sExtraArg.dfYOff -= nYOffExtract;
    1325          37 :             CPL_IGNORE_RET_VAL(poMEMDS->RasterIO(
    1326             :                 GF_Read, nSpectralXOff, nSpectralYOff, nSpectralXSize,
    1327             :                 nSpectralYSize, pUpsampledSpectralBuffer, nXSize, nYSize,
    1328          37 :                 eWorkDataType, psOptions->nInputSpectralBands, nullptr, 0, 0, 0,
    1329             :                 &sExtraArg));
    1330             :         }
    1331             :         else
    1332             :         {
    1333             :             // We are abusing the contract of the GDAL API by using the
    1334             :             // MEMDataset from several threads. In this case, this is safe. In
    1335             :             // case, that would no longer be the case we could create as many
    1336             :             // MEMDataset as threads pointing to the same buffer.
    1337             : 
    1338             :             // To avoid races in threads, we query now the mask flags,
    1339             :             // so that implicit mask bands are created now.
    1340         192 :             for (int i = 0; i < poMEMDS->GetRasterCount(); i++)
    1341             :             {
    1342         147 :                 poMEMDS->GetRasterBand(i + 1)->GetMaskFlags();
    1343             :             }
    1344             : 
    1345          45 :             std::vector<GDALPansharpenResampleJob> asJobs;
    1346          45 :             asJobs.resize(nTasks);
    1347          45 :             GDALPansharpenResampleJob *pasJobs = &(asJobs[0]);
    1348             :             {
    1349          45 :                 std::vector<void *> ahJobData;
    1350          45 :                 ahJobData.resize(nTasks);
    1351             : 
    1352             : #ifdef DEBUG_TIMING
    1353             :                 struct timeval tv;
    1354             : #endif
    1355          45 :                 const double dfXOff = sExtraArg.dfXOff - nXOffExtract;
    1356             :                 const double dfXSize =
    1357          45 :                     std::min(sExtraArg.dfXSize, nXSizeExtract - dfXOff);
    1358          45 :                 const int nXOffTask = static_cast<int>(dfXOff);
    1359             :                 const int nXSizeTask =
    1360          90 :                     std::clamp(static_cast<int>(0.4999 + dfXSize), 1,
    1361          45 :                                nXSizeExtract - nXOffTask);
    1362             : 
    1363         205 :                 for (int i = 0; i < nTasks; i++)
    1364             :                 {
    1365         177 :                     const size_t iStartLine =
    1366         177 :                         (static_cast<size_t>(i) * nYSize) / nTasks;
    1367         177 :                     const size_t iNextStartLine =
    1368         177 :                         (static_cast<size_t>(i + 1) * nYSize) / nTasks;
    1369         177 :                     pasJobs[i].poMEMDS = poMEMDS;
    1370         177 :                     pasJobs[i].eResampleAlg = eResampleAlg;
    1371         177 :                     pasJobs[i].dfXOff = dfXOff;
    1372         177 :                     pasJobs[i].dfYOff = m_panToMSGT[3] +
    1373         177 :                                         (nYOff + iStartLine) * m_panToMSGT[5] -
    1374             :                                         nYOffExtract;
    1375         177 :                     pasJobs[i].dfXSize = dfXSize;
    1376         177 :                     pasJobs[i].dfYSize =
    1377         531 :                         std::min((iNextStartLine - iStartLine) * m_panToMSGT[5],
    1378         177 :                                  aMSBands[0]->GetYSize() - pasJobs[i].dfYOff);
    1379         177 :                     pasJobs[i].nXOff = nXOffTask;
    1380         177 :                     pasJobs[i].nYOff = static_cast<int>(pasJobs[i].dfYOff);
    1381         177 :                     pasJobs[i].nXSize = nXSizeTask;
    1382         177 :                     pasJobs[i].nYSize = std::clamp(
    1383         354 :                         static_cast<int>(0.4999 + pasJobs[i].dfYSize), 1,
    1384         177 :                         nYSizeExtract - pasJobs[i].nYOff);
    1385         177 :                     pasJobs[i].pBuffer = pUpsampledSpectralBuffer +
    1386         177 :                                          static_cast<size_t>(iStartLine) *
    1387         177 :                                              nXSize * nDataTypeSize;
    1388         177 :                     pasJobs[i].eDT = eWorkDataType;
    1389         177 :                     pasJobs[i].nBufXSize = nXSize;
    1390         177 :                     pasJobs[i].nBufYSize =
    1391         177 :                         static_cast<int>(iNextStartLine - iStartLine);
    1392         177 :                     pasJobs[i].nBandCount = psOptions->nInputSpectralBands;
    1393         177 :                     pasJobs[i].nBandSpace =
    1394         177 :                         static_cast<GSpacing>(nXSize) * nYSize * nDataTypeSize;
    1395             : #ifdef DEBUG_TIMING
    1396             :                     pasJobs[i].ptv = &tv;
    1397             : #endif
    1398         177 :                     pasJobs[i].eErr = CE_Failure;
    1399             : 
    1400         177 :                     ahJobData[i] = &(pasJobs[i]);
    1401             : 
    1402         177 :                     if (pasJobs[i].nYOff + pasJobs[i].nYSize == nYSizeExtract)
    1403             :                     {
    1404          17 :                         pasJobs[i].dfYSize = std::min(
    1405          51 :                             (nYSize - iStartLine) * m_panToMSGT[5],
    1406          17 :                             aMSBands[0]->GetYSize() - pasJobs[i].dfYOff);
    1407          17 :                         pasJobs[i].nBufYSize =
    1408          17 :                             nYSize - static_cast<int>(iStartLine);
    1409          17 :                         nTasks = i + 1;
    1410          17 :                         ahJobData.resize(nTasks);
    1411          17 :                         break;
    1412             :                     }
    1413             :                 }
    1414             : #ifdef DEBUG_TIMING
    1415             :                 gettimeofday(&tv, nullptr);
    1416             : #endif
    1417          45 :                 poThreadPool->SubmitJobs(PansharpenResampleJobThreadFunc,
    1418             :                                          ahJobData);
    1419          45 :                 poThreadPool->WaitCompletion();
    1420             : 
    1421         222 :                 for (int i = 0; i < nTasks; i++)
    1422             :                 {
    1423         177 :                     if (pasJobs[i].eErr == CE_Failure)
    1424             :                     {
    1425           0 :                         CPLError(CE_Failure, CPLE_AppDefined, "%s",
    1426           0 :                                  pasJobs[i].osLastErrorMsg.c_str());
    1427           0 :                         GDALClose(poMEMDS);
    1428           0 :                         VSIFree(pSpectralBuffer);
    1429           0 :                         VSIFree(pUpsampledSpectralBuffer);
    1430           0 :                         VSIFree(pPanBuffer);
    1431             : 
    1432           0 :                         return CE_Failure;
    1433             :                     }
    1434             :                 }
    1435             :             }
    1436             :         }
    1437             : 
    1438          82 :         GDALClose(poMEMDS);
    1439             : 
    1440          82 :         VSIFree(pSpectralBuffer);
    1441             :     }
    1442             :     else
    1443             :     {
    1444          10 :         if (!anInputBands.empty())
    1445             :         {
    1446             :             // Use dataset RasterIO when possible.
    1447          20 :             eErr = aMSBands[0]->GetDataset()->RasterIO(
    1448             :                 GF_Read, nSpectralXOff, nSpectralYOff, nSpectralXSize,
    1449             :                 nSpectralYSize, pUpsampledSpectralBuffer, nXSize, nYSize,
    1450          10 :                 eWorkDataType, static_cast<int>(anInputBands.size()),
    1451          10 :                 &anInputBands[0], 0, 0, 0, &sExtraArg);
    1452             :         }
    1453             :         else
    1454             :         {
    1455           0 :             for (int i = 0;
    1456           0 :                  eErr == CE_None && i < psOptions->nInputSpectralBands; i++)
    1457             :             {
    1458           0 :                 eErr = aMSBands[i]->RasterIO(
    1459             :                     GF_Read, nSpectralXOff, nSpectralYOff, nSpectralXSize,
    1460             :                     nSpectralYSize,
    1461           0 :                     pUpsampledSpectralBuffer + static_cast<size_t>(i) * nXSize *
    1462           0 :                                                    nYSize * nDataTypeSize,
    1463             :                     nXSize, nYSize, eWorkDataType, 0, 0, &sExtraArg);
    1464             :             }
    1465             :         }
    1466          10 :         if (eErr != CE_None)
    1467             :         {
    1468           0 :             VSIFree(pUpsampledSpectralBuffer);
    1469           0 :             VSIFree(pPanBuffer);
    1470           0 :             return CE_Failure;
    1471             :         }
    1472             :     }
    1473             : 
    1474             :     // In case NBITS was not set on the spectral bands, clamp the values
    1475             :     // if overshoot might have occurred.
    1476          92 :     int nBitDepth = psOptions->nBitDepth;
    1477          92 :     if (nBitDepth &&
    1478           0 :         (eResampleAlg == GRIORA_Cubic || eResampleAlg == GRIORA_CubicSpline ||
    1479             :          eResampleAlg == GRIORA_Lanczos))
    1480             :     {
    1481          12 :         for (int i = 0; i < psOptions->nInputSpectralBands; i++)
    1482             :         {
    1483           6 :             GDALRasterBand *poBand = aMSBands[i];
    1484           6 :             int nBandBitDepth = 0;
    1485             :             const char *pszNBITS =
    1486           6 :                 poBand->GetMetadataItem(GDALMD_NBITS, GDAL_MDD_IMAGE_STRUCTURE);
    1487           6 :             if (pszNBITS)
    1488           4 :                 nBandBitDepth = atoi(pszNBITS);
    1489           6 :             if (nBandBitDepth < nBitDepth)
    1490             :             {
    1491           2 :                 if (eWorkDataType == GDT_UInt8 && nBitDepth >= 0 &&
    1492             :                     nBitDepth <= 8)
    1493             :                 {
    1494           1 :                     ClampValues(
    1495             :                         reinterpret_cast<GByte *>(pUpsampledSpectralBuffer) +
    1496           1 :                             static_cast<size_t>(i) * nXSize * nYSize,
    1497           1 :                         static_cast<size_t>(nXSize) * nYSize,
    1498           1 :                         static_cast<GByte>((1 << nBitDepth) - 1));
    1499             :                 }
    1500           1 :                 else if (eWorkDataType == GDT_UInt16 && nBitDepth >= 0 &&
    1501             :                          nBitDepth <= 16)
    1502             :                 {
    1503           1 :                     ClampValues(
    1504           1 :                         reinterpret_cast<GUInt16 *>(pUpsampledSpectralBuffer) +
    1505           1 :                             static_cast<size_t>(i) * nXSize * nYSize,
    1506           1 :                         static_cast<size_t>(nXSize) * nYSize,
    1507           1 :                         static_cast<GUInt16>((1 << nBitDepth) - 1));
    1508             :                 }
    1509             : #ifndef LIMIT_TYPES
    1510             :                 else if (eWorkDataType == GDT_UInt32)
    1511             :                 {
    1512             :                     ClampValues(reinterpret_cast<GUInt32*>(pUpsampledSpectralBuffer) +
    1513             :                                 static_cast<size_t>(i) * nXSize * nYSize,
    1514             :                                 static_cast<size_t>(nXSize)*nYSize,
    1515             :                                 (static_cast<GUInt32>((1 << nBitDepth)-1));
    1516             :                 }
    1517             : #endif
    1518             :             }
    1519             :         }
    1520             :     }
    1521             : 
    1522          92 :     const GUInt32 nMaxValue = (nBitDepth >= 0 && nBitDepth <= 31)
    1523         184 :                                   ? (1U << nBitDepth) - 1
    1524             :                                   : UINT32_MAX;
    1525             : 
    1526          92 :     double *padfTempBuffer = nullptr;
    1527          92 :     GDALDataType eBufDataTypeOri = eBufDataType;
    1528          92 :     void *pDataBufOri = pDataBuf;
    1529             :     // CFloat64 is the query type used by gdallocationinfo...
    1530             : #ifdef LIMIT_TYPES
    1531          92 :     if (eBufDataType != GDT_UInt8 && eBufDataType != GDT_UInt16)
    1532             : #else
    1533             :     if (eBufDataType == GDT_CFloat64)
    1534             : #endif
    1535             :     {
    1536          50 :         padfTempBuffer = static_cast<double *>(VSI_MALLOC3_VERBOSE(
    1537             :             nXSize, nYSize, psOptions->nOutPansharpenedBands * sizeof(double)));
    1538          50 :         if (padfTempBuffer == nullptr)
    1539             :         {
    1540           0 :             VSIFree(pUpsampledSpectralBuffer);
    1541           0 :             VSIFree(pPanBuffer);
    1542           0 :             return CE_Failure;
    1543             :         }
    1544          50 :         pDataBuf = padfTempBuffer;
    1545          50 :         eBufDataType = GDT_Float64;
    1546             :     }
    1547             : 
    1548          92 :     if (nTasks > 1)
    1549             :     {
    1550          88 :         std::vector<GDALPansharpenJob> asJobs;
    1551          44 :         asJobs.resize(nTasks);
    1552          44 :         GDALPansharpenJob *pasJobs = &(asJobs[0]);
    1553             :         {
    1554          88 :             std::vector<void *> ahJobData;
    1555          44 :             ahJobData.resize(nTasks);
    1556             : #ifdef DEBUG_TIMING
    1557             :             struct timeval tv;
    1558             : #endif
    1559         220 :             for (int i = 0; i < nTasks; i++)
    1560             :             {
    1561         176 :                 const size_t iStartLine =
    1562         176 :                     (static_cast<size_t>(i) * nYSize) / nTasks;
    1563         176 :                 const size_t iNextStartLine =
    1564         176 :                     (static_cast<size_t>(i + 1) * nYSize) / nTasks;
    1565         176 :                 pasJobs[i].poPansharpenOperation = this;
    1566         176 :                 pasJobs[i].eWorkDataType = eWorkDataType;
    1567         176 :                 pasJobs[i].eBufDataType = eBufDataType;
    1568         176 :                 pasJobs[i].pPanBuffer =
    1569         176 :                     pPanBuffer + iStartLine * nXSize * nDataTypeSize;
    1570         176 :                 pasJobs[i].pUpsampledSpectralBuffer =
    1571         176 :                     pUpsampledSpectralBuffer +
    1572         176 :                     iStartLine * nXSize * nDataTypeSize;
    1573         176 :                 pasJobs[i].pDataBuf =
    1574         176 :                     static_cast<GByte *>(pDataBuf) +
    1575         352 :                     iStartLine * nXSize *
    1576         176 :                         GDALGetDataTypeSizeBytes(eBufDataType);
    1577         176 :                 pasJobs[i].nValues = (iNextStartLine - iStartLine) * nXSize;
    1578         176 :                 pasJobs[i].nBandValues = static_cast<size_t>(nXSize) * nYSize;
    1579         176 :                 pasJobs[i].nMaxValue = nMaxValue;
    1580             : #ifdef DEBUG_TIMING
    1581             :                 pasJobs[i].ptv = &tv;
    1582             : #endif
    1583         176 :                 ahJobData[i] = &(pasJobs[i]);
    1584             :             }
    1585             : #ifdef DEBUG_TIMING
    1586             :             gettimeofday(&tv, nullptr);
    1587             : #endif
    1588          44 :             poThreadPool->SubmitJobs(PansharpenJobThreadFunc, ahJobData);
    1589          44 :             poThreadPool->WaitCompletion();
    1590             :         }
    1591             : 
    1592          44 :         eErr = CE_None;
    1593         220 :         for (int i = 0; i < nTasks; i++)
    1594             :         {
    1595         176 :             if (pasJobs[i].eErr != CE_None)
    1596           0 :                 eErr = CE_Failure;
    1597             :         }
    1598             :     }
    1599             :     else
    1600             :     {
    1601          48 :         eErr = PansharpenChunk(eWorkDataType, eBufDataType, pPanBuffer,
    1602             :                                pUpsampledSpectralBuffer, pDataBuf,
    1603          48 :                                static_cast<size_t>(nXSize) * nYSize,
    1604          48 :                                static_cast<size_t>(nXSize) * nYSize, nMaxValue);
    1605             :     }
    1606             : 
    1607          92 :     if (padfTempBuffer)
    1608             :     {
    1609          50 :         GDALCopyWords64(padfTempBuffer, GDT_Float64, sizeof(double),
    1610             :                         pDataBufOri, eBufDataTypeOri,
    1611             :                         GDALGetDataTypeSizeBytes(eBufDataTypeOri),
    1612          50 :                         static_cast<size_t>(nXSize) * nYSize *
    1613          50 :                             psOptions->nOutPansharpenedBands);
    1614          50 :         VSIFree(padfTempBuffer);
    1615             :     }
    1616             : 
    1617          92 :     VSIFree(pUpsampledSpectralBuffer);
    1618          92 :     VSIFree(pPanBuffer);
    1619             : 
    1620          92 :     return eErr;
    1621             : }
    1622             : 
    1623             : /************************************************************************/
    1624             : /*                  PansharpenResampleJobThreadFunc()                   */
    1625             : /************************************************************************/
    1626             : 
    1627         177 : void GDALPansharpenOperation::PansharpenResampleJobThreadFunc(void *pUserData)
    1628             : {
    1629         177 :     GDALPansharpenResampleJob *psJob =
    1630             :         static_cast<GDALPansharpenResampleJob *>(pUserData);
    1631             : 
    1632             : #ifdef DEBUG_TIMING
    1633             :     struct timeval tv;
    1634             :     gettimeofday(&tv, nullptr);
    1635             :     const GIntBig launch_time =
    1636             :         static_cast<GIntBig>(psJob->ptv->tv_sec) * 1000000 +
    1637             :         static_cast<GIntBig>(psJob->ptv->tv_usec);
    1638             :     const GIntBig start_job = static_cast<GIntBig>(tv.tv_sec) * 1000000 +
    1639             :                               static_cast<GIntBig>(tv.tv_usec);
    1640             : #endif
    1641             : 
    1642             :     GDALRasterIOExtraArg sExtraArg;
    1643         177 :     INIT_RASTERIO_EXTRA_ARG(sExtraArg);
    1644             :     // cppcheck-suppress redundantAssignment
    1645         177 :     sExtraArg.eResampleAlg = psJob->eResampleAlg;
    1646         177 :     sExtraArg.bFloatingPointWindowValidity = TRUE;
    1647         177 :     sExtraArg.dfXOff = psJob->dfXOff;
    1648         177 :     sExtraArg.dfYOff = psJob->dfYOff;
    1649         177 :     sExtraArg.dfXSize = psJob->dfXSize;
    1650         177 :     sExtraArg.dfYSize = psJob->dfYSize;
    1651             : 
    1652         354 :     std::vector<int> anBands;
    1653         762 :     for (int i = 0; i < psJob->nBandCount; ++i)
    1654         585 :         anBands.push_back(i + 1);
    1655             :     // This call to RasterIO() in a thread to poMEMDS shared between several
    1656             :     // threads is really risky, but works given the implementation details...
    1657             :     // Do not do this at home!
    1658         177 :     psJob->eErr = psJob->poMEMDS->RasterIO(
    1659             :         GF_Read, psJob->nXOff, psJob->nYOff, psJob->nXSize, psJob->nYSize,
    1660             :         psJob->pBuffer, psJob->nBufXSize, psJob->nBufYSize, psJob->eDT,
    1661         177 :         psJob->nBandCount, anBands.data(), 0, 0, psJob->nBandSpace, &sExtraArg);
    1662         177 :     if (CPLGetLastErrorType() == CE_Failure)
    1663           0 :         psJob->osLastErrorMsg = CPLGetLastErrorMsg();
    1664             : 
    1665             : #ifdef DEBUG_TIMING
    1666             :     struct timeval tv_end;
    1667             :     gettimeofday(&tv_end, nullptr);
    1668             :     const GIntBig end = static_cast<GIntBig>(tv_end.tv_sec) * 1000000 +
    1669             :                         static_cast<GIntBig>(tv_end.tv_usec);
    1670             :     if (start_job - launch_time > 500)
    1671             :         /*ok*/ printf("Resample: Delay before start=" CPL_FRMT_GIB
    1672             :                       ", completion time=" CPL_FRMT_GIB "\n",
    1673             :                       start_job - launch_time, end - start_job);
    1674             : #endif
    1675         177 : }
    1676             : 
    1677             : /************************************************************************/
    1678             : /*                      PansharpenJobThreadFunc()                       */
    1679             : /************************************************************************/
    1680             : 
    1681         176 : void GDALPansharpenOperation::PansharpenJobThreadFunc(void *pUserData)
    1682             : {
    1683         176 :     GDALPansharpenJob *psJob = static_cast<GDALPansharpenJob *>(pUserData);
    1684             : 
    1685             : #ifdef DEBUG_TIMING
    1686             :     struct timeval tv;
    1687             :     gettimeofday(&tv, nullptr);
    1688             :     const GIntBig launch_time =
    1689             :         static_cast<GIntBig>(psJob->ptv->tv_sec) * 1000000 +
    1690             :         static_cast<GIntBig>(psJob->ptv->tv_usec);
    1691             :     const GIntBig start_job = static_cast<GIntBig>(tv.tv_sec) * 1000000 +
    1692             :                               static_cast<GIntBig>(tv.tv_usec);
    1693             : #endif
    1694             : 
    1695             : #if 0
    1696             :     for( int i = 0; i < 1000000; i++ )
    1697             :         acc += i * i;
    1698             :     psJob->eErr = CE_None;
    1699             : #else
    1700         176 :     psJob->eErr = psJob->poPansharpenOperation->PansharpenChunk(
    1701             :         psJob->eWorkDataType, psJob->eBufDataType, psJob->pPanBuffer,
    1702             :         psJob->pUpsampledSpectralBuffer, psJob->pDataBuf, psJob->nValues,
    1703             :         psJob->nBandValues, psJob->nMaxValue);
    1704             : #endif
    1705             : 
    1706             : #ifdef DEBUG_TIMING
    1707             :     struct timeval tv_end;
    1708             :     gettimeofday(&tv_end, nullptr);
    1709             :     const GIntBig end = static_cast<GIntBig>(tv_end.tv_sec) * 1000000 +
    1710             :                         static_cast<GIntBig>(tv_end.tv_usec);
    1711             :     if (start_job - launch_time > 500)
    1712             :         /*ok*/ printf("Pansharpen: Delay before start=" CPL_FRMT_GIB
    1713             :                       ", completion time=" CPL_FRMT_GIB "\n",
    1714             :                       start_job - launch_time, end - start_job);
    1715             : #endif
    1716         176 : }
    1717             : 
    1718             : /************************************************************************/
    1719             : /*                          PansharpenChunk()                           */
    1720             : /************************************************************************/
    1721             : 
    1722         224 : CPLErr GDALPansharpenOperation::PansharpenChunk(
    1723             :     GDALDataType eWorkDataType, GDALDataType eBufDataType,
    1724             :     const void *pPanBuffer, const void *pUpsampledSpectralBuffer,
    1725             :     void *pDataBuf, size_t nValues, size_t nBandValues, GUInt32 nMaxValue) const
    1726             : {
    1727         224 :     CPLErr eErr = CE_None;
    1728             : 
    1729         224 :     switch (eWorkDataType)
    1730             :     {
    1731         110 :         case GDT_UInt8:
    1732         110 :             eErr = WeightedBrovey(
    1733             :                 static_cast<const GByte *>(pPanBuffer),
    1734             :                 static_cast<const GByte *>(pUpsampledSpectralBuffer), pDataBuf,
    1735             :                 eBufDataType, nValues, nBandValues,
    1736             :                 static_cast<GByte>(nMaxValue));
    1737         110 :             break;
    1738             : 
    1739         108 :         case GDT_UInt16:
    1740         108 :             eErr = WeightedBrovey(
    1741             :                 static_cast<const GUInt16 *>(pPanBuffer),
    1742             :                 static_cast<const GUInt16 *>(pUpsampledSpectralBuffer),
    1743             :                 pDataBuf, eBufDataType, nValues, nBandValues,
    1744             :                 static_cast<GUInt16>(nMaxValue));
    1745         108 :             break;
    1746             : 
    1747             : #ifndef LIMIT_TYPES
    1748             :         case GDT_Int8:
    1749             :             eErr = WeightedBrovey(
    1750             :                 static_cast<const GInt8 *>(pPanBuffer),
    1751             :                 static_cast<const GInt8 *>(pUpsampledSpectralBuffer), pDataBuf,
    1752             :                 eBufDataType, nValues, nBandValues);
    1753             :             break;
    1754             : 
    1755             :         case GDT_Int16:
    1756             :             eErr = WeightedBrovey(
    1757             :                 static_cast<const GInt16 *>(pPanBuffer),
    1758             :                 static_cast<const GInt16 *>(pUpsampledSpectralBuffer), pDataBuf,
    1759             :                 eBufDataType, nValues, nBandValues);
    1760             :             break;
    1761             : 
    1762             :         case GDT_UInt32:
    1763             :             eErr = WeightedBrovey(
    1764             :                 static_cast<const GUInt32 *>(pPanBuffer),
    1765             :                 static_cast<const GUInt32 *>(pUpsampledSpectralBuffer),
    1766             :                 pDataBuf, eBufDataType, nValues, nBandValues, nMaxValue);
    1767             :             break;
    1768             : 
    1769             :         case GDT_Int32:
    1770             :             eErr = WeightedBrovey(
    1771             :                 static_cast<const GInt32 *>(pPanBuffer),
    1772             :                 static_cast<const GInt32 *>(pUpsampledSpectralBuffer), pDataBuf,
    1773             :                 eBufDataType, nValues, nBandValues);
    1774             :             break;
    1775             : 
    1776             :         case GDT_UInt64:
    1777             :             eErr = WeightedBrovey(
    1778             :                 static_cast<const std::uint64_t *>(pPanBuffer),
    1779             :                 static_cast<const std::uint64_t *>(pUpsampledSpectralBuffer),
    1780             :                 pDataBuf, eBufDataType, nValues, nBandValues, nMaxValue);
    1781             :             break;
    1782             : 
    1783             :         case GDT_Int64:
    1784             :             eErr = WeightedBrovey(
    1785             :                 static_cast<const std::int64_t *>(pPanBuffer),
    1786             :                 static_cast<const std::int64_t *>(pUpsampledSpectralBuffer),
    1787             :                 pDataBuf, eBufDataType, nValues, nBandValues);
    1788             :             break;
    1789             : 
    1790             :         case GDT_Float16:
    1791             :             eErr = WeightedBrovey(
    1792             :                 static_cast<const GFloat16 *>(pPanBuffer),
    1793             :                 static_cast<const GFloat16 *>(pUpsampledSpectralBuffer),
    1794             :                 pDataBuf, eBufDataType, nValues, nBandValues);
    1795             :             break;
    1796             : 
    1797             :         case GDT_Float32:
    1798             :             eErr = WeightedBrovey(
    1799             :                 static_cast<const float *>(pPanBuffer),
    1800             :                 static_cast<const float *>(pUpsampledSpectralBuffer), pDataBuf,
    1801             :                 eBufDataType, nValues, nBandValues);
    1802             :             break;
    1803             : #endif
    1804           6 :         case GDT_Float64:
    1805           6 :             eErr = WeightedBrovey(
    1806             :                 static_cast<const double *>(pPanBuffer),
    1807             :                 static_cast<const double *>(pUpsampledSpectralBuffer), pDataBuf,
    1808             :                 eBufDataType, nValues, nBandValues);
    1809           6 :             break;
    1810             : 
    1811           0 :         default:
    1812           0 :             CPLError(CE_Failure, CPLE_NotSupported,
    1813             :                      "eWorkDataType not supported");
    1814           0 :             eErr = CE_Failure;
    1815           0 :             break;
    1816             :     }
    1817             : 
    1818         224 :     return eErr;
    1819             : }
    1820             : 
    1821             : /************************************************************************/
    1822             : /*                             GetOptions()                             */
    1823             : /************************************************************************/
    1824             : 
    1825             : /** Return options.
    1826             :  * @return options.
    1827             :  */
    1828         155 : GDALPansharpenOptions *GDALPansharpenOperation::GetOptions()
    1829             : {
    1830         155 :     return psOptions;
    1831             : }
    1832             : 
    1833             : /************************************************************************/
    1834             : /*                   GDALCreatePansharpenOperation()                    */
    1835             : /************************************************************************/
    1836             : 
    1837             : /** Instantiate a pansharpening operation.
    1838             :  *
    1839             :  * The passed options are validated.
    1840             :  *
    1841             :  * @param psOptions a pansharpening option structure allocated with
    1842             :  * GDALCreatePansharpenOptions(). It is duplicated by this function.
    1843             :  * @return a valid pansharpening operation handle, or NULL in case of failure.
    1844             :  *
    1845             :  */
    1846             : 
    1847             : GDALPansharpenOperationH
    1848           0 : GDALCreatePansharpenOperation(const GDALPansharpenOptions *psOptions)
    1849             : {
    1850           0 :     GDALPansharpenOperation *psOperation = new GDALPansharpenOperation();
    1851           0 :     if (psOperation->Initialize(psOptions) == CE_None)
    1852           0 :         return reinterpret_cast<GDALPansharpenOperationH>(psOperation);
    1853           0 :     delete psOperation;
    1854           0 :     return nullptr;
    1855             : }
    1856             : 
    1857             : /************************************************************************/
    1858             : /*                   GDALDestroyPansharpenOperation()                   */
    1859             : /************************************************************************/
    1860             : 
    1861             : /** Destroy a pansharpening operation.
    1862             :  *
    1863             :  * @param hOperation a valid pansharpening operation.
    1864             :  *
    1865             :  */
    1866             : 
    1867           0 : void GDALDestroyPansharpenOperation(GDALPansharpenOperationH hOperation)
    1868             : {
    1869           0 :     delete reinterpret_cast<GDALPansharpenOperation *>(hOperation);
    1870           0 : }
    1871             : 
    1872             : /************************************************************************/
    1873             : /*                    GDALPansharpenProcessRegion()                     */
    1874             : /************************************************************************/
    1875             : 
    1876             : /** Executes a pansharpening operation on a rectangular region of the
    1877             :  * resulting dataset.
    1878             :  *
    1879             :  * The window is expressed with respect to the dimensions of the panchromatic
    1880             :  * band.
    1881             :  *
    1882             :  * Spectral bands are upsampled and merged with the panchromatic band according
    1883             :  * to the select algorithm and options.
    1884             :  *
    1885             :  * @param hOperation a valid pansharpening operation.
    1886             :  * @param nXOff pixel offset.
    1887             :  * @param nYOff pixel offset.
    1888             :  * @param nXSize width of the pansharpened region to compute.
    1889             :  * @param nYSize height of the pansharpened region to compute.
    1890             :  * @param pDataBuf output buffer. Must be nXSize * nYSize *
    1891             :  *                 GDALGetDataTypeSizeBytes(eBufDataType) *
    1892             :  *                 psOptions->nOutPansharpenedBands large.
    1893             :  *                 It begins with all values of the first output band, followed
    1894             :  *                 by values of the second output band, etc...
    1895             :  * @param eBufDataType data type of the output buffer
    1896             :  *
    1897             :  * @return CE_None in case of success, CE_Failure in case of failure.
    1898             :  *
    1899             :  */
    1900           0 : CPLErr GDALPansharpenProcessRegion(GDALPansharpenOperationH hOperation,
    1901             :                                    int nXOff, int nYOff, int nXSize, int nYSize,
    1902             :                                    void *pDataBuf, GDALDataType eBufDataType)
    1903             : {
    1904             :     return reinterpret_cast<GDALPansharpenOperation *>(hOperation)
    1905           0 :         ->ProcessRegion(nXOff, nYOff, nXSize, nYSize, pDataBuf, eBufDataType);
    1906             : }

Generated by: LCOV version 1.14