LCOV - code coverage report
Current view: top level - frmts/ecw - ecwdataset.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 991 1205 82.2 %
Date: 2026-02-12 23:49:34 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             :                                  CSLConstList 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_UInt8 &&
     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 :     const int nXOff = nBlockXOff * nBlockXSize;
    1061           1 :     const int nXSize = std::min(nBlockXSize, nRasterXSize - nXOff);
    1062           1 :     const int nYOff = nBlockYOff * nBlockYSize;
    1063           1 :     const int nYSize = std::min(nBlockYSize, nRasterYSize - nYOff);
    1064             : 
    1065           1 :     const GSpacing nPixelSpace = GDALGetDataTypeSizeBytes(eDataType);
    1066           1 :     const GSpacing nLineSpace = nPixelSpace * nBlockXSize;
    1067             : 
    1068             :     GDALRasterIOExtraArg sExtraArg;
    1069           1 :     INIT_RASTERIO_EXTRA_ARG(sExtraArg);
    1070             : 
    1071           1 :     return IRasterIO(GF_Read, nXOff, nYOff, nXSize, nYSize, pImage, nXSize,
    1072           2 :                      nYSize, eDataType, nPixelSpace, nLineSpace, &sExtraArg);
    1073             : }
    1074             : 
    1075             : /************************************************************************/
    1076             : /* ==================================================================== */
    1077             : /*                            ECWDataset                               */
    1078             : /* ==================================================================== */
    1079             : /************************************************************************/
    1080             : 
    1081             : /************************************************************************/
    1082             : /*                             ECWDataset()                             */
    1083             : /************************************************************************/
    1084             : 
    1085         123 : ECWDataset::ECWDataset(int bIsJPEG2000In)
    1086             : 
    1087             : {
    1088         123 :     this->bIsJPEG2000 = bIsJPEG2000In;
    1089         123 :     bUsingCustomStream = FALSE;
    1090         123 :     poFileView = nullptr;
    1091         123 :     bWinActive = FALSE;
    1092         123 :     panWinBandList = nullptr;
    1093         123 :     eRasterDataType = GDT_UInt8;
    1094         123 :     papszGMLMetadata = nullptr;
    1095             : 
    1096         123 :     bHdrDirty = FALSE;
    1097         123 :     bGeoTransformChanged = FALSE;
    1098         123 :     bProjectionChanged = FALSE;
    1099         123 :     bProjCodeChanged = FALSE;
    1100         123 :     bDatumCodeChanged = FALSE;
    1101         123 :     bUnitsCodeChanged = FALSE;
    1102             : 
    1103         123 :     bUseOldBandRasterIOImplementation = FALSE;
    1104             : #if ECWSDK_VERSION >= 50
    1105             : 
    1106             :     pStatistics = nullptr;
    1107             :     bStatisticsDirty = FALSE;
    1108             :     bStatisticsInitialized = FALSE;
    1109             :     bFileMetaDataDirty = FALSE;
    1110             : 
    1111             : #endif
    1112             : 
    1113         123 :     sCachedMultiBandIO.bEnabled = FALSE;
    1114         123 :     sCachedMultiBandIO.nBandsTried = 0;
    1115         123 :     sCachedMultiBandIO.nXOff = 0;
    1116         123 :     sCachedMultiBandIO.nYOff = 0;
    1117         123 :     sCachedMultiBandIO.nXSize = 0;
    1118         123 :     sCachedMultiBandIO.nYSize = 0;
    1119         123 :     sCachedMultiBandIO.nBufXSize = 0;
    1120         123 :     sCachedMultiBandIO.nBufYSize = 0;
    1121         123 :     sCachedMultiBandIO.eBufType = GDT_Unknown;
    1122         123 :     sCachedMultiBandIO.pabyData = nullptr;
    1123             : 
    1124         123 :     bPreventCopyingSomeMetadata = FALSE;
    1125             : 
    1126         123 :     nBandIndexToPromoteTo8Bit = -1;
    1127             : 
    1128         123 :     poDriver =
    1129         123 :         (GDALDriver *)GDALGetDriverByName(bIsJPEG2000 ? "JP2ECW" : "ECW");
    1130             : 
    1131         123 :     psFileInfo = nullptr;
    1132         123 :     eNCSRequestDataType = NCSCT_UINT8;
    1133         123 :     nWinXOff = 0;
    1134         123 :     nWinYOff = 0;
    1135         123 :     nWinXSize = 0;
    1136         123 :     nWinYSize = 0;
    1137         123 :     nWinBufXSize = 0;
    1138         123 :     nWinBufYSize = 0;
    1139         123 :     nWinBandCount = 0;
    1140         123 :     nWinBufLoaded = FALSE;
    1141         123 :     papCurLineBuf = nullptr;
    1142             : 
    1143         123 :     m_nAdviseReadXOff = -1;
    1144         123 :     m_nAdviseReadYOff = -1;
    1145         123 :     m_nAdviseReadXSize = -1;
    1146         123 :     m_nAdviseReadYSize = -1;
    1147         123 :     m_nAdviseReadBufXSize = -1;
    1148         123 :     m_nAdviseReadBufYSize = -1;
    1149         123 :     m_nAdviseReadBandCount = -1;
    1150         123 :     m_panAdviseReadBandList = nullptr;
    1151         123 : }
    1152             : 
    1153             : /************************************************************************/
    1154             : /*                            ~ECWDataset()                             */
    1155             : /************************************************************************/
    1156             : 
    1157         246 : ECWDataset::~ECWDataset()
    1158             : 
    1159             : {
    1160         123 :     GDALPamDataset::FlushCache(true);
    1161         123 :     CleanupWindow();
    1162             : 
    1163             : #if ECWSDK_VERSION >= 50
    1164             :     NCSFileMetaData *pFileMetaDataCopy = nullptr;
    1165             :     if (bFileMetaDataDirty)
    1166             :     {
    1167             :         NCSCopyMetaData(&pFileMetaDataCopy, psFileInfo->pFileMetaData);
    1168             :     }
    1169             : #endif
    1170             : 
    1171             :     /* -------------------------------------------------------------------- */
    1172             :     /*      Release / dereference iostream.                                 */
    1173             :     /* -------------------------------------------------------------------- */
    1174             :     // The underlying iostream of the CNCSJP2FileView (poFileView) object may
    1175             :     // also be the underlying iostream of other CNCSJP2FileView (poFileView)
    1176             :     // objects.  Consequently, when we delete the CNCSJP2FileView (poFileView)
    1177             :     // object, we must decrement the nFileViewCount attribute of the underlying
    1178             :     // VSIIOStream object, and only delete the VSIIOStream object when
    1179             :     // nFileViewCount is equal to zero.
    1180             : 
    1181         246 :     CPLMutexHolder oHolder(&hECWDatasetMutex);
    1182             : 
    1183         123 :     if (poFileView != nullptr)
    1184             :     {
    1185             : #if ECWSDK_VERSION >= 55
    1186             :         delete poFileView;
    1187             : #else
    1188         123 :         VSIIOStream *poUnderlyingIOStream = (VSIIOStream *)nullptr;
    1189             : 
    1190         123 :         if (bUsingCustomStream)
    1191             :         {
    1192          52 :             poUnderlyingIOStream = ((VSIIOStream *)(poFileView->GetStream()));
    1193             :         }
    1194         123 :         delete poFileView;
    1195             : 
    1196         123 :         if (bUsingCustomStream)
    1197             :         {
    1198          52 :             if (--poUnderlyingIOStream->nFileViewCount == 0)
    1199          52 :                 delete poUnderlyingIOStream;
    1200             :         }
    1201             : #endif
    1202         123 :         poFileView = nullptr;
    1203             :     }
    1204             : 
    1205             :     /* WriteHeader() must be called after closing the file handle to work */
    1206             :     /* on Windows */
    1207         123 :     if (bHdrDirty)
    1208           4 :         WriteHeader();
    1209             : #if ECWSDK_VERSION >= 50
    1210             :     if (bStatisticsDirty)
    1211             :     {
    1212             :         StatisticsWrite();
    1213             :     }
    1214             :     CleanupStatistics();
    1215             : 
    1216             :     if (bFileMetaDataDirty)
    1217             :     {
    1218             :         WriteFileMetaData(pFileMetaDataCopy);
    1219             :         NCSFreeMetaData(pFileMetaDataCopy);
    1220             :     }
    1221             : #endif
    1222             : 
    1223         123 :     CSLDestroy(papszGMLMetadata);
    1224             : 
    1225         123 :     CPLFree(sCachedMultiBandIO.pabyData);
    1226             : 
    1227         123 :     CPLFree(m_panAdviseReadBandList);
    1228         246 : }
    1229             : 
    1230             : #if ECWSDK_VERSION >= 50
    1231             : 
    1232             : /************************************************************************/
    1233             : /*                    StatisticsEnsureInitialized()                     */
    1234             : /************************************************************************/
    1235             : 
    1236             : NCS::CError ECWDataset::StatisticsEnsureInitialized()
    1237             : {
    1238             :     if (bStatisticsInitialized == TRUE)
    1239             :     {
    1240             :         return NCS_SUCCESS;
    1241             :     }
    1242             : 
    1243             :     NCS::CError error = poFileView->GetClientStatistics(&pStatistics);
    1244             :     if (error.Success())
    1245             :     {
    1246             :         bStatisticsInitialized = TRUE;
    1247             :     }
    1248             :     return error;
    1249             : }
    1250             : 
    1251             : /************************************************************************/
    1252             : /*                          StatisticsWrite()                           */
    1253             : /************************************************************************/
    1254             : 
    1255             : NCS::CError ECWDataset::StatisticsWrite()
    1256             : {
    1257             :     CPLDebug("ECW", "In StatisticsWrite()");
    1258             :     NCSFileView *view = NCSEcwEditOpen(GetDescription());
    1259             :     NCS::CError error;
    1260             :     if (view != nullptr)
    1261             :     {
    1262             :         error = NCSEcwEditSetStatistics(view, pStatistics);
    1263             :         if (error.Success())
    1264             :         {
    1265             :             error = NCSEcwEditFlushAll(view);
    1266             :             if (error.Success())
    1267             :             {
    1268             :                 error = NCSEcwEditClose(view);
    1269             :             }
    1270             :         }
    1271             :     }
    1272             : 
    1273             :     bStatisticsDirty = FALSE;
    1274             : 
    1275             :     return error;
    1276             : }
    1277             : 
    1278             : /************************************************************************/
    1279             : /*                         CleanupStatistics()                          */
    1280             : /************************************************************************/
    1281             : 
    1282             : void ECWDataset::CleanupStatistics()
    1283             : {
    1284             :     if (bStatisticsInitialized == TRUE && pStatistics != nullptr)
    1285             :     {
    1286             :         NCSEcwFreeStatistics(pStatistics);
    1287             :     }
    1288             : }
    1289             : 
    1290             : #endif  // #if ECWSDK_VERSION>=50
    1291             : 
    1292             : /************************************************************************/
    1293             : /*                          SetGeoTransform()                           */
    1294             : /************************************************************************/
    1295             : 
    1296           7 : CPLErr ECWDataset::SetGeoTransform(const GDALGeoTransform &gt)
    1297             : {
    1298           7 :     if (bIsJPEG2000 || eAccess == GA_ReadOnly)
    1299           5 :         return GDALPamDataset::SetGeoTransform(gt);
    1300             : 
    1301           2 :     if (!bGeoTransformValid || gt != m_gt)
    1302             :     {
    1303           2 :         m_gt = gt;
    1304           2 :         bGeoTransformValid = TRUE;
    1305           2 :         bHdrDirty = TRUE;
    1306           2 :         bGeoTransformChanged = TRUE;
    1307             :     }
    1308             : 
    1309           2 :     return CE_None;
    1310             : }
    1311             : 
    1312             : /************************************************************************/
    1313             : /*                           SetSpatialRef()                            */
    1314             : /************************************************************************/
    1315             : 
    1316           7 : CPLErr ECWDataset::SetSpatialRef(const OGRSpatialReference *poSRS)
    1317             : {
    1318           7 :     if (bIsJPEG2000 || eAccess == GA_ReadOnly)
    1319           5 :         return GDALPamDataset::SetSpatialRef(poSRS);
    1320             : 
    1321           4 :     if (!((m_oSRS.IsEmpty() && poSRS == nullptr) ||
    1322           2 :           (!m_oSRS.IsEmpty() && poSRS != nullptr && m_oSRS.IsSame(poSRS))))
    1323             :     {
    1324           2 :         m_oSRS.Clear();
    1325           2 :         if (poSRS)
    1326           2 :             m_oSRS = *poSRS;
    1327             : 
    1328           2 :         bHdrDirty = TRUE;
    1329           2 :         bProjectionChanged = TRUE;
    1330             :     }
    1331             : 
    1332           2 :     return CE_None;
    1333             : }
    1334             : 
    1335             : /************************************************************************/
    1336             : /*                          SetMetadataItem()                           */
    1337             : /************************************************************************/
    1338             : 
    1339           4 : CPLErr ECWDataset::SetMetadataItem(const char *pszName, const char *pszValue,
    1340             :                                    const char *pszDomain)
    1341             : {
    1342           4 :     if (!bIsJPEG2000 && eAccess == GA_Update &&
    1343           3 :         (pszDomain == nullptr || EQUAL(pszDomain, "") ||
    1344           3 :          (pszDomain != nullptr && EQUAL(pszDomain, "ECW"))) &&
    1345           3 :         pszName != nullptr &&
    1346           3 :         (strcmp(pszName, "PROJ") == 0 || strcmp(pszName, "DATUM") == 0 ||
    1347           1 :          strcmp(pszName, "UNITS") == 0))
    1348             :     {
    1349           3 :         CPLString osNewVal = pszValue ? pszValue : "";
    1350           3 :         if (osNewVal.size() > 31)
    1351           0 :             osNewVal.resize(31);
    1352           3 :         if (strcmp(pszName, "PROJ") == 0)
    1353             :         {
    1354           1 :             bProjCodeChanged = (osNewVal != m_osProjCode);
    1355           1 :             m_osProjCode = std::move(osNewVal);
    1356           1 :             bHdrDirty |= bProjCodeChanged;
    1357             :         }
    1358           2 :         else if (strcmp(pszName, "DATUM") == 0)
    1359             :         {
    1360           1 :             bDatumCodeChanged |= (osNewVal != m_osDatumCode) ? TRUE : FALSE;
    1361           1 :             m_osDatumCode = std::move(osNewVal);
    1362           1 :             bHdrDirty |= bDatumCodeChanged;
    1363             :         }
    1364             :         else
    1365             :         {
    1366           1 :             bUnitsCodeChanged |= (osNewVal != m_osUnitsCode) ? TRUE : FALSE;
    1367           1 :             m_osUnitsCode = std::move(osNewVal);
    1368           1 :             bHdrDirty |= bUnitsCodeChanged;
    1369             :         }
    1370           3 :         return CE_None;
    1371             :     }
    1372             : #if ECWSDK_VERSION >= 50
    1373             :     else if (psFileInfo != nullptr && psFileInfo->nFormatVersion >= 3 &&
    1374             :              eAccess == GA_Update &&
    1375             :              (pszDomain == nullptr || EQUAL(pszDomain, "")) &&
    1376             :              pszName != nullptr && STARTS_WITH(pszName, "FILE_METADATA_"))
    1377             :     {
    1378             :         bFileMetaDataDirty = TRUE;
    1379             : 
    1380             :         if (psFileInfo->pFileMetaData == nullptr)
    1381             :             NCSInitMetaData(&(psFileInfo->pFileMetaData));
    1382             : 
    1383             :         if (strcmp(pszName, "FILE_METADATA_CLASSIFICATION") == 0)
    1384             :         {
    1385             :             NCSFree(psFileInfo->pFileMetaData->sClassification);
    1386             :             psFileInfo->pFileMetaData->sClassification =
    1387             :                 pszValue ? NCSStrDupT(NCS::CString(pszValue).c_str()) : nullptr;
    1388             :             return GDALDataset::SetMetadataItem(pszName, pszValue, pszDomain);
    1389             :         }
    1390             :         else if (strcmp(pszName, "FILE_METADATA_ACQUISITION_DATE") == 0)
    1391             :         {
    1392             :             NCSFree(psFileInfo->pFileMetaData->sAcquisitionDate);
    1393             :             psFileInfo->pFileMetaData->sAcquisitionDate =
    1394             :                 pszValue ? NCSStrDupT(NCS::CString(pszValue).c_str()) : nullptr;
    1395             :             return GDALDataset::SetMetadataItem(pszName, pszValue, pszDomain);
    1396             :         }
    1397             :         else if (strcmp(pszName, "FILE_METADATA_ACQUISITION_SENSOR_NAME") == 0)
    1398             :         {
    1399             :             NCSFree(psFileInfo->pFileMetaData->sAcquisitionSensorName);
    1400             :             psFileInfo->pFileMetaData->sAcquisitionSensorName =
    1401             :                 pszValue ? NCSStrDupT(NCS::CString(pszValue).c_str()) : nullptr;
    1402             :             return GDALDataset::SetMetadataItem(pszName, pszValue, pszDomain);
    1403             :         }
    1404             :         else if (strcmp(pszName, "FILE_METADATA_COMPRESSION_SOFTWARE") == 0)
    1405             :         {
    1406             :             NCSFree(psFileInfo->pFileMetaData->sCompressionSoftware);
    1407             :             psFileInfo->pFileMetaData->sCompressionSoftware =
    1408             :                 pszValue ? NCSStrDupT(NCS::CString(pszValue).c_str()) : nullptr;
    1409             :             return GDALDataset::SetMetadataItem(pszName, pszValue, pszDomain);
    1410             :         }
    1411             :         else if (strcmp(pszName, "FILE_METADATA_AUTHOR") == 0)
    1412             :         {
    1413             :             NCSFree(psFileInfo->pFileMetaData->sAuthor);
    1414             :             psFileInfo->pFileMetaData->sAuthor =
    1415             :                 pszValue ? NCSStrDupT(NCS::CString(pszValue).c_str()) : nullptr;
    1416             :             return GDALDataset::SetMetadataItem(pszName, pszValue, pszDomain);
    1417             :         }
    1418             :         else if (strcmp(pszName, "FILE_METADATA_COPYRIGHT") == 0)
    1419             :         {
    1420             :             NCSFree(psFileInfo->pFileMetaData->sCopyright);
    1421             :             psFileInfo->pFileMetaData->sCopyright =
    1422             :                 pszValue ? NCSStrDupT(NCS::CString(pszValue).c_str()) : nullptr;
    1423             :             return GDALDataset::SetMetadataItem(pszName, pszValue, pszDomain);
    1424             :         }
    1425             :         else if (strcmp(pszName, "FILE_METADATA_COMPANY") == 0)
    1426             :         {
    1427             :             NCSFree(psFileInfo->pFileMetaData->sCompany);
    1428             :             psFileInfo->pFileMetaData->sCompany =
    1429             :                 pszValue ? NCSStrDupT(NCS::CString(pszValue).c_str()) : nullptr;
    1430             :             return GDALDataset::SetMetadataItem(pszName, pszValue, pszDomain);
    1431             :         }
    1432             :         else if (strcmp(pszName, "FILE_METADATA_EMAIL") == 0)
    1433             :         {
    1434             :             NCSFree(psFileInfo->pFileMetaData->sEmail);
    1435             :             psFileInfo->pFileMetaData->sEmail =
    1436             :                 pszValue ? NCSStrDupT(NCS::CString(pszValue).c_str()) : nullptr;
    1437             :             return GDALDataset::SetMetadataItem(pszName, pszValue, pszDomain);
    1438             :         }
    1439             :         else if (strcmp(pszName, "FILE_METADATA_ADDRESS") == 0)
    1440             :         {
    1441             :             NCSFree(psFileInfo->pFileMetaData->sAddress);
    1442             :             psFileInfo->pFileMetaData->sAddress =
    1443             :                 pszValue ? NCSStrDupT(NCS::CString(pszValue).c_str()) : nullptr;
    1444             :             return GDALDataset::SetMetadataItem(pszName, pszValue, pszDomain);
    1445             :         }
    1446             :         else if (strcmp(pszName, "FILE_METADATA_TELEPHONE") == 0)
    1447             :         {
    1448             :             NCSFree(psFileInfo->pFileMetaData->sTelephone);
    1449             :             psFileInfo->pFileMetaData->sTelephone =
    1450             :                 pszValue ? NCSStrDupT(NCS::CString(pszValue).c_str()) : nullptr;
    1451             :             return GDALDataset::SetMetadataItem(pszName, pszValue, pszDomain);
    1452             :         }
    1453             :         else
    1454             :         {
    1455             :             return GDALPamDataset::SetMetadataItem(pszName, pszValue,
    1456             :                                                    pszDomain);
    1457             :         }
    1458             :     }
    1459             : #endif
    1460             :     else
    1461           1 :         return GDALPamDataset::SetMetadataItem(pszName, pszValue, pszDomain);
    1462             : }
    1463             : 
    1464             : /************************************************************************/
    1465             : /*                            SetMetadata()                             */
    1466             : /************************************************************************/
    1467             : 
    1468           3 : CPLErr ECWDataset::SetMetadata(CSLConstList papszMetadata,
    1469             :                                const char *pszDomain)
    1470             : {
    1471             :     /* The bPreventCopyingSomeMetadata is set by ECWCreateCopy() */
    1472             :     /* just before calling poDS->CloneInfo( poSrcDS, GCIF_PAM_DEFAULT ); */
    1473           3 :     if (bPreventCopyingSomeMetadata &&
    1474           1 :         (pszDomain == nullptr || EQUAL(pszDomain, "")))
    1475             :     {
    1476           1 :         char **papszMetadataDup = nullptr;
    1477           1 :         CSLConstList papszIter = papszMetadata;
    1478           2 :         while (*papszIter)
    1479             :         {
    1480           1 :             char *pszKey = nullptr;
    1481           1 :             CPLParseNameValue(*papszIter, &pszKey);
    1482             :             /* Remove a few metadata item from the source that we don't want in
    1483             :              */
    1484             :             /* the target metadata */
    1485           1 :             if (pszKey != nullptr &&
    1486           1 :                 (EQUAL(pszKey, "VERSION") ||
    1487           1 :                  EQUAL(pszKey, "COMPRESSION_RATE_TARGET") ||
    1488           1 :                  EQUAL(pszKey, "COMPRESSION_RATE_ACTUAL") ||
    1489           1 :                  EQUAL(pszKey, "CLOCKWISE_ROTATION_DEG") ||
    1490           1 :                  EQUAL(pszKey, "COLORSPACE") ||
    1491           1 :                  EQUAL(pszKey, "COMPRESSION_DATE") ||
    1492           1 :                  STARTS_WITH_CI(pszKey, "FILE_METADATA_")))
    1493             :             {
    1494             :                 /* do nothing */
    1495             :             }
    1496             :             else
    1497             :             {
    1498           1 :                 papszMetadataDup = CSLAddString(papszMetadataDup, *papszIter);
    1499             :             }
    1500           1 :             CPLFree(pszKey);
    1501           1 :             papszIter++;
    1502             :         }
    1503             : 
    1504           1 :         bPreventCopyingSomeMetadata = FALSE;
    1505           1 :         CPLErr eErr = SetMetadata(papszMetadataDup, pszDomain);
    1506           1 :         bPreventCopyingSomeMetadata = TRUE;
    1507           1 :         CSLDestroy(papszMetadataDup);
    1508           1 :         return eErr;
    1509             :     }
    1510             : 
    1511           2 :     if (((pszDomain == nullptr || EQUAL(pszDomain, "") ||
    1512           4 :           EQUAL(pszDomain, "ECW")) &&
    1513           2 :          (CSLFetchNameValue(papszMetadata, "PROJ") != nullptr ||
    1514           2 :           CSLFetchNameValue(papszMetadata, "DATUM") != nullptr ||
    1515           2 :           CSLFetchNameValue(papszMetadata, "UNITS") != nullptr))
    1516             : #if ECWSDK_VERSION >= 50
    1517             :         ||
    1518             :         (psFileInfo != nullptr && psFileInfo->nFormatVersion >= 3 &&
    1519             :          eAccess == GA_Update &&
    1520             :          (pszDomain == nullptr || EQUAL(pszDomain, "")) &&
    1521             :          (CSLFetchNameValue(papszMetadata, "FILE_METADATA_CLASSIFICATION") !=
    1522             :               nullptr ||
    1523             :           CSLFetchNameValue(papszMetadata, "FILE_METADATA_ACQUISITION_DATE") !=
    1524             :               nullptr ||
    1525             :           CSLFetchNameValue(papszMetadata,
    1526             :                             "FILE_METADATA_ACQUISITION_SENSOR_NAME") !=
    1527             :               nullptr ||
    1528             :           CSLFetchNameValue(papszMetadata,
    1529             :                             "FILE_METADATA_COMPRESSION_SOFTWARE") != nullptr ||
    1530             :           CSLFetchNameValue(papszMetadata, "FILE_METADATA_AUTHOR") != nullptr ||
    1531             :           CSLFetchNameValue(papszMetadata, "FILE_METADATA_COPYRIGHT") !=
    1532             :               nullptr ||
    1533             :           CSLFetchNameValue(papszMetadata, "FILE_METADATA_COMPANY") !=
    1534             :               nullptr ||
    1535             :           CSLFetchNameValue(papszMetadata, "FILE_METADATA_EMAIL") != nullptr ||
    1536             :           CSLFetchNameValue(papszMetadata, "FILE_METADATA_ADDRESS") !=
    1537             :               nullptr ||
    1538             :           CSLFetchNameValue(papszMetadata, "FILE_METADATA_TELEPHONE") !=
    1539             :               nullptr))
    1540             : #endif
    1541             :     )
    1542             :     {
    1543           0 :         CPLStringList osNewMetadata;
    1544           0 :         CSLConstList papszIter = papszMetadata;
    1545           0 :         while (papszIter && *papszIter)
    1546             :         {
    1547           0 :             if (STARTS_WITH(*papszIter, "PROJ=") ||
    1548           0 :                 STARTS_WITH(*papszIter, "DATUM=") ||
    1549           0 :                 STARTS_WITH(*papszIter, "UNITS=") ||
    1550           0 :                 (STARTS_WITH(*papszIter, "FILE_METADATA_") &&
    1551           0 :                  strchr(*papszIter, '=') != nullptr))
    1552             :             {
    1553           0 :                 char *pszKey = nullptr;
    1554           0 :                 const char *pszValue = CPLParseNameValue(*papszIter, &pszKey);
    1555           0 :                 SetMetadataItem(pszKey, pszValue, pszDomain);
    1556           0 :                 CPLFree(pszKey);
    1557             :             }
    1558             :             else
    1559           0 :                 osNewMetadata.AddString(*papszIter);
    1560           0 :             papszIter++;
    1561             :         }
    1562           0 :         if (!osNewMetadata.empty())
    1563           0 :             return GDALPamDataset::SetMetadata(osNewMetadata.List(), pszDomain);
    1564             :         else
    1565           0 :             return CE_None;
    1566             :     }
    1567             :     else
    1568           2 :         return GDALPamDataset::SetMetadata(papszMetadata, pszDomain);
    1569             : }
    1570             : 
    1571             : /************************************************************************/
    1572             : /*                            WriteHeader()                             */
    1573             : /************************************************************************/
    1574             : 
    1575           4 : void ECWDataset::WriteHeader()
    1576             : {
    1577           4 :     if (!bHdrDirty)
    1578           0 :         return;
    1579             : 
    1580           4 :     CPLAssert(eAccess == GA_Update);
    1581           4 :     CPLAssert(!bIsJPEG2000);
    1582             : 
    1583           4 :     bHdrDirty = FALSE;
    1584             : 
    1585           4 :     NCSEcwEditInfo *psEditInfo = nullptr;
    1586             :     NCSError eErr;
    1587             : 
    1588             :     /* Load original header info */
    1589             : #if ECWSDK_VERSION < 50
    1590           4 :     eErr = NCSEcwEditReadInfo((char *)GetDescription(), &psEditInfo);
    1591             : #else
    1592             :     eErr = NCSEcwEditReadInfo(
    1593             :         NCS::CString::Utf8Decode(GetDescription()).c_str(), &psEditInfo);
    1594             : #endif
    1595           4 :     if (eErr != NCS_SUCCESS)
    1596             :     {
    1597           0 :         CPLError(CE_Failure, CPLE_AppDefined, "NCSEcwEditReadInfo() failed");
    1598           0 :         return;
    1599             :     }
    1600             : 
    1601             :     /* To avoid potential cross-heap issues, we keep the original */
    1602             :     /* strings, and restore them before freeing the structure */
    1603           4 :     char *pszOriginalCode = psEditInfo->szDatum;
    1604           4 :     char *pszOriginalProj = psEditInfo->szProjection;
    1605             : 
    1606             :     /* Alter the structure with user modified information */
    1607             :     char szProjCode[32], szDatumCode[32], szUnits[32];
    1608           4 :     if (bProjectionChanged)
    1609             :     {
    1610           2 :         if (ECWTranslateFromWKT(&m_oSRS, szProjCode, sizeof(szProjCode),
    1611           2 :                                 szDatumCode, sizeof(szDatumCode), szUnits))
    1612             :         {
    1613           2 :             psEditInfo->szDatum = szDatumCode;
    1614           2 :             psEditInfo->szProjection = szProjCode;
    1615           2 :             psEditInfo->eCellSizeUnits = ECWTranslateToCellSizeUnits(szUnits);
    1616           2 :             CPLDebug("ECW", "Rewrite DATUM : %s", psEditInfo->szDatum);
    1617           2 :             CPLDebug("ECW", "Rewrite PROJ : %s", psEditInfo->szProjection);
    1618           2 :             CPLDebug("ECW", "Rewrite UNITS : %s",
    1619             :                      ECWTranslateFromCellSizeUnits(psEditInfo->eCellSizeUnits));
    1620             :         }
    1621             :     }
    1622             : 
    1623           4 :     if (bDatumCodeChanged)
    1624             :     {
    1625           1 :         psEditInfo->szDatum =
    1626           1 :             (char *)((m_osDatumCode.size()) ? m_osDatumCode.c_str() : "RAW");
    1627           1 :         CPLDebug("ECW", "Rewrite DATUM : %s", psEditInfo->szDatum);
    1628             :     }
    1629           4 :     if (bProjCodeChanged)
    1630             :     {
    1631           1 :         psEditInfo->szProjection =
    1632           1 :             (char *)((m_osProjCode.size()) ? m_osProjCode.c_str() : "RAW");
    1633           1 :         CPLDebug("ECW", "Rewrite PROJ : %s", psEditInfo->szProjection);
    1634             :     }
    1635           4 :     if (bUnitsCodeChanged)
    1636             :     {
    1637           2 :         psEditInfo->eCellSizeUnits =
    1638           1 :             ECWTranslateToCellSizeUnits(m_osUnitsCode.c_str());
    1639           1 :         CPLDebug("ECW", "Rewrite UNITS : %s",
    1640             :                  ECWTranslateFromCellSizeUnits(psEditInfo->eCellSizeUnits));
    1641             :     }
    1642             : 
    1643           4 :     if (bGeoTransformChanged)
    1644             :     {
    1645           2 :         psEditInfo->fOriginX = m_gt.xorig;
    1646           2 :         psEditInfo->fCellIncrementX = m_gt.xscale;
    1647           2 :         psEditInfo->fOriginY = m_gt.yorig;
    1648           2 :         psEditInfo->fCellIncrementY = m_gt.yscale;
    1649           2 :         CPLDebug("ECW", "Rewrite Geotransform");
    1650             :     }
    1651             : 
    1652             :     /* Write modified header info */
    1653             : #if ECWSDK_VERSION < 50
    1654           4 :     eErr = NCSEcwEditWriteInfo((char *)GetDescription(), psEditInfo, nullptr,
    1655             :                                nullptr, nullptr);
    1656             : #else
    1657             :     eErr =
    1658             :         NCSEcwEditWriteInfo(NCS::CString::Utf8Decode(GetDescription()).c_str(),
    1659             :                             psEditInfo, nullptr, nullptr, nullptr);
    1660             : #endif
    1661           4 :     if (eErr != NCS_SUCCESS)
    1662             :     {
    1663           0 :         CPLError(CE_Failure, CPLE_AppDefined, "NCSEcwEditWriteInfo() failed");
    1664             :     }
    1665             : 
    1666             :     /* Restore original pointers before free'ing */
    1667           4 :     psEditInfo->szDatum = pszOriginalCode;
    1668           4 :     psEditInfo->szProjection = pszOriginalProj;
    1669             : 
    1670           4 :     NCSEcwEditFreeInfo(psEditInfo);
    1671             : }
    1672             : 
    1673             : /************************************************************************/
    1674             : /*                             AdviseRead()                             */
    1675             : /************************************************************************/
    1676             : 
    1677         249 : CPLErr ECWDataset::AdviseRead(int nXOff, int nYOff, int nXSize, int nYSize,
    1678             :                               int nBufXSize, int nBufYSize,
    1679             :                               CPL_UNUSED GDALDataType eDT, int nBandCount,
    1680             :                               int *panBandList,
    1681             :                               CPL_UNUSED CSLConstList papszOptions)
    1682             : {
    1683         249 :     CPLDebug("ECW", "ECWDataset::AdviseRead(%d,%d,%d,%d->%d,%d)", nXOff, nYOff,
    1684             :              nXSize, nYSize, nBufXSize, nBufYSize);
    1685             : 
    1686             : #if !defined(SDK_CAN_DO_SUPERSAMPLING)
    1687         249 :     if (nBufXSize > nXSize || nBufYSize > nYSize)
    1688             :     {
    1689           0 :         CPLError(CE_Warning, CPLE_AppDefined,
    1690             :                  "Supersampling not directly supported by ECW toolkit,\n"
    1691             :                  "ignoring AdviseRead() request.");
    1692           0 :         return CE_Warning;
    1693             :     }
    1694             : #endif
    1695             : 
    1696             :     /* -------------------------------------------------------------------- */
    1697             :     /*      Do some validation of parameters.                               */
    1698             :     /* -------------------------------------------------------------------- */
    1699             : 
    1700             :     CPLErr eErr;
    1701         249 :     int bStopProcessing = FALSE;
    1702         249 :     eErr = ValidateRasterIOOrAdviseReadParameters(
    1703             :         "AdviseRead()", &bStopProcessing, nXOff, nYOff, nXSize, nYSize,
    1704             :         nBufXSize, nBufYSize, nBandCount, panBandList);
    1705         249 :     if (eErr != CE_None || bStopProcessing)
    1706           0 :         return eErr;
    1707             : 
    1708         249 :     if (nBandCount > 100)
    1709             :     {
    1710           0 :         ReportError(CE_Failure, CPLE_IllegalArg,
    1711             :                     "AdviseRead(): Too many bands : %d", nBandCount);
    1712           0 :         return CE_Failure;
    1713             :     }
    1714             : 
    1715         249 :     if (nBufXSize != nXSize || nBufYSize != nYSize)
    1716             :     {
    1717             :         // This early exit is because experimentally we found that
    1718             :         // performance of requesting at 50% is much slower with
    1719             :         // AdviseRead()...
    1720             :         // At least on JPEG2000 images with SDK 3.3
    1721         200 :         CPLDebug("ECW",
    1722             :                  "Ignoring AdviseRead() for non full resolution request");
    1723         200 :         return CE_None;
    1724             :     }
    1725             : 
    1726             :     // We don't setup the reading window right away, in case the actual read
    1727             :     // pattern wouldn't be compatible of it. Which might be the case for
    1728             :     // example if AdviseRead() requests a full image, but we don't read by
    1729             :     // chunks of the full width of one or several lines
    1730          49 :     m_nAdviseReadXOff = nXOff;
    1731          49 :     m_nAdviseReadYOff = nYOff;
    1732          49 :     m_nAdviseReadXSize = nXSize;
    1733          49 :     m_nAdviseReadYSize = nYSize;
    1734          49 :     m_nAdviseReadBufXSize = nBufXSize;
    1735          49 :     m_nAdviseReadBufYSize = nBufYSize;
    1736          49 :     m_nAdviseReadBandCount = nBandCount;
    1737          49 :     CPLFree(m_panAdviseReadBandList);
    1738          49 :     if (panBandList)
    1739             :     {
    1740          41 :         m_panAdviseReadBandList =
    1741          41 :             static_cast<int *>(CPLMalloc(sizeof(int) * nBandCount));
    1742          41 :         memcpy(m_panAdviseReadBandList, panBandList, sizeof(int) * nBandCount);
    1743             :     }
    1744             :     else
    1745             :     {
    1746           8 :         m_panAdviseReadBandList = nullptr;
    1747             :     }
    1748             : 
    1749          49 :     return CE_None;
    1750             : }
    1751             : 
    1752             : /************************************************************************/
    1753             : /*                       RunDeferredAdviseRead()                        */
    1754             : /************************************************************************/
    1755             : 
    1756          44 : CPLErr ECWDataset::RunDeferredAdviseRead()
    1757             : {
    1758          44 :     CPLAssert(m_nAdviseReadXOff >= 0);
    1759             : 
    1760          44 :     const int nXOff = m_nAdviseReadXOff;
    1761          44 :     const int nYOff = m_nAdviseReadYOff;
    1762          44 :     const int nXSize = m_nAdviseReadXSize;
    1763          44 :     const int nYSize = m_nAdviseReadYSize;
    1764          44 :     const int nBufXSize = m_nAdviseReadBufXSize;
    1765          44 :     const int nBufYSize = m_nAdviseReadBufYSize;
    1766          44 :     const int nBandCount = m_nAdviseReadBandCount;
    1767          44 :     int *panBandList = m_panAdviseReadBandList;
    1768             : 
    1769          44 :     m_nAdviseReadXOff = -1;
    1770          44 :     m_nAdviseReadYOff = -1;
    1771          44 :     m_nAdviseReadXSize = -1;
    1772          44 :     m_nAdviseReadYSize = -1;
    1773          44 :     m_nAdviseReadBufXSize = -1;
    1774          44 :     m_nAdviseReadBufYSize = -1;
    1775          44 :     m_nAdviseReadBandCount = -1;
    1776          44 :     m_panAdviseReadBandList = nullptr;
    1777             : 
    1778             :     /* -------------------------------------------------------------------- */
    1779             :     /*      Adjust band numbers to be zero based.                           */
    1780             :     /* -------------------------------------------------------------------- */
    1781             :     UINT32 *panAdjustedBandList =
    1782          44 :         (UINT32 *)CPLMalloc(sizeof(UINT32) * nBandCount);
    1783          44 :     nBandIndexToPromoteTo8Bit = -1;
    1784          97 :     for (int ii = 0; ii < nBandCount; ii++)
    1785             :     {
    1786          53 :         const int nIdx = (panBandList != nullptr) ? panBandList[ii] - 1 : ii;
    1787             :         ;
    1788          53 :         panAdjustedBandList[ii] = nIdx;
    1789          53 :         if (cpl::down_cast<ECWRasterBand *>(GetRasterBand(nIdx + 1))
    1790          53 :                 ->bPromoteTo8Bit)
    1791           1 :             nBandIndexToPromoteTo8Bit = ii;
    1792             :     }
    1793             : 
    1794             :     /* -------------------------------------------------------------------- */
    1795             :     /*      Cleanup old window cache information.                           */
    1796             :     /* -------------------------------------------------------------------- */
    1797          44 :     CleanupWindow();
    1798             : 
    1799             :     /* -------------------------------------------------------------------- */
    1800             :     /*      Set the new requested window.                                   */
    1801             :     /* -------------------------------------------------------------------- */
    1802          44 :     CNCSError oErr = poFileView->SetView(
    1803          44 :         nBandCount, panAdjustedBandList, nXOff, nYOff, nXOff + nXSize - 1,
    1804          88 :         nYOff + nYSize - 1, nBufXSize, nBufYSize);
    1805             : 
    1806          44 :     CPLFree(panAdjustedBandList);
    1807          44 :     if (oErr.GetErrorNumber() != NCS_SUCCESS)
    1808             :     {
    1809           0 :         ECWReportError(oErr);
    1810             : 
    1811           0 :         bWinActive = FALSE;
    1812           0 :         CPLFree(panBandList);
    1813           0 :         return CE_Failure;
    1814             :     }
    1815             : 
    1816          44 :     bWinActive = TRUE;
    1817             : 
    1818             :     /* -------------------------------------------------------------------- */
    1819             :     /*      Record selected window.                                         */
    1820             :     /* -------------------------------------------------------------------- */
    1821          44 :     nWinXOff = nXOff;
    1822          44 :     nWinYOff = nYOff;
    1823          44 :     nWinXSize = nXSize;
    1824          44 :     nWinYSize = nYSize;
    1825          44 :     nWinBufXSize = nBufXSize;
    1826          44 :     nWinBufYSize = nBufYSize;
    1827             : 
    1828          44 :     panWinBandList = (int *)CPLMalloc(sizeof(int) * nBandCount);
    1829          44 :     if (panBandList != nullptr)
    1830          40 :         memcpy(panWinBandList, panBandList, sizeof(int) * nBandCount);
    1831             :     else
    1832             :     {
    1833          17 :         for (int ii = 0; ii < nBandCount; ii++)
    1834             :         {
    1835          13 :             panWinBandList[ii] = ii + 1;
    1836             :         }
    1837             :     }
    1838          44 :     nWinBandCount = nBandCount;
    1839             : 
    1840          44 :     nWinBufLoaded = -1;
    1841             : 
    1842             :     /* -------------------------------------------------------------------- */
    1843             :     /*      Allocate current scanline buffer.                               */
    1844             :     /* -------------------------------------------------------------------- */
    1845          44 :     papCurLineBuf = (void **)CPLMalloc(sizeof(void *) * nWinBandCount);
    1846          97 :     for (int iBand = 0; iBand < nWinBandCount; iBand++)
    1847         106 :         papCurLineBuf[iBand] =
    1848          53 :             CPLMalloc(static_cast<size_t>(nBufXSize) *
    1849          53 :                       GDALGetDataTypeSizeBytes(eRasterDataType));
    1850             : 
    1851          44 :     CPLFree(panBandList);
    1852             : 
    1853          44 :     return CE_None;
    1854             : }
    1855             : 
    1856             : /************************************************************************/
    1857             : /*                           TryWinRasterIO()                           */
    1858             : /*                                                                      */
    1859             : /*      Try to satisfy the given request based on the currently         */
    1860             : /*      defined window.  Return TRUE on success or FALSE on             */
    1861             : /*      failure.  On failure, the caller should satisfy the request     */
    1862             : /*      another way (not report an error).                              */
    1863             : /************************************************************************/
    1864             : 
    1865        1931 : int ECWDataset::TryWinRasterIO(CPL_UNUSED GDALRWFlag eFlag, int nXOff,
    1866             :                                int nYOff, int nXSize, int nYSize,
    1867             :                                GByte *pabyData, int nBufXSize, int nBufYSize,
    1868             :                                GDALDataType eDT, int nBandCount,
    1869             :                                const int *panBandList, GSpacing nPixelSpace,
    1870             :                                GSpacing nLineSpace, GSpacing nBandSpace,
    1871             :                                GDALRasterIOExtraArg *psExtraArg)
    1872             : {
    1873             :     int iBand, i;
    1874             : 
    1875             :     /* -------------------------------------------------------------------- */
    1876             :     /*      Provide default buffer organization.                            */
    1877             :     /* -------------------------------------------------------------------- */
    1878        1931 :     if (nPixelSpace == 0)
    1879           0 :         nPixelSpace = GDALGetDataTypeSizeBytes(eDT);
    1880        1931 :     if (nLineSpace == 0)
    1881           0 :         nLineSpace = nPixelSpace * nBufXSize;
    1882        1931 :     if (nBandSpace == 0)
    1883           1 :         nBandSpace = nLineSpace * nBufYSize;
    1884             : 
    1885             : /* -------------------------------------------------------------------- */
    1886             : /*      Do some simple tests to see if the current window can           */
    1887             : /*      satisfy our requirement.                                        */
    1888             : /* -------------------------------------------------------------------- */
    1889             : #ifdef NOISY_DEBUG
    1890             :     CPLDebug("ECW", "TryWinRasterIO(%d,%d,%d,%d,%d,%d)", nXOff, nYOff, nXSize,
    1891             :              nYSize, nBufXSize, nBufYSize);
    1892             : #endif
    1893             : 
    1894        1931 :     if (!bWinActive)
    1895             :     {
    1896         528 :         if (nXOff == m_nAdviseReadXOff && nXSize == m_nAdviseReadXSize &&
    1897          44 :             nBufXSize == m_nAdviseReadBufXSize)
    1898             :         {
    1899          44 :             if (RunDeferredAdviseRead() != CE_None)
    1900           0 :                 return FALSE;
    1901             :         }
    1902         528 :         if (!bWinActive)
    1903             :         {
    1904         484 :             return FALSE;
    1905             :         }
    1906             :     }
    1907             : 
    1908        1447 :     if (nXOff != nWinXOff || nXSize != nWinXSize)
    1909           0 :         return FALSE;
    1910             : 
    1911        1447 :     if (nBufXSize != nWinBufXSize)
    1912           0 :         return FALSE;
    1913             : 
    1914        2907 :     for (iBand = 0; iBand < nBandCount; iBand++)
    1915             :     {
    1916        1488 :         for (i = 0; i < nWinBandCount; i++)
    1917             :         {
    1918        1486 :             if (panWinBandList[i] == panBandList[iBand])
    1919        1460 :                 break;
    1920             :         }
    1921             : 
    1922        1462 :         if (i == nWinBandCount)
    1923           2 :             return FALSE;
    1924             :     }
    1925             : 
    1926        1445 :     if (nYOff < nWinYOff || nYOff + nYSize > nWinYOff + nWinYSize)
    1927           0 :         return FALSE;
    1928             : 
    1929             :     /* -------------------------------------------------------------------- */
    1930             :     /*      Now we try more subtle tests.                                   */
    1931             :     /* -------------------------------------------------------------------- */
    1932             :     {
    1933             :         static int nDebugCount = 0;
    1934             : 
    1935        1445 :         if (nDebugCount < 30)
    1936          30 :             CPLDebug(
    1937             :                 "ECW",
    1938             :                 "TryWinRasterIO(%d,%d,%d,%d -> %dx%d) - doing advised read.",
    1939             :                 nXOff, nYOff, nXSize, nYSize, nBufXSize, nBufYSize);
    1940             : 
    1941        1445 :         if (nDebugCount == 29)
    1942           1 :             CPLDebug("ECW", "No more TryWinRasterIO messages will be reported");
    1943             : 
    1944        1445 :         nDebugCount++;
    1945             :     }
    1946             : 
    1947             :     /* -------------------------------------------------------------------- */
    1948             :     /*      Actually load data one buffer line at a time.                   */
    1949             :     /* -------------------------------------------------------------------- */
    1950             :     int iBufLine;
    1951             : 
    1952        4165 :     for (iBufLine = 0; iBufLine < nBufYSize; iBufLine++)
    1953             :     {
    1954        2788 :         double fFileLine = ((iBufLine + 0.5) / nBufYSize) * nYSize + nYOff;
    1955        2788 :         int iWinLine =
    1956        2788 :             (int)(((fFileLine - nWinYOff) / nWinYSize) * nWinBufYSize);
    1957             : 
    1958        2788 :         if (iWinLine == nWinBufLoaded + 1)
    1959        2720 :             LoadNextLine();
    1960             : 
    1961        2788 :         if (iWinLine != nWinBufLoaded)
    1962          68 :             return FALSE;
    1963             : 
    1964             :         /* --------------------------------------------------------------------
    1965             :          */
    1966             :         /*      Copy out all our target bands. */
    1967             :         /* --------------------------------------------------------------------
    1968             :          */
    1969             :         int iWinBand;
    1970        8290 :         for (iBand = 0; iBand < nBandCount; iBand++)
    1971             :         {
    1972       10070 :             for (iWinBand = 0; iWinBand < nWinBandCount; iWinBand++)
    1973             :             {
    1974       10070 :                 if (panWinBandList[iWinBand] == panBandList[iBand])
    1975        5570 :                     break;
    1976             :             }
    1977             : 
    1978        5570 :             GDALCopyWords(papCurLineBuf[iWinBand], eRasterDataType,
    1979             :                           GDALGetDataTypeSizeBytes(eRasterDataType),
    1980        5570 :                           pabyData + nBandSpace * iBand + iBufLine * nLineSpace,
    1981             :                           eDT, (int)nPixelSpace, nBufXSize);
    1982             :         }
    1983             : 
    1984        2870 :         if (psExtraArg->pfnProgress != nullptr &&
    1985         150 :             !psExtraArg->pfnProgress(1.0 * (iBufLine + 1) / nBufYSize, "",
    1986             :                                      psExtraArg->pProgressData))
    1987             :         {
    1988           0 :             return -1;
    1989             :         }
    1990             :     }
    1991             : 
    1992        1377 :     return TRUE;
    1993             : }
    1994             : 
    1995             : /************************************************************************/
    1996             : /*                            LoadNextLine()                            */
    1997             : /************************************************************************/
    1998             : 
    1999        2720 : CPLErr ECWDataset::LoadNextLine()
    2000             : 
    2001             : {
    2002        2720 :     if (!bWinActive)
    2003           0 :         return CE_Failure;
    2004             : 
    2005        2720 :     if (nWinBufLoaded == nWinBufYSize - 1)
    2006             :     {
    2007           0 :         CleanupWindow();
    2008           0 :         return CE_Failure;
    2009             :     }
    2010             : 
    2011             :     NCSEcwReadStatus eRStatus;
    2012        5440 :     eRStatus = poFileView->ReadLineBIL(eNCSRequestDataType,
    2013        2720 :                                        (UINT16)nWinBandCount, papCurLineBuf);
    2014        2720 :     if (eRStatus != NCSECW_READ_OK)
    2015           0 :         return CE_Failure;
    2016             : 
    2017        2720 :     if (nBandIndexToPromoteTo8Bit >= 0)
    2018             :     {
    2019       24450 :         for (int iX = 0; iX < nWinBufXSize; iX++)
    2020             :         {
    2021       24300 :             ((GByte *)papCurLineBuf[nBandIndexToPromoteTo8Bit])[iX] *= 255;
    2022             :         }
    2023             :     }
    2024             : 
    2025        2720 :     nWinBufLoaded++;
    2026             : 
    2027        2720 :     return CE_None;
    2028             : }
    2029             : 
    2030             : /************************************************************************/
    2031             : /*                           CleanupWindow()                            */
    2032             : /************************************************************************/
    2033             : 
    2034         479 : void ECWDataset::CleanupWindow()
    2035             : 
    2036             : {
    2037         479 :     if (!bWinActive)
    2038         435 :         return;
    2039             : 
    2040          44 :     bWinActive = FALSE;
    2041          44 :     CPLFree(panWinBandList);
    2042          44 :     panWinBandList = nullptr;
    2043             : 
    2044          97 :     for (int iBand = 0; iBand < nWinBandCount; iBand++)
    2045          53 :         CPLFree(papCurLineBuf[iBand]);
    2046          44 :     CPLFree(papCurLineBuf);
    2047          44 :     papCurLineBuf = nullptr;
    2048             : }
    2049             : 
    2050             : /************************************************************************/
    2051             : /*                             IRasterIO()                              */
    2052             : /************************************************************************/
    2053             : 
    2054        1699 : CPLErr ECWDataset::IRasterIO(GDALRWFlag eRWFlag, int nXOff, int nYOff,
    2055             :                              int nXSize, int nYSize, void *pData, int nBufXSize,
    2056             :                              int nBufYSize, GDALDataType eBufType,
    2057             :                              int nBandCount, BANDMAP_TYPE panBandMap,
    2058             :                              GSpacing nPixelSpace, GSpacing nLineSpace,
    2059             :                              GSpacing nBandSpace,
    2060             :                              GDALRasterIOExtraArg *psExtraArg)
    2061             : 
    2062             : {
    2063        1699 :     if (eRWFlag == GF_Write)
    2064           0 :         return CE_Failure;
    2065             : 
    2066        1699 :     if (nBandCount > 100)
    2067           0 :         return CE_Failure;
    2068             : 
    2069        1699 :     if (bUseOldBandRasterIOImplementation)
    2070             :         /* Sanity check. Should not happen */
    2071           0 :         return CE_Failure;
    2072        1699 :     int nDataTypeSize = GDALGetDataTypeSizeBytes(eRasterDataType);
    2073             : 
    2074        1699 :     if (nPixelSpace == 0)
    2075             :     {
    2076           0 :         nPixelSpace = nDataTypeSize;
    2077             :     }
    2078             : 
    2079        1699 :     if (nLineSpace == 0)
    2080             :     {
    2081           0 :         nLineSpace = nPixelSpace * nBufXSize;
    2082             :     }
    2083        1699 :     if (nBandSpace == 0)
    2084             :     {
    2085          18 :         nBandSpace =
    2086          18 :             static_cast<GSpacing>(nDataTypeSize) * nBufXSize * nBufYSize;
    2087             :     }
    2088             : 
    2089             :     // Use GDAL upsampling if non nearest
    2090        1699 :     if ((nBufXSize > nXSize || nBufYSize > nYSize) &&
    2091           3 :         psExtraArg->eResampleAlg != GRIORA_NearestNeighbour)
    2092             :     {
    2093           2 :         const int nBufDataTypeSize = GDALGetDataTypeSizeBytes(eBufType);
    2094           2 :         GByte *pabyTemp = (GByte *)VSI_MALLOC3_VERBOSE(
    2095             :             nXSize, nYSize, nBufDataTypeSize * nBandCount);
    2096           2 :         if (pabyTemp == nullptr)
    2097             :         {
    2098           0 :             return CE_Failure;
    2099             :         }
    2100             : 
    2101             :         GDALRasterIOExtraArg sExtraArgDefault;
    2102           2 :         INIT_RASTERIO_EXTRA_ARG(sExtraArgDefault);
    2103           2 :         sExtraArgDefault.pfnProgress = psExtraArg->pfnProgress;
    2104           2 :         sExtraArgDefault.pProgressData = psExtraArg->pProgressData;
    2105             : 
    2106           4 :         CPLErr eErr = IRasterIO(
    2107             :             eRWFlag, nXOff, nYOff, nXSize, nYSize, pabyTemp, nXSize, nYSize,
    2108             :             eBufType, nBandCount, panBandMap, nBufDataTypeSize,
    2109           2 :             (GIntBig)nBufDataTypeSize * nXSize,
    2110           2 :             (GIntBig)nBufDataTypeSize * nXSize * nYSize, &sExtraArgDefault);
    2111             : 
    2112           2 :         if (eErr == CE_None)
    2113             :         {
    2114             :             /* Create a MEM dataset that wraps the input buffer */
    2115             :             auto poMEMDS = std::unique_ptr<MEMDataset>(
    2116           2 :                 MEMDataset::Create("", nXSize, nYSize, 0, eBufType, nullptr));
    2117             : 
    2118           7 :             for (int i = 0; i < nBandCount; i++)
    2119             :             {
    2120           5 :                 auto hBand = MEMCreateRasterBandEx(
    2121           5 :                     poMEMDS.get(), i + 1,
    2122           5 :                     pabyTemp + static_cast<size_t>(i) * nBufDataTypeSize *
    2123           5 :                                    nXSize * nYSize,
    2124             :                     eBufType, 0, 0, false);
    2125           5 :                 poMEMDS->AddMEMBand(hBand);
    2126             : 
    2127           5 :                 const char *pszNBITS = GetRasterBand(i + 1)->GetMetadataItem(
    2128           5 :                     "NBITS", "IMAGE_STRUCTURE");
    2129           5 :                 if (pszNBITS)
    2130           0 :                     poMEMDS->GetRasterBand(i + 1)->SetMetadataItem(
    2131           0 :                         "NBITS", pszNBITS, "IMAGE_STRUCTURE");
    2132             :             }
    2133             : 
    2134             :             GDALRasterIOExtraArg sExtraArgTmp;
    2135           2 :             INIT_RASTERIO_EXTRA_ARG(sExtraArgTmp);
    2136           2 :             CPL_IGNORE_RET_VAL(sExtraArgTmp.eResampleAlg);
    2137           2 :             sExtraArgTmp.eResampleAlg = psExtraArg->eResampleAlg;
    2138             : 
    2139           2 :             CPL_IGNORE_RET_VAL(poMEMDS->RasterIO(
    2140             :                 GF_Read, 0, 0, nXSize, nYSize, pData, nBufXSize, nBufYSize,
    2141             :                 eBufType, nBandCount, nullptr, nPixelSpace, nLineSpace,
    2142             :                 nBandSpace, &sExtraArgTmp));
    2143             :         }
    2144             : 
    2145           2 :         VSIFree(pabyTemp);
    2146             : 
    2147           2 :         return eErr;
    2148             :     }
    2149             : 
    2150             : /* -------------------------------------------------------------------- */
    2151             : /*      ECW SDK 3.3 has a bug with the ECW format when we query the     */
    2152             : /*      number of bands of the dataset, but not in the "natural order". */
    2153             : /*      It ignores the content of panBandMap. (#4234)                   */
    2154             : /* -------------------------------------------------------------------- */
    2155             : #if ECWSDK_VERSION < 40
    2156        1697 :     if (!bIsJPEG2000 && nBandCount == nBands)
    2157             :     {
    2158             :         int i;
    2159          11 :         int bDoBandIRasterIO = FALSE;
    2160          44 :         for (i = 0; i < nBandCount; i++)
    2161             :         {
    2162          33 :             if (panBandMap[i] != i + 1)
    2163             :             {
    2164           2 :                 bDoBandIRasterIO = TRUE;
    2165             :             }
    2166             :         }
    2167          11 :         if (bDoBandIRasterIO)
    2168             :         {
    2169           1 :             return GDALDataset::IRasterIO(eRWFlag, nXOff, nYOff, nXSize, nYSize,
    2170             :                                           pData, nBufXSize, nBufYSize, eBufType,
    2171             :                                           nBandCount, panBandMap, nPixelSpace,
    2172           1 :                                           nLineSpace, nBandSpace, psExtraArg);
    2173             :         }
    2174             :     }
    2175             : #endif
    2176             : 
    2177             :     /* -------------------------------------------------------------------- */
    2178             :     /*      Check if we can directly return the data in case we have cached */
    2179             :     /*      it from a previous call in a multi-band reading pattern.        */
    2180             :     /* -------------------------------------------------------------------- */
    2181        1696 :     if (nBandCount == 1 && *panBandMap > 1 && *panBandMap <= nBands &&
    2182          67 :         sCachedMultiBandIO.nXOff == nXOff &&
    2183          67 :         sCachedMultiBandIO.nYOff == nYOff &&
    2184          30 :         sCachedMultiBandIO.nXSize == nXSize &&
    2185          22 :         sCachedMultiBandIO.nYSize == nYSize &&
    2186          22 :         sCachedMultiBandIO.nBufXSize == nBufXSize &&
    2187          22 :         sCachedMultiBandIO.nBufYSize == nBufYSize &&
    2188          22 :         sCachedMultiBandIO.eBufType == eBufType)
    2189             :     {
    2190          22 :         sCachedMultiBandIO.nBandsTried++;
    2191             : 
    2192          22 :         if (sCachedMultiBandIO.bEnabled &&
    2193           7 :             sCachedMultiBandIO.pabyData != nullptr)
    2194             :         {
    2195             :             int j;
    2196           7 :             const int nBufTypeSize = GDALGetDataTypeSizeBytes(eBufType);
    2197         357 :             for (j = 0; j < nBufYSize; j++)
    2198             :             {
    2199         350 :                 GDALCopyWords(
    2200         350 :                     sCachedMultiBandIO.pabyData +
    2201         350 :                         static_cast<size_t>(*panBandMap - 1) * nBufXSize *
    2202         350 :                             nBufYSize * nBufTypeSize +
    2203         350 :                         static_cast<size_t>(j) * nBufXSize * nBufTypeSize,
    2204         350 :                     eBufType, nBufTypeSize, ((GByte *)pData) + j * nLineSpace,
    2205             :                     eBufType, (int)nPixelSpace, nBufXSize);
    2206             :             }
    2207           7 :             return CE_None;
    2208             :         }
    2209             : 
    2210          45 :         if (!(sCachedMultiBandIO.bEnabled) &&
    2211          22 :             sCachedMultiBandIO.nBandsTried == nBands &&
    2212           7 :             CPLTestBool(CPLGetConfigOption("ECW_CLEVER", "YES")))
    2213             :         {
    2214           7 :             sCachedMultiBandIO.bEnabled = TRUE;
    2215           7 :             CPLDebug(
    2216             :                 "ECW",
    2217             :                 "Detecting successive band reading pattern (for next time)");
    2218             :         }
    2219             :     }
    2220             : 
    2221             :     /* -------------------------------------------------------------------- */
    2222             :     /*      Try to do it based on existing "advised" access.                */
    2223             :     /* -------------------------------------------------------------------- */
    2224             :     int nRet =
    2225        1689 :         TryWinRasterIO(eRWFlag, nXOff, nYOff, nXSize, nYSize, (GByte *)pData,
    2226             :                        nBufXSize, nBufYSize, eBufType, nBandCount, panBandMap,
    2227             :                        nPixelSpace, nLineSpace, nBandSpace, psExtraArg);
    2228        1689 :     if (nRet == TRUE)
    2229        1371 :         return CE_None;
    2230         318 :     else if (nRet < 0)
    2231           0 :         return CE_Failure;
    2232             : 
    2233             :     /* -------------------------------------------------------------------- */
    2234             :     /*      If we are requesting a single line at 1:1, we do a multi-band   */
    2235             :     /*      AdviseRead() and then TryWinRasterIO() again.                   */
    2236             :     /*                                                                      */
    2237             :     /*      Except for reading a 1x1 window when reading a scanline might   */
    2238             :     /*      be longer.                                                      */
    2239             :     /* -------------------------------------------------------------------- */
    2240         318 :     if (nXSize == 1 && nYSize == 1 && nBufXSize == 1 && nBufYSize == 1)
    2241             :     {
    2242             :         /* do nothing */
    2243             :     }
    2244             : 
    2245             : #if !defined(SDK_CAN_DO_SUPERSAMPLING)
    2246             :     /* -------------------------------------------------------------------- */
    2247             :     /*      If we are supersampling we need to fall into the general        */
    2248             :     /*      purpose logic.                                                  */
    2249             :     /* -------------------------------------------------------------------- */
    2250         314 :     else if (nXSize < nBufXSize || nYSize < nBufYSize)
    2251             :     {
    2252           1 :         bUseOldBandRasterIOImplementation = TRUE;
    2253           1 :         CPLErr eErr = GDALDataset::IRasterIO(
    2254             :             eRWFlag, nXOff, nYOff, nXSize, nYSize, pData, nBufXSize, nBufYSize,
    2255             :             eBufType, nBandCount, panBandMap, nPixelSpace, nLineSpace,
    2256             :             nBandSpace, psExtraArg);
    2257           1 :         bUseOldBandRasterIOImplementation = FALSE;
    2258           1 :         return eErr;
    2259             :     }
    2260             : #endif
    2261             : 
    2262         313 :     else if (nBufYSize == 1)
    2263             :     {
    2264             :         // This is tricky, because it expects the rest of the image
    2265             :         // with this buffer width to be read. The preferred way to
    2266             :         // achieve this behavior would be to call AdviseRead before
    2267             :         // call IRasterIO.  The logic could be improved to detect
    2268             :         // successive pattern of single line reading before doing an
    2269             :         // AdviseRead.
    2270             :         CPLErr eErr;
    2271             : 
    2272         241 :         eErr = AdviseRead(nXOff, nYOff, nXSize, GetRasterYSize() - nYOff,
    2273         241 :                           nBufXSize, (nRasterYSize - nYOff) / nYSize, eBufType,
    2274             :                           nBandCount, const_cast<int *>(panBandMap), nullptr);
    2275         482 :         if (eErr == CE_None &&
    2276         241 :             TryWinRasterIO(eRWFlag, nXOff, nYOff, nXSize, nYSize,
    2277             :                            (GByte *)pData, nBufXSize, nBufYSize, eBufType,
    2278             :                            nBandCount, panBandMap, nPixelSpace, nLineSpace,
    2279             :                            nBandSpace, psExtraArg))
    2280           6 :             return CE_None;
    2281             :     }
    2282             : 
    2283         311 :     CPLDebug("ECW", "RasterIO(%d,%d,%d,%d -> %dx%d) - doing interleaved read.",
    2284             :              nXOff, nYOff, nXSize, nYSize, nBufXSize, nBufYSize);
    2285             : 
    2286             :     /* -------------------------------------------------------------------- */
    2287             :     /*      Setup view.                                                     */
    2288             :     /* -------------------------------------------------------------------- */
    2289             :     UINT32 anBandIndices[100];
    2290             :     int i;
    2291             :     NCSError eNCSErr;
    2292         622 :     CNCSError oErr(GetCNCSError(NCS_SUCCESS));
    2293             : 
    2294         641 :     for (i = 0; i < nBandCount; i++)
    2295         330 :         anBandIndices[i] = panBandMap[i] - 1;
    2296             : 
    2297         311 :     CleanupWindow();
    2298             : 
    2299             :     /* -------------------------------------------------------------------- */
    2300             :     /*      Cache data in the context of a multi-band reading pattern.      */
    2301             :     /* -------------------------------------------------------------------- */
    2302         311 :     if (nBandCount == 1 && *panBandMap == 1 && (nBands == 3 || nBands == 4))
    2303             :     {
    2304         219 :         if (sCachedMultiBandIO.bEnabled &&
    2305           4 :             sCachedMultiBandIO.nBandsTried != nBands)
    2306             :         {
    2307           1 :             sCachedMultiBandIO.bEnabled = FALSE;
    2308           1 :             CPLDebug("ECW", "Disabling successive band reading pattern");
    2309             :         }
    2310             : 
    2311         219 :         sCachedMultiBandIO.nXOff = nXOff;
    2312         219 :         sCachedMultiBandIO.nYOff = nYOff;
    2313         219 :         sCachedMultiBandIO.nXSize = nXSize;
    2314         219 :         sCachedMultiBandIO.nYSize = nYSize;
    2315         219 :         sCachedMultiBandIO.nBufXSize = nBufXSize;
    2316         219 :         sCachedMultiBandIO.nBufYSize = nBufYSize;
    2317         219 :         sCachedMultiBandIO.eBufType = eBufType;
    2318         219 :         sCachedMultiBandIO.nBandsTried = 1;
    2319             : 
    2320         219 :         const int nBufTypeSize = GDALGetDataTypeSizeBytes(eBufType);
    2321             : 
    2322         219 :         if (sCachedMultiBandIO.bEnabled)
    2323             :         {
    2324             :             GByte *pNew =
    2325           6 :                 (GByte *)VSIRealloc(sCachedMultiBandIO.pabyData,
    2326           3 :                                     static_cast<size_t>(nBufXSize) * nBufYSize *
    2327           3 :                                         nBands * nBufTypeSize);
    2328           3 :             if (pNew == nullptr)
    2329           0 :                 CPLFree(sCachedMultiBandIO.pabyData);
    2330           3 :             sCachedMultiBandIO.pabyData = pNew;
    2331             :         }
    2332             : 
    2333         219 :         if (sCachedMultiBandIO.bEnabled &&
    2334           3 :             sCachedMultiBandIO.pabyData != nullptr)
    2335             :         {
    2336           3 :             nBandIndexToPromoteTo8Bit = -1;
    2337          12 :             for (i = 0; i < nBands; i++)
    2338             :             {
    2339           9 :                 if (cpl::down_cast<ECWRasterBand *>(GetRasterBand(i + 1))
    2340           9 :                         ->bPromoteTo8Bit)
    2341           0 :                     nBandIndexToPromoteTo8Bit = i;
    2342           9 :                 anBandIndices[i] = i;
    2343             :             }
    2344             : 
    2345           3 :             oErr = poFileView->SetView(nBands, anBandIndices, nXOff, nYOff,
    2346           3 :                                        nXOff + nXSize - 1, nYOff + nYSize - 1,
    2347           3 :                                        nBufXSize, nBufYSize);
    2348           3 :             eNCSErr = oErr.GetErrorNumber();
    2349             : 
    2350           3 :             if (eNCSErr != NCS_SUCCESS)
    2351             :             {
    2352           0 :                 CPLError(CE_Failure, CPLE_AppDefined, "%s",
    2353             :                          NCSGetErrorText(eNCSErr));
    2354             : 
    2355           0 :                 return CE_Failure;
    2356             :             }
    2357             : 
    2358           6 :             CPLErr eErr = ReadBands(
    2359           3 :                 sCachedMultiBandIO.pabyData, nBufXSize, nBufYSize, eBufType,
    2360           3 :                 nBands, nBufTypeSize, nBufXSize * nBufTypeSize,
    2361           3 :                 nBufXSize * nBufYSize * nBufTypeSize, psExtraArg);
    2362           3 :             if (eErr != CE_None)
    2363           0 :                 return eErr;
    2364             : 
    2365             :             int j;
    2366         153 :             for (j = 0; j < nBufYSize; j++)
    2367             :             {
    2368         150 :                 GDALCopyWords(
    2369         150 :                     sCachedMultiBandIO.pabyData +
    2370         150 :                         static_cast<size_t>(j) * nBufXSize * nBufTypeSize,
    2371         150 :                     eBufType, nBufTypeSize, ((GByte *)pData) + j * nLineSpace,
    2372             :                     eBufType, (int)nPixelSpace, nBufXSize);
    2373             :             }
    2374           3 :             return CE_None;
    2375             :         }
    2376             :     }
    2377             : 
    2378         308 :     nBandIndexToPromoteTo8Bit = -1;
    2379         635 :     for (i = 0; i < nBandCount; i++)
    2380             :     {
    2381         327 :         if (((ECWRasterBand *)GetRasterBand(anBandIndices[i] + 1))
    2382         327 :                 ->bPromoteTo8Bit)
    2383           4 :             nBandIndexToPromoteTo8Bit = i;
    2384             :     }
    2385         308 :     oErr = poFileView->SetView(nBandCount, anBandIndices, nXOff, nYOff,
    2386         308 :                                nXOff + nXSize - 1, nYOff + nYSize - 1,
    2387         308 :                                nBufXSize, nBufYSize);
    2388         308 :     eNCSErr = oErr.GetErrorNumber();
    2389             : 
    2390         308 :     if (eNCSErr != NCS_SUCCESS)
    2391             :     {
    2392           0 :         CPLError(CE_Failure, CPLE_AppDefined, "%s", NCSGetErrorText(eNCSErr));
    2393             : 
    2394           0 :         return CE_Failure;
    2395             :     }
    2396             : 
    2397         308 :     return ReadBands(pData, nBufXSize, nBufYSize, eBufType, nBandCount,
    2398         308 :                      nPixelSpace, nLineSpace, nBandSpace, psExtraArg);
    2399             : }
    2400             : 
    2401             : /************************************************************************/
    2402             : /*                         ReadBandsDirectly()                          */
    2403             : /************************************************************************/
    2404             : 
    2405          39 : CPLErr ECWDataset::ReadBandsDirectly(void *pData, int nBufXSize, int nBufYSize,
    2406             :                                      CPL_UNUSED GDALDataType eBufType,
    2407             :                                      int nBandCount,
    2408             :                                      CPL_UNUSED GSpacing nPixelSpace,
    2409             :                                      GSpacing nLineSpace, GSpacing nBandSpace,
    2410             :                                      GDALRasterIOExtraArg *psExtraArg)
    2411             : {
    2412          39 :     CPLDebug("ECW", "ReadBandsDirectly(-> %dx%d) - reading lines directly.",
    2413             :              nBufXSize, nBufYSize);
    2414             : 
    2415          39 :     UINT8 **pBIL = (UINT8 **)NCSMalloc(nBandCount * sizeof(UINT8 *), FALSE);
    2416             : 
    2417         101 :     for (int nB = 0; nB < nBandCount; nB++)
    2418             :     {
    2419          62 :         pBIL[nB] = ((UINT8 *)pData) + (nBandSpace * nB);  // for any bit depth
    2420             :     }
    2421             : 
    2422          39 :     CPLErr eErr = CE_None;
    2423        5574 :     for (int nR = 0; nR < nBufYSize; nR++)
    2424             :     {
    2425       11070 :         if (poFileView->ReadLineBIL(eNCSRequestDataType, (UINT16)nBandCount,
    2426        5535 :                                     (void **)pBIL) != NCSECW_READ_OK)
    2427             :         {
    2428           0 :             eErr = CE_Failure;
    2429           0 :             break;
    2430             :         }
    2431       13722 :         for (int nB = 0; nB < nBandCount; nB++)
    2432             :         {
    2433        8187 :             if (nB == nBandIndexToPromoteTo8Bit)
    2434             :             {
    2435       48999 :                 for (int iX = 0; iX < nBufXSize; iX++)
    2436             :                 {
    2437       48690 :                     pBIL[nB][iX] *= 255;
    2438             :                 }
    2439             :             }
    2440        8187 :             pBIL[nB] += nLineSpace;
    2441             :         }
    2442             : 
    2443        5535 :         if (psExtraArg->pfnProgress != nullptr &&
    2444           0 :             !psExtraArg->pfnProgress(1.0 * (nR + 1) / nBufYSize, "",
    2445             :                                      psExtraArg->pProgressData))
    2446             :         {
    2447           0 :             eErr = CE_Failure;
    2448           0 :             break;
    2449             :         }
    2450             :     }
    2451          39 :     if (pBIL)
    2452             :     {
    2453          39 :         NCSFree(pBIL);
    2454             :     }
    2455          39 :     return eErr;
    2456             : }
    2457             : 
    2458             : /************************************************************************/
    2459             : /*                             ReadBands()                              */
    2460             : /************************************************************************/
    2461             : 
    2462         311 : CPLErr ECWDataset::ReadBands(void *pData, int nBufXSize, int nBufYSize,
    2463             :                              GDALDataType eBufType, int nBandCount,
    2464             :                              GSpacing nPixelSpace, GSpacing nLineSpace,
    2465             :                              GSpacing nBandSpace,
    2466             :                              GDALRasterIOExtraArg *psExtraArg)
    2467             : {
    2468             :     int i;
    2469             :     /* -------------------------------------------------------------------- */
    2470             :     /*      Setup working scanline, and the pointers into it.               */
    2471             :     /* -------------------------------------------------------------------- */
    2472         311 :     const int nDataTypeSizeBytes = GDALGetDataTypeSizeBytes(eRasterDataType);
    2473         311 :     bool bDirect =
    2474          45 :         (eBufType == eRasterDataType) && nDataTypeSizeBytes == nPixelSpace &&
    2475         395 :         nLineSpace == (nPixelSpace * nBufXSize) &&
    2476             :         nBandSpace ==
    2477          39 :             (static_cast<GSpacing>(nDataTypeSizeBytes) * nBufXSize * nBufYSize);
    2478         311 :     if (bDirect)
    2479             :     {
    2480          39 :         return ReadBandsDirectly(pData, nBufXSize, nBufYSize, eBufType,
    2481             :                                  nBandCount, nPixelSpace, nLineSpace,
    2482          39 :                                  nBandSpace, psExtraArg);
    2483             :     }
    2484         272 :     CPLDebug("ECW", "ReadBands(-> %dx%d) - reading lines using GDALCopyWords.",
    2485             :              nBufXSize, nBufYSize);
    2486         272 :     CPLErr eErr = CE_None;
    2487         544 :     GByte *pabyBILScanline = (GByte *)CPLMalloc(
    2488         272 :         static_cast<size_t>(nBufXSize) * nDataTypeSizeBytes * nBandCount);
    2489         272 :     GByte **papabyBIL = (GByte **)CPLMalloc(nBandCount * sizeof(void *));
    2490             : 
    2491         546 :     for (i = 0; i < nBandCount; i++)
    2492         274 :         papabyBIL[i] = pabyBILScanline +
    2493         274 :                        static_cast<size_t>(i) * nBufXSize * nDataTypeSizeBytes;
    2494             : 
    2495             :     /* -------------------------------------------------------------------- */
    2496             :     /*      Read back all the data for the requested view.                  */
    2497             :     /* -------------------------------------------------------------------- */
    2498        4272 :     for (int iScanline = 0; iScanline < nBufYSize; iScanline++)
    2499             :     {
    2500             :         NCSEcwReadStatus eRStatus;
    2501             : 
    2502        8000 :         eRStatus = poFileView->ReadLineBIL(
    2503        4000 :             eNCSRequestDataType, (UINT16)nBandCount, (void **)papabyBIL);
    2504        4000 :         if (eRStatus != NCSECW_READ_OK)
    2505             :         {
    2506           0 :             eErr = CE_Failure;
    2507           0 :             CPLError(CE_Failure, CPLE_AppDefined,
    2508             :                      "NCScbmReadViewLineBIL failed.");
    2509           0 :             break;
    2510             :         }
    2511             : 
    2512        8080 :         for (i = 0; i < nBandCount; i++)
    2513             :         {
    2514        4080 :             if (i == nBandIndexToPromoteTo8Bit)
    2515             :             {
    2516       24450 :                 for (int iX = 0; iX < nBufXSize; iX++)
    2517             :                 {
    2518       24300 :                     papabyBIL[i][iX] *= 255;
    2519             :                 }
    2520             :             }
    2521             : 
    2522        4080 :             GDALCopyWords(pabyBILScanline + static_cast<size_t>(i) *
    2523        4080 :                                                 nDataTypeSizeBytes * nBufXSize,
    2524             :                           eRasterDataType, nDataTypeSizeBytes,
    2525        4080 :                           ((GByte *)pData) + nLineSpace * iScanline +
    2526        4080 :                               nBandSpace * i,
    2527             :                           eBufType, (int)nPixelSpace, nBufXSize);
    2528             :         }
    2529             : 
    2530        4000 :         if (psExtraArg->pfnProgress != nullptr &&
    2531           0 :             !psExtraArg->pfnProgress(1.0 * (iScanline + 1) / nBufYSize, "",
    2532             :                                      psExtraArg->pProgressData))
    2533             :         {
    2534           0 :             eErr = CE_Failure;
    2535           0 :             break;
    2536             :         }
    2537             :     }
    2538             : 
    2539         272 :     CPLFree(pabyBILScanline);
    2540         272 :     CPLFree(papabyBIL);
    2541             : 
    2542         272 :     return eErr;
    2543             : }
    2544             : 
    2545             : /************************************************************************/
    2546             : /*                            OpenJPEG2000()                            */
    2547             : /*                                                                      */
    2548             : /*          Open method that only supports JPEG2000 files.              */
    2549             : /************************************************************************/
    2550             : 
    2551          89 : GDALDataset *ECWDataset::OpenJPEG2000(GDALOpenInfo *poOpenInfo)
    2552             : 
    2553             : {
    2554          89 :     if (!ECWDatasetIdentifyJPEG2000(poOpenInfo))
    2555           0 :         return nullptr;
    2556             : 
    2557          89 :     return Open(poOpenInfo, TRUE);
    2558             : }
    2559             : 
    2560             : /************************************************************************/
    2561             : /*                              OpenECW()                               */
    2562             : /*                                                                      */
    2563             : /*      Open method that only supports ECW files.                       */
    2564             : /************************************************************************/
    2565             : 
    2566          35 : GDALDataset *ECWDataset::OpenECW(GDALOpenInfo *poOpenInfo)
    2567             : 
    2568             : {
    2569          35 :     if (!ECWDatasetIdentifyECW(poOpenInfo))
    2570           0 :         return nullptr;
    2571             : 
    2572          35 :     return Open(poOpenInfo, FALSE);
    2573             : }
    2574             : 
    2575             : /************************************************************************/
    2576             : /*                            OpenFileView()                            */
    2577             : /************************************************************************/
    2578             : 
    2579         124 : CNCSJP2FileView *ECWDataset::OpenFileView(const char *pszDatasetName,
    2580             :                                           bool bProgressive,
    2581             :                                           int &bUsingCustomStream,
    2582             :                                           CPL_UNUSED bool bWrite)
    2583             : {
    2584             :     /* -------------------------------------------------------------------- */
    2585             :     /*      First we try to open it as a normal CNCSFile, letting the       */
    2586             :     /*      ECW SDK manage the IO itself.   This will only work for real    */
    2587             :     /*      files, and ecwp: or ecwps: sources.                             */
    2588             :     /* -------------------------------------------------------------------- */
    2589         124 :     CNCSJP2FileView *poFileView = nullptr;
    2590             :     NCSError eErr;
    2591         248 :     CNCSError oErr(GetCNCSError(NCS_SUCCESS));
    2592             : 
    2593         124 :     bUsingCustomStream = FALSE;
    2594         124 :     poFileView = new CNCSFile();
    2595             :     // we always open in read only mode. This should be improved in the future.
    2596             :     try
    2597             :     {
    2598             : #ifdef _WIN32
    2599             :         if (CPLTestBool(CPLGetConfigOption("GDAL_FILENAME_IS_UTF8", "YES")))
    2600             :         {
    2601             :             wchar_t *pwszDatasetName =
    2602             :                 CPLRecodeToWChar(pszDatasetName, CPL_ENC_UTF8, CPL_ENC_UCS2);
    2603             :             oErr = poFileView->Open(pwszDatasetName, bProgressive, false);
    2604             :             CPLFree(pwszDatasetName);
    2605             :         }
    2606             :         else
    2607             : #endif
    2608             :         {
    2609             :             oErr =
    2610         124 :                 poFileView->Open((char *)pszDatasetName, bProgressive, false);
    2611             :         }
    2612             :     }
    2613           0 :     catch (...)
    2614             :     {
    2615           0 :         CPLError(CE_Failure, CPLE_AppDefined,
    2616             :                  "Unexpected exception occurred in ECW SDK");
    2617           0 :         delete poFileView;
    2618           0 :         return nullptr;
    2619             :     }
    2620         124 :     eErr = oErr.GetErrorNumber();
    2621             : 
    2622             :     /* -------------------------------------------------------------------- */
    2623             :     /*      If that did not work, trying opening as a virtual file.         */
    2624             :     /* -------------------------------------------------------------------- */
    2625         124 :     if (eErr != NCS_SUCCESS)
    2626             :     {
    2627          53 :         CPLDebug("ECW",
    2628             :                  "NCScbmOpenFileView(%s): eErr=%d, will try VSIL stream.",
    2629             :                  pszDatasetName, (int)eErr);
    2630             : 
    2631          53 :         delete poFileView;
    2632             : 
    2633          53 :         VSILFILE *fpVSIL = VSIFOpenL(pszDatasetName, "rb");
    2634          53 :         if (fpVSIL == nullptr)
    2635             :         {
    2636           0 :             CPLError(CE_Failure, CPLE_OpenFailed, "Failed to open %s.",
    2637             :                      pszDatasetName);
    2638           0 :             return nullptr;
    2639             :         }
    2640             : 
    2641          53 :         if (hECWDatasetMutex == nullptr)
    2642             :         {
    2643           0 :             hECWDatasetMutex = CPLCreateMutex();
    2644             :         }
    2645          53 :         else if (!CPLAcquireMutex(hECWDatasetMutex, 60.0))
    2646             :         {
    2647           0 :             CPLDebug("ECW", "Failed to acquire mutex in 60s.");
    2648             :         }
    2649             :         else
    2650             :         {
    2651          53 :             CPLDebug("ECW", "Got mutex.");
    2652             :         }
    2653             : 
    2654          53 :         poFileView = new CNCSJP2FileView();
    2655             : 
    2656             : #if ECWSDK_VERSION >= 55
    2657             :         NCS::CString streamName(pszDatasetName);
    2658             :         auto vsiIoStream =
    2659             :             NCS::CView::FindSteamByStreamNameFromOpenDatasets(streamName);
    2660             :         if (!vsiIoStream)
    2661             :         {
    2662             :             vsiIoStream = std::make_shared<VSIIOStream>();
    2663             :             std::static_pointer_cast<VSIIOStream>(vsiIoStream)
    2664             :                 ->Access(fpVSIL, FALSE, TRUE, pszDatasetName, 0, -1);
    2665             :             bUsingCustomStream = TRUE;
    2666             :         }
    2667             :         oErr = poFileView->Open(vsiIoStream, bProgressive);
    2668             : #else
    2669          53 :         auto vsiIoStream = new VSIIOStream();
    2670          53 :         vsiIoStream->Access(fpVSIL, FALSE, TRUE, pszDatasetName, 0, -1);
    2671          53 :         oErr = poFileView->Open(vsiIoStream, bProgressive);
    2672             : 
    2673             :         // The CNCSJP2FileView (poFileView) object may not use the iostream
    2674             :         // (poIOStream) passed to the CNCSJP2FileView::Open() method if an
    2675             :         // iostream is already available to the ECW JPEG 2000 SDK for a given
    2676             :         // file.  Consequently, if the iostream passed to
    2677             :         // CNCSJP2FileView::Open() does not become the underlying iostream
    2678             :         // of the CNCSJP2FileView object, then it should be deleted.
    2679             :         //
    2680             :         // In addition, the underlying iostream of the CNCSJP2FileView object
    2681             :         // should not be deleted until all CNCSJP2FileView objects using the
    2682             :         // underlying iostream are deleted. Consequently, each time a
    2683             :         // CNCSJP2FileView object is created, the nFileViewCount attribute
    2684             :         // of the underlying VSIIOStream object must be incremented for use
    2685             :         // in the ECWDataset destructor.
    2686             : 
    2687             :         VSIIOStream *poUnderlyingIOStream =
    2688          53 :             ((VSIIOStream *)(poFileView->GetStream()));
    2689             : 
    2690          53 :         if (poUnderlyingIOStream)
    2691          52 :             poUnderlyingIOStream->nFileViewCount++;
    2692             : 
    2693          53 :         if (vsiIoStream != poUnderlyingIOStream)
    2694             :         {
    2695           1 :             delete vsiIoStream;
    2696             :         }
    2697             :         else
    2698             :         {
    2699          52 :             bUsingCustomStream = TRUE;
    2700             :         }
    2701             : #endif
    2702             : 
    2703          53 :         CPLReleaseMutex(hECWDatasetMutex);
    2704             : 
    2705          53 :         if (oErr.GetErrorNumber() != NCS_SUCCESS)
    2706             :         {
    2707           1 :             delete poFileView;
    2708           1 :             ECWReportError(oErr);
    2709             : 
    2710           1 :             return nullptr;
    2711             :         }
    2712             :     }
    2713             : 
    2714         123 :     return poFileView;
    2715             : }
    2716             : 
    2717             : /************************************************************************/
    2718             : /*                                Open()                                */
    2719             : /************************************************************************/
    2720             : 
    2721         124 : GDALDataset *ECWDataset::Open(GDALOpenInfo *poOpenInfo, int bIsJPEG2000)
    2722             : 
    2723             : {
    2724         124 :     CNCSJP2FileView *poFileView = nullptr;
    2725             :     int i;
    2726         124 :     int bUsingCustomStream = FALSE;
    2727         248 :     CPLString osFilename = poOpenInfo->pszFilename;
    2728             : 
    2729         124 :     ECWInitialize();
    2730             : 
    2731             :     /* Note: J2K_SUBFILE is somehow an obsolete concept that predates
    2732             :      * /vsisubfile/ */
    2733             :     /* syntax and was used mainly(only?) by the NITF driver before its switch */
    2734             :     /* to /vsisubfile */
    2735             : 
    2736             :     /* -------------------------------------------------------------------- */
    2737             :     /*      If we get a J2K_SUBFILE style name, convert it into the         */
    2738             :     /*      corresponding /vsisubfile/ path.                                */
    2739             :     /*                                                                      */
    2740             :     /*      From: J2K_SUBFILE:offset,size,filename                           */
    2741             :     /*      To: /vsisubfile/offset_size,filename                            */
    2742             :     /* -------------------------------------------------------------------- */
    2743         124 :     if (STARTS_WITH_CI(osFilename, "J2K_SUBFILE:"))
    2744             :     {
    2745             :         char **papszTokens =
    2746           0 :             CSLTokenizeString2(osFilename.c_str() + 12, ",", 0);
    2747           0 :         if (CSLCount(papszTokens) >= 3)
    2748             :         {
    2749             :             osFilename.Printf("/vsisubfile/%s_%s,%s", papszTokens[0],
    2750           0 :                               papszTokens[1], papszTokens[2]);
    2751             :         }
    2752             :         else
    2753             :         {
    2754           0 :             CPLError(CE_Failure, CPLE_OpenFailed,
    2755             :                      "Failed to parse J2K_SUBFILE specification.");
    2756           0 :             CSLDestroy(papszTokens);
    2757           0 :             return nullptr;
    2758             :         }
    2759           0 :         CSLDestroy(papszTokens);
    2760             :     }
    2761             : 
    2762             :     /* -------------------------------------------------------------------- */
    2763             :     /*      Open the client interface.                                      */
    2764             :     /* -------------------------------------------------------------------- */
    2765         124 :     poFileView = OpenFileView(osFilename.c_str(), false, bUsingCustomStream,
    2766         124 :                               poOpenInfo->eAccess == GA_Update);
    2767         124 :     if (poFileView == nullptr)
    2768             :     {
    2769             : #if ECWSDK_VERSION < 50
    2770             :         /* Detect what is apparently the ECW v3 file format signature */
    2771           2 :         if (EQUAL(CPLGetExtensionSafe(osFilename).c_str(), "ECW") &&
    2772           3 :             poOpenInfo->nHeaderBytes > 0x30 &&
    2773           1 :             STARTS_WITH_CI((const char *)(poOpenInfo->pabyHeader + 0x20),
    2774             :                            "ecw ECW3"))
    2775             :         {
    2776           1 :             CPLError(CE_Failure, CPLE_AppDefined,
    2777             :                      "Cannot open %s which looks like a ECW format v3 file, "
    2778             :                      "that requires ECW SDK 5.0 or later",
    2779             :                      osFilename.c_str());
    2780             :         }
    2781             : #endif
    2782           1 :         return nullptr;
    2783             :     }
    2784             : 
    2785             :     /* -------------------------------------------------------------------- */
    2786             :     /*      Create a corresponding GDALDataset.                             */
    2787             :     /* -------------------------------------------------------------------- */
    2788         123 :     ECWDataset *poDS = new ECWDataset(bIsJPEG2000);
    2789         123 :     poDS->poFileView = poFileView;
    2790         123 :     poDS->eAccess = poOpenInfo->eAccess;
    2791             : 
    2792             :     // Disable .aux.xml writing for subfiles and such.  Unfortunately
    2793             :     // this will also disable it in some cases where it might be
    2794             :     // applicable.
    2795         123 :     if (bUsingCustomStream)
    2796          52 :         poDS->nPamFlags |= GPF_DISABLED;
    2797             : 
    2798         123 :     poDS->bUsingCustomStream = bUsingCustomStream;
    2799             : 
    2800             :     /* -------------------------------------------------------------------- */
    2801             :     /*      Fetch general file information.                                 */
    2802             :     /* -------------------------------------------------------------------- */
    2803         123 :     poDS->psFileInfo = poFileView->GetFileInfo();
    2804             : 
    2805         123 :     CPLDebug("ECW",
    2806             :              "FileInfo: SizeXY=%d,%d Bands=%d\n"
    2807             :              "       OriginXY=%g,%g  CellIncrementXY=%g,%g\n"
    2808             :              "       ColorSpace=%d, eCellType=%d\n",
    2809         123 :              poDS->psFileInfo->nSizeX, poDS->psFileInfo->nSizeY,
    2810         123 :              poDS->psFileInfo->nBands, poDS->psFileInfo->fOriginX,
    2811         123 :              poDS->psFileInfo->fOriginY, poDS->psFileInfo->fCellIncrementX,
    2812         123 :              poDS->psFileInfo->fCellIncrementY,
    2813         123 :              (int)poDS->psFileInfo->eColorSpace,
    2814         123 :              (int)poDS->psFileInfo->eCellType);
    2815             : 
    2816             :     /* -------------------------------------------------------------------- */
    2817             :     /*      Establish raster info.                                          */
    2818             :     /* -------------------------------------------------------------------- */
    2819         123 :     poDS->nRasterXSize = poDS->psFileInfo->nSizeX;
    2820         123 :     poDS->nRasterYSize = poDS->psFileInfo->nSizeY;
    2821             : 
    2822             :     /* -------------------------------------------------------------------- */
    2823             :     /*      Establish the GDAL data type that corresponds.  A few NCS       */
    2824             :     /*      data types have no direct corresponding value in GDAL so we     */
    2825             :     /*      will coerce to something sufficiently similar.                  */
    2826             :     /* -------------------------------------------------------------------- */
    2827         123 :     poDS->eNCSRequestDataType = poDS->psFileInfo->eCellType;
    2828         123 :     switch (poDS->psFileInfo->eCellType)
    2829             :     {
    2830         103 :         case NCSCT_UINT8:
    2831         103 :             poDS->eRasterDataType = GDT_UInt8;
    2832         103 :             break;
    2833             : 
    2834          10 :         case NCSCT_UINT16:
    2835          10 :             poDS->eRasterDataType = GDT_UInt16;
    2836          10 :             break;
    2837             : 
    2838           2 :         case NCSCT_UINT32:
    2839             :         case NCSCT_UINT64:
    2840           2 :             poDS->eRasterDataType = GDT_UInt32;
    2841           2 :             poDS->eNCSRequestDataType = NCSCT_UINT32;
    2842           2 :             break;
    2843             : 
    2844           6 :         case NCSCT_INT8:
    2845             :         case NCSCT_INT16:
    2846           6 :             poDS->eRasterDataType = GDT_Int16;
    2847           6 :             poDS->eNCSRequestDataType = NCSCT_INT16;
    2848           6 :             break;
    2849             : 
    2850           2 :         case NCSCT_INT32:
    2851             :         case NCSCT_INT64:
    2852           2 :             poDS->eRasterDataType = GDT_Int32;
    2853           2 :             poDS->eNCSRequestDataType = NCSCT_INT32;
    2854           2 :             break;
    2855             : 
    2856           0 :         case NCSCT_IEEE4:
    2857           0 :             poDS->eRasterDataType = GDT_Float32;
    2858           0 :             break;
    2859             : 
    2860           0 :         case NCSCT_IEEE8:
    2861           0 :             poDS->eRasterDataType = GDT_Float64;
    2862           0 :             break;
    2863             : 
    2864           0 :         default:
    2865           0 :             CPLDebug("ECW", "Unhandled case : eCellType = %d",
    2866           0 :                      (int)poDS->psFileInfo->eCellType);
    2867           0 :             break;
    2868             :     }
    2869             : 
    2870             : /* -------------------------------------------------------------------- */
    2871             : /*      If decoding a UInt32 image, check that the SDK is not buggy     */
    2872             : /*      There are issues at least in the 5.x series.                    */
    2873             : /* -------------------------------------------------------------------- */
    2874             : #if ECWSDK_VERSION >= 40
    2875             :     constexpr const char *szDETECT_BUG_FILENAME =
    2876             :         "__detect_ecw_uint32_bug__.j2k";
    2877             :     if (bIsJPEG2000 && poDS->eNCSRequestDataType == NCSCT_UINT32 &&
    2878             :         CPLTestBool(CPLGetConfigOption("ECW_CHECK_CORRECT_DECODING", "TRUE")) &&
    2879             :         strstr(poOpenInfo->pszFilename, szDETECT_BUG_FILENAME) == nullptr)
    2880             :     {
    2881             :         static bool bUINT32_Ok = false;
    2882             :         {
    2883             :             CPLMutexHolder oHolder(&hECWDatasetMutex);
    2884             :             static bool bTestDone = false;
    2885             :             if (!bTestDone)
    2886             :             {
    2887             :                 bTestDone = true;
    2888             :                 // Minimal J2K 2x2 image with NBITS=20, unsigned, reversible
    2889             :                 // compression and following values 0 1048575 1048574 524288
    2890             : 
    2891             :                 static const GByte abyTestUInt32ImageData[] = {
    2892             :                     0xFF, 0x4F, 0xFF, 0x51, 0x00, 0x29, 0x00, 0x02, 0x00, 0x00,
    2893             :                     0x00, 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00,
    2894             :                     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00,
    2895             :                     0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    2896             :                     0x00, 0x01, 0x13, 0x01, 0x01, 0xFF, 0x52, 0x00, 0x0D, 0x01,
    2897             :                     0x00, 0x00, 0x01, 0x00, 0x00, 0x04, 0x04, 0x00, 0x01, 0x99,
    2898             :                     0xFF, 0x5C, 0x00, 0x04, 0x40, 0xA0, 0xFF, 0x90, 0x00, 0x0A,
    2899             :                     0x00, 0x00, 0x00, 0x00, 0x00, 0x1C, 0x00, 0x01, 0xFF, 0x93,
    2900             :                     0xDF, 0xF9, 0x40, 0x50, 0x07, 0x68, 0xE0, 0x12, 0xD2, 0xDA,
    2901             :                     0xDF, 0xFF, 0x7F, 0x5F, 0xFF, 0xD9};
    2902             : 
    2903             :                 const std::string osTmpFilename =
    2904             :                     VSIMemGenerateHiddenFilename(szDETECT_BUG_FILENAME);
    2905             :                 VSIFCloseL(VSIFileFromMemBuffer(
    2906             :                     osTmpFilename.c_str(),
    2907             :                     const_cast<GByte *>(abyTestUInt32ImageData),
    2908             :                     sizeof(abyTestUInt32ImageData), false));
    2909             :                 GDALOpenInfo oOpenInfo(osTmpFilename.c_str(), GA_ReadOnly);
    2910             :                 auto poTmpDS =
    2911             :                     std::unique_ptr<GDALDataset>(Open(&oOpenInfo, true));
    2912             :                 if (poTmpDS)
    2913             :                 {
    2914             :                     uint32_t anValues[4] = {0};
    2915             :                     if (poTmpDS->GetRasterBand(1)->RasterIO(
    2916             :                             GF_Read, 0, 0, 2, 2, anValues, 2, 2, GDT_UInt32, 0,
    2917             :                             0, nullptr) == CE_None &&
    2918             :                         anValues[0] == 0 && anValues[1] == 1048575 &&
    2919             :                         anValues[2] == 1048574 && anValues[3] == 524288)
    2920             :                     {
    2921             :                         bUINT32_Ok = true;
    2922             :                     }
    2923             :                 }
    2924             :                 VSIUnlink(osTmpFilename.c_str());
    2925             :             }
    2926             :         }
    2927             : 
    2928             :         if (!bUINT32_Ok)
    2929             :         {
    2930             :             CPLDebug("ECW", "ECW SDK used cannot correctly decode UInt32 "
    2931             :                             "images. Giving up");
    2932             :             delete poDS;
    2933             :             return nullptr;
    2934             :         }
    2935             :     }
    2936             : #endif
    2937             : 
    2938             :     /* -------------------------------------------------------------------- */
    2939             :     /*      Create band information objects.                                */
    2940             :     /* -------------------------------------------------------------------- */
    2941         336 :     for (i = 0; i < poDS->psFileInfo->nBands; i++)
    2942         213 :         poDS->SetBand(i + 1, new ECWRasterBand(poDS, i + 1, -1,
    2943         213 :                                                poOpenInfo->papszOpenOptions));
    2944             : 
    2945             :     /* -------------------------------------------------------------------- */
    2946             :     /*      Look for supporting coordinate system information.              */
    2947             :     /* -------------------------------------------------------------------- */
    2948         123 :     if (bIsJPEG2000)
    2949             :     {
    2950          89 :         poDS->LoadJP2Metadata(poOpenInfo, osFilename);
    2951             :     }
    2952             :     else
    2953             :     {
    2954          34 :         poDS->ECW2WKTProjection();
    2955             : 
    2956             :         /* --------------------------------------------------------------------
    2957             :          */
    2958             :         /*      Check for world file. */
    2959             :         /* --------------------------------------------------------------------
    2960             :          */
    2961          34 :         if (!poDS->bGeoTransformValid)
    2962             :         {
    2963           0 :             poDS->bGeoTransformValid |=
    2964           0 :                 GDALReadWorldFile2(osFilename, nullptr, poDS->m_gt,
    2965           0 :                                    poOpenInfo->GetSiblingFiles(), nullptr) ||
    2966           0 :                 GDALReadWorldFile2(osFilename, ".wld", poDS->m_gt,
    2967           0 :                                    poOpenInfo->GetSiblingFiles(), nullptr);
    2968             :         }
    2969             :     }
    2970             : 
    2971         123 :     if (poDS->psFileInfo->nCompressionRate > 0)
    2972          67 :         poDS->GDALDataset::SetMetadataItem(
    2973             :             "COMPRESSION_RATE_TARGET",
    2974         134 :             CPLString().Printf("%d", poDS->psFileInfo->nCompressionRate));
    2975         123 :     poDS->GDALDataset::SetMetadataItem(
    2976         123 :         "COLORSPACE", ECWGetColorSpaceName(poDS->psFileInfo->eColorSpace));
    2977             : #if ECWSDK_VERSION >= 50
    2978             :     if (!bIsJPEG2000)
    2979             :         poDS->GDALDataset::SetMetadataItem(
    2980             :             "VERSION",
    2981             :             CPLString().Printf("%d", poDS->psFileInfo->nFormatVersion));
    2982             : #if ECWSDK_VERSION >= 51
    2983             :     // output jp2 header info
    2984             :     if (bIsJPEG2000 && poDS->poFileView)
    2985             :     {
    2986             :         // comments
    2987             :         char *csComments = nullptr;
    2988             :         poDS->poFileView->GetParameter((char *)"JPC:DECOMPRESS:COMMENTS",
    2989             :                                        &csComments);
    2990             :         if (csComments)
    2991             :         {
    2992             :             std::string osComments(csComments);
    2993             : 
    2994             :             // Strip off boring Kakadu COM content
    2995             :             if (STARTS_WITH(osComments.c_str(), "Kakadu-"))
    2996             :             {
    2997             :                 const auto nEOLPos = osComments.find('\n');
    2998             :                 if (nEOLPos == std::string::npos)
    2999             :                     osComments.clear();
    3000             :                 osComments = osComments.substr(nEOLPos + 1);
    3001             :             }
    3002             :             if (STARTS_WITH(
    3003             :                     osComments.c_str(),
    3004             :                     "Kdu-Layer-Info: "
    3005             :                     "log_2{Delta-D(squared-error)/Delta-L(bytes)}, L(bytes)\n"))
    3006             :             {
    3007             :                 while (true)
    3008             :                 {
    3009             :                     const auto nEOLPos = osComments.find('\n');
    3010             :                     if (nEOLPos == std::string::npos)
    3011             :                     {
    3012             :                         osComments.clear();
    3013             :                         break;
    3014             :                     }
    3015             :                     osComments = osComments.substr(nEOLPos + 1);
    3016             :                     if (osComments.find(",  ") == std::string::npos)
    3017             :                         break;
    3018             :                 }
    3019             :             }
    3020             : 
    3021             :             // Strip off boring OpenJPEG COM content
    3022             :             if (STARTS_WITH(osComments.c_str(),
    3023             :                             "Created by OpenJPEG version ") &&
    3024             :                 osComments.find('\n') == std::string::npos)
    3025             :             {
    3026             :                 osComments.clear();
    3027             :             }
    3028             : 
    3029             :             if (!osComments.empty())
    3030             :                 poDS->GDALDataset::SetMetadataItem("ALL_COMMENTS",
    3031             :                                                    osComments.c_str());
    3032             :             NCSFree(csComments);
    3033             :         }
    3034             : 
    3035             :         // Profile
    3036             :         UINT32 nProfile = 2;
    3037             :         UINT32 nRsiz = 0;
    3038             :         poDS->poFileView->GetParameter((char *)"JP2:COMPLIANCE:PROFILE:TYPE",
    3039             :                                        &nRsiz);
    3040             :         if (nRsiz == 0)
    3041             :             nProfile = 2;  // Profile 2 (no restrictions)
    3042             :         else if (nRsiz == 1)
    3043             :             nProfile = 0;  // Profile 0
    3044             :         else if (nRsiz == 2)
    3045             :             nProfile = 1;  // Profile 1, NITF_BIIF_NPJE, NITF_BIIF_EPJE
    3046             :         poDS->GDALDataset::SetMetadataItem("PROFILE",
    3047             :                                            CPLString().Printf("%d", nProfile),
    3048             :                                            JPEG2000_DOMAIN_NAME);
    3049             : 
    3050             :         // number of tiles on X axis
    3051             :         UINT32 nTileNrX = 1;
    3052             :         poDS->poFileView->GetParameter((char *)"JPC:DECOMPRESS:TILENR:X",
    3053             :                                        &nTileNrX);
    3054             :         poDS->GDALDataset::SetMetadataItem("TILES_X",
    3055             :                                            CPLString().Printf("%d", nTileNrX),
    3056             :                                            JPEG2000_DOMAIN_NAME);
    3057             : 
    3058             :         // number of tiles on X axis
    3059             :         UINT32 nTileNrY = 1;
    3060             :         poDS->poFileView->GetParameter((char *)"JPC:DECOMPRESS:TILENR:Y",
    3061             :                                        &nTileNrY);
    3062             :         poDS->GDALDataset::SetMetadataItem("TILES_Y",
    3063             :                                            CPLString().Printf("%d", nTileNrY),
    3064             :                                            JPEG2000_DOMAIN_NAME);
    3065             : 
    3066             :         // Tile Width
    3067             :         UINT32 nTileSizeX = 0;
    3068             :         poDS->poFileView->GetParameter((char *)"JPC:DECOMPRESS:TILESIZE:X",
    3069             :                                        &nTileSizeX);
    3070             :         poDS->GDALDataset::SetMetadataItem("TILE_WIDTH",
    3071             :                                            CPLString().Printf("%d", nTileSizeX),
    3072             :                                            JPEG2000_DOMAIN_NAME);
    3073             : 
    3074             :         // Tile Height
    3075             :         UINT32 nTileSizeY = 0;
    3076             :         poDS->poFileView->GetParameter((char *)"JPC:DECOMPRESS:TILESIZE:Y",
    3077             :                                        &nTileSizeY);
    3078             :         poDS->GDALDataset::SetMetadataItem("TILE_HEIGHT",
    3079             :                                            CPLString().Printf("%d", nTileSizeY),
    3080             :                                            JPEG2000_DOMAIN_NAME);
    3081             : 
    3082             :         // Precinct Sizes on X axis
    3083             :         char *csPreSizeX = nullptr;
    3084             :         poDS->poFileView->GetParameter((char *)"JPC:DECOMPRESS:PRECINCTSIZE:X",
    3085             :                                        &csPreSizeX);
    3086             :         if (csPreSizeX)
    3087             :         {
    3088             :             poDS->GDALDataset::SetMetadataItem("PRECINCT_SIZE_X", csPreSizeX,
    3089             :                                                JPEG2000_DOMAIN_NAME);
    3090             :             NCSFree(csPreSizeX);
    3091             :         }
    3092             : 
    3093             :         // Precinct Sizes on Y axis
    3094             :         char *csPreSizeY = nullptr;
    3095             :         poDS->poFileView->GetParameter((char *)"JPC:DECOMPRESS:PRECINCTSIZE:Y",
    3096             :                                        &csPreSizeY);
    3097             :         if (csPreSizeY)
    3098             :         {
    3099             :             poDS->GDALDataset::SetMetadataItem("PRECINCT_SIZE_Y", csPreSizeY,
    3100             :                                                JPEG2000_DOMAIN_NAME);
    3101             :             NCSFree(csPreSizeY);
    3102             :         }
    3103             : 
    3104             :         // Code Block Size on X axis
    3105             :         UINT32 nCodeBlockSizeX = 0;
    3106             :         poDS->poFileView->GetParameter((char *)"JPC:DECOMPRESS:CODEBLOCK:X",
    3107             :                                        &nCodeBlockSizeX);
    3108             :         poDS->GDALDataset::SetMetadataItem(
    3109             :             "CODE_BLOCK_SIZE_X", CPLString().Printf("%d", nCodeBlockSizeX),
    3110             :             JPEG2000_DOMAIN_NAME);
    3111             : 
    3112             :         // Code Block Size on Y axis
    3113             :         UINT32 nCodeBlockSizeY = 0;
    3114             :         poDS->poFileView->GetParameter((char *)"JPC:DECOMPRESS:CODEBLOCK:Y",
    3115             :                                        &nCodeBlockSizeY);
    3116             :         poDS->GDALDataset::SetMetadataItem(
    3117             :             "CODE_BLOCK_SIZE_Y", CPLString().Printf("%d", nCodeBlockSizeY),
    3118             :             JPEG2000_DOMAIN_NAME);
    3119             : 
    3120             :         // Bitdepth
    3121             :         char *csBitdepth = nullptr;
    3122             :         poDS->poFileView->GetParameter((char *)"JPC:DECOMPRESS:BITDEPTH",
    3123             :                                        &csBitdepth);
    3124             :         if (csBitdepth)
    3125             :         {
    3126             :             poDS->GDALDataset::SetMetadataItem("PRECISION", csBitdepth,
    3127             :                                                JPEG2000_DOMAIN_NAME);
    3128             :             NCSFree(csBitdepth);
    3129             :         }
    3130             : 
    3131             :         // Resolution Levels
    3132             :         UINT32 nLevels = 0;
    3133             :         poDS->poFileView->GetParameter(
    3134             :             (char *)"JPC:DECOMPRESS:RESOLUTION:LEVELS", &nLevels);
    3135             :         poDS->GDALDataset::SetMetadataItem("RESOLUTION_LEVELS",
    3136             :                                            CPLString().Printf("%d", nLevels),
    3137             :                                            JPEG2000_DOMAIN_NAME);
    3138             : 
    3139             :         // Qualaity Layers
    3140             :         UINT32 nLayers = 0;
    3141             :         poDS->poFileView->GetParameter((char *)"JP2:DECOMPRESS:LAYERS",
    3142             :                                        &nLayers);
    3143             :         poDS->GDALDataset::SetMetadataItem("QUALITY_LAYERS",
    3144             :                                            CPLString().Printf("%d", nLayers),
    3145             :                                            JPEG2000_DOMAIN_NAME);
    3146             : 
    3147             :         // Progression Order
    3148             :         char *csOrder = nullptr;
    3149             :         poDS->poFileView->GetParameter(
    3150             :             (char *)"JPC:DECOMPRESS:PROGRESSION:ORDER", &csOrder);
    3151             :         if (csOrder)
    3152             :         {
    3153             :             poDS->GDALDataset::SetMetadataItem("PROGRESSION_ORDER", csOrder,
    3154             :                                                JPEG2000_DOMAIN_NAME);
    3155             :             NCSFree(csOrder);
    3156             :         }
    3157             : 
    3158             :         // DWT Filter
    3159             :         const char *csFilter = nullptr;
    3160             :         UINT32 nFilter;
    3161             :         poDS->poFileView->GetParameter((char *)"JP2:TRANSFORMATION:TYPE",
    3162             :                                        &nFilter);
    3163             :         if (nFilter)
    3164             :             csFilter = "5x3";
    3165             :         else
    3166             :             csFilter = "9x7";
    3167             :         poDS->GDALDataset::SetMetadataItem("TRANSFORMATION_TYPE", csFilter,
    3168             :                                            JPEG2000_DOMAIN_NAME);
    3169             : 
    3170             :         // SOP used?
    3171             :         bool bSOP = 0;
    3172             :         poDS->poFileView->GetParameter((char *)"JP2:DECOMPRESS:SOP:EXISTS",
    3173             :                                        &bSOP);
    3174             :         poDS->SetMetadataItem("USE_SOP", (bSOP) ? "TRUE" : "FALSE",
    3175             :                               JPEG2000_DOMAIN_NAME);
    3176             : 
    3177             :         // EPH used?
    3178             :         bool bEPH = 0;
    3179             :         poDS->poFileView->GetParameter((char *)"JP2:DECOMPRESS:EPH:EXISTS",
    3180             :                                        &bEPH);
    3181             :         poDS->SetMetadataItem("USE_EPH", (bEPH) ? "TRUE" : "FALSE",
    3182             :                               JPEG2000_DOMAIN_NAME);
    3183             : 
    3184             :         // GML JP2 data contained?
    3185             :         bool bGML = 0;
    3186             :         poDS->poFileView->GetParameter((char *)"JP2:GML:JP2:BOX:EXISTS", &bGML);
    3187             :         poDS->SetMetadataItem("GML_JP2_DATA", (bGML) ? "TRUE" : "FALSE",
    3188             :                               JPEG2000_DOMAIN_NAME);
    3189             :     }
    3190             : #endif  // ECWSDK_VERSION>=51
    3191             :     if (!bIsJPEG2000 && poDS->psFileInfo->nFormatVersion >= 3)
    3192             :     {
    3193             :         poDS->GDALDataset::SetMetadataItem(
    3194             :             "COMPRESSION_RATE_ACTUAL",
    3195             :             CPLString().Printf("%f", poDS->psFileInfo->fActualCompressionRate));
    3196             :         poDS->GDALDataset::SetMetadataItem(
    3197             :             "CLOCKWISE_ROTATION_DEG",
    3198             :             CPLString().Printf("%f", poDS->psFileInfo->fCWRotationDegrees));
    3199             :         poDS->GDALDataset::SetMetadataItem("COMPRESSION_DATE",
    3200             :                                            poDS->psFileInfo->sCompressionDate);
    3201             :         // Get file metadata.
    3202             :         poDS->ReadFileMetaDataFromFile();
    3203             :     }
    3204             : #else
    3205         123 :     poDS->GDALDataset::SetMetadataItem(
    3206         246 :         "VERSION", CPLString().Printf("%d", bIsJPEG2000 ? 1 : 2));
    3207             : #endif
    3208             : 
    3209             :     /* -------------------------------------------------------------------- */
    3210             :     /*      Initialize any PAM information.                                 */
    3211             :     /* -------------------------------------------------------------------- */
    3212         123 :     poDS->SetDescription(osFilename);
    3213         123 :     poDS->TryLoadXML(poOpenInfo->GetSiblingFiles());
    3214             : 
    3215             :     /* -------------------------------------------------------------------- */
    3216             :     /*      Vector layers                                                   */
    3217             :     /* -------------------------------------------------------------------- */
    3218         123 :     if (bIsJPEG2000 && poOpenInfo->nOpenFlags & GDAL_OF_VECTOR)
    3219             :     {
    3220           1 :         poDS->LoadVectorLayers(CPLFetchBool(poOpenInfo->papszOpenOptions,
    3221             :                                             "OPEN_REMOTE_GML", false));
    3222             : 
    3223             :         // If file opened in vector-only mode and there's no vector,
    3224             :         // return
    3225           1 :         if ((poOpenInfo->nOpenFlags & GDAL_OF_RASTER) == 0 &&
    3226           0 :             poDS->GetLayerCount() == 0)
    3227             :         {
    3228           0 :             delete poDS;
    3229           0 :             return nullptr;
    3230             :         }
    3231             :     }
    3232             : 
    3233         123 :     return poDS;
    3234             : }
    3235             : 
    3236             : /************************************************************************/
    3237             : /*                       GetMetadataDomainList()                        */
    3238             : /************************************************************************/
    3239             : 
    3240           4 : char **ECWDataset::GetMetadataDomainList()
    3241             : {
    3242           4 :     return BuildMetadataDomainList(
    3243             :         GDALJP2AbstractDataset::GetMetadataDomainList(), TRUE, "ECW", "GML",
    3244           4 :         nullptr);
    3245             : }
    3246             : 
    3247             : /************************************************************************/
    3248             : /*                          GetMetadataItem()                           */
    3249             : /************************************************************************/
    3250             : 
    3251          55 : const char *ECWDataset::GetMetadataItem(const char *pszName,
    3252             :                                         const char *pszDomain)
    3253             : {
    3254          55 :     if (!bIsJPEG2000 && pszDomain != nullptr && EQUAL(pszDomain, "ECW") &&
    3255             :         pszName != nullptr)
    3256             :     {
    3257           6 :         if (EQUAL(pszName, "PROJ"))
    3258           2 :             return m_osProjCode.size() ? m_osProjCode.c_str() : "RAW";
    3259           4 :         if (EQUAL(pszName, "DATUM"))
    3260           2 :             return m_osDatumCode.size() ? m_osDatumCode.c_str() : "RAW";
    3261           2 :         if (EQUAL(pszName, "UNITS"))
    3262           2 :             return m_osUnitsCode.size() ? m_osUnitsCode.c_str() : "METERS";
    3263             :     }
    3264          49 :     return GDALJP2AbstractDataset::GetMetadataItem(pszName, pszDomain);
    3265             : }
    3266             : 
    3267             : /************************************************************************/
    3268             : /*                            GetMetadata()                             */
    3269             : /************************************************************************/
    3270             : 
    3271          90 : CSLConstList ECWDataset::GetMetadata(const char *pszDomain)
    3272             : 
    3273             : {
    3274          90 :     if (!bIsJPEG2000 && pszDomain != nullptr && EQUAL(pszDomain, "ECW"))
    3275             :     {
    3276           0 :         oECWMetadataList.Clear();
    3277             :         oECWMetadataList.AddString(
    3278           0 :             CPLSPrintf("%s=%s", "PROJ", GetMetadataItem("PROJ", "ECW")));
    3279             :         oECWMetadataList.AddString(
    3280           0 :             CPLSPrintf("%s=%s", "DATUM", GetMetadataItem("DATUM", "ECW")));
    3281             :         oECWMetadataList.AddString(
    3282           0 :             CPLSPrintf("%s=%s", "UNITS", GetMetadataItem("UNITS", "ECW")));
    3283           0 :         return oECWMetadataList.List();
    3284             :     }
    3285          90 :     else if (pszDomain == nullptr || !EQUAL(pszDomain, "GML"))
    3286          86 :         return GDALJP2AbstractDataset::GetMetadata(pszDomain);
    3287             :     else
    3288           4 :         return papszGMLMetadata;
    3289             : }
    3290             : 
    3291             : /************************************************************************/
    3292             : /*                   ReadFileMetaDataFromFile()                         */
    3293             : /*                                                                      */
    3294             : /* Gets relevant information from NCSFileMetadata and populates         */
    3295             : /* GDAL metadata.                                                       */
    3296             : /*                                                                      */
    3297             : /************************************************************************/
    3298             : #if ECWSDK_VERSION >= 50
    3299             : void ECWDataset::ReadFileMetaDataFromFile()
    3300             : {
    3301             :     if (psFileInfo->pFileMetaData == nullptr)
    3302             :         return;
    3303             : 
    3304             :     if (psFileInfo->pFileMetaData->sClassification != nullptr)
    3305             :         GDALDataset::SetMetadataItem(
    3306             :             "FILE_METADATA_CLASSIFICATION",
    3307             :             NCS::CString(psFileInfo->pFileMetaData->sClassification));
    3308             :     if (psFileInfo->pFileMetaData->sAcquisitionDate != nullptr)
    3309             :         GDALDataset::SetMetadataItem(
    3310             :             "FILE_METADATA_ACQUISITION_DATE",
    3311             :             NCS::CString(psFileInfo->pFileMetaData->sAcquisitionDate));
    3312             :     if (psFileInfo->pFileMetaData->sAcquisitionSensorName != nullptr)
    3313             :         GDALDataset::SetMetadataItem(
    3314             :             "FILE_METADATA_ACQUISITION_SENSOR_NAME",
    3315             :             NCS::CString(psFileInfo->pFileMetaData->sAcquisitionSensorName));
    3316             :     if (psFileInfo->pFileMetaData->sCompressionSoftware != nullptr)
    3317             :         GDALDataset::SetMetadataItem(
    3318             :             "FILE_METADATA_COMPRESSION_SOFTWARE",
    3319             :             NCS::CString(psFileInfo->pFileMetaData->sCompressionSoftware));
    3320             :     if (psFileInfo->pFileMetaData->sAuthor != nullptr)
    3321             :         GDALDataset::SetMetadataItem(
    3322             :             "FILE_METADATA_AUTHOR",
    3323             :             NCS::CString(psFileInfo->pFileMetaData->sAuthor));
    3324             :     if (psFileInfo->pFileMetaData->sCopyright != nullptr)
    3325             :         GDALDataset::SetMetadataItem(
    3326             :             "FILE_METADATA_COPYRIGHT",
    3327             :             NCS::CString(psFileInfo->pFileMetaData->sCopyright));
    3328             :     if (psFileInfo->pFileMetaData->sCompany != nullptr)
    3329             :         GDALDataset::SetMetadataItem(
    3330             :             "FILE_METADATA_COMPANY",
    3331             :             NCS::CString(psFileInfo->pFileMetaData->sCompany));
    3332             :     if (psFileInfo->pFileMetaData->sEmail != nullptr)
    3333             :         GDALDataset::SetMetadataItem(
    3334             :             "FILE_METADATA_EMAIL",
    3335             :             NCS::CString(psFileInfo->pFileMetaData->sEmail));
    3336             :     if (psFileInfo->pFileMetaData->sAddress != nullptr)
    3337             :         GDALDataset::SetMetadataItem(
    3338             :             "FILE_METADATA_ADDRESS",
    3339             :             NCS::CString(psFileInfo->pFileMetaData->sAddress));
    3340             :     if (psFileInfo->pFileMetaData->sTelephone != nullptr)
    3341             :         GDALDataset::SetMetadataItem(
    3342             :             "FILE_METADATA_TELEPHONE",
    3343             :             NCS::CString(psFileInfo->pFileMetaData->sTelephone));
    3344             : }
    3345             : 
    3346             : /************************************************************************/
    3347             : /*                         WriteFileMetaData()                          */
    3348             : /************************************************************************/
    3349             : 
    3350             : void ECWDataset::WriteFileMetaData(NCSFileMetaData *pFileMetaDataCopy)
    3351             : {
    3352             :     if (!bFileMetaDataDirty)
    3353             :         return;
    3354             : 
    3355             :     CPLAssert(eAccess == GA_Update);
    3356             :     CPLAssert(!bIsJPEG2000);
    3357             : 
    3358             :     bFileMetaDataDirty = FALSE;
    3359             : 
    3360             :     NCSFileView *psFileView = nullptr;
    3361             :     NCSError eErr;
    3362             : 
    3363             :     psFileView = NCSEditOpen(GetDescription());
    3364             :     if (psFileView == nullptr)
    3365             :     {
    3366             :         CPLError(CE_Failure, CPLE_AppDefined, "NCSEditOpen() failed");
    3367             :         return;
    3368             :     }
    3369             : 
    3370             :     eErr = NCSEditSetFileMetaData(psFileView, pFileMetaDataCopy);
    3371             :     if (eErr != NCS_SUCCESS)
    3372             :     {
    3373             :         CPLError(CE_Failure, CPLE_AppDefined,
    3374             :                  "NCSEditSetFileMetaData() failed : %s",
    3375             :                  NCSGetLastErrorText(eErr));
    3376             :     }
    3377             : 
    3378             :     NCSEditFlushAll(psFileView);
    3379             :     NCSEditClose(psFileView);
    3380             : }
    3381             : 
    3382             : #endif
    3383             : /************************************************************************/
    3384             : /*                         ECW2WKTProjection()                          */
    3385             : /*                                                                      */
    3386             : /*      Set the dataset pszProjection string in OGC WKT format by       */
    3387             : /*      looking up the ECW (GDT) coordinate system info in              */
    3388             : /*      ecw_cs.wkt support data file.                                   */
    3389             : /*                                                                      */
    3390             : /*      This code is likely still broken in some circumstances.  For    */
    3391             : /*      instance, I haven't been careful about changing the linear      */
    3392             : /*      projection parameters (false easting/northing) if the units     */
    3393             : /*      is feet.  Lots of cases missing here, and in ecw_cs.wkt.        */
    3394             : /************************************************************************/
    3395             : 
    3396          34 : void ECWDataset::ECW2WKTProjection()
    3397             : 
    3398             : {
    3399          34 :     if (psFileInfo == nullptr)
    3400          20 :         return;
    3401             : 
    3402             :     /* -------------------------------------------------------------------- */
    3403             :     /*      Capture Geotransform.                                           */
    3404             :     /*                                                                      */
    3405             :     /*      We will try to ignore the provided file information if it is    */
    3406             :     /*      origin (0,0) and pixel size (1,1).  I think sometimes I have    */
    3407             :     /*      also seen pixel increments of 0 on invalid datasets.            */
    3408             :     /* -------------------------------------------------------------------- */
    3409          34 :     if (psFileInfo->fOriginX != 0.0 || psFileInfo->fOriginY != 0.0 ||
    3410           0 :         (psFileInfo->fCellIncrementX != 0.0 &&
    3411           0 :          psFileInfo->fCellIncrementX != 1.0) ||
    3412           0 :         (psFileInfo->fCellIncrementY != 0.0 &&
    3413           0 :          psFileInfo->fCellIncrementY != 1.0))
    3414             :     {
    3415          34 :         bGeoTransformValid = TRUE;
    3416             : 
    3417          34 :         m_gt.xorig = psFileInfo->fOriginX;
    3418          34 :         m_gt.xscale = psFileInfo->fCellIncrementX;
    3419          34 :         m_gt.xrot = 0.0;
    3420             : 
    3421          34 :         m_gt.yorig = psFileInfo->fOriginY;
    3422          34 :         m_gt.yrot = 0.0;
    3423             : 
    3424             :         /* By default, set Y-resolution negative assuming images always */
    3425             :         /* have "Upward" orientation (Y coordinates increase "Upward"). */
    3426             :         /* Setting ECW_ALWAYS_UPWARD=FALSE option relexes that policy   */
    3427             :         /* and makes the driver rely on the actual Y-resolution         */
    3428             :         /* value (sign) of an image. This allows correctly processing   */
    3429             :         /* rare images with "Downward" orientation, where Y coordinates */
    3430             :         /* increase "Downward" and Y-resolution is positive.            */
    3431          34 :         if (CPLTestBool(CPLGetConfigOption("ECW_ALWAYS_UPWARD", "TRUE")))
    3432          33 :             m_gt.yscale = -fabs(psFileInfo->fCellIncrementY);
    3433             :         else
    3434           1 :             m_gt.yscale = psFileInfo->fCellIncrementY;
    3435             :     }
    3436             : 
    3437             :     /* -------------------------------------------------------------------- */
    3438             :     /*      do we have projection and datum?                                */
    3439             :     /* -------------------------------------------------------------------- */
    3440             :     CPLString osUnits =
    3441          34 :         ECWTranslateFromCellSizeUnits(psFileInfo->eCellSizeUnits);
    3442             : 
    3443          34 :     CPLDebug("ECW", "projection=%s, datum=%s, units=%s",
    3444          34 :              psFileInfo->szProjection, psFileInfo->szDatum, osUnits.c_str());
    3445             : 
    3446          34 :     if (EQUAL(psFileInfo->szProjection, "RAW"))
    3447          20 :         return;
    3448             : 
    3449             :     /* -------------------------------------------------------------------- */
    3450             :     /*      Set projection if we have it.                                   */
    3451             :     /* -------------------------------------------------------------------- */
    3452          28 :     OGRSpatialReference oSRS;
    3453             : 
    3454             :     /* For backward-compatible with previous behavior. Should we only */
    3455             :     /* restrict to those 2 values ? */
    3456          14 :     if (psFileInfo->eCellSizeUnits != ECW_CELL_UNITS_METERS &&
    3457           4 :         psFileInfo->eCellSizeUnits != ECW_CELL_UNITS_FEET)
    3458           2 :         osUnits = ECWTranslateFromCellSizeUnits(ECW_CELL_UNITS_METERS);
    3459             : 
    3460          14 :     m_osDatumCode = psFileInfo->szDatum;
    3461          14 :     m_osProjCode = psFileInfo->szProjection;
    3462          14 :     m_osUnitsCode = osUnits;
    3463          14 :     if (oSRS.importFromERM(psFileInfo->szProjection, psFileInfo->szDatum,
    3464          14 :                            osUnits) == OGRERR_NONE)
    3465             :     {
    3466          14 :         m_oSRS = std::move(oSRS);
    3467          14 :         m_oSRS.SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
    3468             :     }
    3469             : 
    3470          14 :     CPLErrorReset(); /* see #4187 */
    3471             : }
    3472             : 
    3473             : /************************************************************************/
    3474             : /*                        ECWTranslateFromWKT()                         */
    3475             : /************************************************************************/
    3476             : 
    3477          27 : int ECWTranslateFromWKT(const OGRSpatialReference *poSRS, char *pszProjection,
    3478             :                         int nProjectionLen, char *pszDatum, int nDatumLen,
    3479             :                         char *pszUnits)
    3480             : 
    3481             : {
    3482          54 :     OGRSpatialReference oSRS;
    3483             : 
    3484          27 :     strcpy(pszProjection, "RAW");
    3485          27 :     strcpy(pszDatum, "RAW");
    3486          27 :     strcpy(pszUnits, "METERS");
    3487             : 
    3488          27 :     if (poSRS == nullptr || poSRS->IsEmpty())
    3489           0 :         return FALSE;
    3490             : 
    3491          27 :     oSRS = *poSRS;
    3492             : 
    3493          27 :     if (oSRS.IsLocal())
    3494           0 :         return TRUE;
    3495             : 
    3496             :     /* -------------------------------------------------------------------- */
    3497             :     /*      Do we have an overall EPSG number for this coordinate system?   */
    3498             :     /* -------------------------------------------------------------------- */
    3499          27 :     const char *pszAuthorityCode = nullptr;
    3500          27 :     const char *pszAuthorityName = nullptr;
    3501          27 :     UINT32 nEPSGCode = 0;
    3502             : 
    3503          27 :     if (oSRS.IsProjected())
    3504             :     {
    3505           8 :         pszAuthorityCode = oSRS.GetAuthorityCode("PROJCS");
    3506           8 :         pszAuthorityName = oSRS.GetAuthorityName("PROJCS");
    3507             :     }
    3508          19 :     else if (oSRS.IsGeographic())
    3509             :     {
    3510          19 :         pszAuthorityCode = oSRS.GetAuthorityCode("GEOGCS");
    3511          19 :         pszAuthorityName = oSRS.GetAuthorityName("GEOGCS");
    3512             :     }
    3513             : 
    3514          27 :     if (pszAuthorityName != nullptr && EQUAL(pszAuthorityName, "EPSG") &&
    3515          11 :         pszAuthorityCode != nullptr && atoi(pszAuthorityCode) > 0)
    3516          11 :         nEPSGCode = (UINT32)atoi(pszAuthorityCode);
    3517             : 
    3518          27 :     if (nEPSGCode != 0)
    3519             :     {
    3520          11 :         char *pszEPSGProj = nullptr, *pszEPSGDatum = nullptr;
    3521             :         CNCSError oErr = CNCSJP2FileView::GetProjectionAndDatum(
    3522          11 :             atoi(pszAuthorityCode), &pszEPSGProj, &pszEPSGDatum);
    3523             : 
    3524          22 :         CPLDebug("ECW", "GetGDTProjDat(%d) = %s/%s", atoi(pszAuthorityCode),
    3525          11 :                  pszEPSGProj ? pszEPSGProj : "(null)",
    3526          11 :                  pszEPSGDatum ? pszEPSGDatum : "(null)");
    3527             : 
    3528          22 :         if (oErr.GetErrorNumber() == NCS_SUCCESS && pszEPSGProj != nullptr &&
    3529          11 :             pszEPSGDatum != nullptr)
    3530             :         {
    3531          11 :             strncpy(pszProjection, pszEPSGProj, nProjectionLen);
    3532          11 :             strncpy(pszDatum, pszEPSGDatum, nDatumLen);
    3533          11 :             pszProjection[nProjectionLen - 1] = 0;
    3534          11 :             pszDatum[nDatumLen - 1] = 0;
    3535          11 :             NCSFree(pszEPSGProj);
    3536          11 :             NCSFree(pszEPSGDatum);
    3537          11 :             return TRUE;
    3538             :         }
    3539             : 
    3540           0 :         NCSFree(pszEPSGProj);
    3541           0 :         NCSFree(pszEPSGDatum);
    3542             :     }
    3543             : 
    3544             :     /* -------------------------------------------------------------------- */
    3545             :     /*      Fallback to translating based on the ecw_cs.wkt file, and       */
    3546             :     /*      various jiffy rules.                                            */
    3547             :     /* -------------------------------------------------------------------- */
    3548             : 
    3549          16 :     return oSRS.exportToERM(pszProjection, pszDatum, pszUnits) == OGRERR_NONE;
    3550             : }
    3551             : 
    3552             : /************************************************************************/
    3553             : /*                    ECWTranslateToCellSizeUnits()                     */
    3554             : /************************************************************************/
    3555             : 
    3556          28 : CellSizeUnits ECWTranslateToCellSizeUnits(const char *pszUnits)
    3557             : {
    3558          28 :     if (EQUAL(pszUnits, "METERS"))
    3559          26 :         return ECW_CELL_UNITS_METERS;
    3560           2 :     else if (EQUAL(pszUnits, "DEGREES"))
    3561           0 :         return ECW_CELL_UNITS_DEGREES;
    3562           2 :     else if (EQUAL(pszUnits, "FEET"))
    3563           2 :         return ECW_CELL_UNITS_FEET;
    3564           0 :     else if (EQUAL(pszUnits, "UNKNOWN"))
    3565           0 :         return ECW_CELL_UNITS_UNKNOWN;
    3566           0 :     else if (EQUAL(pszUnits, "INVALID"))
    3567           0 :         return ECW_CELL_UNITS_INVALID;
    3568             :     else
    3569             :     {
    3570           0 :         CPLError(CE_Warning, CPLE_AppDefined,
    3571             :                  "Unrecognized value for UNITS : %s", pszUnits);
    3572           0 :         return ECW_CELL_UNITS_INVALID;
    3573             :     }
    3574             : }
    3575             : 
    3576             : /************************************************************************/
    3577             : /*                  ECWGetColorInterpretationByName()                   */
    3578             : /************************************************************************/
    3579             : 
    3580         228 : GDALColorInterp ECWGetColorInterpretationByName(const char *pszName)
    3581             : {
    3582         228 :     if (EQUAL(pszName, NCS_BANDDESC_AllOpacity))
    3583           7 :         return GCI_AlphaBand;
    3584         221 :     else if (EQUAL(pszName, NCS_BANDDESC_Blue))
    3585          51 :         return GCI_BlueBand;
    3586         170 :     else if (EQUAL(pszName, NCS_BANDDESC_Green))
    3587          51 :         return GCI_GreenBand;
    3588         119 :     else if (EQUAL(pszName, NCS_BANDDESC_Red))
    3589          51 :         return GCI_RedBand;
    3590          68 :     else if (EQUAL(pszName, NCS_BANDDESC_Greyscale))
    3591           0 :         return GCI_GrayIndex;
    3592          68 :     else if (EQUAL(pszName, NCS_BANDDESC_GreyscaleOpacity))
    3593           0 :         return GCI_AlphaBand;
    3594          68 :     return GCI_Undefined;
    3595             : }
    3596             : 
    3597             : /************************************************************************/
    3598             : /*                   ECWGetColorInterpretationName()                    */
    3599             : /************************************************************************/
    3600             : 
    3601          59 : const char *ECWGetColorInterpretationName(GDALColorInterp eColorInterpretation,
    3602             :                                           int nBandNumber)
    3603             : {
    3604          59 :     const char *pszResult = nullptr;
    3605          59 :     switch (eColorInterpretation)
    3606             :     {
    3607           0 :         case GCI_AlphaBand:
    3608           0 :             pszResult = NCS_BANDDESC_AllOpacity;
    3609           0 :             break;
    3610          17 :         case GCI_GrayIndex:
    3611          17 :             pszResult = NCS_BANDDESC_Greyscale;
    3612          17 :             break;
    3613          12 :         case GCI_RedBand:
    3614             :         case GCI_GreenBand:
    3615             :         case GCI_BlueBand:
    3616          12 :             pszResult = GDALGetColorInterpretationName(eColorInterpretation);
    3617          12 :             break;
    3618          30 :         case GCI_Undefined:
    3619          30 :             if (nBandNumber == 0)
    3620             :             {
    3621          20 :                 pszResult = "Red";
    3622             :             }
    3623          10 :             else if (nBandNumber == 1)
    3624             :             {
    3625           4 :                 pszResult = "Green";
    3626             :             }
    3627           6 :             else if (nBandNumber == 2)
    3628             :             {
    3629           3 :                 pszResult = "Blue";
    3630             :             }
    3631             :             else
    3632             :             {
    3633           3 :                 pszResult = CPLSPrintf(NCS_BANDDESC_Band, nBandNumber + 1);
    3634             :             }
    3635          30 :             break;
    3636           0 :         default:
    3637           0 :             pszResult = CPLSPrintf(NCS_BANDDESC_Band, nBandNumber + 1);
    3638           0 :             break;
    3639             :     }
    3640          59 :     return pszResult;
    3641             : }
    3642             : 
    3643             : /************************************************************************/
    3644             : /*                        ECWGetColorSpaceName()                        */
    3645             : /************************************************************************/
    3646             : 
    3647         123 : const char *ECWGetColorSpaceName(NCSFileColorSpace colorSpace)
    3648             : {
    3649         123 :     switch (colorSpace)
    3650             :     {
    3651           0 :         case NCSCS_NONE:
    3652           0 :             return "NONE";
    3653             :             break;
    3654          54 :         case NCSCS_GREYSCALE:
    3655          54 :             return "GREYSCALE";
    3656             :             break;
    3657           0 :         case NCSCS_YUV:
    3658           0 :             return "YUV";
    3659             :             break;
    3660          36 :         case NCSCS_MULTIBAND:
    3661          36 :             return "MULTIBAND";
    3662             :             break;
    3663          33 :         case NCSCS_sRGB:
    3664          33 :             return "RGB";
    3665             :             break;
    3666           0 :         case NCSCS_YCbCr:
    3667           0 :             return "YCbCr";
    3668             :             break;
    3669           0 :         default:
    3670           0 :             return "unrecognized";
    3671             :     }
    3672             : }
    3673             : 
    3674             : /************************************************************************/
    3675             : /*                   ECWTranslateFromCellSizeUnits()                    */
    3676             : /************************************************************************/
    3677             : 
    3678          77 : const char *ECWTranslateFromCellSizeUnits(CellSizeUnits eUnits)
    3679             : {
    3680          77 :     if (eUnits == ECW_CELL_UNITS_METERS)
    3681          71 :         return "METERS";
    3682           6 :     else if (eUnits == ECW_CELL_UNITS_DEGREES)
    3683           2 :         return "DEGREES";
    3684           4 :     else if (eUnits == ECW_CELL_UNITS_FEET)
    3685           4 :         return "FEET";
    3686           0 :     else if (eUnits == ECW_CELL_UNITS_UNKNOWN)
    3687           0 :         return "UNKNOWN";
    3688             :     else
    3689           0 :         return "INVALID";
    3690             : }
    3691             : 
    3692             : /************************************************************************/
    3693             : /*                           ECWInitialize()                            */
    3694             : /*                                                                      */
    3695             : /*      Initialize NCS library.  We try to defer this as late as        */
    3696             : /*      possible since de-initializing it seems to be expensive/slow    */
    3697             : /*      on some system.                                                 */
    3698             : /************************************************************************/
    3699             : 
    3700         193 : void ECWInitialize()
    3701             : 
    3702             : {
    3703         193 :     CPLMutexHolder oHolder(&hECWDatasetMutex);
    3704             : 
    3705         193 :     if (bNCSInitialized)
    3706         187 :         return;
    3707             : 
    3708             : #ifndef _WIN32
    3709           6 :     NCSecwInit();
    3710             : #endif
    3711           6 :     bNCSInitialized = TRUE;
    3712             : 
    3713             :     /* -------------------------------------------------------------------- */
    3714             :     /*      This will disable automatic conversion of YCbCr to RGB by       */
    3715             :     /*      the toolkit.                                                    */
    3716             :     /* -------------------------------------------------------------------- */
    3717           6 :     if (!CPLTestBool(CPLGetConfigOption("CONVERT_YCBCR_TO_RGB", "YES")))
    3718           0 :         NCSecwSetConfig(NCSCFG_JP2_MANAGE_ICC, FALSE);
    3719             : #if ECWSDK_VERSION >= 50
    3720             :     NCSecwSetConfig(NCSCFG_ECWP_CLIENT_HTTP_USER_AGENT,
    3721             :                     "ECW GDAL Driver/" NCS_ECWJP2_FULL_VERSION_STRING_DOT_DEL);
    3722             : #endif
    3723             :     /* -------------------------------------------------------------------- */
    3724             :     /*      Initialize cache memory limit.  Default is apparently 1/4 RAM.  */
    3725             :     /* -------------------------------------------------------------------- */
    3726             :     const char *pszEcwCacheSize =
    3727           6 :         CPLGetConfigOption("GDAL_ECW_CACHE_MAXMEM", nullptr);
    3728           6 :     if (pszEcwCacheSize == nullptr)
    3729           6 :         pszEcwCacheSize = CPLGetConfigOption("ECW_CACHE_MAXMEM", nullptr);
    3730             : 
    3731           6 :     if (pszEcwCacheSize != nullptr)
    3732           0 :         NCSecwSetConfig(NCSCFG_CACHE_MAXMEM, (UINT32)atoi(pszEcwCacheSize));
    3733             : 
    3734             : /* -------------------------------------------------------------------- */
    3735             : /*      Version 3.x and 4.x of the ECWJP2 SDK did not resolve datum and */
    3736             : /*      projection to EPSG code using internal mapping.                 */
    3737             : /*      Version 5.x do so we provide means to achieve old               */
    3738             : /*      behavior.                                                      */
    3739             : /* -------------------------------------------------------------------- */
    3740             : #if ECWSDK_VERSION >= 50
    3741             :     if (CPLTestBool(CPLGetConfigOption("ECW_DO_NOT_RESOLVE_DATUM_PROJECTION",
    3742             :                                        "NO")) == TRUE)
    3743             :         NCSecwSetConfig(NCSCFG_PROJECTION_FORMAT,
    3744             :                         NCS_PROJECTION_ERMAPPER_FORMAT);
    3745             : #endif
    3746             :     /* -------------------------------------------------------------------- */
    3747             :     /*      Allow configuration of a local cache based on configuration     */
    3748             :     /*      options.  Setting the location turns things on.                 */
    3749             :     /* -------------------------------------------------------------------- */
    3750           6 :     const char *pszOpt = nullptr;
    3751           6 :     CPL_IGNORE_RET_VAL(pszOpt);
    3752             : 
    3753             : #if ECWSDK_VERSION >= 40
    3754             :     pszOpt = CPLGetConfigOption("ECWP_CACHE_SIZE_MB", nullptr);
    3755             :     if (pszOpt)
    3756             :         NCSecwSetConfig(NCSCFG_ECWP_CACHE_SIZE_MB, (INT32)atoi(pszOpt));
    3757             : 
    3758             :     pszOpt = CPLGetConfigOption("ECWP_CACHE_LOCATION", nullptr);
    3759             :     if (pszOpt)
    3760             :     {
    3761             :         NCSecwSetConfig(NCSCFG_ECWP_CACHE_LOCATION, pszOpt);
    3762             :         NCSecwSetConfig(NCSCFG_ECWP_CACHE_ENABLED, (BOOLEAN)TRUE);
    3763             :     }
    3764             : #endif
    3765             : 
    3766             :     /* -------------------------------------------------------------------- */
    3767             :     /*      Various other configuration items.                              */
    3768             :     /* -------------------------------------------------------------------- */
    3769           6 :     pszOpt = CPLGetConfigOption("ECWP_BLOCKING_TIME_MS", nullptr);
    3770           6 :     if (pszOpt)
    3771           0 :         NCSecwSetConfig(NCSCFG_BLOCKING_TIME_MS, (NCSTimeStampMs)atoi(pszOpt));
    3772             : 
    3773             :     // I believe 10s means we wait for complete data back from
    3774             :     // ECWP almost all the time which is good for our blocking model.
    3775           6 :     pszOpt = CPLGetConfigOption("ECWP_REFRESH_TIME_MS", "10000");
    3776           6 :     if (pszOpt)
    3777           6 :         NCSecwSetConfig(NCSCFG_REFRESH_TIME_MS, (NCSTimeStampMs)atoi(pszOpt));
    3778             : 
    3779           6 :     pszOpt = CPLGetConfigOption("ECW_TEXTURE_DITHER", nullptr);
    3780           6 :     if (pszOpt)
    3781           0 :         NCSecwSetConfig(NCSCFG_TEXTURE_DITHER, (BOOLEAN)CPLTestBool(pszOpt));
    3782             : 
    3783           6 :     pszOpt = CPLGetConfigOption("ECW_FORCE_FILE_REOPEN", nullptr);
    3784           6 :     if (pszOpt)
    3785           0 :         NCSecwSetConfig(NCSCFG_FORCE_FILE_REOPEN, (BOOLEAN)CPLTestBool(pszOpt));
    3786             : 
    3787           6 :     pszOpt = CPLGetConfigOption("ECW_CACHE_MAXOPEN", nullptr);
    3788           6 :     if (pszOpt)
    3789           0 :         NCSecwSetConfig(NCSCFG_CACHE_MAXOPEN, (UINT32)atoi(pszOpt));
    3790             : 
    3791             : #if ECWSDK_VERSION >= 40
    3792             :     pszOpt = CPLGetConfigOption("ECW_AUTOGEN_J2I", nullptr);
    3793             :     if (pszOpt)
    3794             :         NCSecwSetConfig(NCSCFG_JP2_AUTOGEN_J2I, (BOOLEAN)CPLTestBool(pszOpt));
    3795             : 
    3796             :     pszOpt = CPLGetConfigOption("ECW_OPTIMIZE_USE_NEAREST_NEIGHBOUR", nullptr);
    3797             :     if (pszOpt)
    3798             :         NCSecwSetConfig(NCSCFG_OPTIMIZE_USE_NEAREST_NEIGHBOUR,
    3799             :                         (BOOLEAN)CPLTestBool(pszOpt));
    3800             : 
    3801             :     pszOpt = CPLGetConfigOption("ECW_RESILIENT_DECODING", nullptr);
    3802             :     if (pszOpt)
    3803             :         NCSecwSetConfig(NCSCFG_RESILIENT_DECODING,
    3804             :                         (BOOLEAN)CPLTestBool(pszOpt));
    3805             : #endif
    3806             : }
    3807             : 
    3808             : /************************************************************************/
    3809             : /*                         GDALDeregister_ECW()                         */
    3810             : /************************************************************************/
    3811             : 
    3812           9 : static void GDALDeregister_ECW(GDALDriver *)
    3813             : 
    3814             : {
    3815             :     /* For unknown reason, this cleanup can take up to 3 seconds (see #3134) for
    3816             :      * SDK 3.3. */
    3817             :     /* Not worth it */
    3818             : #if ECWSDK_VERSION >= 50
    3819             : #ifndef _WIN32
    3820             :     if (bNCSInitialized)
    3821             :     {
    3822             :         bNCSInitialized = FALSE;
    3823             :         NCSecwShutdown();
    3824             :     }
    3825             : #endif
    3826             : #endif
    3827             : 
    3828           9 :     if (hECWDatasetMutex != nullptr)
    3829             :     {
    3830           4 :         CPLDestroyMutex(hECWDatasetMutex);
    3831           4 :         hECWDatasetMutex = nullptr;
    3832             :     }
    3833           9 : }
    3834             : 
    3835             : #if ECWSDK_VERSION < 40
    3836             : namespace
    3837             : {
    3838          82 : NCSError NCS_CALL EcwFileOpenForReadACB(char *szFileName, void **ppClientData)
    3839             : {
    3840          82 :     *ppClientData = VSIFOpenL(szFileName, "rb");
    3841          82 :     if (*ppClientData == nullptr)
    3842             :     {
    3843          15 :         return NCS_FILE_OPEN_FAILED;
    3844             :     }
    3845             :     else
    3846             :     {
    3847          67 :         return NCS_SUCCESS;
    3848             :     }
    3849             : }
    3850             : 
    3851           0 : NCSError NCS_CALL EcwFileOpenForReadWCB(wchar_t *wszFileName,
    3852             :                                         void **ppClientData)
    3853             : {
    3854             :     char *szFileName =
    3855           0 :         CPLRecodeFromWChar(wszFileName, CPL_ENC_UCS2, CPL_ENC_UTF8);
    3856           0 :     *ppClientData = VSIFOpenL(szFileName, "rb");
    3857           0 :     CPLFree(szFileName);
    3858           0 :     if (*ppClientData == nullptr)
    3859             :     {
    3860           0 :         return NCS_FILE_OPEN_FAILED;
    3861             :     }
    3862             :     else
    3863             :     {
    3864           0 :         return NCS_SUCCESS;
    3865             :     }
    3866             : }
    3867             : 
    3868          67 : NCSError NCS_CALL EcwFileCloseCB(void *pClientData)
    3869             : {
    3870          67 :     if (0 == VSIFCloseL(reinterpret_cast<VSILFILE *>(pClientData)))
    3871             :     {
    3872          67 :         return NCS_SUCCESS;
    3873             :     }
    3874             :     else
    3875             :     {
    3876           0 :         return NCS_FILE_CLOSE_ERROR;
    3877             :     }
    3878             : }
    3879             : 
    3880        1557 : NCSError NCS_CALL EcwFileReadCB(void *pClientData, void *pBuffer,
    3881             :                                 UINT32 nLength)
    3882             : {
    3883        1557 :     if (nLength == VSIFReadL(pBuffer, 1, nLength,
    3884             :                              reinterpret_cast<VSILFILE *>(pClientData)))
    3885             :     {
    3886        1557 :         return NCS_SUCCESS;
    3887             :     }
    3888             :     else
    3889             :     {
    3890           0 :         return NCS_FILE_IO_ERROR;
    3891             :     }
    3892             : }
    3893             : 
    3894         208 : NCSError NCS_CALL EcwFileSeekCB(void *pClientData, UINT64 nOffset)
    3895             : {
    3896         208 :     if (0 ==
    3897         208 :         VSIFSeekL(reinterpret_cast<VSILFILE *>(pClientData), nOffset, SEEK_SET))
    3898             :     {
    3899         208 :         return NCS_SUCCESS;
    3900             :     }
    3901             :     else
    3902             :     {
    3903           0 :         return NCS_FILE_SEEK_ERROR;
    3904             :     }
    3905             : }
    3906             : 
    3907         108 : NCSError NCS_CALL EcwFileTellCB(void *pClientData, UINT64 *pOffset)
    3908             : {
    3909         108 :     *pOffset = VSIFTellL(reinterpret_cast<VSILFILE *>(pClientData));
    3910         108 :     return NCS_SUCCESS;
    3911             : }
    3912             : }  // namespace
    3913             : #endif  // ECWSDK_VERSION < 40
    3914             : 
    3915             : /************************************************************************/
    3916             : /*                          GDALRegister_ECW()                          */
    3917             : /************************************************************************/
    3918             : 
    3919          14 : void GDALRegister_ECW()
    3920             : 
    3921             : {
    3922          14 :     if (!GDAL_CHECK_VERSION("ECW driver"))
    3923           0 :         return;
    3924             : 
    3925          14 :     if (GDALGetDriverByName(ECW_DRIVER_NAME) != nullptr)
    3926           0 :         return;
    3927             : #if ECWSDK_VERSION < 40
    3928          14 :     CNCSJPCFileIOStream::SetIOCallbacks(
    3929             :         EcwFileOpenForReadACB, EcwFileOpenForReadWCB, EcwFileCloseCB,
    3930             :         EcwFileReadCB, EcwFileSeekCB, EcwFileTellCB);
    3931             : #endif  // ECWSDK_VERSION < 40
    3932          14 :     GDALDriver *poDriver = new GDALDriver();
    3933             : 
    3934          14 :     ECWDriverSetCommonMetadata(poDriver);
    3935          14 :     poDriver->pfnOpen = ECWDataset::OpenECW;
    3936          14 :     poDriver->pfnUnloadDriver = GDALDeregister_ECW;
    3937             : #ifdef HAVE_COMPRESS
    3938             :     // The create method does not work with SDK 3.3 ( crash in
    3939             :     // CNCSJP2FileView::WriteLineBIL() due to m_pFile being nullptr ).
    3940             : #if ECWSDK_VERSION >= 50
    3941             :     poDriver->pfnCreate = ECWCreateECW;
    3942             : #endif
    3943          14 :     poDriver->pfnCreateCopy = ECWCreateCopyECW;
    3944             : #endif
    3945             : 
    3946          14 :     GetGDALDriverManager()->RegisterDriver(poDriver);
    3947             : }
    3948             : 
    3949             : /************************************************************************/
    3950             : /*                      GDALRegister_ECW_JP2ECW()                       */
    3951             : /*                                                                      */
    3952             : /*      This function exists so that when built as a plugin, there      */
    3953             : /*      is a function that will register both drivers.                  */
    3954             : /************************************************************************/
    3955             : 
    3956          14 : void GDALRegister_ECW_JP2ECW()
    3957             : 
    3958             : {
    3959          14 :     GDALRegister_ECW();
    3960          14 :     GDALRegister_JP2ECW();
    3961          14 : }
    3962             : 
    3963             : /************************************************************************/
    3964             : /*                       ECWDatasetOpenJPEG2000()                       */
    3965             : /************************************************************************/
    3966          29 : GDALDataset *ECWDatasetOpenJPEG2000(GDALOpenInfo *poOpenInfo)
    3967             : {
    3968          29 :     return ECWDataset::OpenJPEG2000(poOpenInfo);
    3969             : }
    3970             : 
    3971             : /************************************************************************/
    3972             : /*                        GDALRegister_JP2ECW()                         */
    3973             : /************************************************************************/
    3974          14 : void GDALRegister_JP2ECW()
    3975             : 
    3976             : {
    3977          14 :     if (!GDAL_CHECK_VERSION("JP2ECW driver"))
    3978           0 :         return;
    3979             : 
    3980          14 :     if (GDALGetDriverByName(JP2ECW_DRIVER_NAME) != nullptr)
    3981           0 :         return;
    3982             : 
    3983          14 :     GDALDriver *poDriver = new GDALDriver();
    3984          14 :     JP2ECWDriverSetCommonMetadata(poDriver);
    3985          14 :     poDriver->pfnOpen = ECWDataset::OpenJPEG2000;
    3986             : #ifdef HAVE_COMPRESS
    3987          14 :     poDriver->pfnCreate = ECWCreateJPEG2000;
    3988          14 :     poDriver->pfnCreateCopy = ECWCreateCopyJPEG2000;
    3989             : #endif
    3990             : 
    3991          14 :     GetGDALDriverManager()->RegisterDriver(poDriver);
    3992             : }

Generated by: LCOV version 1.14