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

Generated by: LCOV version 1.14