LCOV - code coverage report
Current view: top level - alg - gdalwarper.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 710 845 84.0 %
Date: 2025-02-20 10:14:44 Functions: 22 23 95.7 %

          Line data    Source code
       1             : /******************************************************************************
       2             :  *
       3             :  * Project:  High Performance Image Reprojector
       4             :  * Purpose:  Implementation of high level convenience APIs for warper.
       5             :  * Author:   Frank Warmerdam, warmerdam@pobox.com
       6             :  *
       7             :  ******************************************************************************
       8             :  * Copyright (c) 2003, Frank Warmerdam <warmerdam@pobox.com>
       9             :  * Copyright (c) 2008-2012, Even Rouault <even dot rouault at spatialys.com>
      10             :  *
      11             :  * SPDX-License-Identifier: MIT
      12             :  ****************************************************************************/
      13             : 
      14             : #include "cpl_port.h"
      15             : #include "gdalwarper.h"
      16             : 
      17             : #include <stdlib.h>
      18             : #include <string.h>
      19             : 
      20             : #include <algorithm>
      21             : #include <cmath>
      22             : #include <limits>
      23             : 
      24             : #include "cpl_conv.h"
      25             : #include "cpl_error.h"
      26             : #include "cpl_float.h"
      27             : #include "cpl_mask.h"
      28             : #include "cpl_minixml.h"
      29             : #include "cpl_progress.h"
      30             : #include "cpl_string.h"
      31             : #include "cpl_vsi.h"
      32             : #include "gdal.h"
      33             : #include "gdal_priv.h"
      34             : #include "ogr_api.h"
      35             : #include "ogr_core.h"
      36             : #include "vrtdataset.h"  // for VRTSerializeNoData
      37             : 
      38             : #if (defined(__x86_64) || defined(_M_X64))
      39             : #include <emmintrin.h>
      40             : #endif
      41             : 
      42             : /************************************************************************/
      43             : /*                         GDALReprojectImage()                         */
      44             : /************************************************************************/
      45             : 
      46             : /**
      47             :  * Reproject image.
      48             :  *
      49             :  * This is a convenience function utilizing the GDALWarpOperation class to
      50             :  * reproject an image from a source to a destination.  In particular, this
      51             :  * function takes care of establishing the transformation function to
      52             :  * implement the reprojection, and will default a variety of other
      53             :  * warp options.
      54             :  *
      55             :  * Nodata values set on destination dataset are taken into account.
      56             :  *
      57             :  * No metadata, projection info, or color tables are transferred
      58             :  * to the output file. Source overviews are not considered.
      59             :  *
      60             :  * For more advanced warping capabilities, consider using GDALWarp().
      61             :  *
      62             :  * @param hSrcDS the source image file.
      63             :  * @param pszSrcWKT the source projection.  If NULL the source projection
      64             :  * is read from from hSrcDS.
      65             :  * @param hDstDS the destination image file.
      66             :  * @param pszDstWKT the destination projection.  If NULL the destination
      67             :  * projection will be read from hDstDS.
      68             :  * @param eResampleAlg the type of resampling to use.
      69             :  * @param dfWarpMemoryLimit the amount of memory (in bytes) that the warp
      70             :  * API is allowed to use for caching.  This is in addition to the memory
      71             :  * already allocated to the GDAL caching (as per GDALSetCacheMax()).  May be
      72             :  * 0.0 to use default memory settings.
      73             :  * @param dfMaxError maximum error measured in input pixels that is allowed
      74             :  * in approximating the transformation (0.0 for exact calculations).
      75             :  * @param pfnProgress a GDALProgressFunc() compatible callback function for
      76             :  * reporting progress or NULL.
      77             :  * @param pProgressArg argument to be passed to pfnProgress.  May be NULL.
      78             :  * @param psOptions warp options, normally NULL.
      79             :  *
      80             :  * @return CE_None on success or CE_Failure if something goes wrong.
      81             :  * @see GDALWarp()
      82             :  */
      83             : 
      84          90 : CPLErr CPL_STDCALL GDALReprojectImage(
      85             :     GDALDatasetH hSrcDS, const char *pszSrcWKT, GDALDatasetH hDstDS,
      86             :     const char *pszDstWKT, GDALResampleAlg eResampleAlg,
      87             :     CPL_UNUSED double dfWarpMemoryLimit, double dfMaxError,
      88             :     GDALProgressFunc pfnProgress, void *pProgressArg,
      89             :     GDALWarpOptions *psOptions)
      90             : 
      91             : {
      92             :     /* -------------------------------------------------------------------- */
      93             :     /*      Setup a reprojection based transformer.                         */
      94             :     /* -------------------------------------------------------------------- */
      95          90 :     void *hTransformArg = GDALCreateGenImgProjTransformer(
      96             :         hSrcDS, pszSrcWKT, hDstDS, pszDstWKT, TRUE, 1000.0, 0);
      97             : 
      98          90 :     if (hTransformArg == nullptr)
      99           0 :         return CE_Failure;
     100             : 
     101             :     /* -------------------------------------------------------------------- */
     102             :     /*      Create a copy of the user provided options, or a defaulted      */
     103             :     /*      options structure.                                              */
     104             :     /* -------------------------------------------------------------------- */
     105             :     GDALWarpOptions *psWOptions = psOptions == nullptr
     106          90 :                                       ? GDALCreateWarpOptions()
     107           1 :                                       : GDALCloneWarpOptions(psOptions);
     108             : 
     109          90 :     psWOptions->eResampleAlg = eResampleAlg;
     110             : 
     111             :     /* -------------------------------------------------------------------- */
     112             :     /*      Set transform.                                                  */
     113             :     /* -------------------------------------------------------------------- */
     114          90 :     if (dfMaxError > 0.0)
     115             :     {
     116           3 :         psWOptions->pTransformerArg = GDALCreateApproxTransformer(
     117             :             GDALGenImgProjTransform, hTransformArg, dfMaxError);
     118             : 
     119           3 :         psWOptions->pfnTransformer = GDALApproxTransform;
     120             :     }
     121             :     else
     122             :     {
     123          87 :         psWOptions->pfnTransformer = GDALGenImgProjTransform;
     124          87 :         psWOptions->pTransformerArg = hTransformArg;
     125             :     }
     126             : 
     127             :     /* -------------------------------------------------------------------- */
     128             :     /*      Set file and band mapping.                                      */
     129             :     /* -------------------------------------------------------------------- */
     130          90 :     psWOptions->hSrcDS = hSrcDS;
     131          90 :     psWOptions->hDstDS = hDstDS;
     132             : 
     133          90 :     int nSrcBands = GDALGetRasterCount(hSrcDS);
     134             :     {
     135          90 :         GDALRasterBandH hBand = GDALGetRasterBand(hSrcDS, nSrcBands);
     136          90 :         if (hBand && GDALGetRasterColorInterpretation(hBand) == GCI_AlphaBand)
     137             :         {
     138          12 :             psWOptions->nSrcAlphaBand = nSrcBands;
     139          12 :             nSrcBands--;
     140             :         }
     141             :     }
     142             : 
     143          90 :     int nDstBands = GDALGetRasterCount(hDstDS);
     144             :     {
     145          90 :         GDALRasterBandH hBand = GDALGetRasterBand(hDstDS, nDstBands);
     146          90 :         if (hBand && GDALGetRasterColorInterpretation(hBand) == GCI_AlphaBand)
     147             :         {
     148          12 :             psWOptions->nDstAlphaBand = nDstBands;
     149          12 :             nDstBands--;
     150             :         }
     151             :     }
     152             : 
     153          90 :     GDALWarpInitDefaultBandMapping(psWOptions, std::min(nSrcBands, nDstBands));
     154             : 
     155             :     /* -------------------------------------------------------------------- */
     156             :     /*      Set source nodata values if the source dataset seems to have    */
     157             :     /*      any. Same for target nodata values                              */
     158             :     /* -------------------------------------------------------------------- */
     159         204 :     for (int iBand = 0; iBand < psWOptions->nBandCount; iBand++)
     160             :     {
     161         114 :         GDALRasterBandH hBand = GDALGetRasterBand(hSrcDS, iBand + 1);
     162             : 
     163         114 :         int bGotNoData = FALSE;
     164         114 :         double dfNoDataValue = GDALGetRasterNoDataValue(hBand, &bGotNoData);
     165         114 :         if (bGotNoData)
     166             :         {
     167           7 :             GDALWarpInitSrcNoDataReal(psWOptions, -1.1e20);
     168           7 :             psWOptions->padfSrcNoDataReal[iBand] = dfNoDataValue;
     169             :         }
     170             : 
     171             :         // Deal with target band.
     172         114 :         hBand = GDALGetRasterBand(hDstDS, iBand + 1);
     173             : 
     174         114 :         dfNoDataValue = GDALGetRasterNoDataValue(hBand, &bGotNoData);
     175         114 :         if (bGotNoData)
     176             :         {
     177           2 :             GDALWarpInitDstNoDataReal(psWOptions, -1.1e20);
     178           2 :             psWOptions->padfDstNoDataReal[iBand] = dfNoDataValue;
     179             :         }
     180             :     }
     181             : 
     182             :     /* -------------------------------------------------------------------- */
     183             :     /*      Set the progress function.                                      */
     184             :     /* -------------------------------------------------------------------- */
     185          90 :     if (pfnProgress != nullptr)
     186             :     {
     187           3 :         psWOptions->pfnProgress = pfnProgress;
     188           3 :         psWOptions->pProgressArg = pProgressArg;
     189             :     }
     190             : 
     191             :     /* -------------------------------------------------------------------- */
     192             :     /*      Create a warp options based on the options.                     */
     193             :     /* -------------------------------------------------------------------- */
     194          90 :     GDALWarpOperation oWarper;
     195          90 :     CPLErr eErr = oWarper.Initialize(psWOptions);
     196             : 
     197          90 :     if (eErr == CE_None)
     198          90 :         eErr = oWarper.ChunkAndWarpImage(0, 0, GDALGetRasterXSize(hDstDS),
     199             :                                          GDALGetRasterYSize(hDstDS));
     200             : 
     201             :     /* -------------------------------------------------------------------- */
     202             :     /*      Cleanup.                                                        */
     203             :     /* -------------------------------------------------------------------- */
     204          90 :     GDALDestroyGenImgProjTransformer(hTransformArg);
     205             : 
     206          90 :     if (dfMaxError > 0.0)
     207           3 :         GDALDestroyApproxTransformer(psWOptions->pTransformerArg);
     208             : 
     209          90 :     GDALDestroyWarpOptions(psWOptions);
     210             : 
     211          90 :     return eErr;
     212             : }
     213             : 
     214             : /************************************************************************/
     215             : /*                    GDALCreateAndReprojectImage()                     */
     216             : /*                                                                      */
     217             : /*      This is a "quicky" reprojection API.                            */
     218             : /************************************************************************/
     219             : 
     220             : /** Reproject an image and create the target reprojected image */
     221           0 : CPLErr CPL_STDCALL GDALCreateAndReprojectImage(
     222             :     GDALDatasetH hSrcDS, const char *pszSrcWKT, const char *pszDstFilename,
     223             :     const char *pszDstWKT, GDALDriverH hDstDriver, char **papszCreateOptions,
     224             :     GDALResampleAlg eResampleAlg, double dfWarpMemoryLimit, double dfMaxError,
     225             :     GDALProgressFunc pfnProgress, void *pProgressArg,
     226             :     GDALWarpOptions *psOptions)
     227             : 
     228             : {
     229           0 :     VALIDATE_POINTER1(hSrcDS, "GDALCreateAndReprojectImage", CE_Failure);
     230             : 
     231             :     /* -------------------------------------------------------------------- */
     232             :     /*      Default a few parameters.                                       */
     233             :     /* -------------------------------------------------------------------- */
     234           0 :     if (hDstDriver == nullptr)
     235             :     {
     236           0 :         hDstDriver = GDALGetDriverByName("GTiff");
     237           0 :         if (hDstDriver == nullptr)
     238             :         {
     239           0 :             CPLError(CE_Failure, CPLE_AppDefined,
     240             :                      "GDALCreateAndReprojectImage needs GTiff driver");
     241           0 :             return CE_Failure;
     242             :         }
     243             :     }
     244             : 
     245           0 :     if (pszSrcWKT == nullptr)
     246           0 :         pszSrcWKT = GDALGetProjectionRef(hSrcDS);
     247             : 
     248           0 :     if (pszDstWKT == nullptr)
     249           0 :         pszDstWKT = pszSrcWKT;
     250             : 
     251             :     /* -------------------------------------------------------------------- */
     252             :     /*      Create a transformation object from the source to               */
     253             :     /*      destination coordinate system.                                  */
     254             :     /* -------------------------------------------------------------------- */
     255           0 :     void *hTransformArg = GDALCreateGenImgProjTransformer(
     256             :         hSrcDS, pszSrcWKT, nullptr, pszDstWKT, TRUE, 1000.0, 0);
     257             : 
     258           0 :     if (hTransformArg == nullptr)
     259           0 :         return CE_Failure;
     260             : 
     261             :     /* -------------------------------------------------------------------- */
     262             :     /*      Get approximate output definition.                              */
     263             :     /* -------------------------------------------------------------------- */
     264           0 :     double adfDstGeoTransform[6] = {};
     265           0 :     int nPixels = 0;
     266           0 :     int nLines = 0;
     267             : 
     268           0 :     if (GDALSuggestedWarpOutput(hSrcDS, GDALGenImgProjTransform, hTransformArg,
     269             :                                 adfDstGeoTransform, &nPixels,
     270           0 :                                 &nLines) != CE_None)
     271           0 :         return CE_Failure;
     272             : 
     273           0 :     GDALDestroyGenImgProjTransformer(hTransformArg);
     274             : 
     275             :     /* -------------------------------------------------------------------- */
     276             :     /*      Create the output file.                                         */
     277             :     /* -------------------------------------------------------------------- */
     278           0 :     GDALDatasetH hDstDS = GDALCreate(
     279             :         hDstDriver, pszDstFilename, nPixels, nLines, GDALGetRasterCount(hSrcDS),
     280             :         GDALGetRasterDataType(GDALGetRasterBand(hSrcDS, 1)),
     281             :         papszCreateOptions);
     282             : 
     283           0 :     if (hDstDS == nullptr)
     284           0 :         return CE_Failure;
     285             : 
     286             :     /* -------------------------------------------------------------------- */
     287             :     /*      Write out the projection definition.                            */
     288             :     /* -------------------------------------------------------------------- */
     289           0 :     GDALSetProjection(hDstDS, pszDstWKT);
     290           0 :     GDALSetGeoTransform(hDstDS, adfDstGeoTransform);
     291             : 
     292             :     /* -------------------------------------------------------------------- */
     293             :     /*      Perform the reprojection.                                       */
     294             :     /* -------------------------------------------------------------------- */
     295           0 :     CPLErr eErr = GDALReprojectImage(
     296             :         hSrcDS, pszSrcWKT, hDstDS, pszDstWKT, eResampleAlg, dfWarpMemoryLimit,
     297             :         dfMaxError, pfnProgress, pProgressArg, psOptions);
     298             : 
     299           0 :     GDALClose(hDstDS);
     300             : 
     301           0 :     return eErr;
     302             : }
     303             : 
     304             : /************************************************************************/
     305             : /*                       GDALWarpNoDataMaskerT()                        */
     306             : /************************************************************************/
     307             : 
     308             : template <class T>
     309         714 : static CPLErr GDALWarpNoDataMaskerT(const double *padfNoData, size_t nPixels,
     310             :                                     const T *pData, GUInt32 *panValidityMask,
     311             :                                     int *pbOutAllValid)
     312             : {
     313             :     // Nothing to do if value is out of range.
     314         714 :     if (padfNoData[0] < cpl::NumericLimits<T>::min() ||
     315        1426 :         padfNoData[0] > cpl::NumericLimits<T>::max() + 0.000001 ||
     316         712 :         padfNoData[1] != 0.0)
     317             :     {
     318           2 :         *pbOutAllValid = TRUE;
     319           2 :         return CE_None;
     320             :     }
     321             : 
     322         712 :     const int nNoData = static_cast<int>(floor(padfNoData[0] + 0.000001));
     323         712 :     int bAllValid = TRUE;
     324    92288679 :     for (size_t iOffset = 0; iOffset < nPixels; ++iOffset)
     325             :     {
     326    92287921 :         if (pData[iOffset] == nNoData)
     327             :         {
     328    87182425 :             bAllValid = FALSE;
     329    87182425 :             CPLMaskClear(panValidityMask, iOffset);
     330             :         }
     331             :     }
     332         712 :     *pbOutAllValid = bAllValid;
     333             : 
     334         712 :     return CE_None;
     335             : }
     336             : 
     337             : /************************************************************************/
     338             : /*                        GDALWarpNoDataMasker()                        */
     339             : /*                                                                      */
     340             : /*      GDALMaskFunc for establishing a validity mask for a source      */
     341             : /*      band based on a provided NODATA value.                          */
     342             : /************************************************************************/
     343             : 
     344         839 : CPLErr GDALWarpNoDataMasker(void *pMaskFuncArg, int nBandCount,
     345             :                             GDALDataType eType, int /* nXOff */,
     346             :                             int /* nYOff */, int nXSize, int nYSize,
     347             :                             GByte **ppImageData, int bMaskIsFloat,
     348             :                             void *pValidityMask, int *pbOutAllValid)
     349             : 
     350             : {
     351         839 :     const double *padfNoData = static_cast<double *>(pMaskFuncArg);
     352         839 :     GUInt32 *panValidityMask = static_cast<GUInt32 *>(pValidityMask);
     353         839 :     const size_t nPixels = static_cast<size_t>(nXSize) * nYSize;
     354             : 
     355         839 :     *pbOutAllValid = FALSE;
     356             : 
     357         839 :     if (nBandCount != 1 || bMaskIsFloat)
     358             :     {
     359           0 :         CPLError(
     360             :             CE_Failure, CPLE_AppDefined,
     361             :             "Invalid nBandCount or bMaskIsFloat argument in SourceNoDataMask");
     362           0 :         return CE_Failure;
     363             :     }
     364             : 
     365         839 :     switch (eType)
     366             :     {
     367         654 :         case GDT_Byte:
     368         654 :             return GDALWarpNoDataMaskerT(padfNoData, nPixels,
     369             :                                          *ppImageData,  // Already a GByte *.
     370         654 :                                          panValidityMask, pbOutAllValid);
     371             : 
     372          56 :         case GDT_Int16:
     373          56 :             return GDALWarpNoDataMaskerT(
     374             :                 padfNoData, nPixels, reinterpret_cast<GInt16 *>(*ppImageData),
     375          56 :                 panValidityMask, pbOutAllValid);
     376             : 
     377           4 :         case GDT_UInt16:
     378           4 :             return GDALWarpNoDataMaskerT(
     379             :                 padfNoData, nPixels, reinterpret_cast<GUInt16 *>(*ppImageData),
     380           4 :                 panValidityMask, pbOutAllValid);
     381             : 
     382          78 :         case GDT_Float32:
     383             :         {
     384          78 :             const float fNoData = static_cast<float>(padfNoData[0]);
     385          78 :             const float *pafData = reinterpret_cast<float *>(*ppImageData);
     386          78 :             const bool bIsNoDataNan = CPL_TO_BOOL(std::isnan(fNoData));
     387             : 
     388             :             // Nothing to do if value is out of range.
     389          78 :             if (padfNoData[1] != 0.0)
     390             :             {
     391           0 :                 *pbOutAllValid = TRUE;
     392           0 :                 return CE_None;
     393             :             }
     394             : 
     395          78 :             int bAllValid = TRUE;
     396       45276 :             for (size_t iOffset = 0; iOffset < nPixels; ++iOffset)
     397             :             {
     398       45198 :                 float fVal = pafData[iOffset];
     399       90166 :                 if ((bIsNoDataNan && std::isnan(fVal)) ||
     400       44968 :                     (!bIsNoDataNan && ARE_REAL_EQUAL(fVal, fNoData)))
     401             :                 {
     402       37394 :                     bAllValid = FALSE;
     403       37394 :                     CPLMaskClear(panValidityMask, iOffset);
     404             :                 }
     405             :             }
     406          78 :             *pbOutAllValid = bAllValid;
     407             :         }
     408          78 :         break;
     409             : 
     410          19 :         case GDT_Float64:
     411             :         {
     412          19 :             const double dfNoData = padfNoData[0];
     413          19 :             const double *padfData = reinterpret_cast<double *>(*ppImageData);
     414          19 :             const bool bIsNoDataNan = CPL_TO_BOOL(std::isnan(dfNoData));
     415             : 
     416             :             // Nothing to do if value is out of range.
     417          19 :             if (padfNoData[1] != 0.0)
     418             :             {
     419           0 :                 *pbOutAllValid = TRUE;
     420           0 :                 return CE_None;
     421             :             }
     422             : 
     423          19 :             int bAllValid = TRUE;
     424        2905 :             for (size_t iOffset = 0; iOffset < nPixels; ++iOffset)
     425             :             {
     426        2886 :                 double dfVal = padfData[iOffset];
     427        3401 :                 if ((bIsNoDataNan && std::isnan(dfVal)) ||
     428         515 :                     (!bIsNoDataNan && ARE_REAL_EQUAL(dfVal, dfNoData)))
     429             :                 {
     430        2502 :                     bAllValid = FALSE;
     431        2502 :                     CPLMaskClear(panValidityMask, iOffset);
     432             :                 }
     433             :             }
     434          19 :             *pbOutAllValid = bAllValid;
     435             :         }
     436          19 :         break;
     437             : 
     438          28 :         default:
     439             :         {
     440          28 :             const int nWordSize = GDALGetDataTypeSizeBytes(eType);
     441             : 
     442             :             const bool bIsNoDataRealNan =
     443          28 :                 CPL_TO_BOOL(std::isnan(padfNoData[0]));
     444             : 
     445             :             double *padfWrk =
     446          28 :                 static_cast<double *>(CPLMalloc(nXSize * sizeof(double) * 2));
     447          28 :             int bAllValid = TRUE;
     448         314 :             for (int iLine = 0; iLine < nYSize; iLine++)
     449             :             {
     450         286 :                 GDALCopyWords((*ppImageData) + nWordSize * iLine * nXSize,
     451             :                               eType, nWordSize, padfWrk, GDT_CFloat64, 16,
     452             :                               nXSize);
     453             : 
     454        6522 :                 for (int iPixel = 0; iPixel < nXSize; ++iPixel)
     455             :                 {
     456           0 :                     if (((bIsNoDataRealNan &&
     457       12472 :                           std::isnan(padfWrk[iPixel * 2])) ||
     458       12472 :                          (!bIsNoDataRealNan &&
     459        6236 :                           ARE_REAL_EQUAL(padfWrk[iPixel * 2], padfNoData[0]))))
     460             :                     {
     461         933 :                         size_t iOffset =
     462         933 :                             iPixel + static_cast<size_t>(iLine) * nXSize;
     463             : 
     464         933 :                         bAllValid = FALSE;
     465         933 :                         CPLMaskClear(panValidityMask, iOffset);
     466             :                     }
     467             :                 }
     468             :             }
     469          28 :             *pbOutAllValid = bAllValid;
     470             : 
     471          28 :             CPLFree(padfWrk);
     472             :         }
     473          28 :         break;
     474             :     }
     475             : 
     476         125 :     return CE_None;
     477             : }
     478             : 
     479             : /************************************************************************/
     480             : /*                       GDALWarpSrcAlphaMasker()                       */
     481             : /*                                                                      */
     482             : /*      GDALMaskFunc for reading source simple 8bit alpha mask          */
     483             : /*      information and building a floating point density mask from     */
     484             : /*      it.                                                             */
     485             : /************************************************************************/
     486             : 
     487          79 : CPLErr GDALWarpSrcAlphaMasker(void *pMaskFuncArg, int /* nBandCount */,
     488             :                               GDALDataType /* eType */, int nXOff, int nYOff,
     489             :                               int nXSize, int nYSize, GByte ** /*ppImageData */,
     490             :                               int bMaskIsFloat, void *pValidityMask,
     491             :                               int *pbOutAllOpaque)
     492             : 
     493             : {
     494          79 :     GDALWarpOptions *psWO = static_cast<GDALWarpOptions *>(pMaskFuncArg);
     495          79 :     float *pafMask = static_cast<float *>(pValidityMask);
     496          79 :     *pbOutAllOpaque = FALSE;
     497          79 :     const size_t nPixels = static_cast<size_t>(nXSize) * nYSize;
     498             : 
     499             :     /* -------------------------------------------------------------------- */
     500             :     /*      Do some minimal checking.                                       */
     501             :     /* -------------------------------------------------------------------- */
     502          79 :     if (!bMaskIsFloat)
     503             :     {
     504           0 :         CPLAssert(false);
     505             :         return CE_Failure;
     506             :     }
     507             : 
     508          79 :     if (psWO == nullptr || psWO->nSrcAlphaBand < 1)
     509             :     {
     510           0 :         CPLAssert(false);
     511             :         return CE_Failure;
     512             :     }
     513             : 
     514             :     /* -------------------------------------------------------------------- */
     515             :     /*      Read the alpha band.                                            */
     516             :     /* -------------------------------------------------------------------- */
     517             :     GDALRasterBandH hAlphaBand =
     518          79 :         GDALGetRasterBand(psWO->hSrcDS, psWO->nSrcAlphaBand);
     519          79 :     if (hAlphaBand == nullptr)
     520           0 :         return CE_Failure;
     521             : 
     522             :     // Rescale.
     523          79 :     const float inv_alpha_max = static_cast<float>(
     524          79 :         1.0 / CPLAtof(CSLFetchNameValueDef(psWO->papszWarpOptions,
     525          79 :                                            "SRC_ALPHA_MAX", "255")));
     526          79 :     bool bOutAllOpaque = true;
     527             : 
     528          79 :     size_t iPixel = 0;
     529             :     CPLErr eErr;
     530             : 
     531             : #if (defined(__x86_64) || defined(_M_X64))
     532          79 :     GDALDataType eDT = GDALGetRasterDataType(hAlphaBand);
     533             :     // Make sure that pafMask is at least 8-byte aligned, which should
     534             :     // normally be always the case if being a ptr returned by malloc().
     535          79 :     if ((eDT == GDT_Byte || eDT == GDT_UInt16) && CPL_IS_ALIGNED(pafMask, 8))
     536             :     {
     537             :         // Read data.
     538         136 :         eErr = GDALRasterIOEx(
     539             :             hAlphaBand, GF_Read, nXOff, nYOff, nXSize, nYSize, pafMask, nXSize,
     540             :             nYSize, eDT, static_cast<GSpacing>(sizeof(int)),
     541          68 :             static_cast<GSpacing>(sizeof(int)) * nXSize, nullptr);
     542             : 
     543          68 :         if (eErr != CE_None)
     544           0 :             return eErr;
     545             : 
     546             :         // Make sure we have the correct alignment before doing SSE
     547             :         // On Linux x86_64, the alignment should be always correct due
     548             :         // the alignment of malloc() being 16 byte.
     549          68 :         const GUInt32 mask = (eDT == GDT_Byte) ? 0xff : 0xffff;
     550          68 :         if (!CPL_IS_ALIGNED(pafMask, 16))
     551             :         {
     552           0 :             pafMask[iPixel] =
     553           0 :                 (reinterpret_cast<GUInt32 *>(pafMask)[iPixel] & mask) *
     554           0 :                 inv_alpha_max;
     555           0 :             if (pafMask[iPixel] >= 1.0f)
     556           0 :                 pafMask[iPixel] = 1.0f;
     557             :             else
     558           0 :                 bOutAllOpaque = false;
     559           0 :             iPixel++;
     560             :         }
     561          68 :         CPLAssert(CPL_IS_ALIGNED(pafMask + iPixel, 16));
     562          68 :         const __m128 xmm_inverse_alpha_max = _mm_load1_ps(&inv_alpha_max);
     563          68 :         const float one_single = 1.0f;
     564          68 :         const __m128 xmm_one = _mm_load1_ps(&one_single);
     565         136 :         const __m128i xmm_i_mask = _mm_set1_epi32(mask);
     566          68 :         __m128 xmmMaskNonOpaque0 = _mm_setzero_ps();
     567          68 :         __m128 xmmMaskNonOpaque1 = _mm_setzero_ps();
     568          68 :         __m128 xmmMaskNonOpaque2 = _mm_setzero_ps();
     569      629236 :         for (; iPixel + 6 * 4 - 1 < nPixels; iPixel += 6 * 4)
     570             :         {
     571     1258340 :             __m128 xmm_mask0 = _mm_cvtepi32_ps(_mm_and_si128(
     572             :                 xmm_i_mask, _mm_load_si128(reinterpret_cast<__m128i *>(
     573      629168 :                                 pafMask + iPixel + 4 * 0))));
     574     1258340 :             __m128 xmm_mask1 = _mm_cvtepi32_ps(_mm_and_si128(
     575             :                 xmm_i_mask, _mm_load_si128(reinterpret_cast<__m128i *>(
     576      629168 :                                 pafMask + iPixel + 4 * 1))));
     577     1258340 :             __m128 xmm_mask2 = _mm_cvtepi32_ps(_mm_and_si128(
     578             :                 xmm_i_mask, _mm_load_si128(reinterpret_cast<__m128i *>(
     579      629168 :                                 pafMask + iPixel + 4 * 2))));
     580     1258340 :             __m128 xmm_mask3 = _mm_cvtepi32_ps(_mm_and_si128(
     581             :                 xmm_i_mask, _mm_load_si128(reinterpret_cast<__m128i *>(
     582      629168 :                                 pafMask + iPixel + 4 * 3))));
     583     1258340 :             __m128 xmm_mask4 = _mm_cvtepi32_ps(_mm_and_si128(
     584             :                 xmm_i_mask, _mm_load_si128(reinterpret_cast<__m128i *>(
     585      629168 :                                 pafMask + iPixel + 4 * 4))));
     586     1887500 :             __m128 xmm_mask5 = _mm_cvtepi32_ps(_mm_and_si128(
     587             :                 xmm_i_mask, _mm_load_si128(reinterpret_cast<__m128i *>(
     588      629168 :                                 pafMask + iPixel + 4 * 5))));
     589      629168 :             xmm_mask0 = _mm_mul_ps(xmm_mask0, xmm_inverse_alpha_max);
     590      629168 :             xmm_mask1 = _mm_mul_ps(xmm_mask1, xmm_inverse_alpha_max);
     591      629168 :             xmm_mask2 = _mm_mul_ps(xmm_mask2, xmm_inverse_alpha_max);
     592      629168 :             xmm_mask3 = _mm_mul_ps(xmm_mask3, xmm_inverse_alpha_max);
     593      629168 :             xmm_mask4 = _mm_mul_ps(xmm_mask4, xmm_inverse_alpha_max);
     594      629168 :             xmm_mask5 = _mm_mul_ps(xmm_mask5, xmm_inverse_alpha_max);
     595             :             xmmMaskNonOpaque0 =
     596     1258340 :                 _mm_or_ps(xmmMaskNonOpaque0, _mm_cmplt_ps(xmm_mask0, xmm_one));
     597             :             xmmMaskNonOpaque1 =
     598     1258340 :                 _mm_or_ps(xmmMaskNonOpaque1, _mm_cmplt_ps(xmm_mask1, xmm_one));
     599             :             xmmMaskNonOpaque2 =
     600     1258340 :                 _mm_or_ps(xmmMaskNonOpaque2, _mm_cmplt_ps(xmm_mask2, xmm_one));
     601             :             xmmMaskNonOpaque0 =
     602     1258340 :                 _mm_or_ps(xmmMaskNonOpaque0, _mm_cmplt_ps(xmm_mask3, xmm_one));
     603             :             xmmMaskNonOpaque1 =
     604     1258340 :                 _mm_or_ps(xmmMaskNonOpaque1, _mm_cmplt_ps(xmm_mask4, xmm_one));
     605             :             xmmMaskNonOpaque2 =
     606     1258340 :                 _mm_or_ps(xmmMaskNonOpaque2, _mm_cmplt_ps(xmm_mask5, xmm_one));
     607      629168 :             xmm_mask0 = _mm_min_ps(xmm_mask0, xmm_one);
     608      629168 :             xmm_mask1 = _mm_min_ps(xmm_mask1, xmm_one);
     609      629168 :             xmm_mask2 = _mm_min_ps(xmm_mask2, xmm_one);
     610      629168 :             xmm_mask3 = _mm_min_ps(xmm_mask3, xmm_one);
     611      629168 :             xmm_mask4 = _mm_min_ps(xmm_mask4, xmm_one);
     612      629168 :             xmm_mask5 = _mm_min_ps(xmm_mask5, xmm_one);
     613      629168 :             _mm_store_ps(pafMask + iPixel + 4 * 0, xmm_mask0);
     614      629168 :             _mm_store_ps(pafMask + iPixel + 4 * 1, xmm_mask1);
     615      629168 :             _mm_store_ps(pafMask + iPixel + 4 * 2, xmm_mask2);
     616      629168 :             _mm_store_ps(pafMask + iPixel + 4 * 3, xmm_mask3);
     617      629168 :             _mm_store_ps(pafMask + iPixel + 4 * 4, xmm_mask4);
     618      629168 :             _mm_store_ps(pafMask + iPixel + 4 * 5, xmm_mask5);
     619             :         }
     620         136 :         if (_mm_movemask_ps(
     621             :                 _mm_or_ps(_mm_or_ps(xmmMaskNonOpaque0, xmmMaskNonOpaque1),
     622          68 :                           xmmMaskNonOpaque2)))
     623             :         {
     624          43 :             bOutAllOpaque = false;
     625             :         }
     626         980 :         for (; iPixel < nPixels; iPixel++)
     627             :         {
     628         912 :             pafMask[iPixel] =
     629         912 :                 (reinterpret_cast<GUInt32 *>(pafMask)[iPixel] & mask) *
     630         912 :                 inv_alpha_max;
     631         912 :             if (pafMask[iPixel] >= 1.0f)
     632         442 :                 pafMask[iPixel] = 1.0f;
     633             :             else
     634         470 :                 bOutAllOpaque = false;
     635          68 :         }
     636             :     }
     637             :     else
     638             : #endif
     639             :     {
     640             :         // Read data.
     641          11 :         eErr = GDALRasterIO(hAlphaBand, GF_Read, nXOff, nYOff, nXSize, nYSize,
     642             :                             pafMask, nXSize, nYSize, GDT_Float32, 0, 0);
     643             : 
     644          11 :         if (eErr != CE_None)
     645           0 :             return eErr;
     646             : 
     647             :         // TODO(rouault): Is loop unrolling by hand (r34564) actually helpful?
     648       12969 :         for (; iPixel + 3 < nPixels; iPixel += 4)
     649             :         {
     650       12958 :             pafMask[iPixel] = pafMask[iPixel] * inv_alpha_max;
     651       12958 :             if (pafMask[iPixel] >= 1.0f)
     652        3028 :                 pafMask[iPixel] = 1.0f;
     653             :             else
     654        9930 :                 bOutAllOpaque = false;
     655       12958 :             pafMask[iPixel + 1] = pafMask[iPixel + 1] * inv_alpha_max;
     656       12958 :             if (pafMask[iPixel + 1] >= 1.0f)
     657        3018 :                 pafMask[iPixel + 1] = 1.0f;
     658             :             else
     659        9940 :                 bOutAllOpaque = false;
     660       12958 :             pafMask[iPixel + 2] = pafMask[iPixel + 2] * inv_alpha_max;
     661       12958 :             if (pafMask[iPixel + 2] >= 1.0f)
     662        3066 :                 pafMask[iPixel + 2] = 1.0f;
     663             :             else
     664        9892 :                 bOutAllOpaque = false;
     665       12958 :             pafMask[iPixel + 3] = pafMask[iPixel + 3] * inv_alpha_max;
     666       12958 :             if (pafMask[iPixel + 3] >= 1.0f)
     667        3016 :                 pafMask[iPixel + 3] = 1.0f;
     668             :             else
     669        9942 :                 bOutAllOpaque = false;
     670             :         }
     671             : 
     672          12 :         for (; iPixel < nPixels; iPixel++)
     673             :         {
     674           1 :             pafMask[iPixel] = pafMask[iPixel] * inv_alpha_max;
     675           1 :             if (pafMask[iPixel] >= 1.0f)
     676           0 :                 pafMask[iPixel] = 1.0f;
     677             :             else
     678           1 :                 bOutAllOpaque = false;
     679             :         }
     680             :     }
     681             : 
     682          79 :     *pbOutAllOpaque = bOutAllOpaque;
     683             : 
     684          79 :     return CE_None;
     685             : }
     686             : 
     687             : /************************************************************************/
     688             : /*                       GDALWarpSrcMaskMasker()                        */
     689             : /*                                                                      */
     690             : /*      GDALMaskFunc for reading source simple 8bit validity mask       */
     691             : /*      information and building a one bit validity mask.               */
     692             : /************************************************************************/
     693             : 
     694           2 : CPLErr GDALWarpSrcMaskMasker(void *pMaskFuncArg, int /* nBandCount */,
     695             :                              GDALDataType /* eType */, int nXOff, int nYOff,
     696             :                              int nXSize, int nYSize, GByte ** /*ppImageData */,
     697             :                              int bMaskIsFloat, void *pValidityMask)
     698             : 
     699             : {
     700           2 :     GDALWarpOptions *psWO = static_cast<GDALWarpOptions *>(pMaskFuncArg);
     701           2 :     GUInt32 *panMask = static_cast<GUInt32 *>(pValidityMask);
     702             : 
     703             :     /* -------------------------------------------------------------------- */
     704             :     /*      Do some minimal checking.                                       */
     705             :     /* -------------------------------------------------------------------- */
     706           2 :     if (bMaskIsFloat)
     707             :     {
     708           0 :         CPLAssert(false);
     709             :         return CE_Failure;
     710             :     }
     711             : 
     712           2 :     if (psWO == nullptr)
     713             :     {
     714           0 :         CPLAssert(false);
     715             :         return CE_Failure;
     716             :     }
     717             : 
     718             :     /* -------------------------------------------------------------------- */
     719             :     /*      Allocate a temporary buffer to read mask byte data into.        */
     720             :     /* -------------------------------------------------------------------- */
     721             :     GByte *pabySrcMask =
     722           2 :         static_cast<GByte *>(VSI_MALLOC2_VERBOSE(nXSize, nYSize));
     723           2 :     if (pabySrcMask == nullptr)
     724             :     {
     725           0 :         return CE_Failure;
     726             :     }
     727             : 
     728             :     /* -------------------------------------------------------------------- */
     729             :     /*      Fetch our mask band.                                            */
     730             :     /* -------------------------------------------------------------------- */
     731           2 :     GDALRasterBandH hMaskBand = nullptr;
     732             :     GDALRasterBandH hSrcBand =
     733           2 :         GDALGetRasterBand(psWO->hSrcDS, psWO->panSrcBands[0]);
     734           2 :     if (hSrcBand != nullptr)
     735           2 :         hMaskBand = GDALGetMaskBand(hSrcBand);
     736             : 
     737           2 :     if (hMaskBand == nullptr)
     738             :     {
     739           0 :         CPLAssert(false);
     740             :         return CE_Failure;
     741             :     }
     742             : 
     743             :     /* -------------------------------------------------------------------- */
     744             :     /*      Read the mask band.                                             */
     745             :     /* -------------------------------------------------------------------- */
     746           2 :     CPLErr eErr = GDALRasterIO(hMaskBand, GF_Read, nXOff, nYOff, nXSize, nYSize,
     747             :                                pabySrcMask, nXSize, nYSize, GDT_Byte, 0, 0);
     748             : 
     749           2 :     if (eErr != CE_None)
     750             :     {
     751           0 :         CPLFree(pabySrcMask);
     752           0 :         return eErr;
     753             :     }
     754             : 
     755             :     /* -------------------------------------------------------------------- */
     756             :     /*      Pack into 1 bit per pixel for validity.                         */
     757             :     /* -------------------------------------------------------------------- */
     758           2 :     const size_t nPixels = static_cast<size_t>(nXSize) * nYSize;
     759      234579 :     for (size_t iPixel = 0; iPixel < nPixels; iPixel++)
     760             :     {
     761      234577 :         if (pabySrcMask[iPixel] == 0)
     762       31660 :             CPLMaskClear(panMask, iPixel);
     763             :     }
     764             : 
     765           2 :     CPLFree(pabySrcMask);
     766             : 
     767           2 :     return CE_None;
     768             : }
     769             : 
     770             : /************************************************************************/
     771             : /*                       GDALWarpDstAlphaMasker()                       */
     772             : /*                                                                      */
     773             : /*      GDALMaskFunc for reading or writing the destination simple      */
     774             : /*      8bit alpha mask information and building a floating point       */
     775             : /*      density mask from it.   Note, writing is distinguished          */
     776             : /*      negative bandcount.                                             */
     777             : /************************************************************************/
     778             : 
     779         490 : CPLErr GDALWarpDstAlphaMasker(void *pMaskFuncArg, int nBandCount,
     780             :                               CPL_UNUSED GDALDataType /* eType */, int nXOff,
     781             :                               int nYOff, int nXSize, int nYSize,
     782             :                               GByte ** /*ppImageData */, int bMaskIsFloat,
     783             :                               void *pValidityMask)
     784             : {
     785             :     /* -------------------------------------------------------------------- */
     786             :     /*      Do some minimal checking.                                       */
     787             :     /* -------------------------------------------------------------------- */
     788         490 :     if (!bMaskIsFloat)
     789             :     {
     790           0 :         CPLAssert(false);
     791             :         return CE_Failure;
     792             :     }
     793             : 
     794         490 :     GDALWarpOptions *psWO = static_cast<GDALWarpOptions *>(pMaskFuncArg);
     795         490 :     if (psWO == nullptr || psWO->nDstAlphaBand < 1)
     796             :     {
     797           0 :         CPLAssert(false);
     798             :         return CE_Failure;
     799             :     }
     800             : 
     801         490 :     float *pafMask = static_cast<float *>(pValidityMask);
     802         490 :     const size_t nPixels = static_cast<size_t>(nXSize) * nYSize;
     803             : 
     804             :     GDALRasterBandH hAlphaBand =
     805         490 :         GDALGetRasterBand(psWO->hDstDS, psWO->nDstAlphaBand);
     806         490 :     if (hAlphaBand == nullptr)
     807           0 :         return CE_Failure;
     808             : 
     809         490 :     size_t iPixel = 0;
     810             : 
     811             :     /* -------------------------------------------------------------------- */
     812             :     /*      Read alpha case.                                                */
     813             :     /* -------------------------------------------------------------------- */
     814         490 :     if (nBandCount >= 0)
     815             :     {
     816             :         const char *pszInitDest =
     817         245 :             CSLFetchNameValue(psWO->papszWarpOptions, "INIT_DEST");
     818             : 
     819             :         // Special logic for destinations being initialized on-the-fly.
     820         245 :         if (pszInitDest != nullptr)
     821             :         {
     822         155 :             memset(pafMask, 0, nPixels * sizeof(float));
     823         155 :             return CE_None;
     824             :         }
     825             : 
     826             :         // Rescale.
     827          90 :         const float inv_alpha_max = static_cast<float>(
     828          90 :             1.0 / CPLAtof(CSLFetchNameValueDef(psWO->papszWarpOptions,
     829          90 :                                                "DST_ALPHA_MAX", "255")));
     830             : 
     831             : #if (defined(__x86_64) || defined(_M_X64))
     832          90 :         const GDALDataType eDT = GDALGetRasterDataType(hAlphaBand);
     833             :         // Make sure that pafMask is at least 8-byte aligned, which should
     834             :         // normally be always the case if being a ptr returned by malloc().
     835          90 :         if ((eDT == GDT_Byte || eDT == GDT_UInt16) &&
     836          81 :             CPL_IS_ALIGNED(pafMask, 8))
     837             :         {
     838             :             // Read data.
     839         162 :             const CPLErr eErr = GDALRasterIOEx(
     840             :                 hAlphaBand, GF_Read, nXOff, nYOff, nXSize, nYSize, pafMask,
     841             :                 nXSize, nYSize, eDT, static_cast<GSpacing>(sizeof(int)),
     842          81 :                 static_cast<GSpacing>(sizeof(int)) * nXSize, nullptr);
     843             : 
     844          81 :             if (eErr != CE_None)
     845           0 :                 return eErr;
     846             : 
     847             :             // Make sure we have the correct alignment before doing SSE
     848             :             // On Linux x86_64, the alignment should be always correct due
     849             :             // the alignment of malloc() being 16 byte.
     850          81 :             const GUInt32 mask = (eDT == GDT_Byte) ? 0xff : 0xffff;
     851          81 :             if (!CPL_IS_ALIGNED(pafMask, 16))
     852             :             {
     853           0 :                 pafMask[iPixel] =
     854           0 :                     (reinterpret_cast<GUInt32 *>(pafMask)[iPixel] & mask) *
     855           0 :                     inv_alpha_max;
     856           0 :                 pafMask[iPixel] = std::min(1.0f, pafMask[iPixel]);
     857           0 :                 iPixel++;
     858             :             }
     859          81 :             CPLAssert(CPL_IS_ALIGNED(pafMask + iPixel, 16));
     860          81 :             const __m128 xmm_inverse_alpha_max = _mm_load1_ps(&inv_alpha_max);
     861          81 :             const float one_single = 1.0f;
     862          81 :             const __m128 xmm_one = _mm_load1_ps(&one_single);
     863         162 :             const __m128i xmm_i_mask = _mm_set1_epi32(mask);
     864      126073 :             for (; iPixel + 31 < nPixels; iPixel += 32)
     865             :             {
     866      251984 :                 __m128 xmm_mask0 = _mm_cvtepi32_ps(_mm_and_si128(
     867             :                     xmm_i_mask, _mm_load_si128(reinterpret_cast<__m128i *>(
     868      125992 :                                     pafMask + iPixel + 4 * 0))));
     869      251984 :                 __m128 xmm_mask1 = _mm_cvtepi32_ps(_mm_and_si128(
     870             :                     xmm_i_mask, _mm_load_si128(reinterpret_cast<__m128i *>(
     871      125992 :                                     pafMask + iPixel + 4 * 1))));
     872      251984 :                 __m128 xmm_mask2 = _mm_cvtepi32_ps(_mm_and_si128(
     873             :                     xmm_i_mask, _mm_load_si128(reinterpret_cast<__m128i *>(
     874      125992 :                                     pafMask + iPixel + 4 * 2))));
     875      251984 :                 __m128 xmm_mask3 = _mm_cvtepi32_ps(_mm_and_si128(
     876             :                     xmm_i_mask, _mm_load_si128(reinterpret_cast<__m128i *>(
     877      125992 :                                     pafMask + iPixel + 4 * 3))));
     878      251984 :                 __m128 xmm_mask4 = _mm_cvtepi32_ps(_mm_and_si128(
     879             :                     xmm_i_mask, _mm_load_si128(reinterpret_cast<__m128i *>(
     880      125992 :                                     pafMask + iPixel + 4 * 4))));
     881      251984 :                 __m128 xmm_mask5 = _mm_cvtepi32_ps(_mm_and_si128(
     882             :                     xmm_i_mask, _mm_load_si128(reinterpret_cast<__m128i *>(
     883      125992 :                                     pafMask + iPixel + 4 * 5))));
     884      251984 :                 __m128 xmm_mask6 = _mm_cvtepi32_ps(_mm_and_si128(
     885             :                     xmm_i_mask, _mm_load_si128(reinterpret_cast<__m128i *>(
     886      125992 :                                     pafMask + iPixel + 4 * 6))));
     887      377976 :                 __m128 xmm_mask7 = _mm_cvtepi32_ps(_mm_and_si128(
     888             :                     xmm_i_mask, _mm_load_si128(reinterpret_cast<__m128i *>(
     889      125992 :                                     pafMask + iPixel + 4 * 7))));
     890      125992 :                 xmm_mask0 = _mm_mul_ps(xmm_mask0, xmm_inverse_alpha_max);
     891      125992 :                 xmm_mask1 = _mm_mul_ps(xmm_mask1, xmm_inverse_alpha_max);
     892      125992 :                 xmm_mask2 = _mm_mul_ps(xmm_mask2, xmm_inverse_alpha_max);
     893      125992 :                 xmm_mask3 = _mm_mul_ps(xmm_mask3, xmm_inverse_alpha_max);
     894      125992 :                 xmm_mask4 = _mm_mul_ps(xmm_mask4, xmm_inverse_alpha_max);
     895      125992 :                 xmm_mask5 = _mm_mul_ps(xmm_mask5, xmm_inverse_alpha_max);
     896      125992 :                 xmm_mask6 = _mm_mul_ps(xmm_mask6, xmm_inverse_alpha_max);
     897      125992 :                 xmm_mask7 = _mm_mul_ps(xmm_mask7, xmm_inverse_alpha_max);
     898      125992 :                 xmm_mask0 = _mm_min_ps(xmm_mask0, xmm_one);
     899      125992 :                 xmm_mask1 = _mm_min_ps(xmm_mask1, xmm_one);
     900      125992 :                 xmm_mask2 = _mm_min_ps(xmm_mask2, xmm_one);
     901      125992 :                 xmm_mask3 = _mm_min_ps(xmm_mask3, xmm_one);
     902      125992 :                 xmm_mask4 = _mm_min_ps(xmm_mask4, xmm_one);
     903      125992 :                 xmm_mask5 = _mm_min_ps(xmm_mask5, xmm_one);
     904      125992 :                 xmm_mask6 = _mm_min_ps(xmm_mask6, xmm_one);
     905      125992 :                 xmm_mask7 = _mm_min_ps(xmm_mask7, xmm_one);
     906      125992 :                 _mm_store_ps(pafMask + iPixel + 4 * 0, xmm_mask0);
     907      125992 :                 _mm_store_ps(pafMask + iPixel + 4 * 1, xmm_mask1);
     908      125992 :                 _mm_store_ps(pafMask + iPixel + 4 * 2, xmm_mask2);
     909      125992 :                 _mm_store_ps(pafMask + iPixel + 4 * 3, xmm_mask3);
     910      125992 :                 _mm_store_ps(pafMask + iPixel + 4 * 4, xmm_mask4);
     911      125992 :                 _mm_store_ps(pafMask + iPixel + 4 * 5, xmm_mask5);
     912      125992 :                 _mm_store_ps(pafMask + iPixel + 4 * 6, xmm_mask6);
     913      125992 :                 _mm_store_ps(pafMask + iPixel + 4 * 7, xmm_mask7);
     914             :             }
     915         825 :             for (; iPixel < nPixels; iPixel++)
     916             :             {
     917         744 :                 pafMask[iPixel] =
     918         744 :                     (reinterpret_cast<GUInt32 *>(pafMask)[iPixel] & mask) *
     919         744 :                     inv_alpha_max;
     920         744 :                 pafMask[iPixel] = std::min(1.0f, pafMask[iPixel]);
     921          81 :             }
     922             :         }
     923             :         else
     924             : #endif
     925             :         {
     926             :             // Read data.
     927             :             const CPLErr eErr =
     928           9 :                 GDALRasterIO(hAlphaBand, GF_Read, nXOff, nYOff, nXSize, nYSize,
     929             :                              pafMask, nXSize, nYSize, GDT_Float32, 0, 0);
     930             : 
     931           9 :             if (eErr != CE_None)
     932           0 :                 return eErr;
     933             : 
     934         842 :             for (; iPixel < nPixels; iPixel++)
     935             :             {
     936         833 :                 pafMask[iPixel] = pafMask[iPixel] * inv_alpha_max;
     937         833 :                 pafMask[iPixel] = std::min(1.0f, pafMask[iPixel]);
     938             :             }
     939             :         }
     940             : 
     941          90 :         return CE_None;
     942             :     }
     943             : 
     944             :     /* -------------------------------------------------------------------- */
     945             :     /*      Write alpha case.                                               */
     946             :     /* -------------------------------------------------------------------- */
     947             :     else
     948             :     {
     949         245 :         GDALDataType eDT = GDALGetRasterDataType(hAlphaBand);
     950             :         const float cst_alpha_max =
     951         245 :             static_cast<float>(CPLAtof(CSLFetchNameValueDef(
     952         245 :                 psWO->papszWarpOptions, "DST_ALPHA_MAX", "255"))) +
     953          29 :             ((eDT == GDT_Byte || eDT == GDT_Int16 || eDT == GDT_UInt16 ||
     954           1 :               eDT == GDT_Int32 || eDT == GDT_UInt32)
     955         274 :                  ? 0.1f
     956         245 :                  : 0.0f);
     957             : 
     958         245 :         CPLErr eErr = CE_None;
     959             : 
     960             : #if (defined(__x86_64) || defined(_M_X64))
     961             :         // Make sure that pafMask is at least 8-byte aligned, which should
     962             :         // normally be always the case if being a ptr returned by malloc()
     963         245 :         if ((eDT == GDT_Byte || eDT == GDT_Int16 || eDT == GDT_UInt16) &&
     964         244 :             CPL_IS_ALIGNED(pafMask, 8))
     965             :         {
     966             :             // Make sure we have the correct alignment before doing SSE
     967             :             // On Linux x86_64, the alignment should be always correct due
     968             :             // the alignment of malloc() being 16 byte
     969         244 :             if (!CPL_IS_ALIGNED(pafMask, 16))
     970             :             {
     971           0 :                 reinterpret_cast<int *>(pafMask)[iPixel] =
     972           0 :                     static_cast<int>(pafMask[iPixel] * cst_alpha_max);
     973           0 :                 iPixel++;
     974             :             }
     975         244 :             CPLAssert(CPL_IS_ALIGNED(pafMask + iPixel, 16));
     976         244 :             const __m128 xmm_alpha_max = _mm_load1_ps(&cst_alpha_max);
     977      279059 :             for (; iPixel + 31 < nPixels; iPixel += 32)
     978             :             {
     979      278815 :                 __m128 xmm_mask0 = _mm_load_ps(pafMask + iPixel + 4 * 0);
     980      278815 :                 __m128 xmm_mask1 = _mm_load_ps(pafMask + iPixel + 4 * 1);
     981      278815 :                 __m128 xmm_mask2 = _mm_load_ps(pafMask + iPixel + 4 * 2);
     982      278815 :                 __m128 xmm_mask3 = _mm_load_ps(pafMask + iPixel + 4 * 3);
     983      278815 :                 __m128 xmm_mask4 = _mm_load_ps(pafMask + iPixel + 4 * 4);
     984      278815 :                 __m128 xmm_mask5 = _mm_load_ps(pafMask + iPixel + 4 * 5);
     985      278815 :                 __m128 xmm_mask6 = _mm_load_ps(pafMask + iPixel + 4 * 6);
     986      557630 :                 __m128 xmm_mask7 = _mm_load_ps(pafMask + iPixel + 4 * 7);
     987      278815 :                 xmm_mask0 = _mm_mul_ps(xmm_mask0, xmm_alpha_max);
     988      278815 :                 xmm_mask1 = _mm_mul_ps(xmm_mask1, xmm_alpha_max);
     989      278815 :                 xmm_mask2 = _mm_mul_ps(xmm_mask2, xmm_alpha_max);
     990      278815 :                 xmm_mask3 = _mm_mul_ps(xmm_mask3, xmm_alpha_max);
     991      278815 :                 xmm_mask4 = _mm_mul_ps(xmm_mask4, xmm_alpha_max);
     992      278815 :                 xmm_mask5 = _mm_mul_ps(xmm_mask5, xmm_alpha_max);
     993      278815 :                 xmm_mask6 = _mm_mul_ps(xmm_mask6, xmm_alpha_max);
     994      278815 :                 xmm_mask7 = _mm_mul_ps(xmm_mask7, xmm_alpha_max);
     995             :                 // Truncate to int.
     996      278815 :                 _mm_store_si128(
     997      278815 :                     reinterpret_cast<__m128i *>(pafMask + iPixel + 4 * 0),
     998             :                     _mm_cvttps_epi32(xmm_mask0));
     999      278815 :                 _mm_store_si128(
    1000      278815 :                     reinterpret_cast<__m128i *>(pafMask + iPixel + 4 * 1),
    1001             :                     _mm_cvttps_epi32(xmm_mask1));
    1002      278815 :                 _mm_store_si128(
    1003      278815 :                     reinterpret_cast<__m128i *>(pafMask + iPixel + 4 * 2),
    1004             :                     _mm_cvttps_epi32(xmm_mask2));
    1005      278815 :                 _mm_store_si128(
    1006      278815 :                     reinterpret_cast<__m128i *>(pafMask + iPixel + 4 * 3),
    1007             :                     _mm_cvttps_epi32(xmm_mask3));
    1008      278815 :                 _mm_store_si128(
    1009      278815 :                     reinterpret_cast<__m128i *>(pafMask + iPixel + 4 * 4),
    1010             :                     _mm_cvttps_epi32(xmm_mask4));
    1011      278815 :                 _mm_store_si128(
    1012      278815 :                     reinterpret_cast<__m128i *>(pafMask + iPixel + 4 * 5),
    1013             :                     _mm_cvttps_epi32(xmm_mask5));
    1014      278815 :                 _mm_store_si128(
    1015      278815 :                     reinterpret_cast<__m128i *>(pafMask + iPixel + 4 * 6),
    1016             :                     _mm_cvttps_epi32(xmm_mask6));
    1017      278815 :                 _mm_store_si128(
    1018      278815 :                     reinterpret_cast<__m128i *>(pafMask + iPixel + 4 * 7),
    1019             :                     _mm_cvttps_epi32(xmm_mask7));
    1020             :             }
    1021        1804 :             for (; iPixel < nPixels; iPixel++)
    1022        1560 :                 reinterpret_cast<int *>(pafMask)[iPixel] =
    1023        1560 :                     static_cast<int>(pafMask[iPixel] * cst_alpha_max);
    1024             : 
    1025             :             // Write data.
    1026             :             // Assumes little endianness here.
    1027         488 :             eErr = GDALRasterIOEx(
    1028             :                 hAlphaBand, GF_Write, nXOff, nYOff, nXSize, nYSize, pafMask,
    1029             :                 nXSize, nYSize, eDT, static_cast<GSpacing>(sizeof(int)),
    1030         244 :                 static_cast<GSpacing>(sizeof(int)) * nXSize, nullptr);
    1031             :         }
    1032             :         else
    1033             : #endif
    1034             :         {
    1035           9 :             for (; iPixel + 3 < nPixels; iPixel += 4)
    1036             :             {
    1037           8 :                 pafMask[iPixel + 0] = static_cast<float>(
    1038           8 :                     static_cast<int>(pafMask[iPixel + 0] * cst_alpha_max));
    1039           8 :                 pafMask[iPixel + 1] = static_cast<float>(
    1040           8 :                     static_cast<int>(pafMask[iPixel + 1] * cst_alpha_max));
    1041           8 :                 pafMask[iPixel + 2] = static_cast<float>(
    1042           8 :                     static_cast<int>(pafMask[iPixel + 2] * cst_alpha_max));
    1043           8 :                 pafMask[iPixel + 3] = static_cast<float>(
    1044           8 :                     static_cast<int>(pafMask[iPixel + 3] * cst_alpha_max));
    1045             :             }
    1046           2 :             for (; iPixel < nPixels; iPixel++)
    1047           1 :                 pafMask[iPixel] = static_cast<float>(
    1048           1 :                     static_cast<int>(pafMask[iPixel] * cst_alpha_max));
    1049             : 
    1050             :             // Write data.
    1051             : 
    1052             :             eErr =
    1053           1 :                 GDALRasterIO(hAlphaBand, GF_Write, nXOff, nYOff, nXSize, nYSize,
    1054             :                              pafMask, nXSize, nYSize, GDT_Float32, 0, 0);
    1055             :         }
    1056         245 :         return eErr;
    1057             :     }
    1058             : }
    1059             : 
    1060             : /************************************************************************/
    1061             : /* ==================================================================== */
    1062             : /*                           GDALWarpOptions                            */
    1063             : /* ==================================================================== */
    1064             : /************************************************************************/
    1065             : 
    1066             : /**
    1067             :  * \var char **GDALWarpOptions::papszWarpOptions;
    1068             :  *
    1069             :  * A string list of additional options controlling the warp operation in
    1070             :  * name=value format.  A suitable string list can be prepared with
    1071             :  * CSLSetNameValue().
    1072             :  *
    1073             :  * The following values are currently supported:
    1074             :  * <ul>
    1075             :  * <li>INIT_DEST=[value] or INIT_DEST=NO_DATA: This option forces the
    1076             :  * destination image to be initialized to the indicated value (for all bands)
    1077             :  * or indicates that it should be initialized to the NO_DATA value in
    1078             :  * padfDstNoDataReal/padfDstNoDataImag.  If this value isn't set the
    1079             :  * destination image will be read and overlaid.</li>
    1080             :  *
    1081             :  * <li>WRITE_FLUSH=YES/NO: This option forces a flush to disk of data after
    1082             :  * each chunk is processed.  In some cases this helps ensure a serial
    1083             :  * writing of the output data otherwise a block of data may be written to disk
    1084             :  * each time a block of data is read for the input buffer resulting in a lot
    1085             :  * of extra seeking around the disk, and reduced IO throughput.  The default
    1086             :  * at this time is NO.</li>
    1087             :  *
    1088             :  * <li>SKIP_NOSOURCE=YES/NO: Skip all processing for chunks for which there
    1089             :  * is no corresponding input data.  This will disable initializing the
    1090             :  * destination (INIT_DEST) and all other processing, and so should be used
    1091             :  * carefully.  Mostly useful to short circuit a lot of extra work in mosaicing
    1092             :  * situations. Starting with GDAL 2.4, gdalwarp will automatically enable this
    1093             :  * option when it is assumed to be safe to do so.</li>
    1094             :  *
    1095             :  * <li>UNIFIED_SRC_NODATA=YES/NO/PARTIAL: This setting determines
    1096             :  * how to take into account nodata values when there are several input bands.
    1097             :  * <ul>
    1098             :  * <li>When YES, all bands are considered as nodata if and only if, all bands
    1099             :  *     match the corresponding nodata values.
    1100             :  *     Note: UNIFIED_SRC_NODATA=YES is set by default, when called from gdalwarp
    1101             :  * / GDALWarp() with an explicit -srcnodata setting.
    1102             :  *
    1103             :  *     Example with nodata values at (1, 2, 3) and target alpha band requested.
    1104             :  *     <ul>
    1105             :  *     <li>input pixel = (1, 2, 3) ==> output pixel = (0, 0, 0, 0)</li>
    1106             :  *     <li>input pixel = (1, 2, 127) ==> output pixel = (1, 2, 127, 255)</li>
    1107             :  *     </ul>
    1108             :  * </li>
    1109             :  * <li>When NO, nodata masking values is considered independently for each band.
    1110             :  *     A potential target alpha band will always be valid if there are multiple
    1111             :  *     bands.
    1112             :  *
    1113             :  *     Example with nodata values at (1, 2, 3) and target alpha band requested.
    1114             :  *     <ul>
    1115             :  *     <li>input pixel = (1, 2, 3) ==> output pixel = (0, 0, 0, 255)</li>
    1116             :  *     <li>input pixel = (1, 2, 127) ==> output pixel = (0, 0, 127, 255)</li>
    1117             :  *     </ul>
    1118             :  *
    1119             :  *     Note: NO was the default behavior before GDAL 3.3.2
    1120             :  * </li>
    1121             :  * <li>When PARTIAL, or not specified at all (default behavior),
    1122             :  *     nodata masking values is considered independently for each band.
    1123             :  *     But, and this is the difference with NO, if for a given pixel, it
    1124             :  *     evaluates to the nodata value of each band, the target pixel is
    1125             :  *     considered as globally invalid, which impacts the value of a potential
    1126             :  *     target alpha band.
    1127             :  *
    1128             :  *     Note: PARTIAL is new to GDAL 3.3.2 and should not be used with
    1129             :  *     earlier versions. The default behavior of GDAL < 3.3.2 was NO.
    1130             :  *
    1131             :  *     Example with nodata values at (1, 2, 3) and target alpha band requested.
    1132             :  *     <ul>
    1133             :  *     <li>input pixel = (1, 2, 3) ==> output pixel = (0, 0, 0, 0)</li>
    1134             :  *     <li>input pixel = (1, 2, 127) ==> output pixel = (0, 0, 127, 255)</li>
    1135             :  *     </ul>
    1136             :  * </li>
    1137             :  * </ul>
    1138             :  * </li>
    1139             :  *
    1140             :  * <li>CUTLINE: This may contain the WKT geometry for a cutline.  It will
    1141             :  * be converted into a geometry by GDALWarpOperation::Initialize() and assigned
    1142             :  * to the GDALWarpOptions hCutline field. The coordinates must be expressed
    1143             :  * in source pixel/line coordinates. Note: this is different from the
    1144             :  * assumptions made for the -cutline option of the gdalwarp utility !</li>
    1145             :  *
    1146             :  * <li>CUTLINE_BLEND_DIST: This may be set with a distance in pixels which
    1147             :  * will be assigned to the dfCutlineBlendDist field in the GDALWarpOptions.</li>
    1148             :  *
    1149             :  * <li>CUTLINE_ALL_TOUCHED: This defaults to FALSE, but may be set to TRUE
    1150             :  * to enable ALL_TOUCHEd mode when rasterizing cutline polygons.  This is
    1151             :  * useful to ensure that that all pixels overlapping the cutline polygon
    1152             :  * will be selected, not just those whose center point falls within the
    1153             :  * polygon.</li>
    1154             :  *
    1155             :  * <li>XSCALE: Ratio expressing the resampling factor (number of destination
    1156             :  * pixels per source pixel) along the target horizontal axis.
    1157             :  * The scale is used to determine the number of source pixels along the x-axis
    1158             :  * that are considered by the resampling algorithm.
    1159             :  * Equals to one for no resampling, below one for downsampling
    1160             :  * and above one for upsampling. This is automatically computed, for each
    1161             :  * processing chunk, and may thus vary among them, depending on the
    1162             :  * shape of output regions vs input regions. Such variations can be undesired
    1163             :  * in some situations. If the resampling factor can be considered as constant
    1164             :  * over the warped area, setting a constant value can lead to more reproducible
    1165             :  * pixel output.</li>
    1166             :  *
    1167             :  * <li>YSCALE: Same as XSCALE, but along the horizontal axis.</li>
    1168             :  *
    1169             :  * <li>OPTIMIZE_SIZE: This defaults to FALSE, but may be set to TRUE
    1170             :  * typically when writing to a compressed dataset (GeoTIFF with
    1171             :  * COMPRESS creation option set for example) for achieving a smaller
    1172             :  * file size. This is achieved by writing at once data aligned on full
    1173             :  * blocks of the target dataset, which avoids partial writes of
    1174             :  * compressed blocks and lost space when they are rewritten at the end
    1175             :  * of the file. However sticking to target block size may cause major
    1176             :  * processing slowdown for some particular reprojections. Starting
    1177             :  * with GDAL 3.8, OPTIMIZE_SIZE mode is automatically enabled when it is safe
    1178             :  * to do so.
    1179             :  * As this parameter influences the shape of warping chunk, and by default the
    1180             :  * XSCALE and YSCALE parameters are computed per warping chunk, this parameter may
    1181             :  * influence the pixel output.
    1182             :  * </li>
    1183             :  *
    1184             :  * <li>NUM_THREADS: (GDAL >= 1.10) Can be set to a numeric value or ALL_CPUS to
    1185             :  * set the number of threads to use to parallelize the computation part of the
    1186             :  * warping. If not set, computation will be done in a single thread.</li>
    1187             :  *
    1188             :  * <li>STREAMABLE_OUTPUT: (GDAL >= 2.0) This defaults to FALSE, but may
    1189             :  * be set to TRUE typically when writing to a streamed file. The
    1190             :  * gdalwarp utility automatically sets this option when writing to
    1191             :  * /vsistdout/ or a named pipe (on Unix).  This option has performance
    1192             :  * impacts for some reprojections.  Note: band interleaved output is
    1193             :  * not currently supported by the warping algorithm in a streamable
    1194             :  * compatible way.</li>
    1195             :  *
    1196             :  * <li>SRC_COORD_PRECISION: (GDAL >= 2.0). Advanced setting. This
    1197             :  * defaults to 0, to indicate that no rounding of computing source
    1198             :  * image coordinates corresponding to the target image must be
    1199             :  * done. If greater than 0 (and typically below 1), this value,
    1200             :  * expressed in pixel, will be used to round computed source image
    1201             :  * coordinates. The purpose of this option is to make the results of
    1202             :  * warping with the approximated transformer more reproducible and not
    1203             :  * sensitive to changes in warping memory size. To achieve that,
    1204             :  * SRC_COORD_PRECISION must be at least 10 times greater than the
    1205             :  * error threshold. The higher the SRC_COORD_PRECISION/error_threshold
    1206             :  * ratio, the higher the performance will be, since exact
    1207             :  * reprojections must statistically be done with a frequency of
    1208             :  * 4*error_threshold/SRC_COORD_PRECISION.</li>
    1209             :  *
    1210             :  * <li>SRC_ALPHA_MAX: (GDAL >= 2.2). Maximum value for the alpha band of the
    1211             :  * source dataset. If the value is not set and the alpha band has a NBITS
    1212             :  * metadata item, it is used to set SRC_ALPHA_MAX = 2^NBITS-1. Otherwise, if the
    1213             :  * value is not set and the alpha band is of type UInt16 (resp Int16), 65535
    1214             :  * (resp 32767) is used. Otherwise, 255 is used.</li>
    1215             :  *
    1216             :  * <li>DST_ALPHA_MAX: (GDAL >= 2.2). Maximum value for the alpha band of the
    1217             :  * destination dataset. If the value is not set and the alpha band has a NBITS
    1218             :  * metadata item, it is used to set DST_ALPHA_MAX = 2^NBITS-1. Otherwise, if the
    1219             :  * value is not set and the alpha band is of type UInt16 (resp Int16), 65535
    1220             :  * (resp 32767) is used. Otherwise, 255 is used.</li>
    1221             :  * </ul>
    1222             :  *
    1223             :  * Normally when computing the source raster data to
    1224             :  * load to generate a particular output area, the warper samples transforms
    1225             :  * 21 points along each edge of the destination region back onto the source
    1226             :  * file, and uses this to compute a bounding window on the source image that
    1227             :  * is sufficient.  Depending on the transformation in effect, the source
    1228             :  * window may be a bit too small, or even missing large areas.  Problem
    1229             :  * situations are those where the transformation is very non-linear or
    1230             :  * "inside out".  Examples are transforming from WGS84 to Polar Stereographic
    1231             :  * for areas around the pole, or transformations where some of the image is
    1232             :  * untransformable.  The following options provide some additional control
    1233             :  * to deal with errors in computing the source window:
    1234             :  * <ul>
    1235             :  *
    1236             :  * <li>SAMPLE_GRID=YES/NO: Setting this option to YES will force the sampling to
    1237             :  * include internal points as well as edge points which can be important if
    1238             :  * the transformation is esoteric inside out, or if large sections of the
    1239             :  * destination image are not transformable into the source coordinate
    1240             :  * system.</li>
    1241             :  *
    1242             :  * <li>SAMPLE_STEPS: Modifies the density of the sampling grid.  The default
    1243             :  * number of steps is 21.   Increasing this can increase the computational
    1244             :  * cost, but improves the accuracy with which the source region is
    1245             :  * computed.
    1246             :  * Starting with GDAL 3.7, this can be set to ALL to mean to sample
    1247             :  * along all edge points of the destination region (if SAMPLE_GRID=NO or not
    1248             :  * specified), or all points of the destination region if SAMPLE_GRID=YES.</li>
    1249             :  *
    1250             :  * <li>SOURCE_EXTRA: This is a number of extra pixels added around the source
    1251             :  * window for a given request, and by default it is 1 to take care of rounding
    1252             :  * error.  Setting this larger will increase the amount of data that needs to
    1253             :  * be read, but can avoid missing source data.</li>
    1254             :  * <li>APPLY_VERTICAL_SHIFT=YES/NO: Force the use of vertical shift.
    1255             :  * This option is generally not necessary, except when using an explicit
    1256             :  * coordinate transformation (COORDINATE_OPERATION), and not specifying
    1257             :  * an explicit source and target SRS.</li>
    1258             :  * <li>MULT_FACTOR_VERTICAL_SHIFT: Multiplication factor for the vertical
    1259             :  * shift. Default 1.0</li>
    1260             :  *
    1261             :  * <li>EXCLUDED_VALUES: (GDAL >= 3.9) Comma-separated tuple of values
    1262             :  * (thus typically "R,G,B"), that are ignored as contributing source
    1263             :  * pixels during resampling. The number of values in the tuple must be the same
    1264             :  * as the number of bands, excluding the alpha band.
    1265             :  * Several tuples of excluded values may be specified using the
    1266             :  * "(R1,G1,B2),(R2,G2,B2)" syntax.
    1267             :  * Only taken into account by Average currently.
    1268             :  * This concept is a bit similar to nodata/alpha, but the main difference is
    1269             :  * that pixels matching one of the excluded value tuples are still considered
    1270             :  * as valid, when determining the target pixel validity/density.
    1271             :  * </li>
    1272             :  *
    1273             :  * <li>EXCLUDED_VALUES_PCT_THRESHOLD=[0-100]: (GDAL >= 3.9) Minimum percentage
    1274             :  * of source pixels that must be set at one of the EXCLUDED_VALUES to cause
    1275             :  * the excluded value, that is in majority among source pixels, to be used as the
    1276             :  * target pixel value. Default value is 50 (%).
    1277             :  * Only taken into account by Average currently.</li>
    1278             :  *
    1279             :  * <li>NODATA_VALUES_PCT_THRESHOLD=[0-100]: (GDAL >= 3.9) Minimum percentage
    1280             :  * of source pixels that must be at nodata (or alpha=0 or any other way to express
    1281             :  * transparent pixel) to cause the target pixel value to not be set. Default
    1282             :  * value is 100 (%), which means that a target pixel is not set only if all
    1283             :  * contributing source pixels are not set.
    1284             :  * Note that NODATA_VALUES_PCT_THRESHOLD is taken into account before
    1285             :  * EXCLUDED_VALUES_PCT_THRESHOLD.
    1286             :  * Only taken into account by Average currently.</li>
    1287             :  *
    1288             :  * <li>MODE_TIES=FIRST/MIN/MAX: (GDAL >= 3.11) Strategy to use when breaking
    1289             :  * ties with MODE resampling. By default, the first value encountered will be used.
    1290             :  * Alternatively, the minimum or maximum value can be selected.</li>
    1291             :  *
    1292             :  * </ul>
    1293             :  */
    1294             : 
    1295             : /************************************************************************/
    1296             : /*                       GDALCreateWarpOptions()                        */
    1297             : /************************************************************************/
    1298             : 
    1299             : /** Create a warp options structure.
    1300             :  *
    1301             :  * Must be deallocated with GDALDestroyWarpOptions()
    1302             :  */
    1303        3011 : GDALWarpOptions *CPL_STDCALL GDALCreateWarpOptions()
    1304             : 
    1305             : {
    1306             :     GDALWarpOptions *psOptions =
    1307        3011 :         static_cast<GDALWarpOptions *>(CPLCalloc(sizeof(GDALWarpOptions), 1));
    1308             : 
    1309        3011 :     psOptions->nBandCount = 0;
    1310        3011 :     psOptions->eResampleAlg = GRA_NearestNeighbour;
    1311        3011 :     psOptions->pfnProgress = GDALDummyProgress;
    1312        3011 :     psOptions->eWorkingDataType = GDT_Unknown;
    1313        3011 :     psOptions->eTieStrategy = GWKTS_First;
    1314             : 
    1315        3011 :     return psOptions;
    1316             : }
    1317             : 
    1318             : /************************************************************************/
    1319             : /*                       GDALDestroyWarpOptions()                       */
    1320             : /************************************************************************/
    1321             : 
    1322             : /** Destroy a warp options structure. */
    1323        3011 : void CPL_STDCALL GDALDestroyWarpOptions(GDALWarpOptions *psOptions)
    1324             : 
    1325             : {
    1326        3011 :     if (psOptions == nullptr)
    1327           0 :         return;
    1328             : 
    1329        3011 :     CSLDestroy(psOptions->papszWarpOptions);
    1330        3011 :     CPLFree(psOptions->panSrcBands);
    1331        3011 :     CPLFree(psOptions->panDstBands);
    1332        3011 :     CPLFree(psOptions->padfSrcNoDataReal);
    1333        3011 :     CPLFree(psOptions->padfSrcNoDataImag);
    1334        3011 :     CPLFree(psOptions->padfDstNoDataReal);
    1335        3011 :     CPLFree(psOptions->padfDstNoDataImag);
    1336        3011 :     CPLFree(psOptions->papfnSrcPerBandValidityMaskFunc);
    1337        3011 :     CPLFree(psOptions->papSrcPerBandValidityMaskFuncArg);
    1338             : 
    1339        3011 :     if (psOptions->hCutline != nullptr)
    1340          48 :         delete static_cast<OGRGeometry *>(psOptions->hCutline);
    1341             : 
    1342        3011 :     CPLFree(psOptions);
    1343             : }
    1344             : 
    1345             : #define COPY_MEM(target, type, count)                                          \
    1346             :     do                                                                         \
    1347             :     {                                                                          \
    1348             :         if ((psSrcOptions->target) != nullptr && (count) != 0)                 \
    1349             :         {                                                                      \
    1350             :             (psDstOptions->target) =                                           \
    1351             :                 static_cast<type *>(CPLMalloc(sizeof(type) * (count)));        \
    1352             :             memcpy((psDstOptions->target), (psSrcOptions->target),             \
    1353             :                    sizeof(type) * (count));                                    \
    1354             :         }                                                                      \
    1355             :         else                                                                   \
    1356             :             (psDstOptions->target) = nullptr;                                  \
    1357             :     } while (false)
    1358             : 
    1359             : /************************************************************************/
    1360             : /*                        GDALCloneWarpOptions()                        */
    1361             : /************************************************************************/
    1362             : 
    1363             : /** Clone a warp options structure.
    1364             :  *
    1365             :  * Must be deallocated with GDALDestroyWarpOptions()
    1366             :  */
    1367             : GDALWarpOptions *CPL_STDCALL
    1368        1599 : GDALCloneWarpOptions(const GDALWarpOptions *psSrcOptions)
    1369             : 
    1370             : {
    1371        1599 :     GDALWarpOptions *psDstOptions = GDALCreateWarpOptions();
    1372             : 
    1373        1599 :     memcpy(psDstOptions, psSrcOptions, sizeof(GDALWarpOptions));
    1374             : 
    1375        1599 :     if (psSrcOptions->papszWarpOptions != nullptr)
    1376        1338 :         psDstOptions->papszWarpOptions =
    1377        1338 :             CSLDuplicate(psSrcOptions->papszWarpOptions);
    1378             : 
    1379        1599 :     COPY_MEM(panSrcBands, int, psSrcOptions->nBandCount);
    1380        1599 :     COPY_MEM(panDstBands, int, psSrcOptions->nBandCount);
    1381        1599 :     COPY_MEM(padfSrcNoDataReal, double, psSrcOptions->nBandCount);
    1382        1599 :     COPY_MEM(padfSrcNoDataImag, double, psSrcOptions->nBandCount);
    1383        1599 :     COPY_MEM(padfDstNoDataReal, double, psSrcOptions->nBandCount);
    1384        1599 :     COPY_MEM(padfDstNoDataImag, double, psSrcOptions->nBandCount);
    1385             :     // cppcheck-suppress pointerSize
    1386        1599 :     COPY_MEM(papfnSrcPerBandValidityMaskFunc, GDALMaskFunc,
    1387             :              psSrcOptions->nBandCount);
    1388        1599 :     psDstOptions->papSrcPerBandValidityMaskFuncArg = nullptr;
    1389             : 
    1390        1599 :     if (psSrcOptions->hCutline != nullptr)
    1391          10 :         psDstOptions->hCutline =
    1392          10 :             OGR_G_Clone(static_cast<OGRGeometryH>(psSrcOptions->hCutline));
    1393        1599 :     psDstOptions->dfCutlineBlendDist = psSrcOptions->dfCutlineBlendDist;
    1394             : 
    1395        1599 :     return psDstOptions;
    1396             : }
    1397             : 
    1398             : namespace
    1399             : {
    1400         143 : void InitNoData(int nBandCount, double **ppdNoDataReal, double dDataReal)
    1401             : {
    1402         143 :     if (nBandCount <= 0)
    1403             :     {
    1404           0 :         return;
    1405             :     }
    1406         143 :     if (*ppdNoDataReal != nullptr)
    1407             :     {
    1408          32 :         return;
    1409             :     }
    1410             : 
    1411         111 :     *ppdNoDataReal =
    1412         111 :         static_cast<double *>(CPLMalloc(sizeof(double) * nBandCount));
    1413             : 
    1414         254 :     for (int i = 0; i < nBandCount; ++i)
    1415             :     {
    1416         143 :         (*ppdNoDataReal)[i] = dDataReal;
    1417             :     }
    1418             : }
    1419             : }  // namespace
    1420             : 
    1421             : /************************************************************************/
    1422             : /*                      GDALWarpInitDstNoDataReal()                     */
    1423             : /************************************************************************/
    1424             : 
    1425             : /**
    1426             :  * \brief Initialize padfDstNoDataReal with specified value.
    1427             :  *
    1428             :  * @param psOptionsIn options to initialize.
    1429             :  * @param dNoDataReal value to initialize to.
    1430             :  *
    1431             :  */
    1432          38 : void CPL_STDCALL GDALWarpInitDstNoDataReal(GDALWarpOptions *psOptionsIn,
    1433             :                                            double dNoDataReal)
    1434             : {
    1435          38 :     VALIDATE_POINTER0(psOptionsIn, "GDALWarpInitDstNoDataReal");
    1436          38 :     InitNoData(psOptionsIn->nBandCount, &psOptionsIn->padfDstNoDataReal,
    1437             :                dNoDataReal);
    1438             : }
    1439             : 
    1440             : /************************************************************************/
    1441             : /*                      GDALWarpInitSrcNoDataReal()                     */
    1442             : /************************************************************************/
    1443             : 
    1444             : /**
    1445             :  * \brief Initialize padfSrcNoDataReal with specified value.
    1446             :  *
    1447             :  * @param psOptionsIn options to initialize.
    1448             :  * @param dNoDataReal value to initialize to.
    1449             :  *
    1450             :  */
    1451          39 : void CPL_STDCALL GDALWarpInitSrcNoDataReal(GDALWarpOptions *psOptionsIn,
    1452             :                                            double dNoDataReal)
    1453             : {
    1454          39 :     VALIDATE_POINTER0(psOptionsIn, "GDALWarpInitSrcNoDataReal");
    1455          39 :     InitNoData(psOptionsIn->nBandCount, &psOptionsIn->padfSrcNoDataReal,
    1456             :                dNoDataReal);
    1457             : }
    1458             : 
    1459             : /************************************************************************/
    1460             : /*                      GDALWarpInitNoDataReal()                        */
    1461             : /************************************************************************/
    1462             : 
    1463             : /**
    1464             :  * \brief Initialize padfSrcNoDataReal and padfDstNoDataReal with specified
    1465             :  * value.
    1466             :  *
    1467             :  * @param psOptionsIn options to initialize.
    1468             :  * @param dNoDataReal value to initialize to.
    1469             :  *
    1470             :  */
    1471           1 : void CPL_STDCALL GDALWarpInitNoDataReal(GDALWarpOptions *psOptionsIn,
    1472             :                                         double dNoDataReal)
    1473             : {
    1474           1 :     GDALWarpInitDstNoDataReal(psOptionsIn, dNoDataReal);
    1475           1 :     GDALWarpInitSrcNoDataReal(psOptionsIn, dNoDataReal);
    1476           1 : }
    1477             : 
    1478             : /************************************************************************/
    1479             : /*                      GDALWarpInitDstNoDataImag()                     */
    1480             : /************************************************************************/
    1481             : 
    1482             : /**
    1483             :  * \brief Initialize padfDstNoDataImag  with specified value.
    1484             :  *
    1485             :  * @param psOptionsIn options to initialize.
    1486             :  * @param dNoDataImag value to initialize to.
    1487             :  *
    1488             :  */
    1489          35 : void CPL_STDCALL GDALWarpInitDstNoDataImag(GDALWarpOptions *psOptionsIn,
    1490             :                                            double dNoDataImag)
    1491             : {
    1492          35 :     VALIDATE_POINTER0(psOptionsIn, "GDALWarpInitDstNoDataImag");
    1493          35 :     InitNoData(psOptionsIn->nBandCount, &psOptionsIn->padfDstNoDataImag,
    1494             :                dNoDataImag);
    1495             : }
    1496             : 
    1497             : /************************************************************************/
    1498             : /*                      GDALWarpInitSrcNoDataImag()                     */
    1499             : /************************************************************************/
    1500             : 
    1501             : /**
    1502             :  * \brief Initialize padfSrcNoDataImag  with specified value.
    1503             :  *
    1504             :  * @param psOptionsIn options to initialize.
    1505             :  * @param dNoDataImag value to initialize to.
    1506             :  *
    1507             :  */
    1508          31 : void CPL_STDCALL GDALWarpInitSrcNoDataImag(GDALWarpOptions *psOptionsIn,
    1509             :                                            double dNoDataImag)
    1510             : {
    1511          31 :     VALIDATE_POINTER0(psOptionsIn, "GDALWarpInitSrcNoDataImag");
    1512          31 :     InitNoData(psOptionsIn->nBandCount, &psOptionsIn->padfSrcNoDataImag,
    1513             :                dNoDataImag);
    1514             : }
    1515             : 
    1516             : /************************************************************************/
    1517             : /*                      GDALWarpResolveWorkingDataType()                */
    1518             : /************************************************************************/
    1519             : 
    1520             : /**
    1521             :  * \brief If the working data type is unknown, this method will determine
    1522             :  *  a valid working data type to support the data in the src and dest
    1523             :  *  data sets and any noData values.
    1524             :  *
    1525             :  * @param psOptions options to initialize.
    1526             :  *
    1527             :  */
    1528        1495 : void CPL_STDCALL GDALWarpResolveWorkingDataType(GDALWarpOptions *psOptions)
    1529             : {
    1530        1495 :     if (psOptions == nullptr)
    1531             :     {
    1532           0 :         return;
    1533             :     }
    1534             :     /* -------------------------------------------------------------------- */
    1535             :     /*      If no working data type was provided, set one now.              */
    1536             :     /*                                                                      */
    1537             :     /*      Ensure that the working data type can encapsulate any value     */
    1538             :     /*      in the target, source, and the no data for either.              */
    1539             :     /* -------------------------------------------------------------------- */
    1540        1495 :     if (psOptions->eWorkingDataType != GDT_Unknown)
    1541             :     {
    1542         326 :         return;
    1543             :     }
    1544             : 
    1545        1169 :     psOptions->eWorkingDataType = GDT_Byte;
    1546             : 
    1547             :     // If none of the provided input nodata values can be represented in the
    1548             :     // data type of the corresponding source band, ignore them.
    1549        1169 :     if (psOptions->hSrcDS && psOptions->padfSrcNoDataReal)
    1550             :     {
    1551          87 :         int nCountInvalidSrcNoDataReal = 0;
    1552         220 :         for (int iBand = 0; iBand < psOptions->nBandCount; iBand++)
    1553             :         {
    1554         266 :             GDALRasterBandH hSrcBand = GDALGetRasterBand(
    1555         133 :                 psOptions->hSrcDS, psOptions->panSrcBands[iBand]);
    1556             : 
    1557         266 :             if (hSrcBand &&
    1558         133 :                 !GDALIsValueExactAs(psOptions->padfSrcNoDataReal[iBand],
    1559             :                                     GDALGetRasterDataType(hSrcBand)))
    1560             :             {
    1561           2 :                 nCountInvalidSrcNoDataReal++;
    1562             :             }
    1563             :         }
    1564          87 :         if (nCountInvalidSrcNoDataReal == psOptions->nBandCount)
    1565             :         {
    1566           2 :             CPLFree(psOptions->padfSrcNoDataReal);
    1567           2 :             psOptions->padfSrcNoDataReal = nullptr;
    1568           2 :             CPLFree(psOptions->padfSrcNoDataImag);
    1569           2 :             psOptions->padfSrcNoDataImag = nullptr;
    1570             :         }
    1571             :     }
    1572             : 
    1573        2618 :     for (int iBand = 0; iBand < psOptions->nBandCount; iBand++)
    1574             :     {
    1575        1449 :         if (psOptions->hDstDS != nullptr)
    1576             :         {
    1577        2798 :             GDALRasterBandH hDstBand = GDALGetRasterBand(
    1578        1399 :                 psOptions->hDstDS, psOptions->panDstBands[iBand]);
    1579             : 
    1580        1399 :             if (hDstBand != nullptr)
    1581             :             {
    1582        1399 :                 psOptions->eWorkingDataType =
    1583        1399 :                     GDALDataTypeUnion(psOptions->eWorkingDataType,
    1584             :                                       GDALGetRasterDataType(hDstBand));
    1585             :             }
    1586             :         }
    1587             : 
    1588        1449 :         if (psOptions->hSrcDS != nullptr)
    1589             :         {
    1590        2858 :             GDALRasterBandH hSrcBand = GDALGetRasterBand(
    1591        1429 :                 psOptions->hSrcDS, psOptions->panSrcBands[iBand]);
    1592             : 
    1593        1429 :             if (hSrcBand != nullptr)
    1594             :             {
    1595        1429 :                 psOptions->eWorkingDataType =
    1596        1429 :                     GDALDataTypeUnion(psOptions->eWorkingDataType,
    1597             :                                       GDALGetRasterDataType(hSrcBand));
    1598             :             }
    1599             :         }
    1600             : 
    1601        1449 :         if (psOptions->padfSrcNoDataReal != nullptr)
    1602             :         {
    1603         141 :             psOptions->eWorkingDataType = GDALDataTypeUnionWithValue(
    1604             :                 psOptions->eWorkingDataType,
    1605         141 :                 psOptions->padfSrcNoDataReal[iBand], false);
    1606             :         }
    1607             : 
    1608        1449 :         if (psOptions->padfSrcNoDataImag != nullptr &&
    1609           4 :             psOptions->padfSrcNoDataImag[iBand] != 0.0)
    1610             :         {
    1611           3 :             psOptions->eWorkingDataType = GDALDataTypeUnionWithValue(
    1612             :                 psOptions->eWorkingDataType,
    1613           3 :                 psOptions->padfSrcNoDataImag[iBand], true);
    1614             :         }
    1615             : 
    1616        1449 :         if (psOptions->padfDstNoDataReal != nullptr)
    1617             :         {
    1618         132 :             psOptions->eWorkingDataType = GDALDataTypeUnionWithValue(
    1619             :                 psOptions->eWorkingDataType,
    1620         132 :                 psOptions->padfDstNoDataReal[iBand], false);
    1621             :         }
    1622             : 
    1623        1449 :         if (psOptions->padfDstNoDataImag != nullptr &&
    1624          52 :             psOptions->padfDstNoDataImag[iBand] != 0.0)
    1625             :         {
    1626           3 :             psOptions->eWorkingDataType = GDALDataTypeUnionWithValue(
    1627             :                 psOptions->eWorkingDataType,
    1628           3 :                 psOptions->padfDstNoDataImag[iBand], true);
    1629             :         }
    1630             :     }
    1631             : 
    1632        2338 :     const bool bApplyVerticalShift = CPLFetchBool(
    1633        1169 :         psOptions->papszWarpOptions, "APPLY_VERTICAL_SHIFT", false);
    1634        1188 :     if (bApplyVerticalShift &&
    1635          19 :         GDALDataTypeIsInteger(psOptions->eWorkingDataType))
    1636             :     {
    1637          16 :         const double dfMultFactorVerticalShift = CPLAtof(CSLFetchNameValueDef(
    1638          16 :             psOptions->papszWarpOptions, "MULT_FACTOR_VERTICAL_SHIFT", "1.0"));
    1639          16 :         if (dfMultFactorVerticalShift != 1)
    1640             :         {
    1641           0 :             psOptions->eWorkingDataType =
    1642           0 :                 GDALDataTypeUnion(psOptions->eWorkingDataType, GDT_Float32);
    1643             :         }
    1644             :     }
    1645             : }
    1646             : 
    1647             : /************************************************************************/
    1648             : /*                      GDALWarpInitDefaultBandMapping()                */
    1649             : /************************************************************************/
    1650             : 
    1651             : /**
    1652             :  * \brief Init src and dst band mappings such that Bands[i] = i+1
    1653             :  *  for nBandCount
    1654             :  *  Does nothing if psOptionsIn->nBandCount is non-zero.
    1655             :  *
    1656             :  * @param psOptionsIn options to initialize.
    1657             :  * @param nBandCount bands to initialize for.
    1658             :  *
    1659             :  */
    1660         312 : void CPL_STDCALL GDALWarpInitDefaultBandMapping(GDALWarpOptions *psOptionsIn,
    1661             :                                                 int nBandCount)
    1662             : {
    1663         312 :     if (psOptionsIn->nBandCount != 0)
    1664             :     {
    1665           0 :         return;
    1666             :     }
    1667             : 
    1668         312 :     psOptionsIn->nBandCount = nBandCount;
    1669             : 
    1670         312 :     psOptionsIn->panSrcBands =
    1671         312 :         static_cast<int *>(CPLMalloc(sizeof(int) * psOptionsIn->nBandCount));
    1672         312 :     psOptionsIn->panDstBands =
    1673         312 :         static_cast<int *>(CPLMalloc(sizeof(int) * psOptionsIn->nBandCount));
    1674             : 
    1675         820 :     for (int i = 0; i < psOptionsIn->nBandCount; i++)
    1676             :     {
    1677         508 :         psOptionsIn->panSrcBands[i] = i + 1;
    1678         508 :         psOptionsIn->panDstBands[i] = i + 1;
    1679             :     }
    1680             : }
    1681             : 
    1682             : /************************************************************************/
    1683             : /*                      GDALSerializeWarpOptions()                      */
    1684             : /************************************************************************/
    1685             : 
    1686          77 : CPLXMLNode *CPL_STDCALL GDALSerializeWarpOptions(const GDALWarpOptions *psWO)
    1687             : 
    1688             : {
    1689             :     /* -------------------------------------------------------------------- */
    1690             :     /*      Create root.                                                    */
    1691             :     /* -------------------------------------------------------------------- */
    1692             :     CPLXMLNode *psTree =
    1693          77 :         CPLCreateXMLNode(nullptr, CXT_Element, "GDALWarpOptions");
    1694             : 
    1695             :     /* -------------------------------------------------------------------- */
    1696             :     /*      WarpMemoryLimit                                                 */
    1697             :     /* -------------------------------------------------------------------- */
    1698          77 :     CPLCreateXMLElementAndValue(
    1699             :         psTree, "WarpMemoryLimit",
    1700         154 :         CPLString().Printf("%g", psWO->dfWarpMemoryLimit));
    1701             : 
    1702             :     /* -------------------------------------------------------------------- */
    1703             :     /*      ResampleAlg                                                     */
    1704             :     /* -------------------------------------------------------------------- */
    1705          77 :     const char *pszAlgName = nullptr;
    1706             : 
    1707          77 :     if (psWO->eResampleAlg == GRA_NearestNeighbour)
    1708          77 :         pszAlgName = "NearestNeighbour";
    1709           0 :     else if (psWO->eResampleAlg == GRA_Bilinear)
    1710           0 :         pszAlgName = "Bilinear";
    1711           0 :     else if (psWO->eResampleAlg == GRA_Cubic)
    1712           0 :         pszAlgName = "Cubic";
    1713           0 :     else if (psWO->eResampleAlg == GRA_CubicSpline)
    1714           0 :         pszAlgName = "CubicSpline";
    1715           0 :     else if (psWO->eResampleAlg == GRA_Lanczos)
    1716           0 :         pszAlgName = "Lanczos";
    1717           0 :     else if (psWO->eResampleAlg == GRA_Average)
    1718           0 :         pszAlgName = "Average";
    1719           0 :     else if (psWO->eResampleAlg == GRA_RMS)
    1720           0 :         pszAlgName = "RootMeanSquare";
    1721           0 :     else if (psWO->eResampleAlg == GRA_Mode)
    1722           0 :         pszAlgName = "Mode";
    1723           0 :     else if (psWO->eResampleAlg == GRA_Max)
    1724           0 :         pszAlgName = "Maximum";
    1725           0 :     else if (psWO->eResampleAlg == GRA_Min)
    1726           0 :         pszAlgName = "Minimum";
    1727           0 :     else if (psWO->eResampleAlg == GRA_Med)
    1728           0 :         pszAlgName = "Median";
    1729           0 :     else if (psWO->eResampleAlg == GRA_Q1)
    1730           0 :         pszAlgName = "Quartile1";
    1731           0 :     else if (psWO->eResampleAlg == GRA_Q3)
    1732           0 :         pszAlgName = "Quartile3";
    1733           0 :     else if (psWO->eResampleAlg == GRA_Sum)
    1734           0 :         pszAlgName = "Sum";
    1735             :     else
    1736           0 :         pszAlgName = "Unknown";
    1737             : 
    1738          77 :     CPLCreateXMLElementAndValue(psTree, "ResampleAlg", pszAlgName);
    1739             : 
    1740             :     /* -------------------------------------------------------------------- */
    1741             :     /*      Working Data Type                                               */
    1742             :     /* -------------------------------------------------------------------- */
    1743          77 :     CPLCreateXMLElementAndValue(psTree, "WorkingDataType",
    1744          77 :                                 GDALGetDataTypeName(psWO->eWorkingDataType));
    1745             : 
    1746             :     /* -------------------------------------------------------------------- */
    1747             :     /*      Name/value warp options.                                        */
    1748             :     /* -------------------------------------------------------------------- */
    1749         314 :     for (int iWO = 0; psWO->papszWarpOptions != nullptr &&
    1750         314 :                       psWO->papszWarpOptions[iWO] != nullptr;
    1751             :          iWO++)
    1752             :     {
    1753         237 :         char *pszName = nullptr;
    1754             :         const char *pszValue =
    1755         237 :             CPLParseNameValue(psWO->papszWarpOptions[iWO], &pszName);
    1756             : 
    1757             :         // EXTRA_ELTS is an internal detail that we will recover
    1758             :         // no need to serialize it.
    1759             :         // And CUTLINE is also serialized in a special way
    1760         237 :         if (pszName != nullptr && !EQUAL(pszName, "EXTRA_ELTS") &&
    1761         160 :             !EQUAL(pszName, "CUTLINE"))
    1762             :         {
    1763             :             CPLXMLNode *psOption =
    1764         160 :                 CPLCreateXMLElementAndValue(psTree, "Option", pszValue);
    1765             : 
    1766         160 :             CPLCreateXMLNode(CPLCreateXMLNode(psOption, CXT_Attribute, "name"),
    1767             :                              CXT_Text, pszName);
    1768             :         }
    1769             : 
    1770         237 :         CPLFree(pszName);
    1771             :     }
    1772             : 
    1773             :     /* -------------------------------------------------------------------- */
    1774             :     /*      Source and Destination Data Source                              */
    1775             :     /* -------------------------------------------------------------------- */
    1776          77 :     if (psWO->hSrcDS != nullptr)
    1777             :     {
    1778          77 :         CPLCreateXMLElementAndValue(psTree, "SourceDataset",
    1779          77 :                                     GDALGetDescription(psWO->hSrcDS));
    1780             : 
    1781             :         CSLConstList papszOpenOptions =
    1782          77 :             GDALDataset::FromHandle(psWO->hSrcDS)->GetOpenOptions();
    1783          77 :         GDALSerializeOpenOptionsToXML(psTree, papszOpenOptions);
    1784             :     }
    1785             : 
    1786         154 :     if (psWO->hDstDS != nullptr &&
    1787          77 :         strlen(GDALGetDescription(psWO->hDstDS)) != 0)
    1788             :     {
    1789           0 :         CPLCreateXMLElementAndValue(psTree, "DestinationDataset",
    1790           0 :                                     GDALGetDescription(psWO->hDstDS));
    1791             :     }
    1792             : 
    1793             :     /* -------------------------------------------------------------------- */
    1794             :     /*      Serialize transformer.                                          */
    1795             :     /* -------------------------------------------------------------------- */
    1796          77 :     if (psWO->pfnTransformer != nullptr)
    1797             :     {
    1798             :         CPLXMLNode *psTransformerContainer =
    1799          77 :             CPLCreateXMLNode(psTree, CXT_Element, "Transformer");
    1800             : 
    1801         154 :         CPLXMLNode *psTransformerTree = GDALSerializeTransformer(
    1802          77 :             psWO->pfnTransformer, psWO->pTransformerArg);
    1803             : 
    1804          77 :         if (psTransformerTree != nullptr)
    1805          77 :             CPLAddXMLChild(psTransformerContainer, psTransformerTree);
    1806             :     }
    1807             : 
    1808             :     /* -------------------------------------------------------------------- */
    1809             :     /*      Band count and lists.                                           */
    1810             :     /* -------------------------------------------------------------------- */
    1811          77 :     CPLXMLNode *psBandList = nullptr;
    1812             : 
    1813          77 :     if (psWO->nBandCount != 0)
    1814          77 :         psBandList = CPLCreateXMLNode(psTree, CXT_Element, "BandList");
    1815             : 
    1816         240 :     for (int i = 0; i < psWO->nBandCount; i++)
    1817             :     {
    1818             :         CPLXMLNode *psBand;
    1819             : 
    1820         163 :         psBand = CPLCreateXMLNode(psBandList, CXT_Element, "BandMapping");
    1821         163 :         if (psWO->panSrcBands != nullptr)
    1822         163 :             CPLCreateXMLNode(CPLCreateXMLNode(psBand, CXT_Attribute, "src"),
    1823             :                              CXT_Text,
    1824         326 :                              CPLString().Printf("%d", psWO->panSrcBands[i]));
    1825         163 :         if (psWO->panDstBands != nullptr)
    1826         163 :             CPLCreateXMLNode(CPLCreateXMLNode(psBand, CXT_Attribute, "dst"),
    1827             :                              CXT_Text,
    1828         326 :                              CPLString().Printf("%d", psWO->panDstBands[i]));
    1829             : 
    1830         163 :         if (psWO->padfSrcNoDataReal != nullptr)
    1831             :         {
    1832          13 :             CPLCreateXMLElementAndValue(
    1833             :                 psBand, "SrcNoDataReal",
    1834          13 :                 VRTSerializeNoData(psWO->padfSrcNoDataReal[i],
    1835          13 :                                    psWO->eWorkingDataType, 16)
    1836             :                     .c_str());
    1837             :         }
    1838             : 
    1839         163 :         if (psWO->padfSrcNoDataImag != nullptr)
    1840             :         {
    1841           2 :             if (std::isnan(psWO->padfSrcNoDataImag[i]))
    1842           0 :                 CPLCreateXMLElementAndValue(psBand, "SrcNoDataImag", "nan");
    1843             :             else
    1844           2 :                 CPLCreateXMLElementAndValue(
    1845             :                     psBand, "SrcNoDataImag",
    1846           4 :                     CPLString().Printf("%.16g", psWO->padfSrcNoDataImag[i]));
    1847             :         }
    1848             :         // Compatibility with GDAL <= 2.2: if we serialize a SrcNoDataReal,
    1849             :         // it needs a SrcNoDataImag as well
    1850         161 :         else if (psWO->padfSrcNoDataReal != nullptr)
    1851             :         {
    1852          11 :             CPLCreateXMLElementAndValue(psBand, "SrcNoDataImag", "0");
    1853             :         }
    1854             : 
    1855         163 :         if (psWO->padfDstNoDataReal != nullptr)
    1856             :         {
    1857          13 :             CPLCreateXMLElementAndValue(
    1858             :                 psBand, "DstNoDataReal",
    1859          13 :                 VRTSerializeNoData(psWO->padfDstNoDataReal[i],
    1860          13 :                                    psWO->eWorkingDataType, 16)
    1861             :                     .c_str());
    1862             :         }
    1863             : 
    1864         163 :         if (psWO->padfDstNoDataImag != nullptr)
    1865             :         {
    1866           2 :             if (std::isnan(psWO->padfDstNoDataImag[i]))
    1867           0 :                 CPLCreateXMLElementAndValue(psBand, "DstNoDataImag", "nan");
    1868             :             else
    1869           2 :                 CPLCreateXMLElementAndValue(
    1870             :                     psBand, "DstNoDataImag",
    1871           4 :                     CPLString().Printf("%.16g", psWO->padfDstNoDataImag[i]));
    1872             :         }
    1873             :         // Compatibility with GDAL <= 2.2: if we serialize a DstNoDataReal,
    1874             :         // it needs a SrcNoDataImag as well
    1875         161 :         else if (psWO->padfDstNoDataReal != nullptr)
    1876             :         {
    1877          11 :             CPLCreateXMLElementAndValue(psBand, "DstNoDataImag", "0");
    1878             :         }
    1879             :     }
    1880             : 
    1881             :     /* -------------------------------------------------------------------- */
    1882             :     /*      Alpha bands.                                                    */
    1883             :     /* -------------------------------------------------------------------- */
    1884          77 :     if (psWO->nSrcAlphaBand > 0)
    1885           0 :         CPLCreateXMLElementAndValue(
    1886             :             psTree, "SrcAlphaBand",
    1887           0 :             CPLString().Printf("%d", psWO->nSrcAlphaBand));
    1888             : 
    1889          77 :     if (psWO->nDstAlphaBand > 0)
    1890          23 :         CPLCreateXMLElementAndValue(
    1891             :             psTree, "DstAlphaBand",
    1892          46 :             CPLString().Printf("%d", psWO->nDstAlphaBand));
    1893             : 
    1894             :     /* -------------------------------------------------------------------- */
    1895             :     /*      Cutline.                                                        */
    1896             :     /* -------------------------------------------------------------------- */
    1897          77 :     if (psWO->hCutline != nullptr)
    1898             :     {
    1899           0 :         char *pszWKT = nullptr;
    1900           0 :         if (OGR_G_ExportToWkt(static_cast<OGRGeometryH>(psWO->hCutline),
    1901           0 :                               &pszWKT) == OGRERR_NONE)
    1902             :         {
    1903           0 :             CPLCreateXMLElementAndValue(psTree, "Cutline", pszWKT);
    1904             :         }
    1905           0 :         CPLFree(pszWKT);
    1906             :     }
    1907             : 
    1908          77 :     if (psWO->dfCutlineBlendDist != 0.0)
    1909           0 :         CPLCreateXMLElementAndValue(
    1910             :             psTree, "CutlineBlendDist",
    1911           0 :             CPLString().Printf("%.5g", psWO->dfCutlineBlendDist));
    1912             : 
    1913          77 :     return psTree;
    1914             : }
    1915             : 
    1916             : /************************************************************************/
    1917             : /*                     GDALDeserializeWarpOptions()                     */
    1918             : /************************************************************************/
    1919             : 
    1920         198 : GDALWarpOptions *CPL_STDCALL GDALDeserializeWarpOptions(CPLXMLNode *psTree)
    1921             : 
    1922             : {
    1923         198 :     CPLErrorReset();
    1924             : 
    1925             :     /* -------------------------------------------------------------------- */
    1926             :     /*      Verify this is the right kind of object.                        */
    1927             :     /* -------------------------------------------------------------------- */
    1928         198 :     if (psTree == nullptr || psTree->eType != CXT_Element ||
    1929         198 :         !EQUAL(psTree->pszValue, "GDALWarpOptions"))
    1930             :     {
    1931           0 :         CPLError(CE_Failure, CPLE_AppDefined,
    1932             :                  "Wrong node, unable to deserialize GDALWarpOptions.");
    1933           0 :         return nullptr;
    1934             :     }
    1935             : 
    1936             :     /* -------------------------------------------------------------------- */
    1937             :     /*      Create pre-initialized warp options.                            */
    1938             :     /* -------------------------------------------------------------------- */
    1939         198 :     GDALWarpOptions *psWO = GDALCreateWarpOptions();
    1940             : 
    1941             :     /* -------------------------------------------------------------------- */
    1942             :     /*      Warp memory limit.                                              */
    1943             :     /* -------------------------------------------------------------------- */
    1944         198 :     psWO->dfWarpMemoryLimit =
    1945         198 :         CPLAtof(CPLGetXMLValue(psTree, "WarpMemoryLimit", "0.0"));
    1946             : 
    1947             :     /* -------------------------------------------------------------------- */
    1948             :     /*      resample algorithm                                              */
    1949             :     /* -------------------------------------------------------------------- */
    1950         198 :     const char *pszValue = CPLGetXMLValue(psTree, "ResampleAlg", "Default");
    1951             : 
    1952         198 :     if (EQUAL(pszValue, "NearestNeighbour"))
    1953         140 :         psWO->eResampleAlg = GRA_NearestNeighbour;
    1954          58 :     else if (EQUAL(pszValue, "Bilinear"))
    1955           8 :         psWO->eResampleAlg = GRA_Bilinear;
    1956          50 :     else if (EQUAL(pszValue, "Cubic"))
    1957           9 :         psWO->eResampleAlg = GRA_Cubic;
    1958          41 :     else if (EQUAL(pszValue, "CubicSpline"))
    1959           9 :         psWO->eResampleAlg = GRA_CubicSpline;
    1960          32 :     else if (EQUAL(pszValue, "Lanczos"))
    1961           4 :         psWO->eResampleAlg = GRA_Lanczos;
    1962          28 :     else if (EQUAL(pszValue, "Average"))
    1963           6 :         psWO->eResampleAlg = GRA_Average;
    1964          22 :     else if (EQUAL(pszValue, "RootMeanSquare"))
    1965           5 :         psWO->eResampleAlg = GRA_RMS;
    1966          17 :     else if (EQUAL(pszValue, "Mode"))
    1967           4 :         psWO->eResampleAlg = GRA_Mode;
    1968          13 :     else if (EQUAL(pszValue, "Maximum"))
    1969           3 :         psWO->eResampleAlg = GRA_Max;
    1970          10 :     else if (EQUAL(pszValue, "Minimum"))
    1971           2 :         psWO->eResampleAlg = GRA_Min;
    1972           8 :     else if (EQUAL(pszValue, "Median"))
    1973           3 :         psWO->eResampleAlg = GRA_Med;
    1974           5 :     else if (EQUAL(pszValue, "Quartile1"))
    1975           2 :         psWO->eResampleAlg = GRA_Q1;
    1976           3 :     else if (EQUAL(pszValue, "Quartile3"))
    1977           2 :         psWO->eResampleAlg = GRA_Q3;
    1978           1 :     else if (EQUAL(pszValue, "Sum"))
    1979           1 :         psWO->eResampleAlg = GRA_Sum;
    1980           0 :     else if (EQUAL(pszValue, "Default"))
    1981             :         /* leave as is */;
    1982             :     else
    1983             :     {
    1984           0 :         CPLError(CE_Failure, CPLE_AppDefined,
    1985             :                  "Unrecognised ResampleAlg value '%s'.", pszValue);
    1986             :     }
    1987             : 
    1988             :     /* -------------------------------------------------------------------- */
    1989             :     /*      Working data type.                                              */
    1990             :     /* -------------------------------------------------------------------- */
    1991         198 :     psWO->eWorkingDataType = GDALGetDataTypeByName(
    1992             :         CPLGetXMLValue(psTree, "WorkingDataType", "Unknown"));
    1993             : 
    1994             :     /* -------------------------------------------------------------------- */
    1995             :     /*      Name/value warp options.                                        */
    1996             :     /* -------------------------------------------------------------------- */
    1997        1849 :     for (CPLXMLNode *psItem = psTree->psChild; psItem != nullptr;
    1998        1651 :          psItem = psItem->psNext)
    1999             :     {
    2000        1651 :         if (psItem->eType == CXT_Element && EQUAL(psItem->pszValue, "Option"))
    2001             :         {
    2002         346 :             const char *pszName = CPLGetXMLValue(psItem, "Name", nullptr);
    2003         346 :             pszValue = CPLGetXMLValue(psItem, "", nullptr);
    2004             : 
    2005         346 :             if (pszName != nullptr && pszValue != nullptr)
    2006             :             {
    2007         346 :                 psWO->papszWarpOptions =
    2008         346 :                     CSLSetNameValue(psWO->papszWarpOptions, pszName, pszValue);
    2009             :             }
    2010             :         }
    2011             :     }
    2012             : 
    2013             :     /* -------------------------------------------------------------------- */
    2014             :     /*      Source Dataset.                                                 */
    2015             :     /* -------------------------------------------------------------------- */
    2016         198 :     pszValue = CPLGetXMLValue(psTree, "SourceDataset", nullptr);
    2017             : 
    2018         198 :     if (pszValue != nullptr)
    2019             :     {
    2020             :         CPLXMLNode *psGeoLocNode =
    2021         198 :             CPLSearchXMLNode(psTree, "GeoLocTransformer");
    2022         198 :         if (psGeoLocNode)
    2023             :         {
    2024           1 :             CPLCreateXMLElementAndValue(psGeoLocNode, "SourceDataset",
    2025             :                                         pszValue);
    2026             :         }
    2027             : 
    2028         396 :         CPLConfigOptionSetter oSetter("CPL_ALLOW_VSISTDIN", "NO", true);
    2029             : 
    2030         198 :         char **papszOpenOptions = GDALDeserializeOpenOptionsFromXML(psTree);
    2031         198 :         psWO->hSrcDS =
    2032         198 :             GDALOpenEx(pszValue, GDAL_OF_RASTER | GDAL_OF_VERBOSE_ERROR,
    2033             :                        nullptr, papszOpenOptions, nullptr);
    2034         198 :         CSLDestroy(papszOpenOptions);
    2035             :     }
    2036             : 
    2037             :     /* -------------------------------------------------------------------- */
    2038             :     /*      Destination Dataset.                                            */
    2039             :     /* -------------------------------------------------------------------- */
    2040         198 :     pszValue = CPLGetXMLValue(psTree, "DestinationDataset", nullptr);
    2041             : 
    2042         198 :     if (pszValue != nullptr)
    2043             :     {
    2044           0 :         psWO->hDstDS = GDALOpenShared(pszValue, GA_Update);
    2045             :     }
    2046             : 
    2047             :     /* -------------------------------------------------------------------- */
    2048             :     /*      First, count band mappings so we can establish the bandcount.   */
    2049             :     /* -------------------------------------------------------------------- */
    2050         198 :     CPLXMLNode *psBandTree = CPLGetXMLNode(psTree, "BandList");
    2051             : 
    2052         198 :     int nBandCount = 0;
    2053         198 :     CPLXMLNode *psBand = psBandTree ? psBandTree->psChild : nullptr;
    2054         563 :     for (; psBand != nullptr; psBand = psBand->psNext)
    2055             :     {
    2056         365 :         if (psBand->eType != CXT_Element ||
    2057         365 :             !EQUAL(psBand->pszValue, "BandMapping"))
    2058           0 :             continue;
    2059             : 
    2060         365 :         nBandCount++;
    2061             :     }
    2062             : 
    2063         198 :     GDALWarpInitDefaultBandMapping(psWO, nBandCount);
    2064             : 
    2065             :     /* ==================================================================== */
    2066             :     /*      Now actually process each bandmapping.                          */
    2067             :     /* ==================================================================== */
    2068         198 :     int iBand = 0;
    2069             : 
    2070         198 :     psBand = psBandTree ? psBandTree->psChild : nullptr;
    2071             : 
    2072         563 :     for (; psBand != nullptr; psBand = psBand->psNext)
    2073             :     {
    2074         365 :         if (psBand->eType != CXT_Element ||
    2075         365 :             !EQUAL(psBand->pszValue, "BandMapping"))
    2076           0 :             continue;
    2077             : 
    2078             :         /* --------------------------------------------------------------------
    2079             :          */
    2080             :         /*      Source band */
    2081             :         /* --------------------------------------------------------------------
    2082             :          */
    2083         365 :         pszValue = CPLGetXMLValue(psBand, "src", nullptr);
    2084         365 :         if (pszValue != nullptr)
    2085         365 :             psWO->panSrcBands[iBand] = atoi(pszValue);
    2086             : 
    2087             :         /* --------------------------------------------------------------------
    2088             :          */
    2089             :         /*      Destination band. */
    2090             :         /* --------------------------------------------------------------------
    2091             :          */
    2092         365 :         pszValue = CPLGetXMLValue(psBand, "dst", nullptr);
    2093         365 :         if (pszValue != nullptr)
    2094         365 :             psWO->panDstBands[iBand] = atoi(pszValue);
    2095             : 
    2096          66 :         const auto NormalizeValue = [](const char *pszValueIn,
    2097             :                                        GDALDataType eDataType) -> double
    2098             :         {
    2099          78 :             if (eDataType == GDT_Float32 &&
    2100          78 :                 CPLString().Printf(
    2101          12 :                     "%.16g", -std::numeric_limits<float>::max()) == pszValueIn)
    2102             :             {
    2103           0 :                 return std::numeric_limits<float>::lowest();
    2104             :             }
    2105          78 :             else if (eDataType == GDT_Float32 &&
    2106          78 :                      CPLString().Printf("%.16g",
    2107          12 :                                         std::numeric_limits<float>::max()) ==
    2108             :                          pszValueIn)
    2109             :             {
    2110           0 :                 return std::numeric_limits<float>::max();
    2111             :             }
    2112             :             else
    2113             :             {
    2114          66 :                 return CPLAtof(pszValueIn);
    2115             :             }
    2116             :         };
    2117             : 
    2118             :         /* --------------------------------------------------------------------
    2119             :          */
    2120             :         /*      Source nodata. */
    2121             :         /* --------------------------------------------------------------------
    2122             :          */
    2123         365 :         pszValue = CPLGetXMLValue(psBand, "SrcNoDataReal", nullptr);
    2124         365 :         if (pszValue != nullptr)
    2125             :         {
    2126          31 :             GDALWarpInitSrcNoDataReal(psWO, -1.1e20);
    2127          62 :             psWO->padfSrcNoDataReal[iBand] =
    2128          31 :                 NormalizeValue(pszValue, psWO->eWorkingDataType);
    2129             :         }
    2130             : 
    2131         365 :         pszValue = CPLGetXMLValue(psBand, "SrcNoDataImag", nullptr);
    2132         365 :         if (pszValue != nullptr)
    2133             :         {
    2134          31 :             GDALWarpInitSrcNoDataImag(psWO, 0);
    2135          31 :             psWO->padfSrcNoDataImag[iBand] = CPLAtof(pszValue);
    2136             :         }
    2137             : 
    2138             :         /* --------------------------------------------------------------------
    2139             :          */
    2140             :         /*      Destination nodata. */
    2141             :         /* --------------------------------------------------------------------
    2142             :          */
    2143         365 :         pszValue = CPLGetXMLValue(psBand, "DstNoDataReal", nullptr);
    2144         365 :         if (pszValue != nullptr)
    2145             :         {
    2146          35 :             GDALWarpInitDstNoDataReal(psWO, -1.1e20);
    2147          70 :             psWO->padfDstNoDataReal[iBand] =
    2148          35 :                 NormalizeValue(pszValue, psWO->eWorkingDataType);
    2149             :         }
    2150             : 
    2151         365 :         pszValue = CPLGetXMLValue(psBand, "DstNoDataImag", nullptr);
    2152         365 :         if (pszValue != nullptr)
    2153             :         {
    2154          35 :             GDALWarpInitDstNoDataImag(psWO, 0);
    2155          35 :             psWO->padfDstNoDataImag[iBand] = CPLAtof(pszValue);
    2156             :         }
    2157             : 
    2158         365 :         iBand++;
    2159             :     }
    2160             : 
    2161             :     /* -------------------------------------------------------------------- */
    2162             :     /*      Alpha bands.                                                    */
    2163             :     /* -------------------------------------------------------------------- */
    2164         198 :     psWO->nSrcAlphaBand = atoi(CPLGetXMLValue(psTree, "SrcAlphaBand", "0"));
    2165         198 :     psWO->nDstAlphaBand = atoi(CPLGetXMLValue(psTree, "DstAlphaBand", "0"));
    2166             : 
    2167             :     /* -------------------------------------------------------------------- */
    2168             :     /*      Cutline.                                                        */
    2169             :     /* -------------------------------------------------------------------- */
    2170         198 :     const char *pszWKT = CPLGetXMLValue(psTree, "Cutline", nullptr);
    2171         198 :     if (pszWKT)
    2172             :     {
    2173           7 :         char *pszWKTTemp = const_cast<char *>(pszWKT);
    2174           7 :         OGRGeometryH hCutline = nullptr;
    2175           7 :         OGR_G_CreateFromWkt(&pszWKTTemp, nullptr, &hCutline);
    2176           7 :         psWO->hCutline = hCutline;
    2177             :     }
    2178             : 
    2179         198 :     psWO->dfCutlineBlendDist =
    2180         198 :         CPLAtof(CPLGetXMLValue(psTree, "CutlineBlendDist", "0"));
    2181             : 
    2182             :     /* -------------------------------------------------------------------- */
    2183             :     /*      Transformation.                                                 */
    2184             :     /* -------------------------------------------------------------------- */
    2185         198 :     CPLXMLNode *psTransformer = CPLGetXMLNode(psTree, "Transformer");
    2186             : 
    2187         198 :     if (psTransformer != nullptr && psTransformer->psChild != nullptr)
    2188             :     {
    2189         198 :         GDALDeserializeTransformer(psTransformer->psChild,
    2190             :                                    &(psWO->pfnTransformer),
    2191             :                                    &(psWO->pTransformerArg));
    2192             :     }
    2193             : 
    2194             :     /* -------------------------------------------------------------------- */
    2195             :     /*      If any error has occurred, cleanup else return success.          */
    2196             :     /* -------------------------------------------------------------------- */
    2197         198 :     if (CPLGetLastErrorType() != CE_None)
    2198             :     {
    2199           0 :         if (psWO->pTransformerArg)
    2200             :         {
    2201           0 :             GDALDestroyTransformer(psWO->pTransformerArg);
    2202           0 :             psWO->pTransformerArg = nullptr;
    2203             :         }
    2204           0 :         if (psWO->hSrcDS != nullptr)
    2205             :         {
    2206           0 :             GDALClose(psWO->hSrcDS);
    2207           0 :             psWO->hSrcDS = nullptr;
    2208             :         }
    2209           0 :         if (psWO->hDstDS != nullptr)
    2210             :         {
    2211           0 :             GDALClose(psWO->hDstDS);
    2212           0 :             psWO->hDstDS = nullptr;
    2213             :         }
    2214           0 :         GDALDestroyWarpOptions(psWO);
    2215           0 :         return nullptr;
    2216             :     }
    2217             : 
    2218         198 :     return psWO;
    2219             : }

Generated by: LCOV version 1.14