LCOV - code coverage report
Current view: top level - frmts/ecw - ecwdataset.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 981 1204 81.5 %
Date: 2024-11-21 22:18:42 Functions: 51 53 96.2 %

          Line data    Source code
       1             : /******************************************************************************
       2             :  *
       3             :  * Project:  GDAL
       4             :  * Purpose:  ECW (ERDAS Wavelet Compression Format) Driver
       5             :  * Author:   Frank Warmerdam, warmerdam@pobox.com
       6             :  *
       7             :  ******************************************************************************
       8             :  * Copyright (c) 2001, Frank Warmerdam <warmerdam@pobox.com>
       9             :  * Copyright (c) 2007-2013, Even Rouault <even dot rouault at spatialys.com>
      10             :  *
      11             :  * SPDX-License-Identifier: MIT
      12             :  ****************************************************************************/
      13             : 
      14             : // ncsjpcbuffer.h needs the min and max macros.
      15             : #undef NOMINMAX
      16             : 
      17             : #include "cpl_minixml.h"
      18             : #include "gdal_ecw.h"
      19             : #include "gdal_frmts.h"
      20             : #include "ogr_spatialref.h"
      21             : #include "ogr_api.h"
      22             : 
      23             : #include "../mem/memdataset.h"
      24             : 
      25             : #include "ecwdrivercore.h"
      26             : 
      27             : #include <algorithm>
      28             : #include <cmath>
      29             : 
      30             : #undef NOISY_DEBUG
      31             : 
      32             : static CPLMutex *hECWDatasetMutex = nullptr;
      33             : static int bNCSInitialized = FALSE;
      34             : 
      35             : void ECWInitialize(void);
      36             : 
      37             : constexpr int DEFAULT_BLOCK_SIZE = 256;
      38             : 
      39             : GDALDataset *ECWDatasetOpenJPEG2000(GDALOpenInfo *poOpenInfo);
      40             : 
      41             : /************************************************************************/
      42             : /*                           ECWReportError()                           */
      43             : /************************************************************************/
      44             : 
      45           1 : void ECWReportError(CNCSError &oErr, const char *pszMsg)
      46             : {
      47             : #if ECWSDK_VERSION < 50
      48           1 :     char *pszErrorMessage = oErr.GetErrorMessage();
      49           1 :     CPLError(CE_Failure, CPLE_AppDefined, "%s%s", pszMsg, pszErrorMessage);
      50           1 :     NCSFree(pszErrorMessage);
      51             : #else
      52             :     CPLError(CE_Failure, CPLE_AppDefined, "%s%s", pszMsg,
      53             :              NCSGetLastErrorText(oErr));
      54             : #endif
      55           1 : }
      56             : 
      57             : /************************************************************************/
      58             : /*                           ECWRasterBand()                            */
      59             : /************************************************************************/
      60             : 
      61         295 : ECWRasterBand::ECWRasterBand(ECWDataset *poDSIn, int nBandIn, int iOverviewIn,
      62         295 :                              char **papszOpenOptions)
      63             : 
      64             : {
      65         295 :     this->poDS = poDSIn;
      66         295 :     poGDS = poDSIn;
      67             : 
      68         295 :     this->iOverview = iOverviewIn;
      69         295 :     this->nBand = nBandIn;
      70         295 :     eDataType = poDSIn->eRasterDataType;
      71             : 
      72         295 :     nRasterXSize = poDS->GetRasterXSize() / (1 << (iOverview + 1));
      73         295 :     nRasterYSize = poDS->GetRasterYSize() / (1 << (iOverview + 1));
      74             : 
      75             : #if ECWSDK_VERSION >= 51
      76             :     if (poDSIn->bIsJPEG2000 && poDSIn->poFileView)
      77             :     {
      78             :         UINT32 nTileWidth = 0;
      79             :         poDSIn->poFileView->GetParameter(
      80             :             const_cast<char *>("JPC:DECOMPRESS:TILESIZE:X"), &nTileWidth);
      81             :         if (nTileWidth <= static_cast<UINT32>(INT_MAX))
      82             :         {
      83             :             nBlockXSize = static_cast<int>(nTileWidth);
      84             :         }
      85             :         nBlockXSize = MIN(nBlockXSize, nRasterXSize);
      86             : 
      87             :         UINT32 nTileHeight = 0;
      88             :         poDSIn->poFileView->GetParameter(
      89             :             const_cast<char *>("JPC:DECOMPRESS:TILESIZE:Y"), &nTileHeight);
      90             :         if (nTileHeight <= static_cast<UINT32>(INT_MAX))
      91             :         {
      92             :             nBlockYSize = static_cast<int>(nTileHeight);
      93             :         }
      94             :         nBlockYSize = MIN(nBlockYSize, nRasterYSize);
      95             :     }
      96             : #endif
      97             : 
      98             :     // Slightly arbitrary value. Too large values would defeat the purpose
      99             :     // of the block concept.
     100         295 :     constexpr int LIMIT_FOR_BLOCK_SIZE = 2048;
     101         295 :     if (nBlockXSize <= 0 || nBlockYSize <= 0 ||
     102           0 :         nBlockXSize > LIMIT_FOR_BLOCK_SIZE ||
     103           0 :         nBlockYSize > LIMIT_FOR_BLOCK_SIZE)
     104             :     {
     105         295 :         nBlockXSize = DEFAULT_BLOCK_SIZE;
     106         295 :         nBlockYSize = DEFAULT_BLOCK_SIZE;
     107             :     }
     108             : 
     109             :     /* -------------------------------------------------------------------- */
     110             :     /*      Work out band color interpretation.                             */
     111             :     /* -------------------------------------------------------------------- */
     112         295 :     if (poDSIn->psFileInfo->eColorSpace == NCSCS_NONE)
     113           0 :         eBandInterp = GCI_Undefined;
     114         295 :     else if (poDSIn->psFileInfo->eColorSpace == NCSCS_GREYSCALE)
     115             :     {
     116          67 :         eBandInterp = GCI_GrayIndex;
     117             :         // we could also have alpha band.
     118          67 :         if (strcmp(poDSIn->psFileInfo->pBands[nBand - 1].szDesc,
     119          67 :                    NCS_BANDDESC_AllOpacity) == 0 ||
     120          67 :             strcmp(poDSIn->psFileInfo->pBands[nBand - 1].szDesc,
     121             :                    NCS_BANDDESC_GreyscaleOpacity) == 0)
     122             :         {
     123           0 :             eBandInterp = GCI_AlphaBand;
     124             :         }
     125             :     }
     126         228 :     else if (poDSIn->psFileInfo->eColorSpace == NCSCS_MULTIBAND)
     127             :     {
     128          62 :         eBandInterp = ECWGetColorInterpretationByName(
     129          62 :             poDSIn->psFileInfo->pBands[nBand - 1].szDesc);
     130             :     }
     131         166 :     else if (poDSIn->psFileInfo->eColorSpace == NCSCS_sRGB)
     132             :     {
     133         332 :         eBandInterp = ECWGetColorInterpretationByName(
     134         166 :             poDSIn->psFileInfo->pBands[nBand - 1].szDesc);
     135         166 :         if (eBandInterp == GCI_Undefined)
     136             :         {
     137           6 :             if (nBand == 1)
     138           2 :                 eBandInterp = GCI_RedBand;
     139           4 :             else if (nBand == 2)
     140           2 :                 eBandInterp = GCI_GreenBand;
     141           2 :             else if (nBand == 3)
     142           2 :                 eBandInterp = GCI_BlueBand;
     143           0 :             else if (nBand == 4)
     144             :             {
     145           0 :                 if (strcmp(poDSIn->psFileInfo->pBands[nBand - 1].szDesc,
     146             :                            NCS_BANDDESC_AllOpacity) == 0)
     147           0 :                     eBandInterp = GCI_AlphaBand;
     148             :                 else
     149           0 :                     eBandInterp = GCI_Undefined;
     150             :             }
     151             :             else
     152             :             {
     153           0 :                 eBandInterp = GCI_Undefined;
     154             :             }
     155             :         }
     156             :     }
     157           0 :     else if (poDSIn->psFileInfo->eColorSpace == NCSCS_YCbCr)
     158             :     {
     159           0 :         if (CPLTestBool(CPLGetConfigOption("CONVERT_YCBCR_TO_RGB", "YES")))
     160             :         {
     161           0 :             if (nBand == 1)
     162           0 :                 eBandInterp = GCI_RedBand;
     163           0 :             else if (nBand == 2)
     164           0 :                 eBandInterp = GCI_GreenBand;
     165           0 :             else if (nBand == 3)
     166           0 :                 eBandInterp = GCI_BlueBand;
     167             :             else
     168           0 :                 eBandInterp = GCI_Undefined;
     169             :         }
     170             :         else
     171             :         {
     172           0 :             if (nBand == 1)
     173           0 :                 eBandInterp = GCI_YCbCr_YBand;
     174           0 :             else if (nBand == 2)
     175           0 :                 eBandInterp = GCI_YCbCr_CbBand;
     176           0 :             else if (nBand == 3)
     177           0 :                 eBandInterp = GCI_YCbCr_CrBand;
     178             :             else
     179           0 :                 eBandInterp = GCI_Undefined;
     180             :         }
     181             :     }
     182             :     else
     183           0 :         eBandInterp = GCI_Undefined;
     184             : 
     185             :     /* -------------------------------------------------------------------- */
     186             :     /*      If this is the base level, create a set of overviews.           */
     187             :     /* -------------------------------------------------------------------- */
     188         295 :     if (iOverview == -1)
     189             :     {
     190             :         int i;
     191         295 :         for (i = 0; nRasterXSize / (1 << (i + 1)) > 128 &&
     192         100 :                     nRasterYSize / (1 << (i + 1)) > 128;
     193             :              i++)
     194             :         {
     195         168 :             apoOverviews.push_back(
     196          84 :                 new ECWRasterBand(poDSIn, nBandIn, i, papszOpenOptions));
     197             :         }
     198             :     }
     199             : 
     200         295 :     bPromoteTo8Bit =
     201          32 :         poDSIn->psFileInfo->nBands == 4 && nBand == 4 &&
     202           8 :         poDSIn->psFileInfo->pBands[0].nBits == 8 &&
     203           4 :         poDSIn->psFileInfo->pBands[1].nBits == 8 &&
     204           4 :         poDSIn->psFileInfo->pBands[2].nBits == 8 &&
     205           4 :         poDSIn->psFileInfo->pBands[3].nBits == 1 &&
     206         329 :         eBandInterp == GCI_AlphaBand &&
     207           2 :         CPLFetchBool(papszOpenOptions, "1BIT_ALPHA_PROMOTION",
     208           2 :                      CPLTestBool(CPLGetConfigOption(
     209             :                          "GDAL_ECW_PROMOTE_1BIT_ALPHA_AS_8BIT", "YES")));
     210         295 :     if (bPromoteTo8Bit)
     211           1 :         CPLDebug("ECW", "Fourth (alpha) band is promoted from 1 bit to 8 bit");
     212             : 
     213         295 :     if ((poDSIn->psFileInfo->pBands[nBand - 1].nBits % 8) != 0 &&
     214          22 :         !bPromoteTo8Bit)
     215          21 :         GDALPamRasterBand::SetMetadataItem(
     216             :             "NBITS",
     217          42 :             CPLString().Printf("%d",
     218          21 :                                poDSIn->psFileInfo->pBands[nBand - 1].nBits),
     219             :             "IMAGE_STRUCTURE");
     220             : 
     221         295 :     GDALRasterBand::SetDescription(
     222         295 :         poDSIn->psFileInfo->pBands[nBand - 1].szDesc);
     223         295 : }
     224             : 
     225             : /************************************************************************/
     226             : /*                          ~ECWRasterBand()                           */
     227             : /************************************************************************/
     228             : 
     229         590 : ECWRasterBand::~ECWRasterBand()
     230             : {
     231         295 :     GDALRasterBand::FlushCache(true);
     232             : 
     233         379 :     while (!apoOverviews.empty())
     234             :     {
     235          84 :         delete apoOverviews.back();
     236          84 :         apoOverviews.pop_back();
     237             :     }
     238         590 : }
     239             : 
     240             : /************************************************************************/
     241             : /*                            GetOverview()                             */
     242             : /************************************************************************/
     243             : 
     244           2 : GDALRasterBand *ECWRasterBand::GetOverview(int iOverviewIn)
     245             : 
     246             : {
     247           2 :     if (iOverviewIn >= 0 && iOverviewIn < (int)apoOverviews.size())
     248           2 :         return apoOverviews[iOverviewIn];
     249             :     else
     250           0 :         return nullptr;
     251             : }
     252             : 
     253             : /************************************************************************/
     254             : /*                       GetColorInterpretation()                       */
     255             : /************************************************************************/
     256             : 
     257          87 : GDALColorInterp ECWRasterBand::GetColorInterpretation()
     258             : 
     259             : {
     260          87 :     return eBandInterp;
     261             : }
     262             : 
     263             : /************************************************************************/
     264             : /*                       SetColorInterpretation()                       */
     265             : /*                                                                      */
     266             : /*      This would normally just be used by folks using the ECW code    */
     267             : /*      to read JP2 streams in other formats (such as NITF) and         */
     268             : /*      providing their own color interpretation regardless of what     */
     269             : /*      ECW might think the stream itself says.                         */
     270             : /************************************************************************/
     271             : 
     272          13 : CPLErr ECWRasterBand::SetColorInterpretation(GDALColorInterp eNewInterp)
     273             : 
     274             : {
     275          13 :     eBandInterp = eNewInterp;
     276             : 
     277          13 :     return CE_None;
     278             : }
     279             : 
     280             : /************************************************************************/
     281             : /*                             AdviseRead()                             */
     282             : /************************************************************************/
     283             : 
     284           0 : CPLErr ECWRasterBand::AdviseRead(int nXOff, int nYOff, int nXSize, int nYSize,
     285             :                                  int nBufXSize, int nBufYSize, GDALDataType eDT,
     286             :                                  char **papszOptions)
     287             : {
     288           0 :     const int nResFactor = 1 << (iOverview + 1);
     289             : 
     290           0 :     return poGDS->AdviseRead(nXOff * nResFactor, nYOff * nResFactor,
     291             :                              nXSize * nResFactor, nYSize * nResFactor,
     292             :                              nBufXSize, nBufYSize, eDT, 1, &nBand,
     293           0 :                              papszOptions);
     294             : }
     295             : 
     296             : // statistics support:
     297             : #if ECWSDK_VERSION >= 50
     298             : 
     299             : /************************************************************************/
     300             : /*                       GetDefaultHistogram()                          */
     301             : /************************************************************************/
     302             : 
     303             : CPLErr ECWRasterBand::GetDefaultHistogram(double *pdfMin, double *pdfMax,
     304             :                                           int *pnBuckets,
     305             :                                           GUIntBig **ppanHistogram, int bForce,
     306             :                                           GDALProgressFunc f,
     307             :                                           void *pProgressData)
     308             : {
     309             :     int bForceCoalesced = bForce;
     310             :     // If file version is smaller than 3, there will be no statistics in the
     311             :     // file. But if it is version 3 or higher we don't want underlying
     312             :     // implementation to compute histogram so we set bForceCoalesced to FALSE.
     313             :     if (poGDS->psFileInfo->nFormatVersion >= 3)
     314             :     {
     315             :         bForceCoalesced = FALSE;
     316             :     }
     317             :     // We check if we have PAM histogram. If we have them we return them. This
     318             :     // will allow to override statistics stored in the file.
     319             :     CPLErr pamError = GDALPamRasterBand::GetDefaultHistogram(
     320             :         pdfMin, pdfMax, pnBuckets, ppanHistogram, bForceCoalesced, f,
     321             :         pProgressData);
     322             :     if (pamError == CE_None || poGDS->psFileInfo->nFormatVersion < 3 ||
     323             :         eBandInterp == GCI_AlphaBand)
     324             :     {
     325             :         return pamError;
     326             :     }
     327             : 
     328             :     NCS::CError error = poGDS->StatisticsEnsureInitialized();
     329             :     if (!error.Success())
     330             :     {
     331             :         CPLError(CE_Warning, CPLE_AppDefined,
     332             :                  "ECWRDataset::StatisticsEnsureInitialized failed in "
     333             :                  "ECWRasterBand::GetDefaultHistogram. ");
     334             :         return CE_Failure;
     335             :     }
     336             :     GetBandIndexAndCountForStatistics(nStatsBandIndex, nStatsBandCount);
     337             :     bool bHistogramFromFile = false;
     338             : 
     339             :     if (poGDS->pStatistics != nullptr)
     340             :     {
     341             :         NCSBandStats &bandStats =
     342             :             poGDS->pStatistics->BandsStats[nStatsBandIndex];
     343             :         if (bandStats.Histogram != nullptr && bandStats.nHistBucketCount > 0)
     344             :         {
     345             :             *pnBuckets = bandStats.nHistBucketCount;
     346             :             *ppanHistogram = static_cast<GUIntBig *>(
     347             :                 VSIMalloc(bandStats.nHistBucketCount * sizeof(GUIntBig)));
     348             :             for (size_t i = 0; i < bandStats.nHistBucketCount; i++)
     349             :             {
     350             :                 (*ppanHistogram)[i] =
     351             :                     static_cast<GUIntBig>(bandStats.Histogram[i]);
     352             :             }
     353             :             // JTO: this is not perfect as You can't tell who wrote the
     354             :             // histogram !!! It will offset it unnecessarily for files with
     355             :             // hists not modified by GDAL.
     356             :             const double dfHalfBucket =
     357             :                 (bandStats.fMaxHist - bandStats.fMinHist) /
     358             :                 (2 * (*pnBuckets - 1));
     359             :             if (pdfMin != nullptr)
     360             :             {
     361             :                 *pdfMin = bandStats.fMinHist - dfHalfBucket;
     362             :             }
     363             :             if (pdfMax != nullptr)
     364             :             {
     365             :                 *pdfMax = bandStats.fMaxHist + dfHalfBucket;
     366             :             }
     367             :             bHistogramFromFile = true;
     368             :         }
     369             :     }
     370             : 
     371             :     if (!bHistogramFromFile)
     372             :     {
     373             :         if (bForce == TRUE)
     374             :         {
     375             :             // compute. Save.
     376             :             pamError = GDALPamRasterBand::GetDefaultHistogram(
     377             :                 pdfMin, pdfMax, pnBuckets, ppanHistogram, TRUE, f,
     378             :                 pProgressData);
     379             :             if (pamError == CE_None)
     380             :             {
     381             :                 const CPLErr error2 = SetDefaultHistogram(
     382             :                     *pdfMin, *pdfMax, *pnBuckets, *ppanHistogram);
     383             :                 if (error2 != CE_None)
     384             :                 {
     385             :                     // Histogram is there but we failed to save it back to file.
     386             :                     CPLError(CE_Warning, CPLE_AppDefined,
     387             :                              "SetDefaultHistogram failed in "
     388             :                              "ECWRasterBand::GetDefaultHistogram. Histogram "
     389             :                              "might not be saved in .ecw file.");
     390             :                 }
     391             :                 return CE_None;
     392             :             }
     393             :             return pamError;
     394             :         }
     395             :         // No histogram, no forced computation.
     396             :         return CE_Warning;
     397             :     }
     398             :     // Statistics were already there and were used.
     399             :     return CE_None;
     400             : }
     401             : 
     402             : /************************************************************************/
     403             : /*                       SetDefaultHistogram()                          */
     404             : /************************************************************************/
     405             : 
     406             : CPLErr ECWRasterBand::SetDefaultHistogram(double dfMin, double dfMax,
     407             :                                           int nBuckets, GUIntBig *panHistogram)
     408             : {
     409             :     // Only version 3 supports saving statistics.
     410             :     if (poGDS->psFileInfo->nFormatVersion < 3 || eBandInterp == GCI_AlphaBand)
     411             :     {
     412             :         return GDALPamRasterBand::SetDefaultHistogram(dfMin, dfMax, nBuckets,
     413             :                                                       panHistogram);
     414             :     }
     415             : 
     416             :     // determine if there are statistics in PAM file.
     417             :     double dummy;
     418             :     int dummy_i;
     419             :     GUIntBig *dummy_histogram = nullptr;
     420             :     bool hasPAMDefaultHistogram =
     421             :         GDALPamRasterBand::GetDefaultHistogram(&dummy, &dummy, &dummy_i,
     422             :                                                &dummy_histogram, FALSE, nullptr,
     423             :                                                nullptr) == CE_None;
     424             :     if (hasPAMDefaultHistogram)
     425             :     {
     426             :         VSIFree(dummy_histogram);
     427             :     }
     428             : 
     429             :     // ECW SDK ignores statistics for opacity bands. So we need to compute
     430             :     // number of bands without opacity.
     431             :     GetBandIndexAndCountForStatistics(nStatsBandIndex, nStatsBandCount);
     432             :     UINT32 bucketCounts[256];
     433             :     std::fill_n(bucketCounts, nStatsBandCount, 0);
     434             :     bucketCounts[nStatsBandIndex] = nBuckets;
     435             : 
     436             :     NCS::CError error = poGDS->StatisticsEnsureInitialized();
     437             :     if (!error.Success())
     438             :     {
     439             :         CPLError(CE_Warning, CPLE_AppDefined,
     440             :                  "ECWRDataset::StatisticsEnsureInitialized failed in "
     441             :                  "ECWRasterBand::SetDefaultHistogram. Default histogram will "
     442             :                  "be written to PAM. ");
     443             :         return GDALPamRasterBand::SetDefaultHistogram(dfMin, dfMax, nBuckets,
     444             :                                                       panHistogram);
     445             :     }
     446             : 
     447             :     NCSFileStatistics *pStatistics = poGDS->pStatistics;
     448             : 
     449             :     if (pStatistics == nullptr)
     450             :     {
     451             :         error =
     452             :             NCSEcwInitStatistics(&pStatistics, nStatsBandCount, bucketCounts);
     453             :         poGDS->bStatisticsDirty = TRUE;
     454             :         poGDS->pStatistics = pStatistics;
     455             :         if (!error.Success())
     456             :         {
     457             :             CPLError(CE_Warning, CPLE_AppDefined,
     458             :                      "NCSEcwInitStatistics failed in "
     459             :                      "ECWRasterBand::SetDefaultHistogram.");
     460             :             return GDALPamRasterBand::SetDefaultHistogram(
     461             :                 dfMin, dfMax, nBuckets, panHistogram);
     462             :         }
     463             :         // no error statistics properly initialized but there were no statistics
     464             :         // previously.
     465             :     }
     466             :     else
     467             :     {
     468             :         // is there a room for our band already?
     469             :         // This should account for following cases:
     470             :         // 1. Existing histogram (for this or different band) has smaller bucket
     471             :         // count.
     472             :         // 2. There is no existing histogram but statistics are set for one or
     473             :         // more bands (pStatistics->nHistBucketCounts is zero).
     474             :         if ((int)pStatistics->BandsStats[nStatsBandIndex].nHistBucketCount !=
     475             :             nBuckets)
     476             :         {
     477             :             // no. There is no room. We need more!
     478             :             NCSFileStatistics *pNewStatistics = nullptr;
     479             :             for (size_t i = 0; i < pStatistics->nNumberOfBands; i++)
     480             :             {
     481             :                 bucketCounts[i] = pStatistics->BandsStats[i].nHistBucketCount;
     482             :             }
     483             :             bucketCounts[nStatsBandIndex] = nBuckets;
     484             :             if (nBuckets <
     485             :                 static_cast<int>(
     486             :                     pStatistics->BandsStats[nStatsBandIndex].nHistBucketCount))
     487             :             {
     488             :                 pStatistics->BandsStats[nStatsBandIndex].nHistBucketCount =
     489             :                     nBuckets;
     490             :             }
     491             :             error = NCSEcwInitStatistics(&pNewStatistics, nStatsBandCount,
     492             :                                          bucketCounts);
     493             :             if (!error.Success())
     494             :             {
     495             :                 CPLError(CE_Warning, CPLE_AppDefined,
     496             :                          "NCSEcwInitStatistics failed in "
     497             :                          "ECWRasterBand::SetDefaultHistogram (reallocate).");
     498             :                 return GDALPamRasterBand::SetDefaultHistogram(
     499             :                     dfMin, dfMax, nBuckets, panHistogram);
     500             :             }
     501             :             // we need to copy existing statistics.
     502             :             error = NCSEcwCopyStatistics(&pNewStatistics, pStatistics);
     503             :             if (!error.Success())
     504             :             {
     505             :                 CPLError(CE_Warning, CPLE_AppDefined,
     506             :                          "NCSEcwCopyStatistics failed in "
     507             :                          "ECWRasterBand::SetDefaultHistogram.");
     508             :                 NCSEcwFreeStatistics(pNewStatistics);
     509             :                 return GDALPamRasterBand::SetDefaultHistogram(
     510             :                     dfMin, dfMax, nBuckets, panHistogram);
     511             :             }
     512             :             pNewStatistics->nNumberOfBands = nStatsBandCount;
     513             :             NCSEcwFreeStatistics(pStatistics);
     514             :             pStatistics = pNewStatistics;
     515             :             poGDS->pStatistics = pStatistics;
     516             :             poGDS->bStatisticsDirty = TRUE;
     517             :         }
     518             :     }
     519             : 
     520             :     // at this point we have allocated statistics structure.
     521             :     double dfHalfBucket = (dfMax - dfMin) / (2 * nBuckets);
     522             :     pStatistics->BandsStats[nStatsBandIndex].fMinHist =
     523             :         static_cast<IEEE4>(dfMin + dfHalfBucket);
     524             :     pStatistics->BandsStats[nStatsBandIndex].fMaxHist =
     525             :         static_cast<IEEE4>(dfMax - dfHalfBucket);
     526             :     for (int i = 0; i < nBuckets; i++)
     527             :     {
     528             :         pStatistics->BandsStats[nStatsBandIndex].Histogram[i] =
     529             :             static_cast<UINT64>(panHistogram[i]);
     530             :     }
     531             : 
     532             :     if (hasPAMDefaultHistogram)
     533             :     {
     534             :         CPLError(CE_Debug, CPLE_AppDefined,
     535             :                  "PAM default histogram will be overwritten.");
     536             :         return GDALPamRasterBand::SetDefaultHistogram(dfMin, dfMax, nBuckets,
     537             :                                                       panHistogram);
     538             :     }
     539             :     return CE_None;
     540             : }
     541             : 
     542             : /************************************************************************/
     543             : /*                   GetBandIndexAndCountForStatistics()                */
     544             : /************************************************************************/
     545             : 
     546             : void ECWRasterBand::GetBandIndexAndCountForStatistics(int &bandIndex,
     547             :                                                       int &bandCount) const
     548             : {
     549             :     bandIndex = nBand - 1;
     550             :     bandCount = poGDS->nBands;
     551             :     for (int i = 0; i < poGDS->nBands; i++)
     552             :     {
     553             :         if (poDS->GetRasterBand(i + 1)->GetColorInterpretation() ==
     554             :             GCI_AlphaBand)
     555             :         {
     556             :             bandCount--;
     557             :             if (i < nBand - 1)
     558             :             {
     559             :                 bandIndex--;
     560             :             }
     561             :         }
     562             :     }
     563             : }
     564             : 
     565             : /************************************************************************/
     566             : /*                           GetMinimum()                               */
     567             : /************************************************************************/
     568             : 
     569             : double ECWRasterBand::GetMinimum(int *pbSuccess)
     570             : {
     571             :     if (poGDS->psFileInfo->nFormatVersion >= 3)
     572             :     {
     573             :         NCS::CError error = poGDS->StatisticsEnsureInitialized();
     574             :         if (error.Success())
     575             :         {
     576             :             GetBandIndexAndCountForStatistics(nStatsBandIndex, nStatsBandCount);
     577             :             if (poGDS->pStatistics != nullptr)
     578             :             {
     579             :                 NCSBandStats &bandStats =
     580             :                     poGDS->pStatistics->BandsStats[nStatsBandIndex];
     581             :                 if (!std::isnan(bandStats.fMinVal))
     582             :                 {
     583             :                     if (pbSuccess)
     584             :                         *pbSuccess = TRUE;
     585             :                     return bandStats.fMinVal;
     586             :                 }
     587             :             }
     588             :         }
     589             :     }
     590             :     return GDALPamRasterBand::GetMinimum(pbSuccess);
     591             : }
     592             : 
     593             : /************************************************************************/
     594             : /*                           GetMaximum()                               */
     595             : /************************************************************************/
     596             : 
     597             : double ECWRasterBand::GetMaximum(int *pbSuccess)
     598             : {
     599             :     if (poGDS->psFileInfo->nFormatVersion >= 3)
     600             :     {
     601             :         NCS::CError error = poGDS->StatisticsEnsureInitialized();
     602             :         if (error.Success())
     603             :         {
     604             :             GetBandIndexAndCountForStatistics(nStatsBandIndex, nStatsBandCount);
     605             :             if (poGDS->pStatistics != nullptr)
     606             :             {
     607             :                 NCSBandStats &bandStats =
     608             :                     poGDS->pStatistics->BandsStats[nStatsBandIndex];
     609             :                 if (!std::isnan(bandStats.fMaxVal))
     610             :                 {
     611             :                     if (pbSuccess)
     612             :                         *pbSuccess = TRUE;
     613             :                     return bandStats.fMaxVal;
     614             :                 }
     615             :             }
     616             :         }
     617             :     }
     618             :     return GDALPamRasterBand::GetMaximum(pbSuccess);
     619             : }
     620             : 
     621             : /************************************************************************/
     622             : /*                          SetMetadataItem()                           */
     623             : /************************************************************************/
     624             : 
     625             : CPLErr ECWRasterBand::SetMetadataItem(const char *pszName, const char *pszValue,
     626             :                                       const char *pszDomain)
     627             : {
     628             :     if (EQUAL(pszName, "STATISTICS_VALID_PERCENT"))
     629             :         return CE_None;
     630             :     return GDALPamRasterBand::SetMetadataItem(pszName, pszValue, pszDomain);
     631             : }
     632             : 
     633             : /************************************************************************/
     634             : /*                          GetStatistics()                             */
     635             : /************************************************************************/
     636             : 
     637             : CPLErr ECWRasterBand::GetStatistics(int bApproxOK, int bForce, double *pdfMin,
     638             :                                     double *pdfMax, double *pdfMean,
     639             :                                     double *padfStdDev)
     640             : {
     641             :     int bForceCoalesced = bForce;
     642             :     // If file version is smaller than 3, there will be no statistics in the
     643             :     // file. But if it is version 3 or higher we don't want underlying
     644             :     // implementation to compute histogram so we set bForceCoalesced to FALSE.
     645             :     if (poGDS->psFileInfo->nFormatVersion >= 3)
     646             :     {
     647             :         bForceCoalesced = FALSE;
     648             :     }
     649             :     // We check if we have PAM histogram. If we have them we return them. This
     650             :     // will allow to override statistics stored in the file.
     651             :     CPLErr pamError = GDALPamRasterBand::GetStatistics(
     652             :         bApproxOK, bForceCoalesced, pdfMin, pdfMax, pdfMean, padfStdDev);
     653             :     if (pamError == CE_None || poGDS->psFileInfo->nFormatVersion < 3 ||
     654             :         eBandInterp == GCI_AlphaBand)
     655             :     {
     656             :         return pamError;
     657             :     }
     658             : 
     659             :     NCS::CError error = poGDS->StatisticsEnsureInitialized();
     660             :     if (!error.Success())
     661             :     {
     662             :         CPLError(CE_Failure, CPLE_AppDefined,
     663             :                  "ECWRDataset::StatisticsEnsureInitialized failed in "
     664             :                  "ECWRasterBand::GetStatistic. ");
     665             :         return CE_Failure;
     666             :     }
     667             :     GetBandIndexAndCountForStatistics(nStatsBandIndex, nStatsBandCount);
     668             :     bool bStatisticsFromFile = false;
     669             : 
     670             :     if (poGDS->pStatistics != nullptr)
     671             :     {
     672             :         bStatisticsFromFile = true;
     673             :         NCSBandStats &bandStats =
     674             :             poGDS->pStatistics->BandsStats[nStatsBandIndex];
     675             :         if (pdfMin != nullptr && !std::isnan(bandStats.fMinVal))
     676             :         {
     677             :             *pdfMin = bandStats.fMinVal;
     678             :         }
     679             :         else
     680             :         {
     681             :             bStatisticsFromFile = false;
     682             :         }
     683             :         if (pdfMax != nullptr && !std::isnan(bandStats.fMaxVal))
     684             :         {
     685             :             *pdfMax = bandStats.fMaxVal;
     686             :         }
     687             :         else
     688             :         {
     689             :             bStatisticsFromFile = false;
     690             :         }
     691             :         if (pdfMean != nullptr && !std::isnan(bandStats.fMeanVal))
     692             :         {
     693             :             *pdfMean = bandStats.fMeanVal;
     694             :         }
     695             :         else
     696             :         {
     697             :             bStatisticsFromFile = false;
     698             :         }
     699             :         if (padfStdDev != nullptr && !std::isnan(bandStats.fStandardDev))
     700             :         {
     701             :             *padfStdDev = bandStats.fStandardDev;
     702             :         }
     703             :         else
     704             :         {
     705             :             bStatisticsFromFile = false;
     706             :         }
     707             :         if (bStatisticsFromFile)
     708             :             return CE_None;
     709             :     }
     710             :     // no required statistics.
     711             :     if (!bStatisticsFromFile && bForce == TRUE)
     712             :     {
     713             :         double dfMin, dfMax, dfMean, dfStdDev;
     714             :         pamError = GDALPamRasterBand::GetStatistics(bApproxOK, TRUE, &dfMin,
     715             :                                                     &dfMax, &dfMean, &dfStdDev);
     716             :         if (pdfMin != nullptr)
     717             :         {
     718             :             *pdfMin = dfMin;
     719             :         }
     720             :         if (pdfMax != nullptr)
     721             :         {
     722             :             *pdfMax = dfMax;
     723             :         }
     724             :         if (pdfMean != nullptr)
     725             :         {
     726             :             *pdfMean = dfMean;
     727             :         }
     728             :         if (padfStdDev != nullptr)
     729             :         {
     730             :             *padfStdDev = dfStdDev;
     731             :         }
     732             :         if (pamError == CE_None)
     733             :         {
     734             :             const CPLErr err = SetStatistics(dfMin, dfMax, dfMean, dfStdDev);
     735             :             if (err != CE_None)
     736             :             {
     737             :                 CPLError(
     738             :                     CE_Warning, CPLE_AppDefined,
     739             :                     "SetStatistics failed in ECWRasterBand::GetStatistics. "
     740             :                     "Statistics might not be saved in .ecw file.");
     741             :             }
     742             :             return CE_None;
     743             :         }
     744             :         // whatever happened we return.
     745             :         return pamError;
     746             :     }
     747             :     // no statistics and we are not forced to return.
     748             :     return CE_Warning;
     749             : }
     750             : 
     751             : /************************************************************************/
     752             : /*                          SetStatistics()                             */
     753             : /************************************************************************/
     754             : 
     755             : CPLErr ECWRasterBand::SetStatistics(double dfMin, double dfMax, double dfMean,
     756             :                                     double dfStdDev)
     757             : {
     758             :     if (poGDS->psFileInfo->nFormatVersion < 3 || eBandInterp == GCI_AlphaBand)
     759             :     {
     760             :         return GDALPamRasterBand::SetStatistics(dfMin, dfMax, dfMean, dfStdDev);
     761             :     }
     762             :     double dummy;
     763             :     bool hasPAMStatistics =
     764             :         GDALPamRasterBand::GetStatistics(TRUE, FALSE, &dummy, &dummy, &dummy,
     765             :                                          &dummy) == CE_None;
     766             : 
     767             :     NCS::CError error = poGDS->StatisticsEnsureInitialized();
     768             :     if (!error.Success())
     769             :     {
     770             :         CPLError(
     771             :             CE_Warning, CPLE_AppDefined,
     772             :             "ECWRDataset::StatisticsEnsureInitialized failed in "
     773             :             "ECWRasterBand::SetStatistic. Statistics will be written to PAM. ");
     774             :         return GDALPamRasterBand::SetStatistics(dfMin, dfMax, dfMean, dfStdDev);
     775             :     }
     776             :     GetBandIndexAndCountForStatistics(nStatsBandIndex, nStatsBandCount);
     777             :     if (poGDS->pStatistics == nullptr)
     778             :     {
     779             :         error =
     780             :             NCSEcwInitStatistics(&poGDS->pStatistics, nStatsBandCount, nullptr);
     781             :         if (!error.Success())
     782             :         {
     783             :             CPLError(
     784             :                 CE_Warning, CPLE_AppDefined,
     785             :                 "NCSEcwInitStatistics failed in ECWRasterBand::SetStatistic. "
     786             :                 "Statistics will be written to PAM.");
     787             :             return GDALPamRasterBand::SetStatistics(dfMin, dfMax, dfMean,
     788             :                                                     dfStdDev);
     789             :         }
     790             :     }
     791             : 
     792             :     poGDS->pStatistics->BandsStats[nStatsBandIndex].fMinVal =
     793             :         static_cast<IEEE4>(dfMin);
     794             :     poGDS->pStatistics->BandsStats[nStatsBandIndex].fMaxVal =
     795             :         static_cast<IEEE4>(dfMax);
     796             :     poGDS->pStatistics->BandsStats[nStatsBandIndex].fMeanVal =
     797             :         static_cast<IEEE4>(dfMean);
     798             :     poGDS->pStatistics->BandsStats[nStatsBandIndex].fStandardDev =
     799             :         static_cast<IEEE4>(dfStdDev);
     800             :     poGDS->bStatisticsDirty = TRUE;
     801             :     // if we have PAM statistics we need to save them as well. Better option
     802             :     // would be to remove them from PAM file but I don't know how to do that
     803             :     // without messing in PAM internals.
     804             :     if (hasPAMStatistics)
     805             :     {
     806             :         CPLError(CE_Debug, CPLE_AppDefined,
     807             :                  "PAM statistics will be overwritten.");
     808             :         return GDALPamRasterBand::SetStatistics(dfMin, dfMax, dfMean, dfStdDev);
     809             :     }
     810             : 
     811             :     return CE_None;
     812             : }
     813             : #endif
     814             : 
     815             : // #if !defined(SDK_CAN_DO_SUPERSAMPLING)
     816             : /************************************************************************/
     817             : /*                          OldIRasterIO()                              */
     818             : /************************************************************************/
     819             : 
     820             : /* This implementation of IRasterIO(), derived from the one of GDAL 1.9 */
     821             : /* and older versions, is meant at making over-sampling */
     822             : /* work with ECW SDK 3.3. Newer versions of the SDK can do super-sampling in
     823             :  * their */
     824             : /* SetView() call. */
     825             : 
     826           1 : CPLErr ECWRasterBand::OldIRasterIO(GDALRWFlag eRWFlag, int nXOff, int nYOff,
     827             :                                    int nXSize, int nYSize, void *pData,
     828             :                                    int nBufXSize, int nBufYSize,
     829             :                                    GDALDataType eBufType, GSpacing nPixelSpace,
     830             :                                    GSpacing nLineSpace,
     831             :                                    GDALRasterIOExtraArg *psExtraArg)
     832             : 
     833             : {
     834             :     int iBand;
     835           1 :     GByte *pabyWorkBuffer = nullptr;
     836           1 :     const int nResFactor = 1 << (iOverview + 1);
     837             : 
     838           1 :     nXOff *= nResFactor;
     839           1 :     nYOff *= nResFactor;
     840           1 :     nXSize *= nResFactor;
     841           1 :     nYSize *= nResFactor;
     842             : 
     843             :     /* -------------------------------------------------------------------- */
     844             :     /*      Try to do it based on existing "advised" access.                */
     845             :     /* -------------------------------------------------------------------- */
     846           2 :     int nRet = poGDS->TryWinRasterIO(eRWFlag, nXOff, nYOff, nXSize, nYSize,
     847             :                                      static_cast<GByte *>(pData), nBufXSize,
     848           1 :                                      nBufYSize, eBufType, 1, &nBand,
     849             :                                      nPixelSpace, nLineSpace, 0, psExtraArg);
     850           1 :     if (nRet == TRUE)
     851           0 :         return CE_None;
     852           1 :     if (nRet < 0)
     853           0 :         return CE_Failure;
     854             : 
     855             :     /* -------------------------------------------------------------------- */
     856             :     /*      The ECW SDK doesn't supersample, so adjust for this case.       */
     857             :     /* -------------------------------------------------------------------- */
     858             : 
     859           1 :     int nNewXSize = nBufXSize;
     860           1 :     int nNewYSize = nBufYSize;
     861             : 
     862           1 :     if (nXSize < nBufXSize)
     863           1 :         nNewXSize = nXSize;
     864             : 
     865           1 :     if (nYSize < nBufYSize)
     866           1 :         nNewYSize = nYSize;
     867             : 
     868             :     /* -------------------------------------------------------------------- */
     869             :     /*      Can we perform direct loads, or must we load into a working     */
     870             :     /*      buffer, and transform?                                          */
     871             :     /* -------------------------------------------------------------------- */
     872           1 :     const int nRawPixelSize = GDALGetDataTypeSize(poGDS->eRasterDataType) / 8;
     873             : 
     874           1 :     int bDirect = nPixelSpace == 1 && eBufType == GDT_Byte &&
     875           2 :                   nNewXSize == nBufXSize && nNewYSize == nBufYSize;
     876           1 :     if (!bDirect)
     877             :         pabyWorkBuffer =
     878           1 :             static_cast<GByte *>(CPLMalloc(nNewXSize * nRawPixelSize));
     879             : 
     880             :     /* -------------------------------------------------------------------- */
     881             :     /*      Establish access at the desired resolution.                     */
     882             :     /* -------------------------------------------------------------------- */
     883           1 :     poGDS->CleanupWindow();
     884             : 
     885           1 :     iBand = nBand - 1;
     886           1 :     poGDS->nBandIndexToPromoteTo8Bit = (bPromoteTo8Bit) ? 0 : -1;
     887             :     // TODO: Fix writable strings issue.
     888           1 :     CNCSError oErr = poGDS->poFileView->SetView(
     889             :         1, reinterpret_cast<unsigned int *>(&iBand), nXOff, nYOff,
     890           2 :         nXOff + nXSize - 1, nYOff + nYSize - 1, nNewXSize, nNewYSize);
     891           1 :     if (oErr.GetErrorNumber() != NCS_SUCCESS)
     892             :     {
     893           0 :         CPLFree(pabyWorkBuffer);
     894           0 :         ECWReportError(oErr);
     895             : 
     896           0 :         return CE_Failure;
     897             :     }
     898             : 
     899             :     /* -------------------------------------------------------------------- */
     900             :     /*      Read back one scanline at a time, till request is satisfied.    */
     901             :     /*      Supersampling is not supported by the ECW API, so we will do    */
     902             :     /*      it ourselves.                                                   */
     903             :     /* -------------------------------------------------------------------- */
     904           1 :     double dfSrcYInc = static_cast<double>(nNewYSize) / nBufYSize;
     905           1 :     double dfSrcXInc = static_cast<double>(nNewXSize) / nBufXSize;
     906             :     int iSrcLine, iDstLine;
     907           1 :     CPLErr eErr = CE_None;
     908             : 
     909         801 :     for (iSrcLine = 0, iDstLine = 0; iDstLine < nBufYSize; iDstLine++)
     910             :     {
     911             :         NCSEcwReadStatus eRStatus;
     912         800 :         GPtrDiff_t iDstLineOff = iDstLine * (GPtrDiff_t)nLineSpace;
     913             :         unsigned char *pabySrcBuf;
     914             : 
     915         800 :         if (bDirect)
     916           0 :             pabySrcBuf = ((GByte *)pData) + iDstLineOff;
     917             :         else
     918         800 :             pabySrcBuf = pabyWorkBuffer;
     919             : 
     920         800 :         if (nNewYSize == nBufYSize || iSrcLine == (int)(iDstLine * dfSrcYInc))
     921             :         {
     922         800 :             eRStatus = poGDS->poFileView->ReadLineBIL(
     923         400 :                 poGDS->eNCSRequestDataType, 1, (void **)&pabySrcBuf);
     924             : 
     925         400 :             if (eRStatus != NCSECW_READ_OK)
     926             :             {
     927           0 :                 CPLDebug("ECW", "ReadLineBIL status=%d", (int)eRStatus);
     928           0 :                 CPLError(CE_Failure, CPLE_AppDefined,
     929             :                          "NCScbmReadViewLineBIL failed.");
     930           0 :                 eErr = CE_Failure;
     931           0 :                 break;
     932             :             }
     933             : 
     934         400 :             if (bPromoteTo8Bit)
     935             :             {
     936           0 :                 for (int iX = 0; iX < nNewXSize; iX++)
     937             :                 {
     938           0 :                     pabySrcBuf[iX] *= 255;
     939             :                 }
     940             :             }
     941             : 
     942         400 :             if (!bDirect)
     943             :             {
     944         400 :                 if (nNewXSize == nBufXSize)
     945             :                 {
     946           0 :                     GDALCopyWords(pabyWorkBuffer, poGDS->eRasterDataType,
     947             :                                   nRawPixelSize,
     948           0 :                                   ((GByte *)pData) + iDstLine * nLineSpace,
     949             :                                   eBufType, (int)nPixelSpace, nBufXSize);
     950             :                 }
     951             :                 else
     952             :                 {
     953             :                     int iPixel;
     954             : 
     955      320400 :                     for (iPixel = 0; iPixel < nBufXSize; iPixel++)
     956             :                     {
     957      320000 :                         GDALCopyWords(
     958      320000 :                             pabyWorkBuffer +
     959      320000 :                                 nRawPixelSize * ((int)(iPixel * dfSrcXInc)),
     960      320000 :                             poGDS->eRasterDataType, nRawPixelSize,
     961      320000 :                             (GByte *)pData + iDstLineOff + iPixel * nPixelSpace,
     962             :                             eBufType, (int)nPixelSpace, 1);
     963             :                     }
     964             :                 }
     965             :             }
     966             : 
     967         400 :             iSrcLine++;
     968             :         }
     969             :         else
     970             :         {
     971             :             // Just copy the previous line in this case
     972         400 :             GDALCopyWords((GByte *)pData + (iDstLineOff - nLineSpace), eBufType,
     973         400 :                           (int)nPixelSpace, (GByte *)pData + iDstLineOff,
     974             :                           eBufType, (int)nPixelSpace, nBufXSize);
     975             :         }
     976             : 
     977         800 :         if (psExtraArg->pfnProgress != nullptr &&
     978           0 :             !psExtraArg->pfnProgress(1.0 * (iDstLine + 1) / nBufYSize, "",
     979             :                                      psExtraArg->pProgressData))
     980             :         {
     981           0 :             eErr = CE_Failure;
     982           0 :             break;
     983             :         }
     984             :     }
     985             : 
     986           1 :     CPLFree(pabyWorkBuffer);
     987             : 
     988           1 :     return eErr;
     989             : }
     990             : 
     991             : // #endif !defined(SDK_CAN_DO_SUPERSAMPLING)
     992             : 
     993             : /************************************************************************/
     994             : /*                             IRasterIO()                              */
     995             : /************************************************************************/
     996             : 
     997        1664 : CPLErr ECWRasterBand::IRasterIO(GDALRWFlag eRWFlag, int nXOff, int nYOff,
     998             :                                 int nXSize, int nYSize, void *pData,
     999             :                                 int nBufXSize, int nBufYSize,
    1000             :                                 GDALDataType eBufType, GSpacing nPixelSpace,
    1001             :                                 GSpacing nLineSpace,
    1002             :                                 GDALRasterIOExtraArg *psExtraArg)
    1003             : {
    1004        1664 :     if (eRWFlag == GF_Write)
    1005           0 :         return CE_Failure;
    1006             : 
    1007             :     /* -------------------------------------------------------------------- */
    1008             :     /*      Default line and pixel spacing if needed.                       */
    1009             :     /* -------------------------------------------------------------------- */
    1010        1664 :     if (nPixelSpace == 0)
    1011           0 :         nPixelSpace = GDALGetDataTypeSize(eBufType) / 8;
    1012             : 
    1013        1664 :     if (nLineSpace == 0)
    1014           0 :         nLineSpace = nPixelSpace * nBufXSize;
    1015             : 
    1016        1664 :     CPLDebug("ECWRasterBand",
    1017             :              "RasterIO(nBand=%d,iOverview=%d,nXOff=%d,nYOff=%d,nXSize=%d,"
    1018             :              "nYSize=%d -> %dx%d)",
    1019             :              nBand, iOverview, nXOff, nYOff, nXSize, nYSize, nBufXSize,
    1020             :              nBufYSize);
    1021             : 
    1022             : #if !defined(SDK_CAN_DO_SUPERSAMPLING)
    1023        1664 :     if (poGDS->bUseOldBandRasterIOImplementation)
    1024             :     {
    1025           1 :         return OldIRasterIO(eRWFlag, nXOff, nYOff, nXSize, nYSize, pData,
    1026             :                             nBufXSize, nBufYSize, eBufType, nPixelSpace,
    1027           1 :                             nLineSpace, psExtraArg);
    1028             :     }
    1029             : 
    1030             : #endif
    1031             : 
    1032        1663 :     int nResFactor = 1 << (iOverview + 1);
    1033             : 
    1034             :     GDALRasterIOExtraArg sExtraArgTmp;
    1035        1663 :     INIT_RASTERIO_EXTRA_ARG(sExtraArgTmp);
    1036        1663 :     CPL_IGNORE_RET_VAL(sExtraArgTmp.eResampleAlg);
    1037        1663 :     sExtraArgTmp.eResampleAlg = psExtraArg->eResampleAlg;
    1038        1663 :     sExtraArgTmp.pfnProgress = psExtraArg->pfnProgress;
    1039        1663 :     sExtraArgTmp.pProgressData = psExtraArg->pProgressData;
    1040             : 
    1041        3301 :     return poGDS->IRasterIO(
    1042             :         eRWFlag, nXOff * nResFactor, nYOff * nResFactor,
    1043        1663 :         (nXSize == nRasterXSize) ? poGDS->nRasterXSize : nXSize * nResFactor,
    1044          25 :         (nYSize == nRasterYSize) ? poGDS->nRasterYSize : nYSize * nResFactor,
    1045             :         pData, nBufXSize, nBufYSize, eBufType, 1, &nBand, nPixelSpace,
    1046        3326 :         nLineSpace, nLineSpace * nBufYSize, &sExtraArgTmp);
    1047             : }
    1048             : 
    1049             : /************************************************************************/
    1050             : /*                             IReadBlock()                             */
    1051             : /************************************************************************/
    1052             : 
    1053           1 : CPLErr ECWRasterBand::IReadBlock(int nBlockXOff, int nBlockYOff, void *pImage)
    1054             : 
    1055             : {
    1056           1 :     int nXOff = nBlockXOff * nBlockXSize, nYOff = nBlockYOff * nBlockYSize,
    1057           1 :         nXSize = nBlockXSize, nYSize = nBlockYSize;
    1058             : 
    1059           1 :     if (nXOff + nXSize > nRasterXSize)
    1060           0 :         nXSize = nRasterXSize - nXOff;
    1061           1 :     if (nYOff + nYSize > nRasterYSize)
    1062           0 :         nYSize = nRasterYSize - nYOff;
    1063             : 
    1064           1 :     int nPixelSpace = GDALGetDataTypeSize(eDataType) / 8;
    1065           1 :     int nLineSpace = nPixelSpace * nBlockXSize;
    1066             : 
    1067             :     GDALRasterIOExtraArg sExtraArg;
    1068           1 :     INIT_RASTERIO_EXTRA_ARG(sExtraArg);
    1069             : 
    1070           1 :     return IRasterIO(GF_Read, nXOff, nYOff, nXSize, nYSize, pImage, nXSize,
    1071           2 :                      nYSize, eDataType, nPixelSpace, nLineSpace, &sExtraArg);
    1072             : }
    1073             : 
    1074             : /************************************************************************/
    1075             : /* ==================================================================== */
    1076             : /*                            ECWDataset                               */
    1077             : /* ==================================================================== */
    1078             : /************************************************************************/
    1079             : 
    1080             : /************************************************************************/
    1081             : /*                            ECWDataset()                              */
    1082             : /************************************************************************/
    1083             : 
    1084         121 : ECWDataset::ECWDataset(int bIsJPEG2000In)
    1085             : 
    1086             : {
    1087         121 :     this->bIsJPEG2000 = bIsJPEG2000In;
    1088         121 :     bUsingCustomStream = FALSE;
    1089         121 :     poFileView = nullptr;
    1090         121 :     bWinActive = FALSE;
    1091         121 :     panWinBandList = nullptr;
    1092         121 :     eRasterDataType = GDT_Byte;
    1093         121 :     papszGMLMetadata = nullptr;
    1094             : 
    1095         121 :     bHdrDirty = FALSE;
    1096         121 :     bGeoTransformChanged = FALSE;
    1097         121 :     bProjectionChanged = FALSE;
    1098         121 :     bProjCodeChanged = FALSE;
    1099         121 :     bDatumCodeChanged = FALSE;
    1100         121 :     bUnitsCodeChanged = FALSE;
    1101             : 
    1102         121 :     bUseOldBandRasterIOImplementation = FALSE;
    1103             : #if ECWSDK_VERSION >= 50
    1104             : 
    1105             :     pStatistics = nullptr;
    1106             :     bStatisticsDirty = FALSE;
    1107             :     bStatisticsInitialized = FALSE;
    1108             :     bFileMetaDataDirty = FALSE;
    1109             : 
    1110             : #endif
    1111             : 
    1112         121 :     sCachedMultiBandIO.bEnabled = FALSE;
    1113         121 :     sCachedMultiBandIO.nBandsTried = 0;
    1114         121 :     sCachedMultiBandIO.nXOff = 0;
    1115         121 :     sCachedMultiBandIO.nYOff = 0;
    1116         121 :     sCachedMultiBandIO.nXSize = 0;
    1117         121 :     sCachedMultiBandIO.nYSize = 0;
    1118         121 :     sCachedMultiBandIO.nBufXSize = 0;
    1119         121 :     sCachedMultiBandIO.nBufYSize = 0;
    1120         121 :     sCachedMultiBandIO.eBufType = GDT_Unknown;
    1121         121 :     sCachedMultiBandIO.pabyData = nullptr;
    1122             : 
    1123         121 :     bPreventCopyingSomeMetadata = FALSE;
    1124             : 
    1125         121 :     nBandIndexToPromoteTo8Bit = -1;
    1126             : 
    1127         121 :     poDriver =
    1128         121 :         (GDALDriver *)GDALGetDriverByName(bIsJPEG2000 ? "JP2ECW" : "ECW");
    1129             : 
    1130         121 :     psFileInfo = nullptr;
    1131         121 :     eNCSRequestDataType = NCSCT_UINT8;
    1132         121 :     nWinXOff = 0;
    1133         121 :     nWinYOff = 0;
    1134         121 :     nWinXSize = 0;
    1135         121 :     nWinYSize = 0;
    1136         121 :     nWinBufXSize = 0;
    1137         121 :     nWinBufYSize = 0;
    1138         121 :     nWinBandCount = 0;
    1139         121 :     nWinBufLoaded = FALSE;
    1140         121 :     papCurLineBuf = nullptr;
    1141             : 
    1142         121 :     m_nAdviseReadXOff = -1;
    1143         121 :     m_nAdviseReadYOff = -1;
    1144         121 :     m_nAdviseReadXSize = -1;
    1145         121 :     m_nAdviseReadYSize = -1;
    1146         121 :     m_nAdviseReadBufXSize = -1;
    1147         121 :     m_nAdviseReadBufYSize = -1;
    1148         121 :     m_nAdviseReadBandCount = -1;
    1149         121 :     m_panAdviseReadBandList = nullptr;
    1150         121 : }
    1151             : 
    1152             : /************************************************************************/
    1153             : /*                           ~ECWDataset()                              */
    1154             : /************************************************************************/
    1155             : 
    1156         242 : ECWDataset::~ECWDataset()
    1157             : 
    1158             : {
    1159         121 :     GDALPamDataset::FlushCache(true);
    1160         121 :     CleanupWindow();
    1161             : 
    1162             : #if ECWSDK_VERSION >= 50
    1163             :     NCSFileMetaData *pFileMetaDataCopy = nullptr;
    1164             :     if (bFileMetaDataDirty)
    1165             :     {
    1166             :         NCSCopyMetaData(&pFileMetaDataCopy, psFileInfo->pFileMetaData);
    1167             :     }
    1168             : #endif
    1169             : 
    1170             :     /* -------------------------------------------------------------------- */
    1171             :     /*      Release / dereference iostream.                                 */
    1172             :     /* -------------------------------------------------------------------- */
    1173             :     // The underlying iostream of the CNCSJP2FileView (poFileView) object may
    1174             :     // also be the underlying iostream of other CNCSJP2FileView (poFileView)
    1175             :     // objects.  Consequently, when we delete the CNCSJP2FileView (poFileView)
    1176             :     // object, we must decrement the nFileViewCount attribute of the underlying
    1177             :     // VSIIOStream object, and only delete the VSIIOStream object when
    1178             :     // nFileViewCount is equal to zero.
    1179             : 
    1180         242 :     CPLMutexHolder oHolder(&hECWDatasetMutex);
    1181             : 
    1182         121 :     if (poFileView != nullptr)
    1183             :     {
    1184             : #if ECWSDK_VERSION >= 55
    1185             :         delete poFileView;
    1186             : #else
    1187         121 :         VSIIOStream *poUnderlyingIOStream = (VSIIOStream *)nullptr;
    1188             : 
    1189         121 :         if (bUsingCustomStream)
    1190             :         {
    1191          52 :             poUnderlyingIOStream = ((VSIIOStream *)(poFileView->GetStream()));
    1192             :         }
    1193         121 :         delete poFileView;
    1194             : 
    1195         121 :         if (bUsingCustomStream)
    1196             :         {
    1197          52 :             if (--poUnderlyingIOStream->nFileViewCount == 0)
    1198          52 :                 delete poUnderlyingIOStream;
    1199             :         }
    1200             : #endif
    1201         121 :         poFileView = nullptr;
    1202             :     }
    1203             : 
    1204             :     /* WriteHeader() must be called after closing the file handle to work */
    1205             :     /* on Windows */
    1206         121 :     if (bHdrDirty)
    1207           3 :         WriteHeader();
    1208             : #if ECWSDK_VERSION >= 50
    1209             :     if (bStatisticsDirty)
    1210             :     {
    1211             :         StatisticsWrite();
    1212             :     }
    1213             :     CleanupStatistics();
    1214             : 
    1215             :     if (bFileMetaDataDirty)
    1216             :     {
    1217             :         WriteFileMetaData(pFileMetaDataCopy);
    1218             :         NCSFreeMetaData(pFileMetaDataCopy);
    1219             :     }
    1220             : #endif
    1221             : 
    1222         121 :     CSLDestroy(papszGMLMetadata);
    1223             : 
    1224         121 :     CPLFree(sCachedMultiBandIO.pabyData);
    1225             : 
    1226         121 :     CPLFree(m_panAdviseReadBandList);
    1227         242 : }
    1228             : 
    1229             : #if ECWSDK_VERSION >= 50
    1230             : 
    1231             : /************************************************************************/
    1232             : /*                    StatisticsEnsureInitialized()                     */
    1233             : /************************************************************************/
    1234             : 
    1235             : NCS::CError ECWDataset::StatisticsEnsureInitialized()
    1236             : {
    1237             :     if (bStatisticsInitialized == TRUE)
    1238             :     {
    1239             :         return NCS_SUCCESS;
    1240             :     }
    1241             : 
    1242             :     NCS::CError error = poFileView->GetClientStatistics(&pStatistics);
    1243             :     if (error.Success())
    1244             :     {
    1245             :         bStatisticsInitialized = TRUE;
    1246             :     }
    1247             :     return error;
    1248             : }
    1249             : 
    1250             : /************************************************************************/
    1251             : /*                          StatisticsWrite()                           */
    1252             : /************************************************************************/
    1253             : 
    1254             : NCS::CError ECWDataset::StatisticsWrite()
    1255             : {
    1256             :     CPLDebug("ECW", "In StatisticsWrite()");
    1257             :     NCSFileView *view = NCSEcwEditOpen(GetDescription());
    1258             :     NCS::CError error;
    1259             :     if (view != nullptr)
    1260             :     {
    1261             :         error = NCSEcwEditSetStatistics(view, pStatistics);
    1262             :         if (error.Success())
    1263             :         {
    1264             :             error = NCSEcwEditFlushAll(view);
    1265             :             if (error.Success())
    1266             :             {
    1267             :                 error = NCSEcwEditClose(view);
    1268             :             }
    1269             :         }
    1270             :     }
    1271             : 
    1272             :     bStatisticsDirty = FALSE;
    1273             : 
    1274             :     return error;
    1275             : }
    1276             : 
    1277             : /************************************************************************/
    1278             : /*                          CleanupStatistics()                         */
    1279             : /************************************************************************/
    1280             : 
    1281             : void ECWDataset::CleanupStatistics()
    1282             : {
    1283             :     if (bStatisticsInitialized == TRUE && pStatistics != nullptr)
    1284             :     {
    1285             :         NCSEcwFreeStatistics(pStatistics);
    1286             :     }
    1287             : }
    1288             : 
    1289             : #endif  // #if ECWSDK_VERSION>=50
    1290             : 
    1291             : /************************************************************************/
    1292             : /*                          SetGeoTransform()                           */
    1293             : /************************************************************************/
    1294             : 
    1295           6 : CPLErr ECWDataset::SetGeoTransform(double *padfGeoTransform)
    1296             : {
    1297           6 :     if (bIsJPEG2000 || eAccess == GA_ReadOnly)
    1298           5 :         return GDALPamDataset::SetGeoTransform(padfGeoTransform);
    1299             : 
    1300           1 :     if (!bGeoTransformValid || adfGeoTransform[0] != padfGeoTransform[0] ||
    1301           0 :         adfGeoTransform[1] != padfGeoTransform[1] ||
    1302           0 :         adfGeoTransform[2] != padfGeoTransform[2] ||
    1303           0 :         adfGeoTransform[3] != padfGeoTransform[3] ||
    1304           0 :         adfGeoTransform[4] != padfGeoTransform[4] ||
    1305           0 :         adfGeoTransform[5] != padfGeoTransform[5])
    1306             :     {
    1307           1 :         memcpy(adfGeoTransform, padfGeoTransform, 6 * sizeof(double));
    1308           1 :         bGeoTransformValid = TRUE;
    1309           1 :         bHdrDirty = TRUE;
    1310           1 :         bGeoTransformChanged = TRUE;
    1311             :     }
    1312             : 
    1313           1 :     return CE_None;
    1314             : }
    1315             : 
    1316             : /************************************************************************/
    1317             : /*                            SetSpatialRef()                           */
    1318             : /************************************************************************/
    1319             : 
    1320           6 : CPLErr ECWDataset::SetSpatialRef(const OGRSpatialReference *poSRS)
    1321             : {
    1322           6 :     if (bIsJPEG2000 || eAccess == GA_ReadOnly)
    1323           5 :         return GDALPamDataset::SetSpatialRef(poSRS);
    1324             : 
    1325           2 :     if (!((m_oSRS.IsEmpty() && poSRS == nullptr) ||
    1326           1 :           (!m_oSRS.IsEmpty() && poSRS != nullptr && m_oSRS.IsSame(poSRS))))
    1327             :     {
    1328           1 :         m_oSRS.Clear();
    1329           1 :         if (poSRS)
    1330           1 :             m_oSRS = *poSRS;
    1331             : 
    1332           1 :         bHdrDirty = TRUE;
    1333           1 :         bProjectionChanged = TRUE;
    1334             :     }
    1335             : 
    1336           1 :     return CE_None;
    1337             : }
    1338             : 
    1339             : /************************************************************************/
    1340             : /*                            SetMetadataItem()                         */
    1341             : /************************************************************************/
    1342             : 
    1343           4 : CPLErr ECWDataset::SetMetadataItem(const char *pszName, const char *pszValue,
    1344             :                                    const char *pszDomain)
    1345             : {
    1346           4 :     if (!bIsJPEG2000 && eAccess == GA_Update &&
    1347           3 :         (pszDomain == nullptr || EQUAL(pszDomain, "") ||
    1348           3 :          (pszDomain != nullptr && EQUAL(pszDomain, "ECW"))) &&
    1349           3 :         pszName != nullptr &&
    1350           3 :         (strcmp(pszName, "PROJ") == 0 || strcmp(pszName, "DATUM") == 0 ||
    1351           1 :          strcmp(pszName, "UNITS") == 0))
    1352             :     {
    1353           3 :         CPLString osNewVal = pszValue ? pszValue : "";
    1354           3 :         if (osNewVal.size() > 31)
    1355           0 :             osNewVal.resize(31);
    1356           3 :         if (strcmp(pszName, "PROJ") == 0)
    1357             :         {
    1358           1 :             bProjCodeChanged = (osNewVal != m_osProjCode);
    1359           1 :             m_osProjCode = std::move(osNewVal);
    1360           1 :             bHdrDirty |= bProjCodeChanged;
    1361             :         }
    1362           2 :         else if (strcmp(pszName, "DATUM") == 0)
    1363             :         {
    1364           1 :             bDatumCodeChanged |= (osNewVal != m_osDatumCode) ? TRUE : FALSE;
    1365           1 :             m_osDatumCode = std::move(osNewVal);
    1366           1 :             bHdrDirty |= bDatumCodeChanged;
    1367             :         }
    1368             :         else
    1369             :         {
    1370           1 :             bUnitsCodeChanged |= (osNewVal != m_osUnitsCode) ? TRUE : FALSE;
    1371           1 :             m_osUnitsCode = std::move(osNewVal);
    1372           1 :             bHdrDirty |= bUnitsCodeChanged;
    1373             :         }
    1374           3 :         return CE_None;
    1375             :     }
    1376             : #if ECWSDK_VERSION >= 50
    1377             :     else if (psFileInfo != nullptr && psFileInfo->nFormatVersion >= 3 &&
    1378             :              eAccess == GA_Update &&
    1379             :              (pszDomain == nullptr || EQUAL(pszDomain, "")) &&
    1380             :              pszName != nullptr && STARTS_WITH(pszName, "FILE_METADATA_"))
    1381             :     {
    1382             :         bFileMetaDataDirty = TRUE;
    1383             : 
    1384             :         if (psFileInfo->pFileMetaData == nullptr)
    1385             :             NCSInitMetaData(&(psFileInfo->pFileMetaData));
    1386             : 
    1387             :         if (strcmp(pszName, "FILE_METADATA_CLASSIFICATION") == 0)
    1388             :         {
    1389             :             NCSFree(psFileInfo->pFileMetaData->sClassification);
    1390             :             psFileInfo->pFileMetaData->sClassification =
    1391             :                 pszValue ? NCSStrDupT(NCS::CString(pszValue).c_str()) : nullptr;
    1392             :             return GDALDataset::SetMetadataItem(pszName, pszValue, pszDomain);
    1393             :         }
    1394             :         else if (strcmp(pszName, "FILE_METADATA_ACQUISITION_DATE") == 0)
    1395             :         {
    1396             :             NCSFree(psFileInfo->pFileMetaData->sAcquisitionDate);
    1397             :             psFileInfo->pFileMetaData->sAcquisitionDate =
    1398             :                 pszValue ? NCSStrDupT(NCS::CString(pszValue).c_str()) : nullptr;
    1399             :             return GDALDataset::SetMetadataItem(pszName, pszValue, pszDomain);
    1400             :         }
    1401             :         else if (strcmp(pszName, "FILE_METADATA_ACQUISITION_SENSOR_NAME") == 0)
    1402             :         {
    1403             :             NCSFree(psFileInfo->pFileMetaData->sAcquisitionSensorName);
    1404             :             psFileInfo->pFileMetaData->sAcquisitionSensorName =
    1405             :                 pszValue ? NCSStrDupT(NCS::CString(pszValue).c_str()) : nullptr;
    1406             :             return GDALDataset::SetMetadataItem(pszName, pszValue, pszDomain);
    1407             :         }
    1408             :         else if (strcmp(pszName, "FILE_METADATA_COMPRESSION_SOFTWARE") == 0)
    1409             :         {
    1410             :             NCSFree(psFileInfo->pFileMetaData->sCompressionSoftware);
    1411             :             psFileInfo->pFileMetaData->sCompressionSoftware =
    1412             :                 pszValue ? NCSStrDupT(NCS::CString(pszValue).c_str()) : nullptr;
    1413             :             return GDALDataset::SetMetadataItem(pszName, pszValue, pszDomain);
    1414             :         }
    1415             :         else if (strcmp(pszName, "FILE_METADATA_AUTHOR") == 0)
    1416             :         {
    1417             :             NCSFree(psFileInfo->pFileMetaData->sAuthor);
    1418             :             psFileInfo->pFileMetaData->sAuthor =
    1419             :                 pszValue ? NCSStrDupT(NCS::CString(pszValue).c_str()) : nullptr;
    1420             :             return GDALDataset::SetMetadataItem(pszName, pszValue, pszDomain);
    1421             :         }
    1422             :         else if (strcmp(pszName, "FILE_METADATA_COPYRIGHT") == 0)
    1423             :         {
    1424             :             NCSFree(psFileInfo->pFileMetaData->sCopyright);
    1425             :             psFileInfo->pFileMetaData->sCopyright =
    1426             :                 pszValue ? NCSStrDupT(NCS::CString(pszValue).c_str()) : nullptr;
    1427             :             return GDALDataset::SetMetadataItem(pszName, pszValue, pszDomain);
    1428             :         }
    1429             :         else if (strcmp(pszName, "FILE_METADATA_COMPANY") == 0)
    1430             :         {
    1431             :             NCSFree(psFileInfo->pFileMetaData->sCompany);
    1432             :             psFileInfo->pFileMetaData->sCompany =
    1433             :                 pszValue ? NCSStrDupT(NCS::CString(pszValue).c_str()) : nullptr;
    1434             :             return GDALDataset::SetMetadataItem(pszName, pszValue, pszDomain);
    1435             :         }
    1436             :         else if (strcmp(pszName, "FILE_METADATA_EMAIL") == 0)
    1437             :         {
    1438             :             NCSFree(psFileInfo->pFileMetaData->sEmail);
    1439             :             psFileInfo->pFileMetaData->sEmail =
    1440             :                 pszValue ? NCSStrDupT(NCS::CString(pszValue).c_str()) : nullptr;
    1441             :             return GDALDataset::SetMetadataItem(pszName, pszValue, pszDomain);
    1442             :         }
    1443             :         else if (strcmp(pszName, "FILE_METADATA_ADDRESS") == 0)
    1444             :         {
    1445             :             NCSFree(psFileInfo->pFileMetaData->sAddress);
    1446             :             psFileInfo->pFileMetaData->sAddress =
    1447             :                 pszValue ? NCSStrDupT(NCS::CString(pszValue).c_str()) : nullptr;
    1448             :             return GDALDataset::SetMetadataItem(pszName, pszValue, pszDomain);
    1449             :         }
    1450             :         else if (strcmp(pszName, "FILE_METADATA_TELEPHONE") == 0)
    1451             :         {
    1452             :             NCSFree(psFileInfo->pFileMetaData->sTelephone);
    1453             :             psFileInfo->pFileMetaData->sTelephone =
    1454             :                 pszValue ? NCSStrDupT(NCS::CString(pszValue).c_str()) : nullptr;
    1455             :             return GDALDataset::SetMetadataItem(pszName, pszValue, pszDomain);
    1456             :         }
    1457             :         else
    1458             :         {
    1459             :             return GDALPamDataset::SetMetadataItem(pszName, pszValue,
    1460             :                                                    pszDomain);
    1461             :         }
    1462             :     }
    1463             : #endif
    1464             :     else
    1465           1 :         return GDALPamDataset::SetMetadataItem(pszName, pszValue, pszDomain);
    1466             : }
    1467             : 
    1468             : /************************************************************************/
    1469             : /*                              SetMetadata()                           */
    1470             : /************************************************************************/
    1471             : 
    1472           2 : CPLErr ECWDataset::SetMetadata(char **papszMetadata, const char *pszDomain)
    1473             : {
    1474             :     /* The bPreventCopyingSomeMetadata is set by ECWCreateCopy() */
    1475             :     /* just before calling poDS->CloneInfo( poSrcDS, GCIF_PAM_DEFAULT ); */
    1476           2 :     if (bPreventCopyingSomeMetadata &&
    1477           1 :         (pszDomain == nullptr || EQUAL(pszDomain, "")))
    1478             :     {
    1479           1 :         char **papszMetadataDup = nullptr;
    1480           1 :         char **papszIter = papszMetadata;
    1481           2 :         while (*papszIter)
    1482             :         {
    1483           1 :             char *pszKey = nullptr;
    1484           1 :             CPLParseNameValue(*papszIter, &pszKey);
    1485             :             /* Remove a few metadata item from the source that we don't want in
    1486             :              */
    1487             :             /* the target metadata */
    1488           1 :             if (pszKey != nullptr &&
    1489           1 :                 (EQUAL(pszKey, "VERSION") ||
    1490           1 :                  EQUAL(pszKey, "COMPRESSION_RATE_TARGET") ||
    1491           1 :                  EQUAL(pszKey, "COMPRESSION_RATE_ACTUAL") ||
    1492           1 :                  EQUAL(pszKey, "CLOCKWISE_ROTATION_DEG") ||
    1493           1 :                  EQUAL(pszKey, "COLORSPACE") ||
    1494           1 :                  EQUAL(pszKey, "COMPRESSION_DATE") ||
    1495           1 :                  STARTS_WITH_CI(pszKey, "FILE_METADATA_")))
    1496             :             {
    1497             :                 /* do nothing */
    1498             :             }
    1499             :             else
    1500             :             {
    1501           1 :                 papszMetadataDup = CSLAddString(papszMetadataDup, *papszIter);
    1502             :             }
    1503           1 :             CPLFree(pszKey);
    1504           1 :             papszIter++;
    1505             :         }
    1506             : 
    1507           1 :         bPreventCopyingSomeMetadata = FALSE;
    1508           1 :         CPLErr eErr = SetMetadata(papszMetadataDup, pszDomain);
    1509           1 :         bPreventCopyingSomeMetadata = TRUE;
    1510           1 :         CSLDestroy(papszMetadataDup);
    1511           1 :         return eErr;
    1512             :     }
    1513             : 
    1514           1 :     if (((pszDomain == nullptr || EQUAL(pszDomain, "") ||
    1515           2 :           EQUAL(pszDomain, "ECW")) &&
    1516           1 :          (CSLFetchNameValue(papszMetadata, "PROJ") != nullptr ||
    1517           1 :           CSLFetchNameValue(papszMetadata, "DATUM") != nullptr ||
    1518           1 :           CSLFetchNameValue(papszMetadata, "UNITS") != nullptr))
    1519             : #if ECWSDK_VERSION >= 50
    1520             :         ||
    1521             :         (psFileInfo != nullptr && psFileInfo->nFormatVersion >= 3 &&
    1522             :          eAccess == GA_Update &&
    1523             :          (pszDomain == nullptr || EQUAL(pszDomain, "")) &&
    1524             :          (CSLFetchNameValue(papszMetadata, "FILE_METADATA_CLASSIFICATION") !=
    1525             :               nullptr ||
    1526             :           CSLFetchNameValue(papszMetadata, "FILE_METADATA_ACQUISITION_DATE") !=
    1527             :               nullptr ||
    1528             :           CSLFetchNameValue(papszMetadata,
    1529             :                             "FILE_METADATA_ACQUISITION_SENSOR_NAME") !=
    1530             :               nullptr ||
    1531             :           CSLFetchNameValue(papszMetadata,
    1532             :                             "FILE_METADATA_COMPRESSION_SOFTWARE") != nullptr ||
    1533             :           CSLFetchNameValue(papszMetadata, "FILE_METADATA_AUTHOR") != nullptr ||
    1534             :           CSLFetchNameValue(papszMetadata, "FILE_METADATA_COPYRIGHT") !=
    1535             :               nullptr ||
    1536             :           CSLFetchNameValue(papszMetadata, "FILE_METADATA_COMPANY") !=
    1537             :               nullptr ||
    1538             :           CSLFetchNameValue(papszMetadata, "FILE_METADATA_EMAIL") != nullptr ||
    1539             :           CSLFetchNameValue(papszMetadata, "FILE_METADATA_ADDRESS") !=
    1540             :               nullptr ||
    1541             :           CSLFetchNameValue(papszMetadata, "FILE_METADATA_TELEPHONE") !=
    1542             :               nullptr))
    1543             : #endif
    1544             :     )
    1545             :     {
    1546           0 :         CPLStringList osNewMetadata;
    1547           0 :         char **papszIter = papszMetadata;
    1548           0 :         while (papszIter && *papszIter)
    1549             :         {
    1550           0 :             if (STARTS_WITH(*papszIter, "PROJ=") ||
    1551           0 :                 STARTS_WITH(*papszIter, "DATUM=") ||
    1552           0 :                 STARTS_WITH(*papszIter, "UNITS=") ||
    1553           0 :                 (STARTS_WITH(*papszIter, "FILE_METADATA_") &&
    1554           0 :                  strchr(*papszIter, '=') != nullptr))
    1555             :             {
    1556           0 :                 char *pszKey = nullptr;
    1557           0 :                 const char *pszValue = CPLParseNameValue(*papszIter, &pszKey);
    1558           0 :                 SetMetadataItem(pszKey, pszValue, pszDomain);
    1559           0 :                 CPLFree(pszKey);
    1560             :             }
    1561             :             else
    1562           0 :                 osNewMetadata.AddString(*papszIter);
    1563           0 :             papszIter++;
    1564             :         }
    1565           0 :         if (!osNewMetadata.empty())
    1566           0 :             return GDALPamDataset::SetMetadata(osNewMetadata.List(), pszDomain);
    1567             :         else
    1568           0 :             return CE_None;
    1569             :     }
    1570             :     else
    1571           1 :         return GDALPamDataset::SetMetadata(papszMetadata, pszDomain);
    1572             : }
    1573             : 
    1574             : /************************************************************************/
    1575             : /*                             WriteHeader()                            */
    1576             : /************************************************************************/
    1577             : 
    1578           3 : void ECWDataset::WriteHeader()
    1579             : {
    1580           3 :     if (!bHdrDirty)
    1581           0 :         return;
    1582             : 
    1583           3 :     CPLAssert(eAccess == GA_Update);
    1584           3 :     CPLAssert(!bIsJPEG2000);
    1585             : 
    1586           3 :     bHdrDirty = FALSE;
    1587             : 
    1588           3 :     NCSEcwEditInfo *psEditInfo = nullptr;
    1589             :     NCSError eErr;
    1590             : 
    1591             :     /* Load original header info */
    1592             : #if ECWSDK_VERSION < 50
    1593           3 :     eErr = NCSEcwEditReadInfo((char *)GetDescription(), &psEditInfo);
    1594             : #else
    1595             :     eErr = NCSEcwEditReadInfo(
    1596             :         NCS::CString::Utf8Decode(GetDescription()).c_str(), &psEditInfo);
    1597             : #endif
    1598           3 :     if (eErr != NCS_SUCCESS)
    1599             :     {
    1600           0 :         CPLError(CE_Failure, CPLE_AppDefined, "NCSEcwEditReadInfo() failed");
    1601           0 :         return;
    1602             :     }
    1603             : 
    1604             :     /* To avoid potential cross-heap issues, we keep the original */
    1605             :     /* strings, and restore them before freeing the structure */
    1606           3 :     char *pszOriginalCode = psEditInfo->szDatum;
    1607           3 :     char *pszOriginalProj = psEditInfo->szProjection;
    1608             : 
    1609             :     /* Alter the structure with user modified information */
    1610             :     char szProjCode[32], szDatumCode[32], szUnits[32];
    1611           3 :     if (bProjectionChanged)
    1612             :     {
    1613           1 :         if (ECWTranslateFromWKT(&m_oSRS, szProjCode, sizeof(szProjCode),
    1614           1 :                                 szDatumCode, sizeof(szDatumCode), szUnits))
    1615             :         {
    1616           1 :             psEditInfo->szDatum = szDatumCode;
    1617           1 :             psEditInfo->szProjection = szProjCode;
    1618           1 :             psEditInfo->eCellSizeUnits = ECWTranslateToCellSizeUnits(szUnits);
    1619           1 :             CPLDebug("ECW", "Rewrite DATUM : %s", psEditInfo->szDatum);
    1620           1 :             CPLDebug("ECW", "Rewrite PROJ : %s", psEditInfo->szProjection);
    1621           1 :             CPLDebug("ECW", "Rewrite UNITS : %s",
    1622             :                      ECWTranslateFromCellSizeUnits(psEditInfo->eCellSizeUnits));
    1623             :         }
    1624             :     }
    1625             : 
    1626           3 :     if (bDatumCodeChanged)
    1627             :     {
    1628           1 :         psEditInfo->szDatum =
    1629           1 :             (char *)((m_osDatumCode.size()) ? m_osDatumCode.c_str() : "RAW");
    1630           1 :         CPLDebug("ECW", "Rewrite DATUM : %s", psEditInfo->szDatum);
    1631             :     }
    1632           3 :     if (bProjCodeChanged)
    1633             :     {
    1634           1 :         psEditInfo->szProjection =
    1635           1 :             (char *)((m_osProjCode.size()) ? m_osProjCode.c_str() : "RAW");
    1636           1 :         CPLDebug("ECW", "Rewrite PROJ : %s", psEditInfo->szProjection);
    1637             :     }
    1638           3 :     if (bUnitsCodeChanged)
    1639             :     {
    1640           2 :         psEditInfo->eCellSizeUnits =
    1641           1 :             ECWTranslateToCellSizeUnits(m_osUnitsCode.c_str());
    1642           1 :         CPLDebug("ECW", "Rewrite UNITS : %s",
    1643             :                  ECWTranslateFromCellSizeUnits(psEditInfo->eCellSizeUnits));
    1644             :     }
    1645             : 
    1646           3 :     if (bGeoTransformChanged)
    1647             :     {
    1648           1 :         psEditInfo->fOriginX = adfGeoTransform[0];
    1649           1 :         psEditInfo->fCellIncrementX = adfGeoTransform[1];
    1650           1 :         psEditInfo->fOriginY = adfGeoTransform[3];
    1651           1 :         psEditInfo->fCellIncrementY = adfGeoTransform[5];
    1652           1 :         CPLDebug("ECW", "Rewrite Geotransform");
    1653             :     }
    1654             : 
    1655             :     /* Write modified header info */
    1656             : #if ECWSDK_VERSION < 50
    1657           3 :     eErr = NCSEcwEditWriteInfo((char *)GetDescription(), psEditInfo, nullptr,
    1658             :                                nullptr, nullptr);
    1659             : #else
    1660             :     eErr =
    1661             :         NCSEcwEditWriteInfo(NCS::CString::Utf8Decode(GetDescription()).c_str(),
    1662             :                             psEditInfo, nullptr, nullptr, nullptr);
    1663             : #endif
    1664           3 :     if (eErr != NCS_SUCCESS)
    1665             :     {
    1666           0 :         CPLError(CE_Failure, CPLE_AppDefined, "NCSEcwEditWriteInfo() failed");
    1667             :     }
    1668             : 
    1669             :     /* Restore original pointers before free'ing */
    1670           3 :     psEditInfo->szDatum = pszOriginalCode;
    1671           3 :     psEditInfo->szProjection = pszOriginalProj;
    1672             : 
    1673           3 :     NCSEcwEditFreeInfo(psEditInfo);
    1674             : }
    1675             : 
    1676             : /************************************************************************/
    1677             : /*                             AdviseRead()                             */
    1678             : /************************************************************************/
    1679             : 
    1680         249 : CPLErr ECWDataset::AdviseRead(int nXOff, int nYOff, int nXSize, int nYSize,
    1681             :                               int nBufXSize, int nBufYSize,
    1682             :                               CPL_UNUSED GDALDataType eDT, int nBandCount,
    1683             :                               int *panBandList, CPL_UNUSED char **papszOptions)
    1684             : {
    1685         249 :     CPLDebug("ECW", "ECWDataset::AdviseRead(%d,%d,%d,%d->%d,%d)", nXOff, nYOff,
    1686             :              nXSize, nYSize, nBufXSize, nBufYSize);
    1687             : 
    1688             : #if !defined(SDK_CAN_DO_SUPERSAMPLING)
    1689         249 :     if (nBufXSize > nXSize || nBufYSize > nYSize)
    1690             :     {
    1691           0 :         CPLError(CE_Warning, CPLE_AppDefined,
    1692             :                  "Supersampling not directly supported by ECW toolkit,\n"
    1693             :                  "ignoring AdviseRead() request.");
    1694           0 :         return CE_Warning;
    1695             :     }
    1696             : #endif
    1697             : 
    1698             :     /* -------------------------------------------------------------------- */
    1699             :     /*      Do some validation of parameters.                               */
    1700             :     /* -------------------------------------------------------------------- */
    1701             : 
    1702             :     CPLErr eErr;
    1703         249 :     int bStopProcessing = FALSE;
    1704         249 :     eErr = ValidateRasterIOOrAdviseReadParameters(
    1705             :         "AdviseRead()", &bStopProcessing, nXOff, nYOff, nXSize, nYSize,
    1706             :         nBufXSize, nBufYSize, nBandCount, panBandList);
    1707         249 :     if (eErr != CE_None || bStopProcessing)
    1708           0 :         return eErr;
    1709             : 
    1710         249 :     if (nBandCount > 100)
    1711             :     {
    1712           0 :         ReportError(CE_Failure, CPLE_IllegalArg,
    1713             :                     "AdviseRead(): Too many bands : %d", nBandCount);
    1714           0 :         return CE_Failure;
    1715             :     }
    1716             : 
    1717         249 :     if (nBufXSize != nXSize || nBufYSize != nYSize)
    1718             :     {
    1719             :         // This early exit is because experimentally we found that
    1720             :         // performance of requesting at 50% is much slower with
    1721             :         // AdviseRead()...
    1722             :         // At least on JPEG2000 images with SDK 3.3
    1723         200 :         CPLDebug("ECW",
    1724             :                  "Ignoring AdviseRead() for non full resolution request");
    1725         200 :         return CE_None;
    1726             :     }
    1727             : 
    1728             :     // We don't setup the reading window right away, in case the actual read
    1729             :     // pattern wouldn't be compatible of it. Which might be the case for
    1730             :     // example if AdviseRead() requests a full image, but we don't read by
    1731             :     // chunks of the full width of one or several lines
    1732          49 :     m_nAdviseReadXOff = nXOff;
    1733          49 :     m_nAdviseReadYOff = nYOff;
    1734          49 :     m_nAdviseReadXSize = nXSize;
    1735          49 :     m_nAdviseReadYSize = nYSize;
    1736          49 :     m_nAdviseReadBufXSize = nBufXSize;
    1737          49 :     m_nAdviseReadBufYSize = nBufYSize;
    1738          49 :     m_nAdviseReadBandCount = nBandCount;
    1739          49 :     CPLFree(m_panAdviseReadBandList);
    1740          49 :     if (panBandList)
    1741             :     {
    1742          41 :         m_panAdviseReadBandList =
    1743          41 :             static_cast<int *>(CPLMalloc(sizeof(int) * nBandCount));
    1744          41 :         memcpy(m_panAdviseReadBandList, panBandList, sizeof(int) * nBandCount);
    1745             :     }
    1746             :     else
    1747             :     {
    1748           8 :         m_panAdviseReadBandList = nullptr;
    1749             :     }
    1750             : 
    1751          49 :     return CE_None;
    1752             : }
    1753             : 
    1754             : /************************************************************************/
    1755             : /*                        RunDeferredAdviseRead()                        */
    1756             : /************************************************************************/
    1757             : 
    1758          44 : CPLErr ECWDataset::RunDeferredAdviseRead()
    1759             : {
    1760          44 :     CPLAssert(m_nAdviseReadXOff >= 0);
    1761             : 
    1762          44 :     const int nXOff = m_nAdviseReadXOff;
    1763          44 :     const int nYOff = m_nAdviseReadYOff;
    1764          44 :     const int nXSize = m_nAdviseReadXSize;
    1765          44 :     const int nYSize = m_nAdviseReadYSize;
    1766          44 :     const int nBufXSize = m_nAdviseReadBufXSize;
    1767          44 :     const int nBufYSize = m_nAdviseReadBufYSize;
    1768          44 :     const int nBandCount = m_nAdviseReadBandCount;
    1769          44 :     int *panBandList = m_panAdviseReadBandList;
    1770             : 
    1771          44 :     m_nAdviseReadXOff = -1;
    1772          44 :     m_nAdviseReadYOff = -1;
    1773          44 :     m_nAdviseReadXSize = -1;
    1774          44 :     m_nAdviseReadYSize = -1;
    1775          44 :     m_nAdviseReadBufXSize = -1;
    1776          44 :     m_nAdviseReadBufYSize = -1;
    1777          44 :     m_nAdviseReadBandCount = -1;
    1778          44 :     m_panAdviseReadBandList = nullptr;
    1779             : 
    1780             :     /* -------------------------------------------------------------------- */
    1781             :     /*      Adjust band numbers to be zero based.                           */
    1782             :     /* -------------------------------------------------------------------- */
    1783          44 :     int *panAdjustedBandList = (int *)CPLMalloc(sizeof(int) * nBandCount);
    1784          44 :     nBandIndexToPromoteTo8Bit = -1;
    1785          97 :     for (int ii = 0; ii < nBandCount; ii++)
    1786             :     {
    1787          53 :         panAdjustedBandList[ii] =
    1788          53 :             (panBandList != nullptr) ? panBandList[ii] - 1 : ii;
    1789          53 :         if (((ECWRasterBand *)GetRasterBand(panAdjustedBandList[ii] + 1))
    1790          53 :                 ->bPromoteTo8Bit)
    1791           1 :             nBandIndexToPromoteTo8Bit = ii;
    1792             :     }
    1793             : 
    1794             :     /* -------------------------------------------------------------------- */
    1795             :     /*      Cleanup old window cache information.                           */
    1796             :     /* -------------------------------------------------------------------- */
    1797          44 :     CleanupWindow();
    1798             : 
    1799             :     /* -------------------------------------------------------------------- */
    1800             :     /*      Set the new requested window.                                   */
    1801             :     /* -------------------------------------------------------------------- */
    1802          44 :     CNCSError oErr = poFileView->SetView(
    1803             :         nBandCount, (UINT32 *)panAdjustedBandList, nXOff, nYOff,
    1804          88 :         nXOff + nXSize - 1, nYOff + nYSize - 1, nBufXSize, nBufYSize);
    1805             : 
    1806          44 :     CPLFree(panAdjustedBandList);
    1807          44 :     if (oErr.GetErrorNumber() != NCS_SUCCESS)
    1808             :     {
    1809           0 :         ECWReportError(oErr);
    1810             : 
    1811           0 :         bWinActive = FALSE;
    1812           0 :         CPLFree(panBandList);
    1813           0 :         return CE_Failure;
    1814             :     }
    1815             : 
    1816          44 :     bWinActive = TRUE;
    1817             : 
    1818             :     /* -------------------------------------------------------------------- */
    1819             :     /*      Record selected window.                                         */
    1820             :     /* -------------------------------------------------------------------- */
    1821          44 :     nWinXOff = nXOff;
    1822          44 :     nWinYOff = nYOff;
    1823          44 :     nWinXSize = nXSize;
    1824          44 :     nWinYSize = nYSize;
    1825          44 :     nWinBufXSize = nBufXSize;
    1826          44 :     nWinBufYSize = nBufYSize;
    1827             : 
    1828          44 :     panWinBandList = (int *)CPLMalloc(sizeof(int) * nBandCount);
    1829          44 :     if (panBandList != nullptr)
    1830          40 :         memcpy(panWinBandList, panBandList, sizeof(int) * nBandCount);
    1831             :     else
    1832             :     {
    1833          17 :         for (int ii = 0; ii < nBandCount; ii++)
    1834             :         {
    1835          13 :             panWinBandList[ii] = ii + 1;
    1836             :         }
    1837             :     }
    1838          44 :     nWinBandCount = nBandCount;
    1839             : 
    1840          44 :     nWinBufLoaded = -1;
    1841             : 
    1842             :     /* -------------------------------------------------------------------- */
    1843             :     /*      Allocate current scanline buffer.                               */
    1844             :     /* -------------------------------------------------------------------- */
    1845          44 :     papCurLineBuf = (void **)CPLMalloc(sizeof(void *) * nWinBandCount);
    1846          97 :     for (int iBand = 0; iBand < nWinBandCount; iBand++)
    1847         106 :         papCurLineBuf[iBand] =
    1848          53 :             CPLMalloc(nBufXSize * (GDALGetDataTypeSize(eRasterDataType) / 8));
    1849             : 
    1850          44 :     CPLFree(panBandList);
    1851             : 
    1852          44 :     return CE_None;
    1853             : }
    1854             : 
    1855             : /************************************************************************/
    1856             : /*                           TryWinRasterIO()                           */
    1857             : /*                                                                      */
    1858             : /*      Try to satisfy the given request based on the currently         */
    1859             : /*      defined window.  Return TRUE on success or FALSE on             */
    1860             : /*      failure.  On failure, the caller should satisfy the request     */
    1861             : /*      another way (not report an error).                              */
    1862             : /************************************************************************/
    1863             : 
    1864        1931 : int ECWDataset::TryWinRasterIO(CPL_UNUSED GDALRWFlag eFlag, int nXOff,
    1865             :                                int nYOff, int nXSize, int nYSize,
    1866             :                                GByte *pabyData, int nBufXSize, int nBufYSize,
    1867             :                                GDALDataType eDT, int nBandCount,
    1868             :                                const int *panBandList, GSpacing nPixelSpace,
    1869             :                                GSpacing nLineSpace, GSpacing nBandSpace,
    1870             :                                GDALRasterIOExtraArg *psExtraArg)
    1871             : {
    1872             :     int iBand, i;
    1873             : 
    1874             :     /* -------------------------------------------------------------------- */
    1875             :     /*      Provide default buffer organization.                            */
    1876             :     /* -------------------------------------------------------------------- */
    1877        1931 :     if (nPixelSpace == 0)
    1878           0 :         nPixelSpace = GDALGetDataTypeSize(eDT) / 8;
    1879        1931 :     if (nLineSpace == 0)
    1880           0 :         nLineSpace = nPixelSpace * nBufXSize;
    1881        1931 :     if (nBandSpace == 0)
    1882           1 :         nBandSpace = nLineSpace * nBufYSize;
    1883             : 
    1884             : /* -------------------------------------------------------------------- */
    1885             : /*      Do some simple tests to see if the current window can           */
    1886             : /*      satisfy our requirement.                                        */
    1887             : /* -------------------------------------------------------------------- */
    1888             : #ifdef NOISY_DEBUG
    1889             :     CPLDebug("ECW", "TryWinRasterIO(%d,%d,%d,%d,%d,%d)", nXOff, nYOff, nXSize,
    1890             :              nYSize, nBufXSize, nBufYSize);
    1891             : #endif
    1892             : 
    1893        1931 :     if (!bWinActive)
    1894             :     {
    1895         528 :         if (nXOff == m_nAdviseReadXOff && nXSize == m_nAdviseReadXSize &&
    1896          44 :             nBufXSize == m_nAdviseReadBufXSize)
    1897             :         {
    1898          44 :             if (RunDeferredAdviseRead() != CE_None)
    1899           0 :                 return FALSE;
    1900             :         }
    1901         528 :         if (!bWinActive)
    1902             :         {
    1903         484 :             return FALSE;
    1904             :         }
    1905             :     }
    1906             : 
    1907        1447 :     if (nXOff != nWinXOff || nXSize != nWinXSize)
    1908           0 :         return FALSE;
    1909             : 
    1910        1447 :     if (nBufXSize != nWinBufXSize)
    1911           0 :         return FALSE;
    1912             : 
    1913        2907 :     for (iBand = 0; iBand < nBandCount; iBand++)
    1914             :     {
    1915        1488 :         for (i = 0; i < nWinBandCount; i++)
    1916             :         {
    1917        1486 :             if (panWinBandList[i] == panBandList[iBand])
    1918        1460 :                 break;
    1919             :         }
    1920             : 
    1921        1462 :         if (i == nWinBandCount)
    1922           2 :             return FALSE;
    1923             :     }
    1924             : 
    1925        1445 :     if (nYOff < nWinYOff || nYOff + nYSize > nWinYOff + nWinYSize)
    1926           0 :         return FALSE;
    1927             : 
    1928             :     /* -------------------------------------------------------------------- */
    1929             :     /*      Now we try more subtle tests.                                   */
    1930             :     /* -------------------------------------------------------------------- */
    1931             :     {
    1932             :         static int nDebugCount = 0;
    1933             : 
    1934        1445 :         if (nDebugCount < 30)
    1935          30 :             CPLDebug(
    1936             :                 "ECW",
    1937             :                 "TryWinRasterIO(%d,%d,%d,%d -> %dx%d) - doing advised read.",
    1938             :                 nXOff, nYOff, nXSize, nYSize, nBufXSize, nBufYSize);
    1939             : 
    1940        1445 :         if (nDebugCount == 29)
    1941           1 :             CPLDebug("ECW", "No more TryWinRasterIO messages will be reported");
    1942             : 
    1943        1445 :         nDebugCount++;
    1944             :     }
    1945             : 
    1946             :     /* -------------------------------------------------------------------- */
    1947             :     /*      Actually load data one buffer line at a time.                   */
    1948             :     /* -------------------------------------------------------------------- */
    1949             :     int iBufLine;
    1950             : 
    1951        4165 :     for (iBufLine = 0; iBufLine < nBufYSize; iBufLine++)
    1952             :     {
    1953        2788 :         double fFileLine = ((iBufLine + 0.5) / nBufYSize) * nYSize + nYOff;
    1954        2788 :         int iWinLine =
    1955        2788 :             (int)(((fFileLine - nWinYOff) / nWinYSize) * nWinBufYSize);
    1956             : 
    1957        2788 :         if (iWinLine == nWinBufLoaded + 1)
    1958        2720 :             LoadNextLine();
    1959             : 
    1960        2788 :         if (iWinLine != nWinBufLoaded)
    1961          68 :             return FALSE;
    1962             : 
    1963             :         /* --------------------------------------------------------------------
    1964             :          */
    1965             :         /*      Copy out all our target bands. */
    1966             :         /* --------------------------------------------------------------------
    1967             :          */
    1968             :         int iWinBand;
    1969        8290 :         for (iBand = 0; iBand < nBandCount; iBand++)
    1970             :         {
    1971       10070 :             for (iWinBand = 0; iWinBand < nWinBandCount; iWinBand++)
    1972             :             {
    1973       10070 :                 if (panWinBandList[iWinBand] == panBandList[iBand])
    1974        5570 :                     break;
    1975             :             }
    1976             : 
    1977       11140 :             GDALCopyWords(papCurLineBuf[iWinBand], eRasterDataType,
    1978        5570 :                           GDALGetDataTypeSize(eRasterDataType) / 8,
    1979        5570 :                           pabyData + nBandSpace * iBand + iBufLine * nLineSpace,
    1980             :                           eDT, (int)nPixelSpace, nBufXSize);
    1981             :         }
    1982             : 
    1983        2870 :         if (psExtraArg->pfnProgress != nullptr &&
    1984         150 :             !psExtraArg->pfnProgress(1.0 * (iBufLine + 1) / nBufYSize, "",
    1985             :                                      psExtraArg->pProgressData))
    1986             :         {
    1987           0 :             return -1;
    1988             :         }
    1989             :     }
    1990             : 
    1991        1377 :     return TRUE;
    1992             : }
    1993             : 
    1994             : /************************************************************************/
    1995             : /*                            LoadNextLine()                            */
    1996             : /************************************************************************/
    1997             : 
    1998        2720 : CPLErr ECWDataset::LoadNextLine()
    1999             : 
    2000             : {
    2001        2720 :     if (!bWinActive)
    2002           0 :         return CE_Failure;
    2003             : 
    2004        2720 :     if (nWinBufLoaded == nWinBufYSize - 1)
    2005             :     {
    2006           0 :         CleanupWindow();
    2007           0 :         return CE_Failure;
    2008             :     }
    2009             : 
    2010             :     NCSEcwReadStatus eRStatus;
    2011        5440 :     eRStatus = poFileView->ReadLineBIL(eNCSRequestDataType,
    2012        2720 :                                        (UINT16)nWinBandCount, papCurLineBuf);
    2013        2720 :     if (eRStatus != NCSECW_READ_OK)
    2014           0 :         return CE_Failure;
    2015             : 
    2016        2720 :     if (nBandIndexToPromoteTo8Bit >= 0)
    2017             :     {
    2018       24450 :         for (int iX = 0; iX < nWinBufXSize; iX++)
    2019             :         {
    2020       24300 :             ((GByte *)papCurLineBuf[nBandIndexToPromoteTo8Bit])[iX] *= 255;
    2021             :         }
    2022             :     }
    2023             : 
    2024        2720 :     nWinBufLoaded++;
    2025             : 
    2026        2720 :     return CE_None;
    2027             : }
    2028             : 
    2029             : /************************************************************************/
    2030             : /*                           CleanupWindow()                            */
    2031             : /************************************************************************/
    2032             : 
    2033         477 : void ECWDataset::CleanupWindow()
    2034             : 
    2035             : {
    2036         477 :     if (!bWinActive)
    2037         433 :         return;
    2038             : 
    2039          44 :     bWinActive = FALSE;
    2040          44 :     CPLFree(panWinBandList);
    2041          44 :     panWinBandList = nullptr;
    2042             : 
    2043          97 :     for (int iBand = 0; iBand < nWinBandCount; iBand++)
    2044          53 :         CPLFree(papCurLineBuf[iBand]);
    2045          44 :     CPLFree(papCurLineBuf);
    2046          44 :     papCurLineBuf = nullptr;
    2047             : }
    2048             : 
    2049             : /************************************************************************/
    2050             : /*                             IRasterIO()                              */
    2051             : /************************************************************************/
    2052             : 
    2053        1699 : CPLErr ECWDataset::IRasterIO(GDALRWFlag eRWFlag, int nXOff, int nYOff,
    2054             :                              int nXSize, int nYSize, void *pData, int nBufXSize,
    2055             :                              int nBufYSize, GDALDataType eBufType,
    2056             :                              int nBandCount, BANDMAP_TYPE panBandMap,
    2057             :                              GSpacing nPixelSpace, GSpacing nLineSpace,
    2058             :                              GSpacing nBandSpace,
    2059             :                              GDALRasterIOExtraArg *psExtraArg)
    2060             : 
    2061             : {
    2062        1699 :     if (eRWFlag == GF_Write)
    2063           0 :         return CE_Failure;
    2064             : 
    2065        1699 :     if (nBandCount > 100)
    2066           0 :         return CE_Failure;
    2067             : 
    2068        1699 :     if (bUseOldBandRasterIOImplementation)
    2069             :         /* Sanity check. Should not happen */
    2070           0 :         return CE_Failure;
    2071        1699 :     int nDataTypeSize = (GDALGetDataTypeSize(eRasterDataType) / 8);
    2072             : 
    2073        1699 :     if (nPixelSpace == 0)
    2074             :     {
    2075           0 :         nPixelSpace = nDataTypeSize;
    2076             :     }
    2077             : 
    2078        1699 :     if (nLineSpace == 0)
    2079             :     {
    2080           0 :         nLineSpace = nPixelSpace * nBufXSize;
    2081             :     }
    2082        1699 :     if (nBandSpace == 0)
    2083             :     {
    2084          18 :         nBandSpace =
    2085          18 :             static_cast<GSpacing>(nDataTypeSize) * nBufXSize * nBufYSize;
    2086             :     }
    2087             : 
    2088             :     // Use GDAL upsampling if non nearest
    2089        1699 :     if ((nBufXSize > nXSize || nBufYSize > nYSize) &&
    2090           3 :         psExtraArg->eResampleAlg != GRIORA_NearestNeighbour)
    2091             :     {
    2092           2 :         int nBufDataTypeSize = (GDALGetDataTypeSize(eBufType) / 8);
    2093           2 :         GByte *pabyTemp = (GByte *)VSI_MALLOC3_VERBOSE(
    2094             :             nXSize, nYSize, nBufDataTypeSize * nBandCount);
    2095           2 :         if (pabyTemp == nullptr)
    2096             :         {
    2097           0 :             return CE_Failure;
    2098             :         }
    2099             : 
    2100             :         GDALRasterIOExtraArg sExtraArgDefault;
    2101           2 :         INIT_RASTERIO_EXTRA_ARG(sExtraArgDefault);
    2102           2 :         sExtraArgDefault.pfnProgress = psExtraArg->pfnProgress;
    2103           2 :         sExtraArgDefault.pProgressData = psExtraArg->pProgressData;
    2104             : 
    2105           4 :         CPLErr eErr = IRasterIO(
    2106             :             eRWFlag, nXOff, nYOff, nXSize, nYSize, pabyTemp, nXSize, nYSize,
    2107             :             eBufType, nBandCount, panBandMap, nBufDataTypeSize,
    2108           2 :             (GIntBig)nBufDataTypeSize * nXSize,
    2109           2 :             (GIntBig)nBufDataTypeSize * nXSize * nYSize, &sExtraArgDefault);
    2110             : 
    2111           2 :         if (eErr == CE_None)
    2112             :         {
    2113             :             /* Create a MEM dataset that wraps the input buffer */
    2114             :             auto poMEMDS = std::unique_ptr<MEMDataset>(
    2115           2 :                 MEMDataset::Create("", nXSize, nYSize, 0, eBufType, nullptr));
    2116             : 
    2117           7 :             for (int i = 0; i < nBandCount; i++)
    2118             :             {
    2119           5 :                 auto hBand = MEMCreateRasterBandEx(
    2120           5 :                     poMEMDS.get(), i + 1,
    2121           5 :                     pabyTemp + static_cast<size_t>(i) * nBufDataTypeSize *
    2122           5 :                                    nXSize * nYSize,
    2123             :                     eBufType, 0, 0, false);
    2124           5 :                 poMEMDS->AddMEMBand(hBand);
    2125             : 
    2126           5 :                 const char *pszNBITS = GetRasterBand(i + 1)->GetMetadataItem(
    2127           5 :                     "NBITS", "IMAGE_STRUCTURE");
    2128           5 :                 if (pszNBITS)
    2129           0 :                     poMEMDS->GetRasterBand(i + 1)->SetMetadataItem(
    2130           0 :                         "NBITS", pszNBITS, "IMAGE_STRUCTURE");
    2131             :             }
    2132             : 
    2133             :             GDALRasterIOExtraArg sExtraArgTmp;
    2134           2 :             INIT_RASTERIO_EXTRA_ARG(sExtraArgTmp);
    2135           2 :             CPL_IGNORE_RET_VAL(sExtraArgTmp.eResampleAlg);
    2136           2 :             sExtraArgTmp.eResampleAlg = psExtraArg->eResampleAlg;
    2137             : 
    2138           2 :             CPL_IGNORE_RET_VAL(poMEMDS->RasterIO(
    2139             :                 GF_Read, 0, 0, nXSize, nYSize, pData, nBufXSize, nBufYSize,
    2140             :                 eBufType, nBandCount, nullptr, nPixelSpace, nLineSpace,
    2141             :                 nBandSpace, &sExtraArgTmp));
    2142             :         }
    2143             : 
    2144           2 :         VSIFree(pabyTemp);
    2145             : 
    2146           2 :         return eErr;
    2147             :     }
    2148             : 
    2149             : /* -------------------------------------------------------------------- */
    2150             : /*      ECW SDK 3.3 has a bug with the ECW format when we query the     */
    2151             : /*      number of bands of the dataset, but not in the "natural order". */
    2152             : /*      It ignores the content of panBandMap. (#4234)                   */
    2153             : /* -------------------------------------------------------------------- */
    2154             : #if ECWSDK_VERSION < 40
    2155        1697 :     if (!bIsJPEG2000 && nBandCount == nBands)
    2156             :     {
    2157             :         int i;
    2158          11 :         int bDoBandIRasterIO = FALSE;
    2159          44 :         for (i = 0; i < nBandCount; i++)
    2160             :         {
    2161          33 :             if (panBandMap[i] != i + 1)
    2162             :             {
    2163           2 :                 bDoBandIRasterIO = TRUE;
    2164             :             }
    2165             :         }
    2166          11 :         if (bDoBandIRasterIO)
    2167             :         {
    2168           1 :             return GDALDataset::IRasterIO(eRWFlag, nXOff, nYOff, nXSize, nYSize,
    2169             :                                           pData, nBufXSize, nBufYSize, eBufType,
    2170             :                                           nBandCount, panBandMap, nPixelSpace,
    2171           1 :                                           nLineSpace, nBandSpace, psExtraArg);
    2172             :         }
    2173             :     }
    2174             : #endif
    2175             : 
    2176             :     /* -------------------------------------------------------------------- */
    2177             :     /*      Check if we can directly return the data in case we have cached */
    2178             :     /*      it from a previous call in a multi-band reading pattern.        */
    2179             :     /* -------------------------------------------------------------------- */
    2180        1696 :     if (nBandCount == 1 && *panBandMap > 1 && *panBandMap <= nBands &&
    2181          67 :         sCachedMultiBandIO.nXOff == nXOff &&
    2182          67 :         sCachedMultiBandIO.nYOff == nYOff &&
    2183          30 :         sCachedMultiBandIO.nXSize == nXSize &&
    2184          22 :         sCachedMultiBandIO.nYSize == nYSize &&
    2185          22 :         sCachedMultiBandIO.nBufXSize == nBufXSize &&
    2186          22 :         sCachedMultiBandIO.nBufYSize == nBufYSize &&
    2187          22 :         sCachedMultiBandIO.eBufType == eBufType)
    2188             :     {
    2189          22 :         sCachedMultiBandIO.nBandsTried++;
    2190             : 
    2191          22 :         if (sCachedMultiBandIO.bEnabled &&
    2192           7 :             sCachedMultiBandIO.pabyData != nullptr)
    2193             :         {
    2194             :             int j;
    2195           7 :             int nBufTypeSize = GDALGetDataTypeSize(eBufType) / 8;
    2196         357 :             for (j = 0; j < nBufYSize; j++)
    2197             :             {
    2198         350 :                 GDALCopyWords(sCachedMultiBandIO.pabyData +
    2199         350 :                                   (*panBandMap - 1) * nBufXSize * nBufYSize *
    2200         350 :                                       nBufTypeSize +
    2201         350 :                                   j * nBufXSize * nBufTypeSize,
    2202             :                               eBufType, nBufTypeSize,
    2203         350 :                               ((GByte *)pData) + j * nLineSpace, eBufType,
    2204             :                               (int)nPixelSpace, nBufXSize);
    2205             :             }
    2206           7 :             return CE_None;
    2207             :         }
    2208             : 
    2209          45 :         if (!(sCachedMultiBandIO.bEnabled) &&
    2210          22 :             sCachedMultiBandIO.nBandsTried == nBands &&
    2211           7 :             CPLTestBool(CPLGetConfigOption("ECW_CLEVER", "YES")))
    2212             :         {
    2213           7 :             sCachedMultiBandIO.bEnabled = TRUE;
    2214           7 :             CPLDebug(
    2215             :                 "ECW",
    2216             :                 "Detecting successive band reading pattern (for next time)");
    2217             :         }
    2218             :     }
    2219             : 
    2220             :     /* -------------------------------------------------------------------- */
    2221             :     /*      Try to do it based on existing "advised" access.                */
    2222             :     /* -------------------------------------------------------------------- */
    2223             :     int nRet =
    2224        1689 :         TryWinRasterIO(eRWFlag, nXOff, nYOff, nXSize, nYSize, (GByte *)pData,
    2225             :                        nBufXSize, nBufYSize, eBufType, nBandCount, panBandMap,
    2226             :                        nPixelSpace, nLineSpace, nBandSpace, psExtraArg);
    2227        1689 :     if (nRet == TRUE)
    2228        1371 :         return CE_None;
    2229         318 :     else if (nRet < 0)
    2230           0 :         return CE_Failure;
    2231             : 
    2232             :     /* -------------------------------------------------------------------- */
    2233             :     /*      If we are requesting a single line at 1:1, we do a multi-band   */
    2234             :     /*      AdviseRead() and then TryWinRasterIO() again.                   */
    2235             :     /*                                                                      */
    2236             :     /*      Except for reading a 1x1 window when reading a scanline might   */
    2237             :     /*      be longer.                                                      */
    2238             :     /* -------------------------------------------------------------------- */
    2239         318 :     if (nXSize == 1 && nYSize == 1 && nBufXSize == 1 && nBufYSize == 1)
    2240             :     {
    2241             :         /* do nothing */
    2242             :     }
    2243             : 
    2244             : #if !defined(SDK_CAN_DO_SUPERSAMPLING)
    2245             :     /* -------------------------------------------------------------------- */
    2246             :     /*      If we are supersampling we need to fall into the general        */
    2247             :     /*      purpose logic.                                                  */
    2248             :     /* -------------------------------------------------------------------- */
    2249         314 :     else if (nXSize < nBufXSize || nYSize < nBufYSize)
    2250             :     {
    2251           1 :         bUseOldBandRasterIOImplementation = TRUE;
    2252           1 :         CPLErr eErr = GDALDataset::IRasterIO(
    2253             :             eRWFlag, nXOff, nYOff, nXSize, nYSize, pData, nBufXSize, nBufYSize,
    2254             :             eBufType, nBandCount, panBandMap, nPixelSpace, nLineSpace,
    2255             :             nBandSpace, psExtraArg);
    2256           1 :         bUseOldBandRasterIOImplementation = FALSE;
    2257           1 :         return eErr;
    2258             :     }
    2259             : #endif
    2260             : 
    2261         313 :     else if (nBufYSize == 1)
    2262             :     {
    2263             :         // This is tricky, because it expects the rest of the image
    2264             :         // with this buffer width to be read. The preferred way to
    2265             :         // achieve this behavior would be to call AdviseRead before
    2266             :         // call IRasterIO.  The logic could be improved to detect
    2267             :         // successive pattern of single line reading before doing an
    2268             :         // AdviseRead.
    2269             :         CPLErr eErr;
    2270             : 
    2271         241 :         eErr = AdviseRead(nXOff, nYOff, nXSize, GetRasterYSize() - nYOff,
    2272         241 :                           nBufXSize, (nRasterYSize - nYOff) / nYSize, eBufType,
    2273             :                           nBandCount, const_cast<int *>(panBandMap), nullptr);
    2274         482 :         if (eErr == CE_None &&
    2275         241 :             TryWinRasterIO(eRWFlag, nXOff, nYOff, nXSize, nYSize,
    2276             :                            (GByte *)pData, nBufXSize, nBufYSize, eBufType,
    2277             :                            nBandCount, panBandMap, nPixelSpace, nLineSpace,
    2278             :                            nBandSpace, psExtraArg))
    2279           6 :             return CE_None;
    2280             :     }
    2281             : 
    2282         311 :     CPLDebug("ECW", "RasterIO(%d,%d,%d,%d -> %dx%d) - doing interleaved read.",
    2283             :              nXOff, nYOff, nXSize, nYSize, nBufXSize, nBufYSize);
    2284             : 
    2285             :     /* -------------------------------------------------------------------- */
    2286             :     /*      Setup view.                                                     */
    2287             :     /* -------------------------------------------------------------------- */
    2288             :     UINT32 anBandIndices[100];
    2289             :     int i;
    2290             :     NCSError eNCSErr;
    2291         622 :     CNCSError oErr(GetCNCSError(NCS_SUCCESS));
    2292             : 
    2293         641 :     for (i = 0; i < nBandCount; i++)
    2294         330 :         anBandIndices[i] = panBandMap[i] - 1;
    2295             : 
    2296         311 :     CleanupWindow();
    2297             : 
    2298             :     /* -------------------------------------------------------------------- */
    2299             :     /*      Cache data in the context of a multi-band reading pattern.      */
    2300             :     /* -------------------------------------------------------------------- */
    2301         311 :     if (nBandCount == 1 && *panBandMap == 1 && (nBands == 3 || nBands == 4))
    2302             :     {
    2303         219 :         if (sCachedMultiBandIO.bEnabled &&
    2304           4 :             sCachedMultiBandIO.nBandsTried != nBands)
    2305             :         {
    2306           1 :             sCachedMultiBandIO.bEnabled = FALSE;
    2307           1 :             CPLDebug("ECW", "Disabling successive band reading pattern");
    2308             :         }
    2309             : 
    2310         219 :         sCachedMultiBandIO.nXOff = nXOff;
    2311         219 :         sCachedMultiBandIO.nYOff = nYOff;
    2312         219 :         sCachedMultiBandIO.nXSize = nXSize;
    2313         219 :         sCachedMultiBandIO.nYSize = nYSize;
    2314         219 :         sCachedMultiBandIO.nBufXSize = nBufXSize;
    2315         219 :         sCachedMultiBandIO.nBufYSize = nBufYSize;
    2316         219 :         sCachedMultiBandIO.eBufType = eBufType;
    2317         219 :         sCachedMultiBandIO.nBandsTried = 1;
    2318             : 
    2319         219 :         int nBufTypeSize = GDALGetDataTypeSize(eBufType) / 8;
    2320             : 
    2321         219 :         if (sCachedMultiBandIO.bEnabled)
    2322             :         {
    2323           6 :             GByte *pNew = (GByte *)VSIRealloc(sCachedMultiBandIO.pabyData,
    2324           3 :                                               nBufXSize * nBufYSize * nBands *
    2325             :                                                   nBufTypeSize);
    2326           3 :             if (pNew == nullptr)
    2327           0 :                 CPLFree(sCachedMultiBandIO.pabyData);
    2328           3 :             sCachedMultiBandIO.pabyData = pNew;
    2329             :         }
    2330             : 
    2331         219 :         if (sCachedMultiBandIO.bEnabled &&
    2332           3 :             sCachedMultiBandIO.pabyData != nullptr)
    2333             :         {
    2334           3 :             nBandIndexToPromoteTo8Bit = -1;
    2335          12 :             for (i = 0; i < nBands; i++)
    2336             :             {
    2337           9 :                 if (((ECWRasterBand *)GetRasterBand(i + 1))->bPromoteTo8Bit)
    2338           0 :                     nBandIndexToPromoteTo8Bit = i;
    2339           9 :                 anBandIndices[i] = i;
    2340             :             }
    2341             : 
    2342           3 :             oErr = poFileView->SetView(nBands, anBandIndices, nXOff, nYOff,
    2343           3 :                                        nXOff + nXSize - 1, nYOff + nYSize - 1,
    2344           3 :                                        nBufXSize, nBufYSize);
    2345           3 :             eNCSErr = oErr.GetErrorNumber();
    2346             : 
    2347           3 :             if (eNCSErr != NCS_SUCCESS)
    2348             :             {
    2349           0 :                 CPLError(CE_Failure, CPLE_AppDefined, "%s",
    2350             :                          NCSGetErrorText(eNCSErr));
    2351             : 
    2352           0 :                 return CE_Failure;
    2353             :             }
    2354             : 
    2355           6 :             CPLErr eErr = ReadBands(
    2356           3 :                 sCachedMultiBandIO.pabyData, nBufXSize, nBufYSize, eBufType,
    2357           3 :                 nBands, nBufTypeSize, nBufXSize * nBufTypeSize,
    2358           3 :                 nBufXSize * nBufYSize * nBufTypeSize, psExtraArg);
    2359           3 :             if (eErr != CE_None)
    2360           0 :                 return eErr;
    2361             : 
    2362             :             int j;
    2363         153 :             for (j = 0; j < nBufYSize; j++)
    2364             :             {
    2365         150 :                 GDALCopyWords(
    2366         150 :                     sCachedMultiBandIO.pabyData + j * nBufXSize * nBufTypeSize,
    2367         150 :                     eBufType, nBufTypeSize, ((GByte *)pData) + j * nLineSpace,
    2368             :                     eBufType, (int)nPixelSpace, nBufXSize);
    2369             :             }
    2370           3 :             return CE_None;
    2371             :         }
    2372             :     }
    2373             : 
    2374         308 :     nBandIndexToPromoteTo8Bit = -1;
    2375         635 :     for (i = 0; i < nBandCount; i++)
    2376             :     {
    2377         327 :         if (((ECWRasterBand *)GetRasterBand(anBandIndices[i] + 1))
    2378         327 :                 ->bPromoteTo8Bit)
    2379           4 :             nBandIndexToPromoteTo8Bit = i;
    2380             :     }
    2381         308 :     oErr = poFileView->SetView(nBandCount, anBandIndices, nXOff, nYOff,
    2382         308 :                                nXOff + nXSize - 1, nYOff + nYSize - 1,
    2383         308 :                                nBufXSize, nBufYSize);
    2384         308 :     eNCSErr = oErr.GetErrorNumber();
    2385             : 
    2386         308 :     if (eNCSErr != NCS_SUCCESS)
    2387             :     {
    2388           0 :         CPLError(CE_Failure, CPLE_AppDefined, "%s", NCSGetErrorText(eNCSErr));
    2389             : 
    2390           0 :         return CE_Failure;
    2391             :     }
    2392             : 
    2393         308 :     return ReadBands(pData, nBufXSize, nBufYSize, eBufType, nBandCount,
    2394         308 :                      nPixelSpace, nLineSpace, nBandSpace, psExtraArg);
    2395             : }
    2396             : 
    2397             : /************************************************************************/
    2398             : /*                        ReadBandsDirectly()                           */
    2399             : /************************************************************************/
    2400             : 
    2401          39 : CPLErr ECWDataset::ReadBandsDirectly(void *pData, int nBufXSize, int nBufYSize,
    2402             :                                      CPL_UNUSED GDALDataType eBufType,
    2403             :                                      int nBandCount,
    2404             :                                      CPL_UNUSED GSpacing nPixelSpace,
    2405             :                                      GSpacing nLineSpace, GSpacing nBandSpace,
    2406             :                                      GDALRasterIOExtraArg *psExtraArg)
    2407             : {
    2408          39 :     CPLDebug("ECW", "ReadBandsDirectly(-> %dx%d) - reading lines directly.",
    2409             :              nBufXSize, nBufYSize);
    2410             : 
    2411          39 :     UINT8 **pBIL = (UINT8 **)NCSMalloc(nBandCount * sizeof(UINT8 *), FALSE);
    2412             : 
    2413         101 :     for (int nB = 0; nB < nBandCount; nB++)
    2414             :     {
    2415          62 :         pBIL[nB] = ((UINT8 *)pData) + (nBandSpace * nB);  // for any bit depth
    2416             :     }
    2417             : 
    2418          39 :     CPLErr eErr = CE_None;
    2419        5574 :     for (int nR = 0; nR < nBufYSize; nR++)
    2420             :     {
    2421       11070 :         if (poFileView->ReadLineBIL(eNCSRequestDataType, (UINT16)nBandCount,
    2422        5535 :                                     (void **)pBIL) != NCSECW_READ_OK)
    2423             :         {
    2424           0 :             eErr = CE_Failure;
    2425           0 :             break;
    2426             :         }
    2427       13722 :         for (int nB = 0; nB < nBandCount; nB++)
    2428             :         {
    2429        8187 :             if (nB == nBandIndexToPromoteTo8Bit)
    2430             :             {
    2431       48999 :                 for (int iX = 0; iX < nBufXSize; iX++)
    2432             :                 {
    2433       48690 :                     pBIL[nB][iX] *= 255;
    2434             :                 }
    2435             :             }
    2436        8187 :             pBIL[nB] += nLineSpace;
    2437             :         }
    2438             : 
    2439        5535 :         if (psExtraArg->pfnProgress != nullptr &&
    2440           0 :             !psExtraArg->pfnProgress(1.0 * (nR + 1) / nBufYSize, "",
    2441             :                                      psExtraArg->pProgressData))
    2442             :         {
    2443           0 :             eErr = CE_Failure;
    2444           0 :             break;
    2445             :         }
    2446             :     }
    2447          39 :     if (pBIL)
    2448             :     {
    2449          39 :         NCSFree(pBIL);
    2450             :     }
    2451          39 :     return eErr;
    2452             : }
    2453             : 
    2454             : /************************************************************************/
    2455             : /*                            ReadBands()                               */
    2456             : /************************************************************************/
    2457             : 
    2458         311 : CPLErr ECWDataset::ReadBands(void *pData, int nBufXSize, int nBufYSize,
    2459             :                              GDALDataType eBufType, int nBandCount,
    2460             :                              GSpacing nPixelSpace, GSpacing nLineSpace,
    2461             :                              GSpacing nBandSpace,
    2462             :                              GDALRasterIOExtraArg *psExtraArg)
    2463             : {
    2464             :     int i;
    2465             :     /* -------------------------------------------------------------------- */
    2466             :     /*      Setup working scanline, and the pointers into it.               */
    2467             :     /* -------------------------------------------------------------------- */
    2468         311 :     int nDataTypeSize = (GDALGetDataTypeSize(eRasterDataType) / 8);
    2469         311 :     bool bDirect =
    2470          45 :         (eBufType == eRasterDataType) && nDataTypeSize == nPixelSpace &&
    2471         395 :         nLineSpace == (nPixelSpace * nBufXSize) &&
    2472             :         nBandSpace ==
    2473          39 :             (static_cast<GSpacing>(nDataTypeSize) * nBufXSize * nBufYSize);
    2474         311 :     if (bDirect)
    2475             :     {
    2476          39 :         return ReadBandsDirectly(pData, nBufXSize, nBufYSize, eBufType,
    2477             :                                  nBandCount, nPixelSpace, nLineSpace,
    2478          39 :                                  nBandSpace, psExtraArg);
    2479             :     }
    2480         272 :     CPLDebug("ECW", "ReadBands(-> %dx%d) - reading lines using GDALCopyWords.",
    2481             :              nBufXSize, nBufYSize);
    2482         272 :     CPLErr eErr = CE_None;
    2483             :     GByte *pabyBILScanline =
    2484         272 :         (GByte *)CPLMalloc(nBufXSize * nDataTypeSize * nBandCount);
    2485         272 :     GByte **papabyBIL = (GByte **)CPLMalloc(nBandCount * sizeof(void *));
    2486             : 
    2487         546 :     for (i = 0; i < nBandCount; i++)
    2488         274 :         papabyBIL[i] = pabyBILScanline + i * nBufXSize * nDataTypeSize;
    2489             : 
    2490             :     /* -------------------------------------------------------------------- */
    2491             :     /*      Read back all the data for the requested view.                  */
    2492             :     /* -------------------------------------------------------------------- */
    2493        4272 :     for (int iScanline = 0; iScanline < nBufYSize; iScanline++)
    2494             :     {
    2495             :         NCSEcwReadStatus eRStatus;
    2496             : 
    2497        8000 :         eRStatus = poFileView->ReadLineBIL(
    2498        4000 :             eNCSRequestDataType, (UINT16)nBandCount, (void **)papabyBIL);
    2499        4000 :         if (eRStatus != NCSECW_READ_OK)
    2500             :         {
    2501           0 :             eErr = CE_Failure;
    2502           0 :             CPLError(CE_Failure, CPLE_AppDefined,
    2503             :                      "NCScbmReadViewLineBIL failed.");
    2504           0 :             break;
    2505             :         }
    2506             : 
    2507        8080 :         for (i = 0; i < nBandCount; i++)
    2508             :         {
    2509        4080 :             if (i == nBandIndexToPromoteTo8Bit)
    2510             :             {
    2511       24450 :                 for (int iX = 0; iX < nBufXSize; iX++)
    2512             :                 {
    2513       24300 :                     papabyBIL[i][iX] *= 255;
    2514             :                 }
    2515             :             }
    2516             : 
    2517        4080 :             GDALCopyWords(pabyBILScanline + i * nDataTypeSize * nBufXSize,
    2518             :                           eRasterDataType, nDataTypeSize,
    2519        4080 :                           ((GByte *)pData) + nLineSpace * iScanline +
    2520        4080 :                               nBandSpace * i,
    2521             :                           eBufType, (int)nPixelSpace, nBufXSize);
    2522             :         }
    2523             : 
    2524        4000 :         if (psExtraArg->pfnProgress != nullptr &&
    2525           0 :             !psExtraArg->pfnProgress(1.0 * (iScanline + 1) / nBufYSize, "",
    2526             :                                      psExtraArg->pProgressData))
    2527             :         {
    2528           0 :             eErr = CE_Failure;
    2529           0 :             break;
    2530             :         }
    2531             :     }
    2532             : 
    2533         272 :     CPLFree(pabyBILScanline);
    2534         272 :     CPLFree(papabyBIL);
    2535             : 
    2536         272 :     return eErr;
    2537             : }
    2538             : 
    2539             : /************************************************************************/
    2540             : /*                            OpenJPEG2000()                            */
    2541             : /*                                                                      */
    2542             : /*          Open method that only supports JPEG2000 files.              */
    2543             : /************************************************************************/
    2544             : 
    2545          89 : GDALDataset *ECWDataset::OpenJPEG2000(GDALOpenInfo *poOpenInfo)
    2546             : 
    2547             : {
    2548          89 :     if (!ECWDatasetIdentifyJPEG2000(poOpenInfo))
    2549           0 :         return nullptr;
    2550             : 
    2551          89 :     return Open(poOpenInfo, TRUE);
    2552             : }
    2553             : 
    2554             : /************************************************************************/
    2555             : /*                              OpenECW()                               */
    2556             : /*                                                                      */
    2557             : /*      Open method that only supports ECW files.                       */
    2558             : /************************************************************************/
    2559             : 
    2560          33 : GDALDataset *ECWDataset::OpenECW(GDALOpenInfo *poOpenInfo)
    2561             : 
    2562             : {
    2563          33 :     if (!ECWDatasetIdentifyECW(poOpenInfo))
    2564           0 :         return nullptr;
    2565             : 
    2566          33 :     return Open(poOpenInfo, FALSE);
    2567             : }
    2568             : 
    2569             : /************************************************************************/
    2570             : /*                            OpenFileView()                            */
    2571             : /************************************************************************/
    2572             : 
    2573         122 : CNCSJP2FileView *ECWDataset::OpenFileView(const char *pszDatasetName,
    2574             :                                           bool bProgressive,
    2575             :                                           int &bUsingCustomStream,
    2576             :                                           CPL_UNUSED bool bWrite)
    2577             : {
    2578             :     /* -------------------------------------------------------------------- */
    2579             :     /*      First we try to open it as a normal CNCSFile, letting the       */
    2580             :     /*      ECW SDK manage the IO itself.   This will only work for real    */
    2581             :     /*      files, and ecwp: or ecwps: sources.                             */
    2582             :     /* -------------------------------------------------------------------- */
    2583         122 :     CNCSJP2FileView *poFileView = nullptr;
    2584             :     NCSError eErr;
    2585         244 :     CNCSError oErr(GetCNCSError(NCS_SUCCESS));
    2586             : 
    2587         122 :     bUsingCustomStream = FALSE;
    2588         122 :     poFileView = new CNCSFile();
    2589             :     // we always open in read only mode. This should be improved in the future.
    2590             :     try
    2591             :     {
    2592             : #ifdef _WIN32
    2593             :         if (CPLTestBool(CPLGetConfigOption("GDAL_FILENAME_IS_UTF8", "YES")))
    2594             :         {
    2595             :             wchar_t *pwszDatasetName =
    2596             :                 CPLRecodeToWChar(pszDatasetName, CPL_ENC_UTF8, CPL_ENC_UCS2);
    2597             :             oErr = poFileView->Open(pwszDatasetName, bProgressive, false);
    2598             :             CPLFree(pwszDatasetName);
    2599             :         }
    2600             :         else
    2601             : #endif
    2602             :         {
    2603             :             oErr =
    2604         122 :                 poFileView->Open((char *)pszDatasetName, bProgressive, false);
    2605             :         }
    2606             :     }
    2607           0 :     catch (...)
    2608             :     {
    2609           0 :         CPLError(CE_Failure, CPLE_AppDefined,
    2610             :                  "Unexpected exception occurred in ECW SDK");
    2611           0 :         delete poFileView;
    2612           0 :         return nullptr;
    2613             :     }
    2614         122 :     eErr = oErr.GetErrorNumber();
    2615             : 
    2616             :     /* -------------------------------------------------------------------- */
    2617             :     /*      If that did not work, trying opening as a virtual file.         */
    2618             :     /* -------------------------------------------------------------------- */
    2619         122 :     if (eErr != NCS_SUCCESS)
    2620             :     {
    2621          53 :         CPLDebug("ECW",
    2622             :                  "NCScbmOpenFileView(%s): eErr=%d, will try VSIL stream.",
    2623             :                  pszDatasetName, (int)eErr);
    2624             : 
    2625          53 :         delete poFileView;
    2626             : 
    2627          53 :         VSILFILE *fpVSIL = VSIFOpenL(pszDatasetName, "rb");
    2628          53 :         if (fpVSIL == nullptr)
    2629             :         {
    2630           0 :             CPLError(CE_Failure, CPLE_OpenFailed, "Failed to open %s.",
    2631             :                      pszDatasetName);
    2632           0 :             return nullptr;
    2633             :         }
    2634             : 
    2635          53 :         if (hECWDatasetMutex == nullptr)
    2636             :         {
    2637           0 :             hECWDatasetMutex = CPLCreateMutex();
    2638             :         }
    2639          53 :         else if (!CPLAcquireMutex(hECWDatasetMutex, 60.0))
    2640             :         {
    2641           0 :             CPLDebug("ECW", "Failed to acquire mutex in 60s.");
    2642             :         }
    2643             :         else
    2644             :         {
    2645          53 :             CPLDebug("ECW", "Got mutex.");
    2646             :         }
    2647             : 
    2648          53 :         poFileView = new CNCSJP2FileView();
    2649             : 
    2650             : #if ECWSDK_VERSION >= 55
    2651             :         NCS::CString streamName(pszDatasetName);
    2652             :         auto vsiIoStream =
    2653             :             NCS::CView::FindSteamByStreamNameFromOpenDatasets(streamName);
    2654             :         if (!vsiIoStream)
    2655             :         {
    2656             :             vsiIoStream = std::make_shared<VSIIOStream>();
    2657             :             std::static_pointer_cast<VSIIOStream>(vsiIoStream)
    2658             :                 ->Access(fpVSIL, FALSE, TRUE, pszDatasetName, 0, -1);
    2659             :             bUsingCustomStream = TRUE;
    2660             :         }
    2661             :         oErr = poFileView->Open(vsiIoStream, bProgressive);
    2662             : #else
    2663          53 :         auto vsiIoStream = new VSIIOStream();
    2664          53 :         vsiIoStream->Access(fpVSIL, FALSE, TRUE, pszDatasetName, 0, -1);
    2665          53 :         oErr = poFileView->Open(vsiIoStream, bProgressive);
    2666             : 
    2667             :         // The CNCSJP2FileView (poFileView) object may not use the iostream
    2668             :         // (poIOStream) passed to the CNCSJP2FileView::Open() method if an
    2669             :         // iostream is already available to the ECW JPEG 2000 SDK for a given
    2670             :         // file.  Consequently, if the iostream passed to
    2671             :         // CNCSJP2FileView::Open() does not become the underlying iostream
    2672             :         // of the CNCSJP2FileView object, then it should be deleted.
    2673             :         //
    2674             :         // In addition, the underlying iostream of the CNCSJP2FileView object
    2675             :         // should not be deleted until all CNCSJP2FileView objects using the
    2676             :         // underlying iostream are deleted. Consequently, each time a
    2677             :         // CNCSJP2FileView object is created, the nFileViewCount attribute
    2678             :         // of the underlying VSIIOStream object must be incremented for use
    2679             :         // in the ECWDataset destructor.
    2680             : 
    2681             :         VSIIOStream *poUnderlyingIOStream =
    2682          53 :             ((VSIIOStream *)(poFileView->GetStream()));
    2683             : 
    2684          53 :         if (poUnderlyingIOStream)
    2685          52 :             poUnderlyingIOStream->nFileViewCount++;
    2686             : 
    2687          53 :         if (vsiIoStream != poUnderlyingIOStream)
    2688             :         {
    2689           1 :             delete vsiIoStream;
    2690             :         }
    2691             :         else
    2692             :         {
    2693          52 :             bUsingCustomStream = TRUE;
    2694             :         }
    2695             : #endif
    2696             : 
    2697          53 :         CPLReleaseMutex(hECWDatasetMutex);
    2698             : 
    2699          53 :         if (oErr.GetErrorNumber() != NCS_SUCCESS)
    2700             :         {
    2701           1 :             delete poFileView;
    2702           1 :             ECWReportError(oErr);
    2703             : 
    2704           1 :             return nullptr;
    2705             :         }
    2706             :     }
    2707             : 
    2708         121 :     return poFileView;
    2709             : }
    2710             : 
    2711             : /************************************************************************/
    2712             : /*                                Open()                                */
    2713             : /************************************************************************/
    2714             : 
    2715         122 : GDALDataset *ECWDataset::Open(GDALOpenInfo *poOpenInfo, int bIsJPEG2000)
    2716             : 
    2717             : {
    2718         122 :     CNCSJP2FileView *poFileView = nullptr;
    2719             :     int i;
    2720         122 :     int bUsingCustomStream = FALSE;
    2721         244 :     CPLString osFilename = poOpenInfo->pszFilename;
    2722             : 
    2723         122 :     ECWInitialize();
    2724             : 
    2725             :     /* Note: J2K_SUBFILE is somehow an obsolete concept that predates
    2726             :      * /vsisubfile/ */
    2727             :     /* syntax and was used mainly(only?) by the NITF driver before its switch */
    2728             :     /* to /vsisubfile */
    2729             : 
    2730             :     /* -------------------------------------------------------------------- */
    2731             :     /*      If we get a J2K_SUBFILE style name, convert it into the         */
    2732             :     /*      corresponding /vsisubfile/ path.                                */
    2733             :     /*                                                                      */
    2734             :     /*      From: J2K_SUBFILE:offset,size,filename                           */
    2735             :     /*      To: /vsisubfile/offset_size,filename                            */
    2736             :     /* -------------------------------------------------------------------- */
    2737         122 :     if (STARTS_WITH_CI(osFilename, "J2K_SUBFILE:"))
    2738             :     {
    2739             :         char **papszTokens =
    2740           0 :             CSLTokenizeString2(osFilename.c_str() + 12, ",", 0);
    2741           0 :         if (CSLCount(papszTokens) >= 3)
    2742             :         {
    2743             :             osFilename.Printf("/vsisubfile/%s_%s,%s", papszTokens[0],
    2744           0 :                               papszTokens[1], papszTokens[2]);
    2745             :         }
    2746             :         else
    2747             :         {
    2748           0 :             CPLError(CE_Failure, CPLE_OpenFailed,
    2749             :                      "Failed to parse J2K_SUBFILE specification.");
    2750           0 :             CSLDestroy(papszTokens);
    2751           0 :             return nullptr;
    2752             :         }
    2753           0 :         CSLDestroy(papszTokens);
    2754             :     }
    2755             : 
    2756             :     /* -------------------------------------------------------------------- */
    2757             :     /*      Open the client interface.                                      */
    2758             :     /* -------------------------------------------------------------------- */
    2759         122 :     poFileView = OpenFileView(osFilename.c_str(), false, bUsingCustomStream,
    2760         122 :                               poOpenInfo->eAccess == GA_Update);
    2761         122 :     if (poFileView == nullptr)
    2762             :     {
    2763             : #if ECWSDK_VERSION < 50
    2764             :         /* Detect what is apparently the ECW v3 file format signature */
    2765           1 :         if (EQUAL(CPLGetExtension(osFilename), "ECW") &&
    2766           2 :             poOpenInfo->nHeaderBytes > 0x30 &&
    2767           1 :             STARTS_WITH_CI((const char *)(poOpenInfo->pabyHeader + 0x20),
    2768             :                            "ecw ECW3"))
    2769             :         {
    2770           1 :             CPLError(CE_Failure, CPLE_AppDefined,
    2771             :                      "Cannot open %s which looks like a ECW format v3 file, "
    2772             :                      "that requires ECW SDK 5.0 or later",
    2773             :                      osFilename.c_str());
    2774             :         }
    2775             : #endif
    2776           1 :         return nullptr;
    2777             :     }
    2778             : 
    2779             :     /* -------------------------------------------------------------------- */
    2780             :     /*      Create a corresponding GDALDataset.                             */
    2781             :     /* -------------------------------------------------------------------- */
    2782         121 :     ECWDataset *poDS = new ECWDataset(bIsJPEG2000);
    2783         121 :     poDS->poFileView = poFileView;
    2784         121 :     poDS->eAccess = poOpenInfo->eAccess;
    2785             : 
    2786             :     // Disable .aux.xml writing for subfiles and such.  Unfortunately
    2787             :     // this will also disable it in some cases where it might be
    2788             :     // applicable.
    2789         121 :     if (bUsingCustomStream)
    2790          52 :         poDS->nPamFlags |= GPF_DISABLED;
    2791             : 
    2792         121 :     poDS->bUsingCustomStream = bUsingCustomStream;
    2793             : 
    2794             :     /* -------------------------------------------------------------------- */
    2795             :     /*      Fetch general file information.                                 */
    2796             :     /* -------------------------------------------------------------------- */
    2797         121 :     poDS->psFileInfo = poFileView->GetFileInfo();
    2798             : 
    2799         121 :     CPLDebug("ECW",
    2800             :              "FileInfo: SizeXY=%d,%d Bands=%d\n"
    2801             :              "       OriginXY=%g,%g  CellIncrementXY=%g,%g\n"
    2802             :              "       ColorSpace=%d, eCellType=%d\n",
    2803         121 :              poDS->psFileInfo->nSizeX, poDS->psFileInfo->nSizeY,
    2804         121 :              poDS->psFileInfo->nBands, poDS->psFileInfo->fOriginX,
    2805         121 :              poDS->psFileInfo->fOriginY, poDS->psFileInfo->fCellIncrementX,
    2806         121 :              poDS->psFileInfo->fCellIncrementY,
    2807         121 :              (int)poDS->psFileInfo->eColorSpace,
    2808         121 :              (int)poDS->psFileInfo->eCellType);
    2809             : 
    2810             :     /* -------------------------------------------------------------------- */
    2811             :     /*      Establish raster info.                                          */
    2812             :     /* -------------------------------------------------------------------- */
    2813         121 :     poDS->nRasterXSize = poDS->psFileInfo->nSizeX;
    2814         121 :     poDS->nRasterYSize = poDS->psFileInfo->nSizeY;
    2815             : 
    2816             :     /* -------------------------------------------------------------------- */
    2817             :     /*      Establish the GDAL data type that corresponds.  A few NCS       */
    2818             :     /*      data types have no direct corresponding value in GDAL so we     */
    2819             :     /*      will coerce to something sufficiently similar.                  */
    2820             :     /* -------------------------------------------------------------------- */
    2821         121 :     poDS->eNCSRequestDataType = poDS->psFileInfo->eCellType;
    2822         121 :     switch (poDS->psFileInfo->eCellType)
    2823             :     {
    2824         101 :         case NCSCT_UINT8:
    2825         101 :             poDS->eRasterDataType = GDT_Byte;
    2826         101 :             break;
    2827             : 
    2828          10 :         case NCSCT_UINT16:
    2829          10 :             poDS->eRasterDataType = GDT_UInt16;
    2830          10 :             break;
    2831             : 
    2832           2 :         case NCSCT_UINT32:
    2833             :         case NCSCT_UINT64:
    2834           2 :             poDS->eRasterDataType = GDT_UInt32;
    2835           2 :             poDS->eNCSRequestDataType = NCSCT_UINT32;
    2836           2 :             break;
    2837             : 
    2838           6 :         case NCSCT_INT8:
    2839             :         case NCSCT_INT16:
    2840           6 :             poDS->eRasterDataType = GDT_Int16;
    2841           6 :             poDS->eNCSRequestDataType = NCSCT_INT16;
    2842           6 :             break;
    2843             : 
    2844           2 :         case NCSCT_INT32:
    2845             :         case NCSCT_INT64:
    2846           2 :             poDS->eRasterDataType = GDT_Int32;
    2847           2 :             poDS->eNCSRequestDataType = NCSCT_INT32;
    2848           2 :             break;
    2849             : 
    2850           0 :         case NCSCT_IEEE4:
    2851           0 :             poDS->eRasterDataType = GDT_Float32;
    2852           0 :             break;
    2853             : 
    2854           0 :         case NCSCT_IEEE8:
    2855           0 :             poDS->eRasterDataType = GDT_Float64;
    2856           0 :             break;
    2857             : 
    2858           0 :         default:
    2859           0 :             CPLDebug("ECW", "Unhandled case : eCellType = %d",
    2860           0 :                      (int)poDS->psFileInfo->eCellType);
    2861           0 :             break;
    2862             :     }
    2863             : 
    2864             : /* -------------------------------------------------------------------- */
    2865             : /*      If decoding a UInt32 image, check that the SDK is not buggy     */
    2866             : /*      There are issues at least in the 5.x series.                    */
    2867             : /* -------------------------------------------------------------------- */
    2868             : #if ECWSDK_VERSION >= 40
    2869             :     constexpr const char *szDETECT_BUG_FILENAME =
    2870             :         "__detect_ecw_uint32_bug__.j2k";
    2871             :     if (bIsJPEG2000 && poDS->eNCSRequestDataType == NCSCT_UINT32 &&
    2872             :         CPLTestBool(CPLGetConfigOption("ECW_CHECK_CORRECT_DECODING", "TRUE")) &&
    2873             :         strstr(poOpenInfo->pszFilename, szDETECT_BUG_FILENAME) == nullptr)
    2874             :     {
    2875             :         static bool bUINT32_Ok = false;
    2876             :         {
    2877             :             CPLMutexHolder oHolder(&hECWDatasetMutex);
    2878             :             static bool bTestDone = false;
    2879             :             if (!bTestDone)
    2880             :             {
    2881             :                 bTestDone = true;
    2882             :                 // Minimal J2K 2x2 image with NBITS=20, unsigned, reversible
    2883             :                 // compression and following values 0 1048575 1048574 524288
    2884             : 
    2885             :                 static const GByte abyTestUInt32ImageData[] = {
    2886             :                     0xFF, 0x4F, 0xFF, 0x51, 0x00, 0x29, 0x00, 0x02, 0x00, 0x00,
    2887             :                     0x00, 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00,
    2888             :                     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00,
    2889             :                     0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    2890             :                     0x00, 0x01, 0x13, 0x01, 0x01, 0xFF, 0x52, 0x00, 0x0D, 0x01,
    2891             :                     0x00, 0x00, 0x01, 0x00, 0x00, 0x04, 0x04, 0x00, 0x01, 0x99,
    2892             :                     0xFF, 0x5C, 0x00, 0x04, 0x40, 0xA0, 0xFF, 0x90, 0x00, 0x0A,
    2893             :                     0x00, 0x00, 0x00, 0x00, 0x00, 0x1C, 0x00, 0x01, 0xFF, 0x93,
    2894             :                     0xDF, 0xF9, 0x40, 0x50, 0x07, 0x68, 0xE0, 0x12, 0xD2, 0xDA,
    2895             :                     0xDF, 0xFF, 0x7F, 0x5F, 0xFF, 0xD9};
    2896             : 
    2897             :                 const std::string osTmpFilename =
    2898             :                     VSIMemGenerateHiddenFilename(szDETECT_BUG_FILENAME);
    2899             :                 VSIFCloseL(VSIFileFromMemBuffer(
    2900             :                     osTmpFilename.c_str(),
    2901             :                     const_cast<GByte *>(abyTestUInt32ImageData),
    2902             :                     sizeof(abyTestUInt32ImageData), false));
    2903             :                 GDALOpenInfo oOpenInfo(osTmpFilename.c_str(), GA_ReadOnly);
    2904             :                 auto poTmpDS =
    2905             :                     std::unique_ptr<GDALDataset>(Open(&oOpenInfo, true));
    2906             :                 if (poTmpDS)
    2907             :                 {
    2908             :                     uint32_t anValues[4] = {0};
    2909             :                     if (poTmpDS->GetRasterBand(1)->RasterIO(
    2910             :                             GF_Read, 0, 0, 2, 2, anValues, 2, 2, GDT_UInt32, 0,
    2911             :                             0, nullptr) == CE_None &&
    2912             :                         anValues[0] == 0 && anValues[1] == 1048575 &&
    2913             :                         anValues[2] == 1048574 && anValues[3] == 524288)
    2914             :                     {
    2915             :                         bUINT32_Ok = true;
    2916             :                     }
    2917             :                 }
    2918             :                 VSIUnlink(osTmpFilename.c_str());
    2919             :             }
    2920             :         }
    2921             : 
    2922             :         if (!bUINT32_Ok)
    2923             :         {
    2924             :             CPLDebug("ECW", "ECW SDK used cannot correctly decode UInt32 "
    2925             :                             "images. Giving up");
    2926             :             delete poDS;
    2927             :             return nullptr;
    2928             :         }
    2929             :     }
    2930             : #endif
    2931             : 
    2932             :     /* -------------------------------------------------------------------- */
    2933             :     /*      Create band information objects.                                */
    2934             :     /* -------------------------------------------------------------------- */
    2935         332 :     for (i = 0; i < poDS->psFileInfo->nBands; i++)
    2936         211 :         poDS->SetBand(i + 1, new ECWRasterBand(poDS, i + 1, -1,
    2937         211 :                                                poOpenInfo->papszOpenOptions));
    2938             : 
    2939             :     /* -------------------------------------------------------------------- */
    2940             :     /*      Look for supporting coordinate system information.              */
    2941             :     /* -------------------------------------------------------------------- */
    2942         121 :     if (bIsJPEG2000)
    2943             :     {
    2944          89 :         poDS->LoadJP2Metadata(poOpenInfo, osFilename);
    2945             :     }
    2946             :     else
    2947             :     {
    2948          32 :         poDS->ECW2WKTProjection();
    2949             : 
    2950             :         /* --------------------------------------------------------------------
    2951             :          */
    2952             :         /*      Check for world file. */
    2953             :         /* --------------------------------------------------------------------
    2954             :          */
    2955          32 :         if (!poDS->bGeoTransformValid)
    2956             :         {
    2957           0 :             poDS->bGeoTransformValid |=
    2958           0 :                 GDALReadWorldFile2(osFilename, nullptr, poDS->adfGeoTransform,
    2959           0 :                                    poOpenInfo->GetSiblingFiles(), nullptr) ||
    2960           0 :                 GDALReadWorldFile2(osFilename, ".wld", poDS->adfGeoTransform,
    2961           0 :                                    poOpenInfo->GetSiblingFiles(), nullptr);
    2962             :         }
    2963             :     }
    2964             : 
    2965         121 :     if (poDS->psFileInfo->nCompressionRate > 0)
    2966          65 :         poDS->GDALDataset::SetMetadataItem(
    2967             :             "COMPRESSION_RATE_TARGET",
    2968         130 :             CPLString().Printf("%d", poDS->psFileInfo->nCompressionRate));
    2969         121 :     poDS->GDALDataset::SetMetadataItem(
    2970         121 :         "COLORSPACE", ECWGetColorSpaceName(poDS->psFileInfo->eColorSpace));
    2971             : #if ECWSDK_VERSION >= 50
    2972             :     if (!bIsJPEG2000)
    2973             :         poDS->GDALDataset::SetMetadataItem(
    2974             :             "VERSION",
    2975             :             CPLString().Printf("%d", poDS->psFileInfo->nFormatVersion));
    2976             : #if ECWSDK_VERSION >= 51
    2977             :     // output jp2 header info
    2978             :     if (bIsJPEG2000 && poDS->poFileView)
    2979             :     {
    2980             :         // comments
    2981             :         char *csComments = nullptr;
    2982             :         poDS->poFileView->GetParameter((char *)"JPC:DECOMPRESS:COMMENTS",
    2983             :                                        &csComments);
    2984             :         if (csComments)
    2985             :         {
    2986             :             std::string osComments(csComments);
    2987             : 
    2988             :             // Strip off boring Kakadu COM content
    2989             :             if (STARTS_WITH(osComments.c_str(), "Kakadu-"))
    2990             :             {
    2991             :                 const auto nEOLPos = osComments.find('\n');
    2992             :                 if (nEOLPos == std::string::npos)
    2993             :                     osComments.clear();
    2994             :                 osComments = osComments.substr(nEOLPos + 1);
    2995             :             }
    2996             :             if (STARTS_WITH(
    2997             :                     osComments.c_str(),
    2998             :                     "Kdu-Layer-Info: "
    2999             :                     "log_2{Delta-D(squared-error)/Delta-L(bytes)}, L(bytes)\n"))
    3000             :             {
    3001             :                 while (true)
    3002             :                 {
    3003             :                     const auto nEOLPos = osComments.find('\n');
    3004             :                     if (nEOLPos == std::string::npos)
    3005             :                     {
    3006             :                         osComments.clear();
    3007             :                         break;
    3008             :                     }
    3009             :                     osComments = osComments.substr(nEOLPos + 1);
    3010             :                     if (osComments.find(",  ") == std::string::npos)
    3011             :                         break;
    3012             :                 }
    3013             :             }
    3014             : 
    3015             :             // Strip off boring OpenJPEG COM content
    3016             :             if (STARTS_WITH(osComments.c_str(),
    3017             :                             "Created by OpenJPEG version ") &&
    3018             :                 osComments.find('\n') == std::string::npos)
    3019             :             {
    3020             :                 osComments.clear();
    3021             :             }
    3022             : 
    3023             :             if (!osComments.empty())
    3024             :                 poDS->GDALDataset::SetMetadataItem("ALL_COMMENTS",
    3025             :                                                    osComments.c_str());
    3026             :             NCSFree(csComments);
    3027             :         }
    3028             : 
    3029             :         // Profile
    3030             :         UINT32 nProfile = 2;
    3031             :         UINT32 nRsiz = 0;
    3032             :         poDS->poFileView->GetParameter((char *)"JP2:COMPLIANCE:PROFILE:TYPE",
    3033             :                                        &nRsiz);
    3034             :         if (nRsiz == 0)
    3035             :             nProfile = 2;  // Profile 2 (no restrictions)
    3036             :         else if (nRsiz == 1)
    3037             :             nProfile = 0;  // Profile 0
    3038             :         else if (nRsiz == 2)
    3039             :             nProfile = 1;  // Profile 1, NITF_BIIF_NPJE, NITF_BIIF_EPJE
    3040             :         poDS->GDALDataset::SetMetadataItem("PROFILE",
    3041             :                                            CPLString().Printf("%d", nProfile),
    3042             :                                            JPEG2000_DOMAIN_NAME);
    3043             : 
    3044             :         // number of tiles on X axis
    3045             :         UINT32 nTileNrX = 1;
    3046             :         poDS->poFileView->GetParameter((char *)"JPC:DECOMPRESS:TILENR:X",
    3047             :                                        &nTileNrX);
    3048             :         poDS->GDALDataset::SetMetadataItem("TILES_X",
    3049             :                                            CPLString().Printf("%d", nTileNrX),
    3050             :                                            JPEG2000_DOMAIN_NAME);
    3051             : 
    3052             :         // number of tiles on X axis
    3053             :         UINT32 nTileNrY = 1;
    3054             :         poDS->poFileView->GetParameter((char *)"JPC:DECOMPRESS:TILENR:Y",
    3055             :                                        &nTileNrY);
    3056             :         poDS->GDALDataset::SetMetadataItem("TILES_Y",
    3057             :                                            CPLString().Printf("%d", nTileNrY),
    3058             :                                            JPEG2000_DOMAIN_NAME);
    3059             : 
    3060             :         // Tile Width
    3061             :         UINT32 nTileSizeX = 0;
    3062             :         poDS->poFileView->GetParameter((char *)"JPC:DECOMPRESS:TILESIZE:X",
    3063             :                                        &nTileSizeX);
    3064             :         poDS->GDALDataset::SetMetadataItem("TILE_WIDTH",
    3065             :                                            CPLString().Printf("%d", nTileSizeX),
    3066             :                                            JPEG2000_DOMAIN_NAME);
    3067             : 
    3068             :         // Tile Height
    3069             :         UINT32 nTileSizeY = 0;
    3070             :         poDS->poFileView->GetParameter((char *)"JPC:DECOMPRESS:TILESIZE:Y",
    3071             :                                        &nTileSizeY);
    3072             :         poDS->GDALDataset::SetMetadataItem("TILE_HEIGHT",
    3073             :                                            CPLString().Printf("%d", nTileSizeY),
    3074             :                                            JPEG2000_DOMAIN_NAME);
    3075             : 
    3076             :         // Precinct Sizes on X axis
    3077             :         char *csPreSizeX = nullptr;
    3078             :         poDS->poFileView->GetParameter((char *)"JPC:DECOMPRESS:PRECINCTSIZE:X",
    3079             :                                        &csPreSizeX);
    3080             :         if (csPreSizeX)
    3081             :         {
    3082             :             poDS->GDALDataset::SetMetadataItem("PRECINCT_SIZE_X", csPreSizeX,
    3083             :                                                JPEG2000_DOMAIN_NAME);
    3084             :             NCSFree(csPreSizeX);
    3085             :         }
    3086             : 
    3087             :         // Precinct Sizes on Y axis
    3088             :         char *csPreSizeY = nullptr;
    3089             :         poDS->poFileView->GetParameter((char *)"JPC:DECOMPRESS:PRECINCTSIZE:Y",
    3090             :                                        &csPreSizeY);
    3091             :         if (csPreSizeY)
    3092             :         {
    3093             :             poDS->GDALDataset::SetMetadataItem("PRECINCT_SIZE_Y", csPreSizeY,
    3094             :                                                JPEG2000_DOMAIN_NAME);
    3095             :             NCSFree(csPreSizeY);
    3096             :         }
    3097             : 
    3098             :         // Code Block Size on X axis
    3099             :         UINT32 nCodeBlockSizeX = 0;
    3100             :         poDS->poFileView->GetParameter((char *)"JPC:DECOMPRESS:CODEBLOCK:X",
    3101             :                                        &nCodeBlockSizeX);
    3102             :         poDS->GDALDataset::SetMetadataItem(
    3103             :             "CODE_BLOCK_SIZE_X", CPLString().Printf("%d", nCodeBlockSizeX),
    3104             :             JPEG2000_DOMAIN_NAME);
    3105             : 
    3106             :         // Code Block Size on Y axis
    3107             :         UINT32 nCodeBlockSizeY = 0;
    3108             :         poDS->poFileView->GetParameter((char *)"JPC:DECOMPRESS:CODEBLOCK:Y",
    3109             :                                        &nCodeBlockSizeY);
    3110             :         poDS->GDALDataset::SetMetadataItem(
    3111             :             "CODE_BLOCK_SIZE_Y", CPLString().Printf("%d", nCodeBlockSizeY),
    3112             :             JPEG2000_DOMAIN_NAME);
    3113             : 
    3114             :         // Bitdepth
    3115             :         char *csBitdepth = nullptr;
    3116             :         poDS->poFileView->GetParameter((char *)"JPC:DECOMPRESS:BITDEPTH",
    3117             :                                        &csBitdepth);
    3118             :         if (csBitdepth)
    3119             :         {
    3120             :             poDS->GDALDataset::SetMetadataItem("PRECISION", csBitdepth,
    3121             :                                                JPEG2000_DOMAIN_NAME);
    3122             :             NCSFree(csBitdepth);
    3123             :         }
    3124             : 
    3125             :         // Resolution Levels
    3126             :         UINT32 nLevels = 0;
    3127             :         poDS->poFileView->GetParameter(
    3128             :             (char *)"JPC:DECOMPRESS:RESOLUTION:LEVELS", &nLevels);
    3129             :         poDS->GDALDataset::SetMetadataItem("RESOLUTION_LEVELS",
    3130             :                                            CPLString().Printf("%d", nLevels),
    3131             :                                            JPEG2000_DOMAIN_NAME);
    3132             : 
    3133             :         // Qualaity Layers
    3134             :         UINT32 nLayers = 0;
    3135             :         poDS->poFileView->GetParameter((char *)"JP2:DECOMPRESS:LAYERS",
    3136             :                                        &nLayers);
    3137             :         poDS->GDALDataset::SetMetadataItem("QUALITY_LAYERS",
    3138             :                                            CPLString().Printf("%d", nLayers),
    3139             :                                            JPEG2000_DOMAIN_NAME);
    3140             : 
    3141             :         // Progression Order
    3142             :         char *csOrder = nullptr;
    3143             :         poDS->poFileView->GetParameter(
    3144             :             (char *)"JPC:DECOMPRESS:PROGRESSION:ORDER", &csOrder);
    3145             :         if (csOrder)
    3146             :         {
    3147             :             poDS->GDALDataset::SetMetadataItem("PROGRESSION_ORDER", csOrder,
    3148             :                                                JPEG2000_DOMAIN_NAME);
    3149             :             NCSFree(csOrder);
    3150             :         }
    3151             : 
    3152             :         // DWT Filter
    3153             :         const char *csFilter = nullptr;
    3154             :         UINT32 nFilter;
    3155             :         poDS->poFileView->GetParameter((char *)"JP2:TRANSFORMATION:TYPE",
    3156             :                                        &nFilter);
    3157             :         if (nFilter)
    3158             :             csFilter = "5x3";
    3159             :         else
    3160             :             csFilter = "9x7";
    3161             :         poDS->GDALDataset::SetMetadataItem("TRANSFORMATION_TYPE", csFilter,
    3162             :                                            JPEG2000_DOMAIN_NAME);
    3163             : 
    3164             :         // SOP used?
    3165             :         bool bSOP = 0;
    3166             :         poDS->poFileView->GetParameter((char *)"JP2:DECOMPRESS:SOP:EXISTS",
    3167             :                                        &bSOP);
    3168             :         poDS->SetMetadataItem("USE_SOP", (bSOP) ? "TRUE" : "FALSE",
    3169             :                               JPEG2000_DOMAIN_NAME);
    3170             : 
    3171             :         // EPH used?
    3172             :         bool bEPH = 0;
    3173             :         poDS->poFileView->GetParameter((char *)"JP2:DECOMPRESS:EPH:EXISTS",
    3174             :                                        &bEPH);
    3175             :         poDS->SetMetadataItem("USE_EPH", (bEPH) ? "TRUE" : "FALSE",
    3176             :                               JPEG2000_DOMAIN_NAME);
    3177             : 
    3178             :         // GML JP2 data contained?
    3179             :         bool bGML = 0;
    3180             :         poDS->poFileView->GetParameter((char *)"JP2:GML:JP2:BOX:EXISTS", &bGML);
    3181             :         poDS->SetMetadataItem("GML_JP2_DATA", (bGML) ? "TRUE" : "FALSE",
    3182             :                               JPEG2000_DOMAIN_NAME);
    3183             :     }
    3184             : #endif  // ECWSDK_VERSION>=51
    3185             :     if (!bIsJPEG2000 && poDS->psFileInfo->nFormatVersion >= 3)
    3186             :     {
    3187             :         poDS->GDALDataset::SetMetadataItem(
    3188             :             "COMPRESSION_RATE_ACTUAL",
    3189             :             CPLString().Printf("%f", poDS->psFileInfo->fActualCompressionRate));
    3190             :         poDS->GDALDataset::SetMetadataItem(
    3191             :             "CLOCKWISE_ROTATION_DEG",
    3192             :             CPLString().Printf("%f", poDS->psFileInfo->fCWRotationDegrees));
    3193             :         poDS->GDALDataset::SetMetadataItem("COMPRESSION_DATE",
    3194             :                                            poDS->psFileInfo->sCompressionDate);
    3195             :         // Get file metadata.
    3196             :         poDS->ReadFileMetaDataFromFile();
    3197             :     }
    3198             : #else
    3199         121 :     poDS->GDALDataset::SetMetadataItem(
    3200         242 :         "VERSION", CPLString().Printf("%d", bIsJPEG2000 ? 1 : 2));
    3201             : #endif
    3202             : 
    3203             :     /* -------------------------------------------------------------------- */
    3204             :     /*      Initialize any PAM information.                                 */
    3205             :     /* -------------------------------------------------------------------- */
    3206         121 :     poDS->SetDescription(osFilename);
    3207         121 :     poDS->TryLoadXML(poOpenInfo->GetSiblingFiles());
    3208             : 
    3209             :     /* -------------------------------------------------------------------- */
    3210             :     /*      Vector layers                                                   */
    3211             :     /* -------------------------------------------------------------------- */
    3212         121 :     if (bIsJPEG2000 && poOpenInfo->nOpenFlags & GDAL_OF_VECTOR)
    3213             :     {
    3214           1 :         poDS->LoadVectorLayers(CPLFetchBool(poOpenInfo->papszOpenOptions,
    3215             :                                             "OPEN_REMOTE_GML", false));
    3216             : 
    3217             :         // If file opened in vector-only mode and there's no vector,
    3218             :         // return
    3219           1 :         if ((poOpenInfo->nOpenFlags & GDAL_OF_RASTER) == 0 &&
    3220           0 :             poDS->GetLayerCount() == 0)
    3221             :         {
    3222           0 :             delete poDS;
    3223           0 :             return nullptr;
    3224             :         }
    3225             :     }
    3226             : 
    3227         121 :     return poDS;
    3228             : }
    3229             : 
    3230             : /************************************************************************/
    3231             : /*                      GetMetadataDomainList()                         */
    3232             : /************************************************************************/
    3233             : 
    3234           4 : char **ECWDataset::GetMetadataDomainList()
    3235             : {
    3236           4 :     return BuildMetadataDomainList(
    3237             :         GDALJP2AbstractDataset::GetMetadataDomainList(), TRUE, "ECW", "GML",
    3238           4 :         nullptr);
    3239             : }
    3240             : 
    3241             : /************************************************************************/
    3242             : /*                           GetMetadataItem()                          */
    3243             : /************************************************************************/
    3244             : 
    3245          55 : const char *ECWDataset::GetMetadataItem(const char *pszName,
    3246             :                                         const char *pszDomain)
    3247             : {
    3248          55 :     if (!bIsJPEG2000 && pszDomain != nullptr && EQUAL(pszDomain, "ECW") &&
    3249             :         pszName != nullptr)
    3250             :     {
    3251           6 :         if (EQUAL(pszName, "PROJ"))
    3252           2 :             return m_osProjCode.size() ? m_osProjCode.c_str() : "RAW";
    3253           4 :         if (EQUAL(pszName, "DATUM"))
    3254           2 :             return m_osDatumCode.size() ? m_osDatumCode.c_str() : "RAW";
    3255           2 :         if (EQUAL(pszName, "UNITS"))
    3256           2 :             return m_osUnitsCode.size() ? m_osUnitsCode.c_str() : "METERS";
    3257             :     }
    3258          49 :     return GDALJP2AbstractDataset::GetMetadataItem(pszName, pszDomain);
    3259             : }
    3260             : 
    3261             : /************************************************************************/
    3262             : /*                            GetMetadata()                             */
    3263             : /************************************************************************/
    3264             : 
    3265          90 : char **ECWDataset::GetMetadata(const char *pszDomain)
    3266             : 
    3267             : {
    3268          90 :     if (!bIsJPEG2000 && pszDomain != nullptr && EQUAL(pszDomain, "ECW"))
    3269             :     {
    3270           0 :         oECWMetadataList.Clear();
    3271             :         oECWMetadataList.AddString(
    3272           0 :             CPLSPrintf("%s=%s", "PROJ", GetMetadataItem("PROJ", "ECW")));
    3273             :         oECWMetadataList.AddString(
    3274           0 :             CPLSPrintf("%s=%s", "DATUM", GetMetadataItem("DATUM", "ECW")));
    3275             :         oECWMetadataList.AddString(
    3276           0 :             CPLSPrintf("%s=%s", "UNITS", GetMetadataItem("UNITS", "ECW")));
    3277           0 :         return oECWMetadataList.List();
    3278             :     }
    3279          90 :     else if (pszDomain == nullptr || !EQUAL(pszDomain, "GML"))
    3280          86 :         return GDALJP2AbstractDataset::GetMetadata(pszDomain);
    3281             :     else
    3282           4 :         return papszGMLMetadata;
    3283             : }
    3284             : 
    3285             : /************************************************************************/
    3286             : /*                   ReadFileMetaDataFromFile()                         */
    3287             : /*                                                                      */
    3288             : /* Gets relevant information from NCSFileMetadata and populates         */
    3289             : /* GDAL metadata.                                                       */
    3290             : /*                                                                      */
    3291             : /************************************************************************/
    3292             : #if ECWSDK_VERSION >= 50
    3293             : void ECWDataset::ReadFileMetaDataFromFile()
    3294             : {
    3295             :     if (psFileInfo->pFileMetaData == nullptr)
    3296             :         return;
    3297             : 
    3298             :     if (psFileInfo->pFileMetaData->sClassification != nullptr)
    3299             :         GDALDataset::SetMetadataItem(
    3300             :             "FILE_METADATA_CLASSIFICATION",
    3301             :             NCS::CString(psFileInfo->pFileMetaData->sClassification));
    3302             :     if (psFileInfo->pFileMetaData->sAcquisitionDate != nullptr)
    3303             :         GDALDataset::SetMetadataItem(
    3304             :             "FILE_METADATA_ACQUISITION_DATE",
    3305             :             NCS::CString(psFileInfo->pFileMetaData->sAcquisitionDate));
    3306             :     if (psFileInfo->pFileMetaData->sAcquisitionSensorName != nullptr)
    3307             :         GDALDataset::SetMetadataItem(
    3308             :             "FILE_METADATA_ACQUISITION_SENSOR_NAME",
    3309             :             NCS::CString(psFileInfo->pFileMetaData->sAcquisitionSensorName));
    3310             :     if (psFileInfo->pFileMetaData->sCompressionSoftware != nullptr)
    3311             :         GDALDataset::SetMetadataItem(
    3312             :             "FILE_METADATA_COMPRESSION_SOFTWARE",
    3313             :             NCS::CString(psFileInfo->pFileMetaData->sCompressionSoftware));
    3314             :     if (psFileInfo->pFileMetaData->sAuthor != nullptr)
    3315             :         GDALDataset::SetMetadataItem(
    3316             :             "FILE_METADATA_AUTHOR",
    3317             :             NCS::CString(psFileInfo->pFileMetaData->sAuthor));
    3318             :     if (psFileInfo->pFileMetaData->sCopyright != nullptr)
    3319             :         GDALDataset::SetMetadataItem(
    3320             :             "FILE_METADATA_COPYRIGHT",
    3321             :             NCS::CString(psFileInfo->pFileMetaData->sCopyright));
    3322             :     if (psFileInfo->pFileMetaData->sCompany != nullptr)
    3323             :         GDALDataset::SetMetadataItem(
    3324             :             "FILE_METADATA_COMPANY",
    3325             :             NCS::CString(psFileInfo->pFileMetaData->sCompany));
    3326             :     if (psFileInfo->pFileMetaData->sEmail != nullptr)
    3327             :         GDALDataset::SetMetadataItem(
    3328             :             "FILE_METADATA_EMAIL",
    3329             :             NCS::CString(psFileInfo->pFileMetaData->sEmail));
    3330             :     if (psFileInfo->pFileMetaData->sAddress != nullptr)
    3331             :         GDALDataset::SetMetadataItem(
    3332             :             "FILE_METADATA_ADDRESS",
    3333             :             NCS::CString(psFileInfo->pFileMetaData->sAddress));
    3334             :     if (psFileInfo->pFileMetaData->sTelephone != nullptr)
    3335             :         GDALDataset::SetMetadataItem(
    3336             :             "FILE_METADATA_TELEPHONE",
    3337             :             NCS::CString(psFileInfo->pFileMetaData->sTelephone));
    3338             : }
    3339             : 
    3340             : /************************************************************************/
    3341             : /*                       WriteFileMetaData()                            */
    3342             : /************************************************************************/
    3343             : 
    3344             : void ECWDataset::WriteFileMetaData(NCSFileMetaData *pFileMetaDataCopy)
    3345             : {
    3346             :     if (!bFileMetaDataDirty)
    3347             :         return;
    3348             : 
    3349             :     CPLAssert(eAccess == GA_Update);
    3350             :     CPLAssert(!bIsJPEG2000);
    3351             : 
    3352             :     bFileMetaDataDirty = FALSE;
    3353             : 
    3354             :     NCSFileView *psFileView = nullptr;
    3355             :     NCSError eErr;
    3356             : 
    3357             :     psFileView = NCSEditOpen(GetDescription());
    3358             :     if (psFileView == nullptr)
    3359             :     {
    3360             :         CPLError(CE_Failure, CPLE_AppDefined, "NCSEditOpen() failed");
    3361             :         return;
    3362             :     }
    3363             : 
    3364             :     eErr = NCSEditSetFileMetaData(psFileView, pFileMetaDataCopy);
    3365             :     if (eErr != NCS_SUCCESS)
    3366             :     {
    3367             :         CPLError(CE_Failure, CPLE_AppDefined,
    3368             :                  "NCSEditSetFileMetaData() failed : %s",
    3369             :                  NCSGetLastErrorText(eErr));
    3370             :     }
    3371             : 
    3372             :     NCSEditFlushAll(psFileView);
    3373             :     NCSEditClose(psFileView);
    3374             : }
    3375             : 
    3376             : #endif
    3377             : /************************************************************************/
    3378             : /*                         ECW2WKTProjection()                          */
    3379             : /*                                                                      */
    3380             : /*      Set the dataset pszProjection string in OGC WKT format by       */
    3381             : /*      looking up the ECW (GDT) coordinate system info in              */
    3382             : /*      ecw_cs.wkt support data file.                                   */
    3383             : /*                                                                      */
    3384             : /*      This code is likely still broken in some circumstances.  For    */
    3385             : /*      instance, I haven't been careful about changing the linear      */
    3386             : /*      projection parameters (false easting/northing) if the units     */
    3387             : /*      is feet.  Lots of cases missing here, and in ecw_cs.wkt.        */
    3388             : /************************************************************************/
    3389             : 
    3390          32 : void ECWDataset::ECW2WKTProjection()
    3391             : 
    3392             : {
    3393          32 :     if (psFileInfo == nullptr)
    3394          20 :         return;
    3395             : 
    3396             :     /* -------------------------------------------------------------------- */
    3397             :     /*      Capture Geotransform.                                           */
    3398             :     /*                                                                      */
    3399             :     /*      We will try to ignore the provided file information if it is    */
    3400             :     /*      origin (0,0) and pixel size (1,1).  I think sometimes I have    */
    3401             :     /*      also seen pixel increments of 0 on invalid datasets.            */
    3402             :     /* -------------------------------------------------------------------- */
    3403          32 :     if (psFileInfo->fOriginX != 0.0 || psFileInfo->fOriginY != 0.0 ||
    3404           0 :         (psFileInfo->fCellIncrementX != 0.0 &&
    3405           0 :          psFileInfo->fCellIncrementX != 1.0) ||
    3406           0 :         (psFileInfo->fCellIncrementY != 0.0 &&
    3407           0 :          psFileInfo->fCellIncrementY != 1.0))
    3408             :     {
    3409          32 :         bGeoTransformValid = TRUE;
    3410             : 
    3411          32 :         adfGeoTransform[0] = psFileInfo->fOriginX;
    3412          32 :         adfGeoTransform[1] = psFileInfo->fCellIncrementX;
    3413          32 :         adfGeoTransform[2] = 0.0;
    3414             : 
    3415          32 :         adfGeoTransform[3] = psFileInfo->fOriginY;
    3416          32 :         adfGeoTransform[4] = 0.0;
    3417             : 
    3418             :         /* By default, set Y-resolution negative assuming images always */
    3419             :         /* have "Upward" orientation (Y coordinates increase "Upward"). */
    3420             :         /* Setting ECW_ALWAYS_UPWARD=FALSE option relexes that policy   */
    3421             :         /* and makes the driver rely on the actual Y-resolution         */
    3422             :         /* value (sign) of an image. This allows correctly processing   */
    3423             :         /* rare images with "Downward" orientation, where Y coordinates */
    3424             :         /* increase "Downward" and Y-resolution is positive.            */
    3425          32 :         if (CPLTestBool(CPLGetConfigOption("ECW_ALWAYS_UPWARD", "TRUE")))
    3426          31 :             adfGeoTransform[5] = -fabs(psFileInfo->fCellIncrementY);
    3427             :         else
    3428           1 :             adfGeoTransform[5] = psFileInfo->fCellIncrementY;
    3429             :     }
    3430             : 
    3431             :     /* -------------------------------------------------------------------- */
    3432             :     /*      do we have projection and datum?                                */
    3433             :     /* -------------------------------------------------------------------- */
    3434             :     CPLString osUnits =
    3435          32 :         ECWTranslateFromCellSizeUnits(psFileInfo->eCellSizeUnits);
    3436             : 
    3437          32 :     CPLDebug("ECW", "projection=%s, datum=%s, units=%s",
    3438          32 :              psFileInfo->szProjection, psFileInfo->szDatum, osUnits.c_str());
    3439             : 
    3440          32 :     if (EQUAL(psFileInfo->szProjection, "RAW"))
    3441          20 :         return;
    3442             : 
    3443             :     /* -------------------------------------------------------------------- */
    3444             :     /*      Set projection if we have it.                                   */
    3445             :     /* -------------------------------------------------------------------- */
    3446          24 :     OGRSpatialReference oSRS;
    3447             : 
    3448             :     /* For backward-compatible with previous behavior. Should we only */
    3449             :     /* restrict to those 2 values ? */
    3450          12 :     if (psFileInfo->eCellSizeUnits != ECW_CELL_UNITS_METERS &&
    3451           2 :         psFileInfo->eCellSizeUnits != ECW_CELL_UNITS_FEET)
    3452           0 :         osUnits = ECWTranslateFromCellSizeUnits(ECW_CELL_UNITS_METERS);
    3453             : 
    3454          12 :     m_osDatumCode = psFileInfo->szDatum;
    3455          12 :     m_osProjCode = psFileInfo->szProjection;
    3456          12 :     m_osUnitsCode = osUnits;
    3457          12 :     if (oSRS.importFromERM(psFileInfo->szProjection, psFileInfo->szDatum,
    3458          12 :                            osUnits) == OGRERR_NONE)
    3459             :     {
    3460          12 :         m_oSRS = std::move(oSRS);
    3461          12 :         m_oSRS.SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
    3462             :     }
    3463             : 
    3464          12 :     CPLErrorReset(); /* see #4187 */
    3465             : }
    3466             : 
    3467             : /************************************************************************/
    3468             : /*                        ECWTranslateFromWKT()                         */
    3469             : /************************************************************************/
    3470             : 
    3471          25 : int ECWTranslateFromWKT(const OGRSpatialReference *poSRS, char *pszProjection,
    3472             :                         int nProjectionLen, char *pszDatum, int nDatumLen,
    3473             :                         char *pszUnits)
    3474             : 
    3475             : {
    3476          50 :     OGRSpatialReference oSRS;
    3477             : 
    3478          25 :     strcpy(pszProjection, "RAW");
    3479          25 :     strcpy(pszDatum, "RAW");
    3480          25 :     strcpy(pszUnits, "METERS");
    3481             : 
    3482          25 :     if (poSRS == nullptr || poSRS->IsEmpty())
    3483           0 :         return FALSE;
    3484             : 
    3485          25 :     oSRS = *poSRS;
    3486             : 
    3487          25 :     if (oSRS.IsLocal())
    3488           0 :         return TRUE;
    3489             : 
    3490             :     /* -------------------------------------------------------------------- */
    3491             :     /*      Do we have an overall EPSG number for this coordinate system?   */
    3492             :     /* -------------------------------------------------------------------- */
    3493          25 :     const char *pszAuthorityCode = nullptr;
    3494          25 :     const char *pszAuthorityName = nullptr;
    3495          25 :     UINT32 nEPSGCode = 0;
    3496             : 
    3497          25 :     if (oSRS.IsProjected())
    3498             :     {
    3499           8 :         pszAuthorityCode = oSRS.GetAuthorityCode("PROJCS");
    3500           8 :         pszAuthorityName = oSRS.GetAuthorityName("PROJCS");
    3501             :     }
    3502          17 :     else if (oSRS.IsGeographic())
    3503             :     {
    3504          17 :         pszAuthorityCode = oSRS.GetAuthorityCode("GEOGCS");
    3505          17 :         pszAuthorityName = oSRS.GetAuthorityName("GEOGCS");
    3506             :     }
    3507             : 
    3508          25 :     if (pszAuthorityName != nullptr && EQUAL(pszAuthorityName, "EPSG") &&
    3509           9 :         pszAuthorityCode != nullptr && atoi(pszAuthorityCode) > 0)
    3510           9 :         nEPSGCode = (UINT32)atoi(pszAuthorityCode);
    3511             : 
    3512          25 :     if (nEPSGCode != 0)
    3513             :     {
    3514           9 :         char *pszEPSGProj = nullptr, *pszEPSGDatum = nullptr;
    3515             :         CNCSError oErr = CNCSJP2FileView::GetProjectionAndDatum(
    3516           9 :             atoi(pszAuthorityCode), &pszEPSGProj, &pszEPSGDatum);
    3517             : 
    3518          18 :         CPLDebug("ECW", "GetGDTProjDat(%d) = %s/%s", atoi(pszAuthorityCode),
    3519           9 :                  pszEPSGProj ? pszEPSGProj : "(null)",
    3520           9 :                  pszEPSGDatum ? pszEPSGDatum : "(null)");
    3521             : 
    3522          18 :         if (oErr.GetErrorNumber() == NCS_SUCCESS && pszEPSGProj != nullptr &&
    3523           9 :             pszEPSGDatum != nullptr)
    3524             :         {
    3525           9 :             strncpy(pszProjection, pszEPSGProj, nProjectionLen);
    3526           9 :             strncpy(pszDatum, pszEPSGDatum, nDatumLen);
    3527           9 :             pszProjection[nProjectionLen - 1] = 0;
    3528           9 :             pszDatum[nDatumLen - 1] = 0;
    3529           9 :             NCSFree(pszEPSGProj);
    3530           9 :             NCSFree(pszEPSGDatum);
    3531           9 :             return TRUE;
    3532             :         }
    3533             : 
    3534           0 :         NCSFree(pszEPSGProj);
    3535           0 :         NCSFree(pszEPSGDatum);
    3536             :     }
    3537             : 
    3538             :     /* -------------------------------------------------------------------- */
    3539             :     /*      Fallback to translating based on the ecw_cs.wkt file, and       */
    3540             :     /*      various jiffy rules.                                            */
    3541             :     /* -------------------------------------------------------------------- */
    3542             : 
    3543          16 :     return oSRS.exportToERM(pszProjection, pszDatum, pszUnits) == OGRERR_NONE;
    3544             : }
    3545             : 
    3546             : /************************************************************************/
    3547             : /*                    ECWTranslateToCellSizeUnits()                     */
    3548             : /************************************************************************/
    3549             : 
    3550          26 : CellSizeUnits ECWTranslateToCellSizeUnits(const char *pszUnits)
    3551             : {
    3552          26 :     if (EQUAL(pszUnits, "METERS"))
    3553          24 :         return ECW_CELL_UNITS_METERS;
    3554           2 :     else if (EQUAL(pszUnits, "DEGREES"))
    3555           0 :         return ECW_CELL_UNITS_DEGREES;
    3556           2 :     else if (EQUAL(pszUnits, "FEET"))
    3557           2 :         return ECW_CELL_UNITS_FEET;
    3558           0 :     else if (EQUAL(pszUnits, "UNKNOWN"))
    3559           0 :         return ECW_CELL_UNITS_UNKNOWN;
    3560           0 :     else if (EQUAL(pszUnits, "INVALID"))
    3561           0 :         return ECW_CELL_UNITS_INVALID;
    3562             :     else
    3563             :     {
    3564           0 :         CPLError(CE_Warning, CPLE_AppDefined,
    3565             :                  "Unrecognized value for UNITS : %s", pszUnits);
    3566           0 :         return ECW_CELL_UNITS_INVALID;
    3567             :     }
    3568             : }
    3569             : 
    3570             : /************************************************************************/
    3571             : /*                   ECWGetColorInterpretationByName()                  */
    3572             : /************************************************************************/
    3573             : 
    3574         228 : GDALColorInterp ECWGetColorInterpretationByName(const char *pszName)
    3575             : {
    3576         228 :     if (EQUAL(pszName, NCS_BANDDESC_AllOpacity))
    3577           7 :         return GCI_AlphaBand;
    3578         221 :     else if (EQUAL(pszName, NCS_BANDDESC_Blue))
    3579          51 :         return GCI_BlueBand;
    3580         170 :     else if (EQUAL(pszName, NCS_BANDDESC_Green))
    3581          51 :         return GCI_GreenBand;
    3582         119 :     else if (EQUAL(pszName, NCS_BANDDESC_Red))
    3583          51 :         return GCI_RedBand;
    3584          68 :     else if (EQUAL(pszName, NCS_BANDDESC_Greyscale))
    3585           0 :         return GCI_GrayIndex;
    3586          68 :     else if (EQUAL(pszName, NCS_BANDDESC_GreyscaleOpacity))
    3587           0 :         return GCI_AlphaBand;
    3588          68 :     return GCI_Undefined;
    3589             : }
    3590             : 
    3591             : /************************************************************************/
    3592             : /*                    ECWGetColorInterpretationName()                   */
    3593             : /************************************************************************/
    3594             : 
    3595          58 : const char *ECWGetColorInterpretationName(GDALColorInterp eColorInterpretation,
    3596             :                                           int nBandNumber)
    3597             : {
    3598          58 :     const char *pszResult = nullptr;
    3599          58 :     switch (eColorInterpretation)
    3600             :     {
    3601           0 :         case GCI_AlphaBand:
    3602           0 :             pszResult = NCS_BANDDESC_AllOpacity;
    3603           0 :             break;
    3604          16 :         case GCI_GrayIndex:
    3605          16 :             pszResult = NCS_BANDDESC_Greyscale;
    3606          16 :             break;
    3607          12 :         case GCI_RedBand:
    3608             :         case GCI_GreenBand:
    3609             :         case GCI_BlueBand:
    3610          12 :             pszResult = GDALGetColorInterpretationName(eColorInterpretation);
    3611          12 :             break;
    3612          30 :         case GCI_Undefined:
    3613          30 :             if (nBandNumber == 0)
    3614             :             {
    3615          20 :                 pszResult = "Red";
    3616             :             }
    3617          10 :             else if (nBandNumber == 1)
    3618             :             {
    3619           4 :                 pszResult = "Green";
    3620             :             }
    3621           6 :             else if (nBandNumber == 2)
    3622             :             {
    3623           3 :                 pszResult = "Blue";
    3624             :             }
    3625             :             else
    3626             :             {
    3627           3 :                 pszResult = CPLSPrintf(NCS_BANDDESC_Band, nBandNumber + 1);
    3628             :             }
    3629          30 :             break;
    3630           0 :         default:
    3631           0 :             pszResult = CPLSPrintf(NCS_BANDDESC_Band, nBandNumber + 1);
    3632           0 :             break;
    3633             :     }
    3634          58 :     return pszResult;
    3635             : }
    3636             : 
    3637             : /************************************************************************/
    3638             : /*                         ECWGetColorSpaceName()                       */
    3639             : /************************************************************************/
    3640             : 
    3641         121 : const char *ECWGetColorSpaceName(NCSFileColorSpace colorSpace)
    3642             : {
    3643         121 :     switch (colorSpace)
    3644             :     {
    3645           0 :         case NCSCS_NONE:
    3646           0 :             return "NONE";
    3647             :             break;
    3648          52 :         case NCSCS_GREYSCALE:
    3649          52 :             return "GREYSCALE";
    3650             :             break;
    3651           0 :         case NCSCS_YUV:
    3652           0 :             return "YUV";
    3653             :             break;
    3654          36 :         case NCSCS_MULTIBAND:
    3655          36 :             return "MULTIBAND";
    3656             :             break;
    3657          33 :         case NCSCS_sRGB:
    3658          33 :             return "RGB";
    3659             :             break;
    3660           0 :         case NCSCS_YCbCr:
    3661           0 :             return "YCbCr";
    3662             :             break;
    3663           0 :         default:
    3664           0 :             return "unrecognized";
    3665             :     }
    3666             : }
    3667             : 
    3668             : /************************************************************************/
    3669             : /*                     ECWTranslateFromCellSizeUnits()                  */
    3670             : /************************************************************************/
    3671             : 
    3672          71 : const char *ECWTranslateFromCellSizeUnits(CellSizeUnits eUnits)
    3673             : {
    3674          71 :     if (eUnits == ECW_CELL_UNITS_METERS)
    3675          67 :         return "METERS";
    3676           4 :     else if (eUnits == ECW_CELL_UNITS_DEGREES)
    3677           0 :         return "DEGREES";
    3678           4 :     else if (eUnits == ECW_CELL_UNITS_FEET)
    3679           4 :         return "FEET";
    3680           0 :     else if (eUnits == ECW_CELL_UNITS_UNKNOWN)
    3681           0 :         return "UNKNOWN";
    3682             :     else
    3683           0 :         return "INVALID";
    3684             : }
    3685             : 
    3686             : /************************************************************************/
    3687             : /*                           ECWInitialize()                            */
    3688             : /*                                                                      */
    3689             : /*      Initialize NCS library.  We try to defer this as late as        */
    3690             : /*      possible since de-initializing it seems to be expensive/slow    */
    3691             : /*      on some system.                                                 */
    3692             : /************************************************************************/
    3693             : 
    3694         190 : void ECWInitialize()
    3695             : 
    3696             : {
    3697         190 :     CPLMutexHolder oHolder(&hECWDatasetMutex);
    3698             : 
    3699         190 :     if (bNCSInitialized)
    3700         184 :         return;
    3701             : 
    3702             : #ifndef _WIN32
    3703           6 :     NCSecwInit();
    3704             : #endif
    3705           6 :     bNCSInitialized = TRUE;
    3706             : 
    3707             :     /* -------------------------------------------------------------------- */
    3708             :     /*      This will disable automatic conversion of YCbCr to RGB by       */
    3709             :     /*      the toolkit.                                                    */
    3710             :     /* -------------------------------------------------------------------- */
    3711           6 :     if (!CPLTestBool(CPLGetConfigOption("CONVERT_YCBCR_TO_RGB", "YES")))
    3712           0 :         NCSecwSetConfig(NCSCFG_JP2_MANAGE_ICC, FALSE);
    3713             : #if ECWSDK_VERSION >= 50
    3714             :     NCSecwSetConfig(NCSCFG_ECWP_CLIENT_HTTP_USER_AGENT,
    3715             :                     "ECW GDAL Driver/" NCS_ECWJP2_FULL_VERSION_STRING_DOT_DEL);
    3716             : #endif
    3717             :     /* -------------------------------------------------------------------- */
    3718             :     /*      Initialize cache memory limit.  Default is apparently 1/4 RAM.  */
    3719             :     /* -------------------------------------------------------------------- */
    3720             :     const char *pszEcwCacheSize =
    3721           6 :         CPLGetConfigOption("GDAL_ECW_CACHE_MAXMEM", nullptr);
    3722           6 :     if (pszEcwCacheSize == nullptr)
    3723           6 :         pszEcwCacheSize = CPLGetConfigOption("ECW_CACHE_MAXMEM", nullptr);
    3724             : 
    3725           6 :     if (pszEcwCacheSize != nullptr)
    3726           0 :         NCSecwSetConfig(NCSCFG_CACHE_MAXMEM, (UINT32)atoi(pszEcwCacheSize));
    3727             : 
    3728             : /* -------------------------------------------------------------------- */
    3729             : /*      Version 3.x and 4.x of the ECWJP2 SDK did not resolve datum and */
    3730             : /*      projection to EPSG code using internal mapping.                 */
    3731             : /*      Version 5.x do so we provide means to achieve old               */
    3732             : /*      behavior.                                                      */
    3733             : /* -------------------------------------------------------------------- */
    3734             : #if ECWSDK_VERSION >= 50
    3735             :     if (CPLTestBool(CPLGetConfigOption("ECW_DO_NOT_RESOLVE_DATUM_PROJECTION",
    3736             :                                        "NO")) == TRUE)
    3737             :         NCSecwSetConfig(NCSCFG_PROJECTION_FORMAT,
    3738             :                         NCS_PROJECTION_ERMAPPER_FORMAT);
    3739             : #endif
    3740             :     /* -------------------------------------------------------------------- */
    3741             :     /*      Allow configuration of a local cache based on configuration     */
    3742             :     /*      options.  Setting the location turns things on.                 */
    3743             :     /* -------------------------------------------------------------------- */
    3744           6 :     const char *pszOpt = nullptr;
    3745           6 :     CPL_IGNORE_RET_VAL(pszOpt);
    3746             : 
    3747             : #if ECWSDK_VERSION >= 40
    3748             :     pszOpt = CPLGetConfigOption("ECWP_CACHE_SIZE_MB", nullptr);
    3749             :     if (pszOpt)
    3750             :         NCSecwSetConfig(NCSCFG_ECWP_CACHE_SIZE_MB, (INT32)atoi(pszOpt));
    3751             : 
    3752             :     pszOpt = CPLGetConfigOption("ECWP_CACHE_LOCATION", nullptr);
    3753             :     if (pszOpt)
    3754             :     {
    3755             :         NCSecwSetConfig(NCSCFG_ECWP_CACHE_LOCATION, pszOpt);
    3756             :         NCSecwSetConfig(NCSCFG_ECWP_CACHE_ENABLED, (BOOLEAN)TRUE);
    3757             :     }
    3758             : #endif
    3759             : 
    3760             :     /* -------------------------------------------------------------------- */
    3761             :     /*      Various other configuration items.                              */
    3762             :     /* -------------------------------------------------------------------- */
    3763           6 :     pszOpt = CPLGetConfigOption("ECWP_BLOCKING_TIME_MS", nullptr);
    3764           6 :     if (pszOpt)
    3765           0 :         NCSecwSetConfig(NCSCFG_BLOCKING_TIME_MS, (NCSTimeStampMs)atoi(pszOpt));
    3766             : 
    3767             :     // I believe 10s means we wait for complete data back from
    3768             :     // ECWP almost all the time which is good for our blocking model.
    3769           6 :     pszOpt = CPLGetConfigOption("ECWP_REFRESH_TIME_MS", "10000");
    3770           6 :     if (pszOpt)
    3771           6 :         NCSecwSetConfig(NCSCFG_REFRESH_TIME_MS, (NCSTimeStampMs)atoi(pszOpt));
    3772             : 
    3773           6 :     pszOpt = CPLGetConfigOption("ECW_TEXTURE_DITHER", nullptr);
    3774           6 :     if (pszOpt)
    3775           0 :         NCSecwSetConfig(NCSCFG_TEXTURE_DITHER, (BOOLEAN)CPLTestBool(pszOpt));
    3776             : 
    3777           6 :     pszOpt = CPLGetConfigOption("ECW_FORCE_FILE_REOPEN", nullptr);
    3778           6 :     if (pszOpt)
    3779           0 :         NCSecwSetConfig(NCSCFG_FORCE_FILE_REOPEN, (BOOLEAN)CPLTestBool(pszOpt));
    3780             : 
    3781           6 :     pszOpt = CPLGetConfigOption("ECW_CACHE_MAXOPEN", nullptr);
    3782           6 :     if (pszOpt)
    3783           0 :         NCSecwSetConfig(NCSCFG_CACHE_MAXOPEN, (UINT32)atoi(pszOpt));
    3784             : 
    3785             : #if ECWSDK_VERSION >= 40
    3786             :     pszOpt = CPLGetConfigOption("ECW_AUTOGEN_J2I", nullptr);
    3787             :     if (pszOpt)
    3788             :         NCSecwSetConfig(NCSCFG_JP2_AUTOGEN_J2I, (BOOLEAN)CPLTestBool(pszOpt));
    3789             : 
    3790             :     pszOpt = CPLGetConfigOption("ECW_OPTIMIZE_USE_NEAREST_NEIGHBOUR", nullptr);
    3791             :     if (pszOpt)
    3792             :         NCSecwSetConfig(NCSCFG_OPTIMIZE_USE_NEAREST_NEIGHBOUR,
    3793             :                         (BOOLEAN)CPLTestBool(pszOpt));
    3794             : 
    3795             :     pszOpt = CPLGetConfigOption("ECW_RESILIENT_DECODING", nullptr);
    3796             :     if (pszOpt)
    3797             :         NCSecwSetConfig(NCSCFG_RESILIENT_DECODING,
    3798             :                         (BOOLEAN)CPLTestBool(pszOpt));
    3799             : #endif
    3800             : }
    3801             : 
    3802             : /************************************************************************/
    3803             : /*                         GDALDeregister_ECW()                         */
    3804             : /************************************************************************/
    3805             : 
    3806           9 : static void GDALDeregister_ECW(GDALDriver *)
    3807             : 
    3808             : {
    3809             :     /* For unknown reason, this cleanup can take up to 3 seconds (see #3134) for
    3810             :      * SDK 3.3. */
    3811             :     /* Not worth it */
    3812             : #if ECWSDK_VERSION >= 50
    3813             : #ifndef _WIN32
    3814             :     if (bNCSInitialized)
    3815             :     {
    3816             :         bNCSInitialized = FALSE;
    3817             :         NCSecwShutdown();
    3818             :     }
    3819             : #endif
    3820             : #endif
    3821             : 
    3822           9 :     if (hECWDatasetMutex != nullptr)
    3823             :     {
    3824           4 :         CPLDestroyMutex(hECWDatasetMutex);
    3825           4 :         hECWDatasetMutex = nullptr;
    3826             :     }
    3827           9 : }
    3828             : 
    3829             : #if ECWSDK_VERSION < 40
    3830             : namespace
    3831             : {
    3832          78 : NCSError NCS_CALL EcwFileOpenForReadACB(char *szFileName, void **ppClientData)
    3833             : {
    3834          78 :     *ppClientData = VSIFOpenL(szFileName, "rb");
    3835          78 :     if (*ppClientData == nullptr)
    3836             :     {
    3837          15 :         return NCS_FILE_OPEN_FAILED;
    3838             :     }
    3839             :     else
    3840             :     {
    3841          63 :         return NCS_SUCCESS;
    3842             :     }
    3843             : }
    3844             : 
    3845           0 : NCSError NCS_CALL EcwFileOpenForReadWCB(wchar_t *wszFileName,
    3846             :                                         void **ppClientData)
    3847             : {
    3848             :     char *szFileName =
    3849           0 :         CPLRecodeFromWChar(wszFileName, CPL_ENC_UCS2, CPL_ENC_UTF8);
    3850           0 :     *ppClientData = VSIFOpenL(szFileName, "rb");
    3851           0 :     CPLFree(szFileName);
    3852           0 :     if (*ppClientData == nullptr)
    3853             :     {
    3854           0 :         return NCS_FILE_OPEN_FAILED;
    3855             :     }
    3856             :     else
    3857             :     {
    3858           0 :         return NCS_SUCCESS;
    3859             :     }
    3860             : }
    3861             : 
    3862          63 : NCSError NCS_CALL EcwFileCloseCB(void *pClientData)
    3863             : {
    3864          63 :     if (0 == VSIFCloseL(reinterpret_cast<VSILFILE *>(pClientData)))
    3865             :     {
    3866          63 :         return NCS_SUCCESS;
    3867             :     }
    3868             :     else
    3869             :     {
    3870           0 :         return NCS_FILE_CLOSE_ERROR;
    3871             :     }
    3872             : }
    3873             : 
    3874        1449 : NCSError NCS_CALL EcwFileReadCB(void *pClientData, void *pBuffer,
    3875             :                                 UINT32 nLength)
    3876             : {
    3877        1449 :     if (nLength == VSIFReadL(pBuffer, 1, nLength,
    3878             :                              reinterpret_cast<VSILFILE *>(pClientData)))
    3879             :     {
    3880        1449 :         return NCS_SUCCESS;
    3881             :     }
    3882             :     else
    3883             :     {
    3884           0 :         return NCS_FILE_IO_ERROR;
    3885             :     }
    3886             : }
    3887             : 
    3888         196 : NCSError NCS_CALL EcwFileSeekCB(void *pClientData, UINT64 nOffset)
    3889             : {
    3890         196 :     if (0 ==
    3891         196 :         VSIFSeekL(reinterpret_cast<VSILFILE *>(pClientData), nOffset, SEEK_SET))
    3892             :     {
    3893         196 :         return NCS_SUCCESS;
    3894             :     }
    3895             :     else
    3896             :     {
    3897           0 :         return NCS_FILE_SEEK_ERROR;
    3898             :     }
    3899             : }
    3900             : 
    3901          96 : NCSError NCS_CALL EcwFileTellCB(void *pClientData, UINT64 *pOffset)
    3902             : {
    3903          96 :     *pOffset = VSIFTellL(reinterpret_cast<VSILFILE *>(pClientData));
    3904          96 :     return NCS_SUCCESS;
    3905             : }
    3906             : }  // namespace
    3907             : #endif  // ECWSDK_VERSION < 40
    3908             : 
    3909             : /************************************************************************/
    3910             : /*                          GDALRegister_ECW()                          */
    3911             : /************************************************************************/
    3912             : 
    3913          13 : void GDALRegister_ECW()
    3914             : 
    3915             : {
    3916          13 :     if (!GDAL_CHECK_VERSION("ECW driver"))
    3917           0 :         return;
    3918             : 
    3919          13 :     if (GDALGetDriverByName(ECW_DRIVER_NAME) != nullptr)
    3920           0 :         return;
    3921             : #if ECWSDK_VERSION < 40
    3922          13 :     CNCSJPCFileIOStream::SetIOCallbacks(
    3923             :         EcwFileOpenForReadACB, EcwFileOpenForReadWCB, EcwFileCloseCB,
    3924             :         EcwFileReadCB, EcwFileSeekCB, EcwFileTellCB);
    3925             : #endif  // ECWSDK_VERSION < 40
    3926          13 :     GDALDriver *poDriver = new GDALDriver();
    3927             : 
    3928          13 :     ECWDriverSetCommonMetadata(poDriver);
    3929          13 :     poDriver->pfnOpen = ECWDataset::OpenECW;
    3930          13 :     poDriver->pfnUnloadDriver = GDALDeregister_ECW;
    3931             : #ifdef HAVE_COMPRESS
    3932             :     // The create method does not work with SDK 3.3 ( crash in
    3933             :     // CNCSJP2FileView::WriteLineBIL() due to m_pFile being nullptr ).
    3934             : #if ECWSDK_VERSION >= 50
    3935             :     poDriver->pfnCreate = ECWCreateECW;
    3936             : #endif
    3937          13 :     poDriver->pfnCreateCopy = ECWCreateCopyECW;
    3938             : #endif
    3939             : 
    3940          13 :     GetGDALDriverManager()->RegisterDriver(poDriver);
    3941             : }
    3942             : 
    3943             : /************************************************************************/
    3944             : /*                      GDALRegister_ECW_JP2ECW()                       */
    3945             : /*                                                                      */
    3946             : /*      This function exists so that when built as a plugin, there      */
    3947             : /*      is a function that will register both drivers.                  */
    3948             : /************************************************************************/
    3949             : 
    3950          13 : void GDALRegister_ECW_JP2ECW()
    3951             : 
    3952             : {
    3953          13 :     GDALRegister_ECW();
    3954          13 :     GDALRegister_JP2ECW();
    3955          13 : }
    3956             : 
    3957             : /************************************************************************/
    3958             : /*                     ECWDatasetOpenJPEG2000()                         */
    3959             : /************************************************************************/
    3960          29 : GDALDataset *ECWDatasetOpenJPEG2000(GDALOpenInfo *poOpenInfo)
    3961             : {
    3962          29 :     return ECWDataset::OpenJPEG2000(poOpenInfo);
    3963             : }
    3964             : 
    3965             : /************************************************************************/
    3966             : /*                        GDALRegister_JP2ECW()                         */
    3967             : /************************************************************************/
    3968          13 : void GDALRegister_JP2ECW()
    3969             : 
    3970             : {
    3971          13 :     if (!GDAL_CHECK_VERSION("JP2ECW driver"))
    3972           0 :         return;
    3973             : 
    3974          13 :     if (GDALGetDriverByName(JP2ECW_DRIVER_NAME) != nullptr)
    3975           0 :         return;
    3976             : 
    3977          13 :     GDALDriver *poDriver = new GDALDriver();
    3978          13 :     JP2ECWDriverSetCommonMetadata(poDriver);
    3979          13 :     poDriver->pfnOpen = ECWDataset::OpenJPEG2000;
    3980             : #ifdef HAVE_COMPRESS
    3981          13 :     poDriver->pfnCreate = ECWCreateJPEG2000;
    3982          13 :     poDriver->pfnCreateCopy = ECWCreateCopyJPEG2000;
    3983             : #endif
    3984             : 
    3985          13 :     GetGDALDriverManager()->RegisterDriver(poDriver);
    3986             : }

Generated by: LCOV version 1.14