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

Generated by: LCOV version 1.14