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

Generated by: LCOV version 1.14