• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

OSGeo / gdal / 15885686134

25 Jun 2025 07:44PM UTC coverage: 71.084%. Remained the same
15885686134

push

github

rouault
gdal_priv.h: fix C++11 compatibility

573814 of 807237 relevant lines covered (71.08%)

250621.56 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

82.1
/frmts/ecw/ecwdataset.cpp
1
/******************************************************************************
2
 *
3
 * Project:  GDAL
4
 * Purpose:  ECW (ERDAS Wavelet Compression Format) Driver
5
 * Author:   Frank Warmerdam, warmerdam@pobox.com
6
 *
7
 ******************************************************************************
8
 * Copyright (c) 2001, Frank Warmerdam <warmerdam@pobox.com>
9
 * Copyright (c) 2007-2013, Even Rouault <even dot rouault at spatialys.com>
10
 *
11
 * SPDX-License-Identifier: MIT
12
 ****************************************************************************/
13

14
// ncsjpcbuffer.h needs the min and max macros.
15
#undef NOMINMAX
16

17
#include "cpl_minixml.h"
18
#include "gdal_ecw.h"
19
#include "gdal_frmts.h"
20
#include "ogr_spatialref.h"
21
#include "ogr_api.h"
22

23
#include "memdataset.h"
24

25
#include "ecwdrivercore.h"
26

27
#include <algorithm>
28
#include <cmath>
29

30
#undef NOISY_DEBUG
31

32
static CPLMutex *hECWDatasetMutex = nullptr;
33
static int bNCSInitialized = FALSE;
34

35
void ECWInitialize(void);
36

37
constexpr int DEFAULT_BLOCK_SIZE = 256;
38

39
GDALDataset *ECWDatasetOpenJPEG2000(GDALOpenInfo *poOpenInfo);
40

41
/************************************************************************/
42
/*                           ECWReportError()                           */
43
/************************************************************************/
44

45
void ECWReportError(CNCSError &oErr, const char *pszMsg)
1✔
46
{
47
#if ECWSDK_VERSION < 50
48
    char *pszErrorMessage = oErr.GetErrorMessage();
1✔
49
    CPLError(CE_Failure, CPLE_AppDefined, "%s%s", pszMsg, pszErrorMessage);
1✔
50
    NCSFree(pszErrorMessage);
1✔
51
#else
52
    CPLError(CE_Failure, CPLE_AppDefined, "%s%s", pszMsg,
53
             NCSGetLastErrorText(oErr));
54
#endif
55
}
1✔
56

57
/************************************************************************/
58
/*                           ECWRasterBand()                            */
59
/************************************************************************/
60

61
ECWRasterBand::ECWRasterBand(ECWDataset *poDSIn, int nBandIn, int iOverviewIn,
297✔
62
                             char **papszOpenOptions)
297✔
63

64
{
65
    this->poDS = poDSIn;
297✔
66
    poGDS = poDSIn;
297✔
67

68
    this->iOverview = iOverviewIn;
297✔
69
    this->nBand = nBandIn;
297✔
70
    eDataType = poDSIn->eRasterDataType;
297✔
71

72
    nRasterXSize = poDS->GetRasterXSize() / (1 << (iOverview + 1));
297✔
73
    nRasterYSize = poDS->GetRasterYSize() / (1 << (iOverview + 1));
297✔
74

75
#if ECWSDK_VERSION >= 51
76
// undefine min macro if any
77
#ifdef min
78
#undef min
79
#endif
80
    if (poDSIn->bIsJPEG2000 && poDSIn->poFileView)
81
    {
82
        UINT32 nTileWidth = 0;
83
        poDSIn->poFileView->GetParameter(
84
            const_cast<char *>("JPC:DECOMPRESS:TILESIZE:X"), &nTileWidth);
85
        if (nTileWidth <= static_cast<UINT32>(INT_MAX))
86
        {
87
            nBlockXSize = static_cast<int>(nTileWidth);
88
        }
89
        nBlockXSize = MIN(nBlockXSize, nRasterXSize);
90

91
        UINT32 nTileHeight = 0;
92
        poDSIn->poFileView->GetParameter(
93
            const_cast<char *>("JPC:DECOMPRESS:TILESIZE:Y"), &nTileHeight);
94
        if (nTileHeight <= static_cast<UINT32>(INT_MAX))
95
        {
96
            nBlockYSize = static_cast<int>(nTileHeight);
97
        }
98
        nBlockYSize = MIN(nBlockYSize, nRasterYSize);
99
    }
100
#endif
101

102
    // Slightly arbitrary value. Too large values would defeat the purpose
103
    // of the block concept.
104
    constexpr int LIMIT_FOR_BLOCK_SIZE = 2048;
297✔
105
    if (nBlockXSize <= 0 || nBlockYSize <= 0 ||
297✔
106
        nBlockXSize > LIMIT_FOR_BLOCK_SIZE ||
×
107
        nBlockYSize > LIMIT_FOR_BLOCK_SIZE)
×
108
    {
109
        nBlockXSize = DEFAULT_BLOCK_SIZE;
297✔
110
        nBlockYSize = DEFAULT_BLOCK_SIZE;
297✔
111
    }
112

113
    /* -------------------------------------------------------------------- */
114
    /*      Work out band color interpretation.                             */
115
    /* -------------------------------------------------------------------- */
116
    if (poDSIn->psFileInfo->eColorSpace == NCSCS_NONE)
297✔
117
        eBandInterp = GCI_Undefined;
×
118
    else if (poDSIn->psFileInfo->eColorSpace == NCSCS_GREYSCALE)
297✔
119
    {
120
        eBandInterp = GCI_GrayIndex;
69✔
121
        // we could also have alpha band.
122
        if (strcmp(poDSIn->psFileInfo->pBands[nBand - 1].szDesc,
69✔
123
                   NCS_BANDDESC_AllOpacity) == 0 ||
69✔
124
            strcmp(poDSIn->psFileInfo->pBands[nBand - 1].szDesc,
69✔
125
                   NCS_BANDDESC_GreyscaleOpacity) == 0)
126
        {
127
            eBandInterp = GCI_AlphaBand;
×
128
        }
129
    }
130
    else if (poDSIn->psFileInfo->eColorSpace == NCSCS_MULTIBAND)
228✔
131
    {
132
        eBandInterp = ECWGetColorInterpretationByName(
62✔
133
            poDSIn->psFileInfo->pBands[nBand - 1].szDesc);
62✔
134
    }
135
    else if (poDSIn->psFileInfo->eColorSpace == NCSCS_sRGB)
166✔
136
    {
137
        eBandInterp = ECWGetColorInterpretationByName(
332✔
138
            poDSIn->psFileInfo->pBands[nBand - 1].szDesc);
166✔
139
        if (eBandInterp == GCI_Undefined)
166✔
140
        {
141
            if (nBand == 1)
6✔
142
                eBandInterp = GCI_RedBand;
2✔
143
            else if (nBand == 2)
4✔
144
                eBandInterp = GCI_GreenBand;
2✔
145
            else if (nBand == 3)
2✔
146
                eBandInterp = GCI_BlueBand;
2✔
147
            else if (nBand == 4)
×
148
            {
149
                if (strcmp(poDSIn->psFileInfo->pBands[nBand - 1].szDesc,
×
150
                           NCS_BANDDESC_AllOpacity) == 0)
151
                    eBandInterp = GCI_AlphaBand;
×
152
                else
153
                    eBandInterp = GCI_Undefined;
×
154
            }
155
            else
156
            {
157
                eBandInterp = GCI_Undefined;
×
158
            }
159
        }
160
    }
161
    else if (poDSIn->psFileInfo->eColorSpace == NCSCS_YCbCr)
×
162
    {
163
        if (CPLTestBool(CPLGetConfigOption("CONVERT_YCBCR_TO_RGB", "YES")))
×
164
        {
165
            if (nBand == 1)
×
166
                eBandInterp = GCI_RedBand;
×
167
            else if (nBand == 2)
×
168
                eBandInterp = GCI_GreenBand;
×
169
            else if (nBand == 3)
×
170
                eBandInterp = GCI_BlueBand;
×
171
            else
172
                eBandInterp = GCI_Undefined;
×
173
        }
174
        else
175
        {
176
            if (nBand == 1)
×
177
                eBandInterp = GCI_YCbCr_YBand;
×
178
            else if (nBand == 2)
×
179
                eBandInterp = GCI_YCbCr_CbBand;
×
180
            else if (nBand == 3)
×
181
                eBandInterp = GCI_YCbCr_CrBand;
×
182
            else
183
                eBandInterp = GCI_Undefined;
×
184
        }
185
    }
186
    else
187
        eBandInterp = GCI_Undefined;
×
188

189
    /* -------------------------------------------------------------------- */
190
    /*      If this is the base level, create a set of overviews.           */
191
    /* -------------------------------------------------------------------- */
192
    if (iOverview == -1)
297✔
193
    {
194
        int i;
195
        for (i = 0; nRasterXSize / (1 << (i + 1)) > 128 &&
297✔
196
                    nRasterYSize / (1 << (i + 1)) > 128;
100✔
197
             i++)
198
        {
199
            apoOverviews.push_back(
168✔
200
                new ECWRasterBand(poDSIn, nBandIn, i, papszOpenOptions));
84✔
201
        }
202
    }
203

204
    bPromoteTo8Bit =
297✔
205
        poDSIn->psFileInfo->nBands == 4 && nBand == 4 &&
32✔
206
        poDSIn->psFileInfo->pBands[0].nBits == 8 &&
8✔
207
        poDSIn->psFileInfo->pBands[1].nBits == 8 &&
4✔
208
        poDSIn->psFileInfo->pBands[2].nBits == 8 &&
4✔
209
        poDSIn->psFileInfo->pBands[3].nBits == 1 &&
4✔
210
        eBandInterp == GCI_AlphaBand &&
331✔
211
        CPLFetchBool(papszOpenOptions, "1BIT_ALPHA_PROMOTION",
2✔
212
                     CPLTestBool(CPLGetConfigOption(
2✔
213
                         "GDAL_ECW_PROMOTE_1BIT_ALPHA_AS_8BIT", "YES")));
214
    if (bPromoteTo8Bit)
297✔
215
        CPLDebug("ECW", "Fourth (alpha) band is promoted from 1 bit to 8 bit");
1✔
216

217
    if ((poDSIn->psFileInfo->pBands[nBand - 1].nBits % 8) != 0 &&
297✔
218
        !bPromoteTo8Bit)
22✔
219
        GDALPamRasterBand::SetMetadataItem(
21✔
220
            "NBITS",
221
            CPLString().Printf("%d",
42✔
222
                               poDSIn->psFileInfo->pBands[nBand - 1].nBits),
21✔
223
            "IMAGE_STRUCTURE");
224

225
    GDALRasterBand::SetDescription(
297✔
226
        poDSIn->psFileInfo->pBands[nBand - 1].szDesc);
297✔
227
}
297✔
228

229
/************************************************************************/
230
/*                          ~ECWRasterBand()                           */
231
/************************************************************************/
232

233
ECWRasterBand::~ECWRasterBand()
594✔
234
{
235
    GDALRasterBand::FlushCache(true);
297✔
236

237
    while (!apoOverviews.empty())
381✔
238
    {
239
        delete apoOverviews.back();
84✔
240
        apoOverviews.pop_back();
84✔
241
    }
242
}
594✔
243

244
/************************************************************************/
245
/*                            GetOverview()                             */
246
/************************************************************************/
247

248
GDALRasterBand *ECWRasterBand::GetOverview(int iOverviewIn)
2✔
249

250
{
251
    if (iOverviewIn >= 0 && iOverviewIn < (int)apoOverviews.size())
2✔
252
        return apoOverviews[iOverviewIn];
2✔
253
    else
254
        return nullptr;
×
255
}
256

257
/************************************************************************/
258
/*                       GetColorInterpretation()                       */
259
/************************************************************************/
260

261
GDALColorInterp ECWRasterBand::GetColorInterpretation()
88✔
262

263
{
264
    return eBandInterp;
88✔
265
}
266

267
/************************************************************************/
268
/*                       SetColorInterpretation()                       */
269
/*                                                                      */
270
/*      This would normally just be used by folks using the ECW code    */
271
/*      to read JP2 streams in other formats (such as NITF) and         */
272
/*      providing their own color interpretation regardless of what     */
273
/*      ECW might think the stream itself says.                         */
274
/************************************************************************/
275

276
CPLErr ECWRasterBand::SetColorInterpretation(GDALColorInterp eNewInterp)
13✔
277

278
{
279
    eBandInterp = eNewInterp;
13✔
280

281
    return CE_None;
13✔
282
}
283

284
/************************************************************************/
285
/*                             AdviseRead()                             */
286
/************************************************************************/
287

288
CPLErr ECWRasterBand::AdviseRead(int nXOff, int nYOff, int nXSize, int nYSize,
×
289
                                 int nBufXSize, int nBufYSize, GDALDataType eDT,
290
                                 char **papszOptions)
291
{
292
    const int nResFactor = 1 << (iOverview + 1);
×
293

294
    return poGDS->AdviseRead(nXOff * nResFactor, nYOff * nResFactor,
×
295
                             nXSize * nResFactor, nYSize * nResFactor,
296
                             nBufXSize, nBufYSize, eDT, 1, &nBand,
297
                             papszOptions);
×
298
}
299

300
// statistics support:
301
#if ECWSDK_VERSION >= 50
302

303
/************************************************************************/
304
/*                       GetDefaultHistogram()                          */
305
/************************************************************************/
306

307
CPLErr ECWRasterBand::GetDefaultHistogram(double *pdfMin, double *pdfMax,
308
                                          int *pnBuckets,
309
                                          GUIntBig **ppanHistogram, int bForce,
310
                                          GDALProgressFunc f,
311
                                          void *pProgressData)
312
{
313
    int bForceCoalesced = bForce;
314
    // If file version is smaller than 3, there will be no statistics in the
315
    // file. But if it is version 3 or higher we don't want underlying
316
    // implementation to compute histogram so we set bForceCoalesced to FALSE.
317
    if (poGDS->psFileInfo->nFormatVersion >= 3)
318
    {
319
        bForceCoalesced = FALSE;
320
    }
321
    // We check if we have PAM histogram. If we have them we return them. This
322
    // will allow to override statistics stored in the file.
323
    CPLErr pamError = GDALPamRasterBand::GetDefaultHistogram(
324
        pdfMin, pdfMax, pnBuckets, ppanHistogram, bForceCoalesced, f,
325
        pProgressData);
326
    if (pamError == CE_None || poGDS->psFileInfo->nFormatVersion < 3 ||
327
        eBandInterp == GCI_AlphaBand)
328
    {
329
        return pamError;
330
    }
331

332
    NCS::CError error = poGDS->StatisticsEnsureInitialized();
333
    if (!error.Success())
334
    {
335
        CPLError(CE_Warning, CPLE_AppDefined,
336
                 "ECWRDataset::StatisticsEnsureInitialized failed in "
337
                 "ECWRasterBand::GetDefaultHistogram. ");
338
        return CE_Failure;
339
    }
340
    GetBandIndexAndCountForStatistics(nStatsBandIndex, nStatsBandCount);
341
    bool bHistogramFromFile = false;
342

343
    if (poGDS->pStatistics != nullptr)
344
    {
345
        NCSBandStats &bandStats =
346
            poGDS->pStatistics->BandsStats[nStatsBandIndex];
347
        if (bandStats.Histogram != nullptr && bandStats.nHistBucketCount > 0)
348
        {
349
            *pnBuckets = bandStats.nHistBucketCount;
350
            *ppanHistogram = static_cast<GUIntBig *>(
351
                VSIMalloc(bandStats.nHistBucketCount * sizeof(GUIntBig)));
352
            for (size_t i = 0; i < bandStats.nHistBucketCount; i++)
353
            {
354
                (*ppanHistogram)[i] =
355
                    static_cast<GUIntBig>(bandStats.Histogram[i]);
356
            }
357
            // JTO: this is not perfect as You can't tell who wrote the
358
            // histogram !!! It will offset it unnecessarily for files with
359
            // hists not modified by GDAL.
360
            const double dfHalfBucket =
361
                (bandStats.fMaxHist - bandStats.fMinHist) /
362
                (2 * (*pnBuckets - 1));
363
            if (pdfMin != nullptr)
364
            {
365
                *pdfMin = bandStats.fMinHist - dfHalfBucket;
366
            }
367
            if (pdfMax != nullptr)
368
            {
369
                *pdfMax = bandStats.fMaxHist + dfHalfBucket;
370
            }
371
            bHistogramFromFile = true;
372
        }
373
    }
374

375
    if (!bHistogramFromFile)
376
    {
377
        if (bForce == TRUE)
378
        {
379
            // compute. Save.
380
            pamError = GDALPamRasterBand::GetDefaultHistogram(
381
                pdfMin, pdfMax, pnBuckets, ppanHistogram, TRUE, f,
382
                pProgressData);
383
            if (pamError == CE_None)
384
            {
385
                const CPLErr error2 = SetDefaultHistogram(
386
                    *pdfMin, *pdfMax, *pnBuckets, *ppanHistogram);
387
                if (error2 != CE_None)
388
                {
389
                    // Histogram is there but we failed to save it back to file.
390
                    CPLError(CE_Warning, CPLE_AppDefined,
391
                             "SetDefaultHistogram failed in "
392
                             "ECWRasterBand::GetDefaultHistogram. Histogram "
393
                             "might not be saved in .ecw file.");
394
                }
395
                return CE_None;
396
            }
397
            return pamError;
398
        }
399
        // No histogram, no forced computation.
400
        return CE_Warning;
401
    }
402
    // Statistics were already there and were used.
403
    return CE_None;
404
}
405

406
/************************************************************************/
407
/*                       SetDefaultHistogram()                          */
408
/************************************************************************/
409

410
CPLErr ECWRasterBand::SetDefaultHistogram(double dfMin, double dfMax,
411
                                          int nBuckets, GUIntBig *panHistogram)
412
{
413
    // Only version 3 supports saving statistics.
414
    if (poGDS->psFileInfo->nFormatVersion < 3 || eBandInterp == GCI_AlphaBand)
415
    {
416
        return GDALPamRasterBand::SetDefaultHistogram(dfMin, dfMax, nBuckets,
417
                                                      panHistogram);
418
    }
419

420
    // determine if there are statistics in PAM file.
421
    double dummy;
422
    int dummy_i;
423
    GUIntBig *dummy_histogram = nullptr;
424
    bool hasPAMDefaultHistogram =
425
        GDALPamRasterBand::GetDefaultHistogram(&dummy, &dummy, &dummy_i,
426
                                               &dummy_histogram, FALSE, nullptr,
427
                                               nullptr) == CE_None;
428
    if (hasPAMDefaultHistogram)
429
    {
430
        VSIFree(dummy_histogram);
431
    }
432

433
    // ECW SDK ignores statistics for opacity bands. So we need to compute
434
    // number of bands without opacity.
435
    GetBandIndexAndCountForStatistics(nStatsBandIndex, nStatsBandCount);
436
    UINT32 bucketCounts[256];
437
    std::fill_n(bucketCounts, nStatsBandCount, 0);
438
    bucketCounts[nStatsBandIndex] = nBuckets;
439

440
    NCS::CError error = poGDS->StatisticsEnsureInitialized();
441
    if (!error.Success())
442
    {
443
        CPLError(CE_Warning, CPLE_AppDefined,
444
                 "ECWRDataset::StatisticsEnsureInitialized failed in "
445
                 "ECWRasterBand::SetDefaultHistogram. Default histogram will "
446
                 "be written to PAM. ");
447
        return GDALPamRasterBand::SetDefaultHistogram(dfMin, dfMax, nBuckets,
448
                                                      panHistogram);
449
    }
450

451
    NCSFileStatistics *pStatistics = poGDS->pStatistics;
452

453
    if (pStatistics == nullptr)
454
    {
455
        error =
456
            NCSEcwInitStatistics(&pStatistics, nStatsBandCount, bucketCounts);
457
        poGDS->bStatisticsDirty = TRUE;
458
        poGDS->pStatistics = pStatistics;
459
        if (!error.Success())
460
        {
461
            CPLError(CE_Warning, CPLE_AppDefined,
462
                     "NCSEcwInitStatistics failed in "
463
                     "ECWRasterBand::SetDefaultHistogram.");
464
            return GDALPamRasterBand::SetDefaultHistogram(
465
                dfMin, dfMax, nBuckets, panHistogram);
466
        }
467
        // no error statistics properly initialized but there were no statistics
468
        // previously.
469
    }
470
    else
471
    {
472
        // is there a room for our band already?
473
        // This should account for following cases:
474
        // 1. Existing histogram (for this or different band) has smaller bucket
475
        // count.
476
        // 2. There is no existing histogram but statistics are set for one or
477
        // more bands (pStatistics->nHistBucketCounts is zero).
478
        if ((int)pStatistics->BandsStats[nStatsBandIndex].nHistBucketCount !=
479
            nBuckets)
480
        {
481
            // no. There is no room. We need more!
482
            NCSFileStatistics *pNewStatistics = nullptr;
483
            for (size_t i = 0; i < pStatistics->nNumberOfBands; i++)
484
            {
485
                bucketCounts[i] = pStatistics->BandsStats[i].nHistBucketCount;
486
            }
487
            bucketCounts[nStatsBandIndex] = nBuckets;
488
            if (nBuckets <
489
                static_cast<int>(
490
                    pStatistics->BandsStats[nStatsBandIndex].nHistBucketCount))
491
            {
492
                pStatistics->BandsStats[nStatsBandIndex].nHistBucketCount =
493
                    nBuckets;
494
            }
495
            error = NCSEcwInitStatistics(&pNewStatistics, nStatsBandCount,
496
                                         bucketCounts);
497
            if (!error.Success())
498
            {
499
                CPLError(CE_Warning, CPLE_AppDefined,
500
                         "NCSEcwInitStatistics failed in "
501
                         "ECWRasterBand::SetDefaultHistogram (reallocate).");
502
                return GDALPamRasterBand::SetDefaultHistogram(
503
                    dfMin, dfMax, nBuckets, panHistogram);
504
            }
505
            // we need to copy existing statistics.
506
            error = NCSEcwCopyStatistics(&pNewStatistics, pStatistics);
507
            if (!error.Success())
508
            {
509
                CPLError(CE_Warning, CPLE_AppDefined,
510
                         "NCSEcwCopyStatistics failed in "
511
                         "ECWRasterBand::SetDefaultHistogram.");
512
                NCSEcwFreeStatistics(pNewStatistics);
513
                return GDALPamRasterBand::SetDefaultHistogram(
514
                    dfMin, dfMax, nBuckets, panHistogram);
515
            }
516
            pNewStatistics->nNumberOfBands = nStatsBandCount;
517
            NCSEcwFreeStatistics(pStatistics);
518
            pStatistics = pNewStatistics;
519
            poGDS->pStatistics = pStatistics;
520
            poGDS->bStatisticsDirty = TRUE;
521
        }
522
    }
523

524
    // at this point we have allocated statistics structure.
525
    double dfHalfBucket = (dfMax - dfMin) / (2 * nBuckets);
526
    pStatistics->BandsStats[nStatsBandIndex].fMinHist =
527
        static_cast<IEEE4>(dfMin + dfHalfBucket);
528
    pStatistics->BandsStats[nStatsBandIndex].fMaxHist =
529
        static_cast<IEEE4>(dfMax - dfHalfBucket);
530
    for (int i = 0; i < nBuckets; i++)
531
    {
532
        pStatistics->BandsStats[nStatsBandIndex].Histogram[i] =
533
            static_cast<UINT64>(panHistogram[i]);
534
    }
535

536
    if (hasPAMDefaultHistogram)
537
    {
538
        CPLError(CE_Debug, CPLE_AppDefined,
539
                 "PAM default histogram will be overwritten.");
540
        return GDALPamRasterBand::SetDefaultHistogram(dfMin, dfMax, nBuckets,
541
                                                      panHistogram);
542
    }
543
    return CE_None;
544
}
545

546
/************************************************************************/
547
/*                   GetBandIndexAndCountForStatistics()                */
548
/************************************************************************/
549

550
void ECWRasterBand::GetBandIndexAndCountForStatistics(int &bandIndex,
551
                                                      int &bandCount) const
552
{
553
    bandIndex = nBand - 1;
554
    bandCount = poGDS->nBands;
555
    for (int i = 0; i < poGDS->nBands; i++)
556
    {
557
        if (poDS->GetRasterBand(i + 1)->GetColorInterpretation() ==
558
            GCI_AlphaBand)
559
        {
560
            bandCount--;
561
            if (i < nBand - 1)
562
            {
563
                bandIndex--;
564
            }
565
        }
566
    }
567
}
568

569
/************************************************************************/
570
/*                           GetMinimum()                               */
571
/************************************************************************/
572

573
double ECWRasterBand::GetMinimum(int *pbSuccess)
574
{
575
    if (poGDS->psFileInfo->nFormatVersion >= 3)
576
    {
577
        NCS::CError error = poGDS->StatisticsEnsureInitialized();
578
        if (error.Success())
579
        {
580
            GetBandIndexAndCountForStatistics(nStatsBandIndex, nStatsBandCount);
581
            if (poGDS->pStatistics != nullptr)
582
            {
583
                NCSBandStats &bandStats =
584
                    poGDS->pStatistics->BandsStats[nStatsBandIndex];
585
                if (!std::isnan(bandStats.fMinVal))
586
                {
587
                    if (pbSuccess)
588
                        *pbSuccess = TRUE;
589
                    return bandStats.fMinVal;
590
                }
591
            }
592
        }
593
    }
594
    return GDALPamRasterBand::GetMinimum(pbSuccess);
595
}
596

597
/************************************************************************/
598
/*                           GetMaximum()                               */
599
/************************************************************************/
600

601
double ECWRasterBand::GetMaximum(int *pbSuccess)
602
{
603
    if (poGDS->psFileInfo->nFormatVersion >= 3)
604
    {
605
        NCS::CError error = poGDS->StatisticsEnsureInitialized();
606
        if (error.Success())
607
        {
608
            GetBandIndexAndCountForStatistics(nStatsBandIndex, nStatsBandCount);
609
            if (poGDS->pStatistics != nullptr)
610
            {
611
                NCSBandStats &bandStats =
612
                    poGDS->pStatistics->BandsStats[nStatsBandIndex];
613
                if (!std::isnan(bandStats.fMaxVal))
614
                {
615
                    if (pbSuccess)
616
                        *pbSuccess = TRUE;
617
                    return bandStats.fMaxVal;
618
                }
619
            }
620
        }
621
    }
622
    return GDALPamRasterBand::GetMaximum(pbSuccess);
623
}
624

625
/************************************************************************/
626
/*                          SetMetadataItem()                           */
627
/************************************************************************/
628

629
CPLErr ECWRasterBand::SetMetadataItem(const char *pszName, const char *pszValue,
630
                                      const char *pszDomain)
631
{
632
    if (EQUAL(pszName, "STATISTICS_VALID_PERCENT"))
633
        return CE_None;
634
    return GDALPamRasterBand::SetMetadataItem(pszName, pszValue, pszDomain);
635
}
636

637
/************************************************************************/
638
/*                          GetStatistics()                             */
639
/************************************************************************/
640

641
CPLErr ECWRasterBand::GetStatistics(int bApproxOK, int bForce, double *pdfMin,
642
                                    double *pdfMax, double *pdfMean,
643
                                    double *padfStdDev)
644
{
645
    int bForceCoalesced = bForce;
646
    // If file version is smaller than 3, there will be no statistics in the
647
    // file. But if it is version 3 or higher we don't want underlying
648
    // implementation to compute histogram so we set bForceCoalesced to FALSE.
649
    if (poGDS->psFileInfo->nFormatVersion >= 3)
650
    {
651
        bForceCoalesced = FALSE;
652
    }
653
    // We check if we have PAM histogram. If we have them we return them. This
654
    // will allow to override statistics stored in the file.
655
    CPLErr pamError = GDALPamRasterBand::GetStatistics(
656
        bApproxOK, bForceCoalesced, pdfMin, pdfMax, pdfMean, padfStdDev);
657
    if (pamError == CE_None || poGDS->psFileInfo->nFormatVersion < 3 ||
658
        eBandInterp == GCI_AlphaBand)
659
    {
660
        return pamError;
661
    }
662

663
    NCS::CError error = poGDS->StatisticsEnsureInitialized();
664
    if (!error.Success())
665
    {
666
        CPLError(CE_Failure, CPLE_AppDefined,
667
                 "ECWRDataset::StatisticsEnsureInitialized failed in "
668
                 "ECWRasterBand::GetStatistic. ");
669
        return CE_Failure;
670
    }
671
    GetBandIndexAndCountForStatistics(nStatsBandIndex, nStatsBandCount);
672
    bool bStatisticsFromFile = false;
673

674
    if (poGDS->pStatistics != nullptr)
675
    {
676
        bStatisticsFromFile = true;
677
        NCSBandStats &bandStats =
678
            poGDS->pStatistics->BandsStats[nStatsBandIndex];
679
        if (pdfMin != nullptr && !std::isnan(bandStats.fMinVal))
680
        {
681
            *pdfMin = bandStats.fMinVal;
682
        }
683
        else
684
        {
685
            bStatisticsFromFile = false;
686
        }
687
        if (pdfMax != nullptr && !std::isnan(bandStats.fMaxVal))
688
        {
689
            *pdfMax = bandStats.fMaxVal;
690
        }
691
        else
692
        {
693
            bStatisticsFromFile = false;
694
        }
695
        if (pdfMean != nullptr && !std::isnan(bandStats.fMeanVal))
696
        {
697
            *pdfMean = bandStats.fMeanVal;
698
        }
699
        else
700
        {
701
            bStatisticsFromFile = false;
702
        }
703
        if (padfStdDev != nullptr && !std::isnan(bandStats.fStandardDev))
704
        {
705
            *padfStdDev = bandStats.fStandardDev;
706
        }
707
        else
708
        {
709
            bStatisticsFromFile = false;
710
        }
711
        if (bStatisticsFromFile)
712
            return CE_None;
713
    }
714
    // no required statistics.
715
    if (!bStatisticsFromFile && bForce == TRUE)
716
    {
717
        double dfMin, dfMax, dfMean, dfStdDev;
718
        pamError = GDALPamRasterBand::GetStatistics(bApproxOK, TRUE, &dfMin,
719
                                                    &dfMax, &dfMean, &dfStdDev);
720
        if (pdfMin != nullptr)
721
        {
722
            *pdfMin = dfMin;
723
        }
724
        if (pdfMax != nullptr)
725
        {
726
            *pdfMax = dfMax;
727
        }
728
        if (pdfMean != nullptr)
729
        {
730
            *pdfMean = dfMean;
731
        }
732
        if (padfStdDev != nullptr)
733
        {
734
            *padfStdDev = dfStdDev;
735
        }
736
        if (pamError == CE_None)
737
        {
738
            const CPLErr err = SetStatistics(dfMin, dfMax, dfMean, dfStdDev);
739
            if (err != CE_None)
740
            {
741
                CPLError(
742
                    CE_Warning, CPLE_AppDefined,
743
                    "SetStatistics failed in ECWRasterBand::GetStatistics. "
744
                    "Statistics might not be saved in .ecw file.");
745
            }
746
            return CE_None;
747
        }
748
        // whatever happened we return.
749
        return pamError;
750
    }
751
    // no statistics and we are not forced to return.
752
    return CE_Warning;
753
}
754

755
/************************************************************************/
756
/*                          SetStatistics()                             */
757
/************************************************************************/
758

759
CPLErr ECWRasterBand::SetStatistics(double dfMin, double dfMax, double dfMean,
760
                                    double dfStdDev)
761
{
762
    if (poGDS->psFileInfo->nFormatVersion < 3 || eBandInterp == GCI_AlphaBand)
763
    {
764
        return GDALPamRasterBand::SetStatistics(dfMin, dfMax, dfMean, dfStdDev);
765
    }
766
    double dummy;
767
    bool hasPAMStatistics =
768
        GDALPamRasterBand::GetStatistics(TRUE, FALSE, &dummy, &dummy, &dummy,
769
                                         &dummy) == CE_None;
770

771
    NCS::CError error = poGDS->StatisticsEnsureInitialized();
772
    if (!error.Success())
773
    {
774
        CPLError(
775
            CE_Warning, CPLE_AppDefined,
776
            "ECWRDataset::StatisticsEnsureInitialized failed in "
777
            "ECWRasterBand::SetStatistic. Statistics will be written to PAM. ");
778
        return GDALPamRasterBand::SetStatistics(dfMin, dfMax, dfMean, dfStdDev);
779
    }
780
    GetBandIndexAndCountForStatistics(nStatsBandIndex, nStatsBandCount);
781
    if (poGDS->pStatistics == nullptr)
782
    {
783
        error =
784
            NCSEcwInitStatistics(&poGDS->pStatistics, nStatsBandCount, nullptr);
785
        if (!error.Success())
786
        {
787
            CPLError(
788
                CE_Warning, CPLE_AppDefined,
789
                "NCSEcwInitStatistics failed in ECWRasterBand::SetStatistic. "
790
                "Statistics will be written to PAM.");
791
            return GDALPamRasterBand::SetStatistics(dfMin, dfMax, dfMean,
792
                                                    dfStdDev);
793
        }
794
    }
795

796
    poGDS->pStatistics->BandsStats[nStatsBandIndex].fMinVal =
797
        static_cast<IEEE4>(dfMin);
798
    poGDS->pStatistics->BandsStats[nStatsBandIndex].fMaxVal =
799
        static_cast<IEEE4>(dfMax);
800
    poGDS->pStatistics->BandsStats[nStatsBandIndex].fMeanVal =
801
        static_cast<IEEE4>(dfMean);
802
    poGDS->pStatistics->BandsStats[nStatsBandIndex].fStandardDev =
803
        static_cast<IEEE4>(dfStdDev);
804
    poGDS->bStatisticsDirty = TRUE;
805
    // if we have PAM statistics we need to save them as well. Better option
806
    // would be to remove them from PAM file but I don't know how to do that
807
    // without messing in PAM internals.
808
    if (hasPAMStatistics)
809
    {
810
        CPLError(CE_Debug, CPLE_AppDefined,
811
                 "PAM statistics will be overwritten.");
812
        return GDALPamRasterBand::SetStatistics(dfMin, dfMax, dfMean, dfStdDev);
813
    }
814

815
    return CE_None;
816
}
817
#endif
818

819
// #if !defined(SDK_CAN_DO_SUPERSAMPLING)
820
/************************************************************************/
821
/*                          OldIRasterIO()                              */
822
/************************************************************************/
823

824
/* This implementation of IRasterIO(), derived from the one of GDAL 1.9 */
825
/* and older versions, is meant at making over-sampling */
826
/* work with ECW SDK 3.3. Newer versions of the SDK can do super-sampling in
827
 * their */
828
/* SetView() call. */
829

830
CPLErr ECWRasterBand::OldIRasterIO(GDALRWFlag eRWFlag, int nXOff, int nYOff,
1✔
831
                                   int nXSize, int nYSize, void *pData,
832
                                   int nBufXSize, int nBufYSize,
833
                                   GDALDataType eBufType, GSpacing nPixelSpace,
834
                                   GSpacing nLineSpace,
835
                                   GDALRasterIOExtraArg *psExtraArg)
836

837
{
838
    int iBand;
839
    GByte *pabyWorkBuffer = nullptr;
1✔
840
    const int nResFactor = 1 << (iOverview + 1);
1✔
841

842
    nXOff *= nResFactor;
1✔
843
    nYOff *= nResFactor;
1✔
844
    nXSize *= nResFactor;
1✔
845
    nYSize *= nResFactor;
1✔
846

847
    /* -------------------------------------------------------------------- */
848
    /*      Try to do it based on existing "advised" access.                */
849
    /* -------------------------------------------------------------------- */
850
    int nRet = poGDS->TryWinRasterIO(eRWFlag, nXOff, nYOff, nXSize, nYSize,
2✔
851
                                     static_cast<GByte *>(pData), nBufXSize,
852
                                     nBufYSize, eBufType, 1, &nBand,
1✔
853
                                     nPixelSpace, nLineSpace, 0, psExtraArg);
854
    if (nRet == TRUE)
1✔
855
        return CE_None;
×
856
    if (nRet < 0)
1✔
857
        return CE_Failure;
×
858

859
    /* -------------------------------------------------------------------- */
860
    /*      The ECW SDK doesn't supersample, so adjust for this case.       */
861
    /* -------------------------------------------------------------------- */
862

863
    int nNewXSize = nBufXSize;
1✔
864
    int nNewYSize = nBufYSize;
1✔
865

866
    if (nXSize < nBufXSize)
1✔
867
        nNewXSize = nXSize;
1✔
868

869
    if (nYSize < nBufYSize)
1✔
870
        nNewYSize = nYSize;
1✔
871

872
    /* -------------------------------------------------------------------- */
873
    /*      Can we perform direct loads, or must we load into a working     */
874
    /*      buffer, and transform?                                          */
875
    /* -------------------------------------------------------------------- */
876
    const int nRawPixelSize = GDALGetDataTypeSizeBytes(poGDS->eRasterDataType);
1✔
877

878
    int bDirect = nPixelSpace == 1 && eBufType == GDT_Byte &&
1✔
879
                  nNewXSize == nBufXSize && nNewYSize == nBufYSize;
2✔
880
    if (!bDirect)
1✔
881
        pabyWorkBuffer =
882
            static_cast<GByte *>(CPLMalloc(nNewXSize * nRawPixelSize));
1✔
883

884
    /* -------------------------------------------------------------------- */
885
    /*      Establish access at the desired resolution.                     */
886
    /* -------------------------------------------------------------------- */
887
    poGDS->CleanupWindow();
1✔
888

889
    iBand = nBand - 1;
1✔
890
    poGDS->nBandIndexToPromoteTo8Bit = (bPromoteTo8Bit) ? 0 : -1;
1✔
891
    // TODO: Fix writable strings issue.
892
    CNCSError oErr = poGDS->poFileView->SetView(
1✔
893
        1, reinterpret_cast<unsigned int *>(&iBand), nXOff, nYOff,
894
        nXOff + nXSize - 1, nYOff + nYSize - 1, nNewXSize, nNewYSize);
2✔
895
    if (oErr.GetErrorNumber() != NCS_SUCCESS)
1✔
896
    {
897
        CPLFree(pabyWorkBuffer);
×
898
        ECWReportError(oErr);
×
899

900
        return CE_Failure;
×
901
    }
902

903
    /* -------------------------------------------------------------------- */
904
    /*      Read back one scanline at a time, till request is satisfied.    */
905
    /*      Supersampling is not supported by the ECW API, so we will do    */
906
    /*      it ourselves.                                                   */
907
    /* -------------------------------------------------------------------- */
908
    double dfSrcYInc = static_cast<double>(nNewYSize) / nBufYSize;
1✔
909
    double dfSrcXInc = static_cast<double>(nNewXSize) / nBufXSize;
1✔
910
    int iSrcLine, iDstLine;
911
    CPLErr eErr = CE_None;
1✔
912

913
    for (iSrcLine = 0, iDstLine = 0; iDstLine < nBufYSize; iDstLine++)
801✔
914
    {
915
        NCSEcwReadStatus eRStatus;
916
        GPtrDiff_t iDstLineOff = iDstLine * (GPtrDiff_t)nLineSpace;
800✔
917
        unsigned char *pabySrcBuf;
918

919
        if (bDirect)
800✔
920
            pabySrcBuf = ((GByte *)pData) + iDstLineOff;
×
921
        else
922
            pabySrcBuf = pabyWorkBuffer;
800✔
923

924
        if (nNewYSize == nBufYSize || iSrcLine == (int)(iDstLine * dfSrcYInc))
800✔
925
        {
926
            eRStatus = poGDS->poFileView->ReadLineBIL(
800✔
927
                poGDS->eNCSRequestDataType, 1, (void **)&pabySrcBuf);
400✔
928

929
            if (eRStatus != NCSECW_READ_OK)
400✔
930
            {
931
                CPLDebug("ECW", "ReadLineBIL status=%d", (int)eRStatus);
×
932
                CPLError(CE_Failure, CPLE_AppDefined,
×
933
                         "NCScbmReadViewLineBIL failed.");
934
                eErr = CE_Failure;
×
935
                break;
×
936
            }
937

938
            if (bPromoteTo8Bit)
400✔
939
            {
940
                for (int iX = 0; iX < nNewXSize; iX++)
×
941
                {
942
                    pabySrcBuf[iX] *= 255;
×
943
                }
944
            }
945

946
            if (!bDirect)
400✔
947
            {
948
                if (nNewXSize == nBufXSize)
400✔
949
                {
950
                    GDALCopyWords(pabyWorkBuffer, poGDS->eRasterDataType,
×
951
                                  nRawPixelSize,
952
                                  ((GByte *)pData) + iDstLine * nLineSpace,
×
953
                                  eBufType, (int)nPixelSpace, nBufXSize);
954
                }
955
                else
956
                {
957
                    int iPixel;
958

959
                    for (iPixel = 0; iPixel < nBufXSize; iPixel++)
320,400✔
960
                    {
961
                        GDALCopyWords(
320,000✔
962
                            pabyWorkBuffer +
320,000✔
963
                                nRawPixelSize * ((int)(iPixel * dfSrcXInc)),
320,000✔
964
                            poGDS->eRasterDataType, nRawPixelSize,
320,000✔
965
                            (GByte *)pData + iDstLineOff + iPixel * nPixelSpace,
320,000✔
966
                            eBufType, (int)nPixelSpace, 1);
967
                    }
968
                }
969
            }
970

971
            iSrcLine++;
400✔
972
        }
973
        else
974
        {
975
            // Just copy the previous line in this case
976
            GDALCopyWords((GByte *)pData + (iDstLineOff - nLineSpace), eBufType,
400✔
977
                          (int)nPixelSpace, (GByte *)pData + iDstLineOff,
400✔
978
                          eBufType, (int)nPixelSpace, nBufXSize);
979
        }
980

981
        if (psExtraArg->pfnProgress != nullptr &&
800✔
982
            !psExtraArg->pfnProgress(1.0 * (iDstLine + 1) / nBufYSize, "",
×
983
                                     psExtraArg->pProgressData))
984
        {
985
            eErr = CE_Failure;
×
986
            break;
×
987
        }
988
    }
989

990
    CPLFree(pabyWorkBuffer);
1✔
991

992
    return eErr;
1✔
993
}
994

995
// #endif !defined(SDK_CAN_DO_SUPERSAMPLING)
996

997
/************************************************************************/
998
/*                             IRasterIO()                              */
999
/************************************************************************/
1000

1001
CPLErr ECWRasterBand::IRasterIO(GDALRWFlag eRWFlag, int nXOff, int nYOff,
1,664✔
1002
                                int nXSize, int nYSize, void *pData,
1003
                                int nBufXSize, int nBufYSize,
1004
                                GDALDataType eBufType, GSpacing nPixelSpace,
1005
                                GSpacing nLineSpace,
1006
                                GDALRasterIOExtraArg *psExtraArg)
1007
{
1008
    if (eRWFlag == GF_Write)
1,664✔
1009
        return CE_Failure;
×
1010

1011
    /* -------------------------------------------------------------------- */
1012
    /*      Default line and pixel spacing if needed.                       */
1013
    /* -------------------------------------------------------------------- */
1014
    if (nPixelSpace == 0)
1,664✔
1015
        nPixelSpace = GDALGetDataTypeSizeBytes(eBufType);
×
1016

1017
    if (nLineSpace == 0)
1,664✔
1018
        nLineSpace = nPixelSpace * nBufXSize;
×
1019

1020
    CPLDebug("ECWRasterBand",
1,664✔
1021
             "RasterIO(nBand=%d,iOverview=%d,nXOff=%d,nYOff=%d,nXSize=%d,"
1022
             "nYSize=%d -> %dx%d)",
1023
             nBand, iOverview, nXOff, nYOff, nXSize, nYSize, nBufXSize,
1024
             nBufYSize);
1025

1026
#if !defined(SDK_CAN_DO_SUPERSAMPLING)
1027
    if (poGDS->bUseOldBandRasterIOImplementation)
1,664✔
1028
    {
1029
        return OldIRasterIO(eRWFlag, nXOff, nYOff, nXSize, nYSize, pData,
1✔
1030
                            nBufXSize, nBufYSize, eBufType, nPixelSpace,
1031
                            nLineSpace, psExtraArg);
1✔
1032
    }
1033

1034
#endif
1035

1036
    int nResFactor = 1 << (iOverview + 1);
1,663✔
1037

1038
    GDALRasterIOExtraArg sExtraArgTmp;
1039
    INIT_RASTERIO_EXTRA_ARG(sExtraArgTmp);
1,663✔
1040
    CPL_IGNORE_RET_VAL(sExtraArgTmp.eResampleAlg);
1,663✔
1041
    sExtraArgTmp.eResampleAlg = psExtraArg->eResampleAlg;
1,663✔
1042
    sExtraArgTmp.pfnProgress = psExtraArg->pfnProgress;
1,663✔
1043
    sExtraArgTmp.pProgressData = psExtraArg->pProgressData;
1,663✔
1044

1045
    return poGDS->IRasterIO(
3,301✔
1046
        eRWFlag, nXOff * nResFactor, nYOff * nResFactor,
1047
        (nXSize == nRasterXSize) ? poGDS->nRasterXSize : nXSize * nResFactor,
1,663✔
1048
        (nYSize == nRasterYSize) ? poGDS->nRasterYSize : nYSize * nResFactor,
25✔
1049
        pData, nBufXSize, nBufYSize, eBufType, 1, &nBand, nPixelSpace,
1050
        nLineSpace, nLineSpace * nBufYSize, &sExtraArgTmp);
3,326✔
1051
}
1052

1053
/************************************************************************/
1054
/*                             IReadBlock()                             */
1055
/************************************************************************/
1056

1057
CPLErr ECWRasterBand::IReadBlock(int nBlockXOff, int nBlockYOff, void *pImage)
1✔
1058

1059
{
1060
    int nXOff = nBlockXOff * nBlockXSize, nYOff = nBlockYOff * nBlockYSize,
1✔
1061
        nXSize = nBlockXSize, nYSize = nBlockYSize;
1✔
1062

1063
    if (nXOff + nXSize > nRasterXSize)
1✔
1064
        nXSize = nRasterXSize - nXOff;
×
1065
    if (nYOff + nYSize > nRasterYSize)
1✔
1066
        nYSize = nRasterYSize - nYOff;
×
1067

1068
    const GSpacing nPixelSpace = GDALGetDataTypeSizeBytes(eDataType);
1✔
1069
    const GSpacing nLineSpace = nPixelSpace * nBlockXSize;
1✔
1070

1071
    GDALRasterIOExtraArg sExtraArg;
1072
    INIT_RASTERIO_EXTRA_ARG(sExtraArg);
1✔
1073

1074
    return IRasterIO(GF_Read, nXOff, nYOff, nXSize, nYSize, pImage, nXSize,
1✔
1075
                     nYSize, eDataType, nPixelSpace, nLineSpace, &sExtraArg);
2✔
1076
}
1077

1078
/************************************************************************/
1079
/* ==================================================================== */
1080
/*                            ECWDataset                               */
1081
/* ==================================================================== */
1082
/************************************************************************/
1083

1084
/************************************************************************/
1085
/*                            ECWDataset()                              */
1086
/************************************************************************/
1087

1088
ECWDataset::ECWDataset(int bIsJPEG2000In)
123✔
1089

1090
{
1091
    this->bIsJPEG2000 = bIsJPEG2000In;
123✔
1092
    bUsingCustomStream = FALSE;
123✔
1093
    poFileView = nullptr;
123✔
1094
    bWinActive = FALSE;
123✔
1095
    panWinBandList = nullptr;
123✔
1096
    eRasterDataType = GDT_Byte;
123✔
1097
    papszGMLMetadata = nullptr;
123✔
1098

1099
    bHdrDirty = FALSE;
123✔
1100
    bGeoTransformChanged = FALSE;
123✔
1101
    bProjectionChanged = FALSE;
123✔
1102
    bProjCodeChanged = FALSE;
123✔
1103
    bDatumCodeChanged = FALSE;
123✔
1104
    bUnitsCodeChanged = FALSE;
123✔
1105

1106
    bUseOldBandRasterIOImplementation = FALSE;
123✔
1107
#if ECWSDK_VERSION >= 50
1108

1109
    pStatistics = nullptr;
1110
    bStatisticsDirty = FALSE;
1111
    bStatisticsInitialized = FALSE;
1112
    bFileMetaDataDirty = FALSE;
1113

1114
#endif
1115

1116
    sCachedMultiBandIO.bEnabled = FALSE;
123✔
1117
    sCachedMultiBandIO.nBandsTried = 0;
123✔
1118
    sCachedMultiBandIO.nXOff = 0;
123✔
1119
    sCachedMultiBandIO.nYOff = 0;
123✔
1120
    sCachedMultiBandIO.nXSize = 0;
123✔
1121
    sCachedMultiBandIO.nYSize = 0;
123✔
1122
    sCachedMultiBandIO.nBufXSize = 0;
123✔
1123
    sCachedMultiBandIO.nBufYSize = 0;
123✔
1124
    sCachedMultiBandIO.eBufType = GDT_Unknown;
123✔
1125
    sCachedMultiBandIO.pabyData = nullptr;
123✔
1126

1127
    bPreventCopyingSomeMetadata = FALSE;
123✔
1128

1129
    nBandIndexToPromoteTo8Bit = -1;
123✔
1130

1131
    poDriver =
123✔
1132
        (GDALDriver *)GDALGetDriverByName(bIsJPEG2000 ? "JP2ECW" : "ECW");
123✔
1133

1134
    psFileInfo = nullptr;
123✔
1135
    eNCSRequestDataType = NCSCT_UINT8;
123✔
1136
    nWinXOff = 0;
123✔
1137
    nWinYOff = 0;
123✔
1138
    nWinXSize = 0;
123✔
1139
    nWinYSize = 0;
123✔
1140
    nWinBufXSize = 0;
123✔
1141
    nWinBufYSize = 0;
123✔
1142
    nWinBandCount = 0;
123✔
1143
    nWinBufLoaded = FALSE;
123✔
1144
    papCurLineBuf = nullptr;
123✔
1145

1146
    m_nAdviseReadXOff = -1;
123✔
1147
    m_nAdviseReadYOff = -1;
123✔
1148
    m_nAdviseReadXSize = -1;
123✔
1149
    m_nAdviseReadYSize = -1;
123✔
1150
    m_nAdviseReadBufXSize = -1;
123✔
1151
    m_nAdviseReadBufYSize = -1;
123✔
1152
    m_nAdviseReadBandCount = -1;
123✔
1153
    m_panAdviseReadBandList = nullptr;
123✔
1154
}
123✔
1155

1156
/************************************************************************/
1157
/*                           ~ECWDataset()                              */
1158
/************************************************************************/
1159

1160
ECWDataset::~ECWDataset()
246✔
1161

1162
{
1163
    GDALPamDataset::FlushCache(true);
123✔
1164
    CleanupWindow();
123✔
1165

1166
#if ECWSDK_VERSION >= 50
1167
    NCSFileMetaData *pFileMetaDataCopy = nullptr;
1168
    if (bFileMetaDataDirty)
1169
    {
1170
        NCSCopyMetaData(&pFileMetaDataCopy, psFileInfo->pFileMetaData);
1171
    }
1172
#endif
1173

1174
    /* -------------------------------------------------------------------- */
1175
    /*      Release / dereference iostream.                                 */
1176
    /* -------------------------------------------------------------------- */
1177
    // The underlying iostream of the CNCSJP2FileView (poFileView) object may
1178
    // also be the underlying iostream of other CNCSJP2FileView (poFileView)
1179
    // objects.  Consequently, when we delete the CNCSJP2FileView (poFileView)
1180
    // object, we must decrement the nFileViewCount attribute of the underlying
1181
    // VSIIOStream object, and only delete the VSIIOStream object when
1182
    // nFileViewCount is equal to zero.
1183

1184
    CPLMutexHolder oHolder(&hECWDatasetMutex);
246✔
1185

1186
    if (poFileView != nullptr)
123✔
1187
    {
1188
#if ECWSDK_VERSION >= 55
1189
        delete poFileView;
1190
#else
1191
        VSIIOStream *poUnderlyingIOStream = (VSIIOStream *)nullptr;
123✔
1192

1193
        if (bUsingCustomStream)
123✔
1194
        {
1195
            poUnderlyingIOStream = ((VSIIOStream *)(poFileView->GetStream()));
52✔
1196
        }
1197
        delete poFileView;
123✔
1198

1199
        if (bUsingCustomStream)
123✔
1200
        {
1201
            if (--poUnderlyingIOStream->nFileViewCount == 0)
52✔
1202
                delete poUnderlyingIOStream;
52✔
1203
        }
1204
#endif
1205
        poFileView = nullptr;
123✔
1206
    }
1207

1208
    /* WriteHeader() must be called after closing the file handle to work */
1209
    /* on Windows */
1210
    if (bHdrDirty)
123✔
1211
        WriteHeader();
4✔
1212
#if ECWSDK_VERSION >= 50
1213
    if (bStatisticsDirty)
1214
    {
1215
        StatisticsWrite();
1216
    }
1217
    CleanupStatistics();
1218

1219
    if (bFileMetaDataDirty)
1220
    {
1221
        WriteFileMetaData(pFileMetaDataCopy);
1222
        NCSFreeMetaData(pFileMetaDataCopy);
1223
    }
1224
#endif
1225

1226
    CSLDestroy(papszGMLMetadata);
123✔
1227

1228
    CPLFree(sCachedMultiBandIO.pabyData);
123✔
1229

1230
    CPLFree(m_panAdviseReadBandList);
123✔
1231
}
246✔
1232

1233
#if ECWSDK_VERSION >= 50
1234

1235
/************************************************************************/
1236
/*                    StatisticsEnsureInitialized()                     */
1237
/************************************************************************/
1238

1239
NCS::CError ECWDataset::StatisticsEnsureInitialized()
1240
{
1241
    if (bStatisticsInitialized == TRUE)
1242
    {
1243
        return NCS_SUCCESS;
1244
    }
1245

1246
    NCS::CError error = poFileView->GetClientStatistics(&pStatistics);
1247
    if (error.Success())
1248
    {
1249
        bStatisticsInitialized = TRUE;
1250
    }
1251
    return error;
1252
}
1253

1254
/************************************************************************/
1255
/*                          StatisticsWrite()                           */
1256
/************************************************************************/
1257

1258
NCS::CError ECWDataset::StatisticsWrite()
1259
{
1260
    CPLDebug("ECW", "In StatisticsWrite()");
1261
    NCSFileView *view = NCSEcwEditOpen(GetDescription());
1262
    NCS::CError error;
1263
    if (view != nullptr)
1264
    {
1265
        error = NCSEcwEditSetStatistics(view, pStatistics);
1266
        if (error.Success())
1267
        {
1268
            error = NCSEcwEditFlushAll(view);
1269
            if (error.Success())
1270
            {
1271
                error = NCSEcwEditClose(view);
1272
            }
1273
        }
1274
    }
1275

1276
    bStatisticsDirty = FALSE;
1277

1278
    return error;
1279
}
1280

1281
/************************************************************************/
1282
/*                          CleanupStatistics()                         */
1283
/************************************************************************/
1284

1285
void ECWDataset::CleanupStatistics()
1286
{
1287
    if (bStatisticsInitialized == TRUE && pStatistics != nullptr)
1288
    {
1289
        NCSEcwFreeStatistics(pStatistics);
1290
    }
1291
}
1292

1293
#endif  // #if ECWSDK_VERSION>=50
1294

1295
/************************************************************************/
1296
/*                          SetGeoTransform()                           */
1297
/************************************************************************/
1298

1299
CPLErr ECWDataset::SetGeoTransform(const GDALGeoTransform &gt)
7✔
1300
{
1301
    if (bIsJPEG2000 || eAccess == GA_ReadOnly)
7✔
1302
        return GDALPamDataset::SetGeoTransform(gt);
5✔
1303

1304
    if (!bGeoTransformValid || gt != m_gt)
2✔
1305
    {
1306
        m_gt = gt;
2✔
1307
        bGeoTransformValid = TRUE;
2✔
1308
        bHdrDirty = TRUE;
2✔
1309
        bGeoTransformChanged = TRUE;
2✔
1310
    }
1311

1312
    return CE_None;
2✔
1313
}
1314

1315
/************************************************************************/
1316
/*                            SetSpatialRef()                           */
1317
/************************************************************************/
1318

1319
CPLErr ECWDataset::SetSpatialRef(const OGRSpatialReference *poSRS)
7✔
1320
{
1321
    if (bIsJPEG2000 || eAccess == GA_ReadOnly)
7✔
1322
        return GDALPamDataset::SetSpatialRef(poSRS);
5✔
1323

1324
    if (!((m_oSRS.IsEmpty() && poSRS == nullptr) ||
4✔
1325
          (!m_oSRS.IsEmpty() && poSRS != nullptr && m_oSRS.IsSame(poSRS))))
2✔
1326
    {
1327
        m_oSRS.Clear();
2✔
1328
        if (poSRS)
2✔
1329
            m_oSRS = *poSRS;
2✔
1330

1331
        bHdrDirty = TRUE;
2✔
1332
        bProjectionChanged = TRUE;
2✔
1333
    }
1334

1335
    return CE_None;
2✔
1336
}
1337

1338
/************************************************************************/
1339
/*                            SetMetadataItem()                         */
1340
/************************************************************************/
1341

1342
CPLErr ECWDataset::SetMetadataItem(const char *pszName, const char *pszValue,
4✔
1343
                                   const char *pszDomain)
1344
{
1345
    if (!bIsJPEG2000 && eAccess == GA_Update &&
4✔
1346
        (pszDomain == nullptr || EQUAL(pszDomain, "") ||
3✔
1347
         (pszDomain != nullptr && EQUAL(pszDomain, "ECW"))) &&
3✔
1348
        pszName != nullptr &&
3✔
1349
        (strcmp(pszName, "PROJ") == 0 || strcmp(pszName, "DATUM") == 0 ||
3✔
1350
         strcmp(pszName, "UNITS") == 0))
1✔
1351
    {
1352
        CPLString osNewVal = pszValue ? pszValue : "";
3✔
1353
        if (osNewVal.size() > 31)
3✔
1354
            osNewVal.resize(31);
×
1355
        if (strcmp(pszName, "PROJ") == 0)
3✔
1356
        {
1357
            bProjCodeChanged = (osNewVal != m_osProjCode);
1✔
1358
            m_osProjCode = std::move(osNewVal);
1✔
1359
            bHdrDirty |= bProjCodeChanged;
1✔
1360
        }
1361
        else if (strcmp(pszName, "DATUM") == 0)
2✔
1362
        {
1363
            bDatumCodeChanged |= (osNewVal != m_osDatumCode) ? TRUE : FALSE;
1✔
1364
            m_osDatumCode = std::move(osNewVal);
1✔
1365
            bHdrDirty |= bDatumCodeChanged;
1✔
1366
        }
1367
        else
1368
        {
1369
            bUnitsCodeChanged |= (osNewVal != m_osUnitsCode) ? TRUE : FALSE;
1✔
1370
            m_osUnitsCode = std::move(osNewVal);
1✔
1371
            bHdrDirty |= bUnitsCodeChanged;
1✔
1372
        }
1373
        return CE_None;
3✔
1374
    }
1375
#if ECWSDK_VERSION >= 50
1376
    else if (psFileInfo != nullptr && psFileInfo->nFormatVersion >= 3 &&
1377
             eAccess == GA_Update &&
1378
             (pszDomain == nullptr || EQUAL(pszDomain, "")) &&
1379
             pszName != nullptr && STARTS_WITH(pszName, "FILE_METADATA_"))
1380
    {
1381
        bFileMetaDataDirty = TRUE;
1382

1383
        if (psFileInfo->pFileMetaData == nullptr)
1384
            NCSInitMetaData(&(psFileInfo->pFileMetaData));
1385

1386
        if (strcmp(pszName, "FILE_METADATA_CLASSIFICATION") == 0)
1387
        {
1388
            NCSFree(psFileInfo->pFileMetaData->sClassification);
1389
            psFileInfo->pFileMetaData->sClassification =
1390
                pszValue ? NCSStrDupT(NCS::CString(pszValue).c_str()) : nullptr;
1391
            return GDALDataset::SetMetadataItem(pszName, pszValue, pszDomain);
1392
        }
1393
        else if (strcmp(pszName, "FILE_METADATA_ACQUISITION_DATE") == 0)
1394
        {
1395
            NCSFree(psFileInfo->pFileMetaData->sAcquisitionDate);
1396
            psFileInfo->pFileMetaData->sAcquisitionDate =
1397
                pszValue ? NCSStrDupT(NCS::CString(pszValue).c_str()) : nullptr;
1398
            return GDALDataset::SetMetadataItem(pszName, pszValue, pszDomain);
1399
        }
1400
        else if (strcmp(pszName, "FILE_METADATA_ACQUISITION_SENSOR_NAME") == 0)
1401
        {
1402
            NCSFree(psFileInfo->pFileMetaData->sAcquisitionSensorName);
1403
            psFileInfo->pFileMetaData->sAcquisitionSensorName =
1404
                pszValue ? NCSStrDupT(NCS::CString(pszValue).c_str()) : nullptr;
1405
            return GDALDataset::SetMetadataItem(pszName, pszValue, pszDomain);
1406
        }
1407
        else if (strcmp(pszName, "FILE_METADATA_COMPRESSION_SOFTWARE") == 0)
1408
        {
1409
            NCSFree(psFileInfo->pFileMetaData->sCompressionSoftware);
1410
            psFileInfo->pFileMetaData->sCompressionSoftware =
1411
                pszValue ? NCSStrDupT(NCS::CString(pszValue).c_str()) : nullptr;
1412
            return GDALDataset::SetMetadataItem(pszName, pszValue, pszDomain);
1413
        }
1414
        else if (strcmp(pszName, "FILE_METADATA_AUTHOR") == 0)
1415
        {
1416
            NCSFree(psFileInfo->pFileMetaData->sAuthor);
1417
            psFileInfo->pFileMetaData->sAuthor =
1418
                pszValue ? NCSStrDupT(NCS::CString(pszValue).c_str()) : nullptr;
1419
            return GDALDataset::SetMetadataItem(pszName, pszValue, pszDomain);
1420
        }
1421
        else if (strcmp(pszName, "FILE_METADATA_COPYRIGHT") == 0)
1422
        {
1423
            NCSFree(psFileInfo->pFileMetaData->sCopyright);
1424
            psFileInfo->pFileMetaData->sCopyright =
1425
                pszValue ? NCSStrDupT(NCS::CString(pszValue).c_str()) : nullptr;
1426
            return GDALDataset::SetMetadataItem(pszName, pszValue, pszDomain);
1427
        }
1428
        else if (strcmp(pszName, "FILE_METADATA_COMPANY") == 0)
1429
        {
1430
            NCSFree(psFileInfo->pFileMetaData->sCompany);
1431
            psFileInfo->pFileMetaData->sCompany =
1432
                pszValue ? NCSStrDupT(NCS::CString(pszValue).c_str()) : nullptr;
1433
            return GDALDataset::SetMetadataItem(pszName, pszValue, pszDomain);
1434
        }
1435
        else if (strcmp(pszName, "FILE_METADATA_EMAIL") == 0)
1436
        {
1437
            NCSFree(psFileInfo->pFileMetaData->sEmail);
1438
            psFileInfo->pFileMetaData->sEmail =
1439
                pszValue ? NCSStrDupT(NCS::CString(pszValue).c_str()) : nullptr;
1440
            return GDALDataset::SetMetadataItem(pszName, pszValue, pszDomain);
1441
        }
1442
        else if (strcmp(pszName, "FILE_METADATA_ADDRESS") == 0)
1443
        {
1444
            NCSFree(psFileInfo->pFileMetaData->sAddress);
1445
            psFileInfo->pFileMetaData->sAddress =
1446
                pszValue ? NCSStrDupT(NCS::CString(pszValue).c_str()) : nullptr;
1447
            return GDALDataset::SetMetadataItem(pszName, pszValue, pszDomain);
1448
        }
1449
        else if (strcmp(pszName, "FILE_METADATA_TELEPHONE") == 0)
1450
        {
1451
            NCSFree(psFileInfo->pFileMetaData->sTelephone);
1452
            psFileInfo->pFileMetaData->sTelephone =
1453
                pszValue ? NCSStrDupT(NCS::CString(pszValue).c_str()) : nullptr;
1454
            return GDALDataset::SetMetadataItem(pszName, pszValue, pszDomain);
1455
        }
1456
        else
1457
        {
1458
            return GDALPamDataset::SetMetadataItem(pszName, pszValue,
1459
                                                   pszDomain);
1460
        }
1461
    }
1462
#endif
1463
    else
1464
        return GDALPamDataset::SetMetadataItem(pszName, pszValue, pszDomain);
1✔
1465
}
1466

1467
/************************************************************************/
1468
/*                              SetMetadata()                           */
1469
/************************************************************************/
1470

1471
CPLErr ECWDataset::SetMetadata(char **papszMetadata, const char *pszDomain)
3✔
1472
{
1473
    /* The bPreventCopyingSomeMetadata is set by ECWCreateCopy() */
1474
    /* just before calling poDS->CloneInfo( poSrcDS, GCIF_PAM_DEFAULT ); */
1475
    if (bPreventCopyingSomeMetadata &&
3✔
1476
        (pszDomain == nullptr || EQUAL(pszDomain, "")))
1✔
1477
    {
1478
        char **papszMetadataDup = nullptr;
1✔
1479
        char **papszIter = papszMetadata;
1✔
1480
        while (*papszIter)
2✔
1481
        {
1482
            char *pszKey = nullptr;
1✔
1483
            CPLParseNameValue(*papszIter, &pszKey);
1✔
1484
            /* Remove a few metadata item from the source that we don't want in
1485
             */
1486
            /* the target metadata */
1487
            if (pszKey != nullptr &&
1✔
1488
                (EQUAL(pszKey, "VERSION") ||
1✔
1489
                 EQUAL(pszKey, "COMPRESSION_RATE_TARGET") ||
1✔
1490
                 EQUAL(pszKey, "COMPRESSION_RATE_ACTUAL") ||
1✔
1491
                 EQUAL(pszKey, "CLOCKWISE_ROTATION_DEG") ||
1✔
1492
                 EQUAL(pszKey, "COLORSPACE") ||
1✔
1493
                 EQUAL(pszKey, "COMPRESSION_DATE") ||
1✔
1494
                 STARTS_WITH_CI(pszKey, "FILE_METADATA_")))
1✔
1495
            {
1496
                /* do nothing */
1497
            }
1498
            else
1499
            {
1500
                papszMetadataDup = CSLAddString(papszMetadataDup, *papszIter);
1✔
1501
            }
1502
            CPLFree(pszKey);
1✔
1503
            papszIter++;
1✔
1504
        }
1505

1506
        bPreventCopyingSomeMetadata = FALSE;
1✔
1507
        CPLErr eErr = SetMetadata(papszMetadataDup, pszDomain);
1✔
1508
        bPreventCopyingSomeMetadata = TRUE;
1✔
1509
        CSLDestroy(papszMetadataDup);
1✔
1510
        return eErr;
1✔
1511
    }
1512

1513
    if (((pszDomain == nullptr || EQUAL(pszDomain, "") ||
2✔
1514
          EQUAL(pszDomain, "ECW")) &&
4✔
1515
         (CSLFetchNameValue(papszMetadata, "PROJ") != nullptr ||
2✔
1516
          CSLFetchNameValue(papszMetadata, "DATUM") != nullptr ||
2✔
1517
          CSLFetchNameValue(papszMetadata, "UNITS") != nullptr))
2✔
1518
#if ECWSDK_VERSION >= 50
1519
        ||
1520
        (psFileInfo != nullptr && psFileInfo->nFormatVersion >= 3 &&
1521
         eAccess == GA_Update &&
1522
         (pszDomain == nullptr || EQUAL(pszDomain, "")) &&
1523
         (CSLFetchNameValue(papszMetadata, "FILE_METADATA_CLASSIFICATION") !=
1524
              nullptr ||
1525
          CSLFetchNameValue(papszMetadata, "FILE_METADATA_ACQUISITION_DATE") !=
1526
              nullptr ||
1527
          CSLFetchNameValue(papszMetadata,
1528
                            "FILE_METADATA_ACQUISITION_SENSOR_NAME") !=
1529
              nullptr ||
1530
          CSLFetchNameValue(papszMetadata,
1531
                            "FILE_METADATA_COMPRESSION_SOFTWARE") != nullptr ||
1532
          CSLFetchNameValue(papszMetadata, "FILE_METADATA_AUTHOR") != nullptr ||
1533
          CSLFetchNameValue(papszMetadata, "FILE_METADATA_COPYRIGHT") !=
1534
              nullptr ||
1535
          CSLFetchNameValue(papszMetadata, "FILE_METADATA_COMPANY") !=
1536
              nullptr ||
1537
          CSLFetchNameValue(papszMetadata, "FILE_METADATA_EMAIL") != nullptr ||
1538
          CSLFetchNameValue(papszMetadata, "FILE_METADATA_ADDRESS") !=
1539
              nullptr ||
1540
          CSLFetchNameValue(papszMetadata, "FILE_METADATA_TELEPHONE") !=
1541
              nullptr))
1542
#endif
1543
    )
1544
    {
1545
        CPLStringList osNewMetadata;
×
1546
        char **papszIter = papszMetadata;
×
1547
        while (papszIter && *papszIter)
×
1548
        {
1549
            if (STARTS_WITH(*papszIter, "PROJ=") ||
×
1550
                STARTS_WITH(*papszIter, "DATUM=") ||
×
1551
                STARTS_WITH(*papszIter, "UNITS=") ||
×
1552
                (STARTS_WITH(*papszIter, "FILE_METADATA_") &&
×
1553
                 strchr(*papszIter, '=') != nullptr))
×
1554
            {
1555
                char *pszKey = nullptr;
×
1556
                const char *pszValue = CPLParseNameValue(*papszIter, &pszKey);
×
1557
                SetMetadataItem(pszKey, pszValue, pszDomain);
×
1558
                CPLFree(pszKey);
×
1559
            }
1560
            else
1561
                osNewMetadata.AddString(*papszIter);
×
1562
            papszIter++;
×
1563
        }
1564
        if (!osNewMetadata.empty())
×
1565
            return GDALPamDataset::SetMetadata(osNewMetadata.List(), pszDomain);
×
1566
        else
1567
            return CE_None;
×
1568
    }
1569
    else
1570
        return GDALPamDataset::SetMetadata(papszMetadata, pszDomain);
2✔
1571
}
1572

1573
/************************************************************************/
1574
/*                             WriteHeader()                            */
1575
/************************************************************************/
1576

1577
void ECWDataset::WriteHeader()
4✔
1578
{
1579
    if (!bHdrDirty)
4✔
1580
        return;
×
1581

1582
    CPLAssert(eAccess == GA_Update);
4✔
1583
    CPLAssert(!bIsJPEG2000);
4✔
1584

1585
    bHdrDirty = FALSE;
4✔
1586

1587
    NCSEcwEditInfo *psEditInfo = nullptr;
4✔
1588
    NCSError eErr;
1589

1590
    /* Load original header info */
1591
#if ECWSDK_VERSION < 50
1592
    eErr = NCSEcwEditReadInfo((char *)GetDescription(), &psEditInfo);
4✔
1593
#else
1594
    eErr = NCSEcwEditReadInfo(
1595
        NCS::CString::Utf8Decode(GetDescription()).c_str(), &psEditInfo);
1596
#endif
1597
    if (eErr != NCS_SUCCESS)
4✔
1598
    {
1599
        CPLError(CE_Failure, CPLE_AppDefined, "NCSEcwEditReadInfo() failed");
×
1600
        return;
×
1601
    }
1602

1603
    /* To avoid potential cross-heap issues, we keep the original */
1604
    /* strings, and restore them before freeing the structure */
1605
    char *pszOriginalCode = psEditInfo->szDatum;
4✔
1606
    char *pszOriginalProj = psEditInfo->szProjection;
4✔
1607

1608
    /* Alter the structure with user modified information */
1609
    char szProjCode[32], szDatumCode[32], szUnits[32];
1610
    if (bProjectionChanged)
4✔
1611
    {
1612
        if (ECWTranslateFromWKT(&m_oSRS, szProjCode, sizeof(szProjCode),
2✔
1613
                                szDatumCode, sizeof(szDatumCode), szUnits))
2✔
1614
        {
1615
            psEditInfo->szDatum = szDatumCode;
2✔
1616
            psEditInfo->szProjection = szProjCode;
2✔
1617
            psEditInfo->eCellSizeUnits = ECWTranslateToCellSizeUnits(szUnits);
2✔
1618
            CPLDebug("ECW", "Rewrite DATUM : %s", psEditInfo->szDatum);
2✔
1619
            CPLDebug("ECW", "Rewrite PROJ : %s", psEditInfo->szProjection);
2✔
1620
            CPLDebug("ECW", "Rewrite UNITS : %s",
2✔
1621
                     ECWTranslateFromCellSizeUnits(psEditInfo->eCellSizeUnits));
1622
        }
1623
    }
1624

1625
    if (bDatumCodeChanged)
4✔
1626
    {
1627
        psEditInfo->szDatum =
1✔
1628
            (char *)((m_osDatumCode.size()) ? m_osDatumCode.c_str() : "RAW");
1✔
1629
        CPLDebug("ECW", "Rewrite DATUM : %s", psEditInfo->szDatum);
1✔
1630
    }
1631
    if (bProjCodeChanged)
4✔
1632
    {
1633
        psEditInfo->szProjection =
1✔
1634
            (char *)((m_osProjCode.size()) ? m_osProjCode.c_str() : "RAW");
1✔
1635
        CPLDebug("ECW", "Rewrite PROJ : %s", psEditInfo->szProjection);
1✔
1636
    }
1637
    if (bUnitsCodeChanged)
4✔
1638
    {
1639
        psEditInfo->eCellSizeUnits =
2✔
1640
            ECWTranslateToCellSizeUnits(m_osUnitsCode.c_str());
1✔
1641
        CPLDebug("ECW", "Rewrite UNITS : %s",
1✔
1642
                 ECWTranslateFromCellSizeUnits(psEditInfo->eCellSizeUnits));
1643
    }
1644

1645
    if (bGeoTransformChanged)
4✔
1646
    {
1647
        psEditInfo->fOriginX = m_gt[0];
2✔
1648
        psEditInfo->fCellIncrementX = m_gt[1];
2✔
1649
        psEditInfo->fOriginY = m_gt[3];
2✔
1650
        psEditInfo->fCellIncrementY = m_gt[5];
2✔
1651
        CPLDebug("ECW", "Rewrite Geotransform");
2✔
1652
    }
1653

1654
    /* Write modified header info */
1655
#if ECWSDK_VERSION < 50
1656
    eErr = NCSEcwEditWriteInfo((char *)GetDescription(), psEditInfo, nullptr,
4✔
1657
                               nullptr, nullptr);
1658
#else
1659
    eErr =
1660
        NCSEcwEditWriteInfo(NCS::CString::Utf8Decode(GetDescription()).c_str(),
1661
                            psEditInfo, nullptr, nullptr, nullptr);
1662
#endif
1663
    if (eErr != NCS_SUCCESS)
4✔
1664
    {
1665
        CPLError(CE_Failure, CPLE_AppDefined, "NCSEcwEditWriteInfo() failed");
×
1666
    }
1667

1668
    /* Restore original pointers before free'ing */
1669
    psEditInfo->szDatum = pszOriginalCode;
4✔
1670
    psEditInfo->szProjection = pszOriginalProj;
4✔
1671

1672
    NCSEcwEditFreeInfo(psEditInfo);
4✔
1673
}
1674

1675
/************************************************************************/
1676
/*                             AdviseRead()                             */
1677
/************************************************************************/
1678

1679
CPLErr ECWDataset::AdviseRead(int nXOff, int nYOff, int nXSize, int nYSize,
249✔
1680
                              int nBufXSize, int nBufYSize,
1681
                              CPL_UNUSED GDALDataType eDT, int nBandCount,
1682
                              int *panBandList, CPL_UNUSED char **papszOptions)
1683
{
1684
    CPLDebug("ECW", "ECWDataset::AdviseRead(%d,%d,%d,%d->%d,%d)", nXOff, nYOff,
249✔
1685
             nXSize, nYSize, nBufXSize, nBufYSize);
1686

1687
#if !defined(SDK_CAN_DO_SUPERSAMPLING)
1688
    if (nBufXSize > nXSize || nBufYSize > nYSize)
249✔
1689
    {
1690
        CPLError(CE_Warning, CPLE_AppDefined,
×
1691
                 "Supersampling not directly supported by ECW toolkit,\n"
1692
                 "ignoring AdviseRead() request.");
1693
        return CE_Warning;
×
1694
    }
1695
#endif
1696

1697
    /* -------------------------------------------------------------------- */
1698
    /*      Do some validation of parameters.                               */
1699
    /* -------------------------------------------------------------------- */
1700

1701
    CPLErr eErr;
1702
    int bStopProcessing = FALSE;
249✔
1703
    eErr = ValidateRasterIOOrAdviseReadParameters(
249✔
1704
        "AdviseRead()", &bStopProcessing, nXOff, nYOff, nXSize, nYSize,
1705
        nBufXSize, nBufYSize, nBandCount, panBandList);
1706
    if (eErr != CE_None || bStopProcessing)
249✔
1707
        return eErr;
×
1708

1709
    if (nBandCount > 100)
249✔
1710
    {
1711
        ReportError(CE_Failure, CPLE_IllegalArg,
×
1712
                    "AdviseRead(): Too many bands : %d", nBandCount);
1713
        return CE_Failure;
×
1714
    }
1715

1716
    if (nBufXSize != nXSize || nBufYSize != nYSize)
249✔
1717
    {
1718
        // This early exit is because experimentally we found that
1719
        // performance of requesting at 50% is much slower with
1720
        // AdviseRead()...
1721
        // At least on JPEG2000 images with SDK 3.3
1722
        CPLDebug("ECW",
200✔
1723
                 "Ignoring AdviseRead() for non full resolution request");
1724
        return CE_None;
200✔
1725
    }
1726

1727
    // We don't setup the reading window right away, in case the actual read
1728
    // pattern wouldn't be compatible of it. Which might be the case for
1729
    // example if AdviseRead() requests a full image, but we don't read by
1730
    // chunks of the full width of one or several lines
1731
    m_nAdviseReadXOff = nXOff;
49✔
1732
    m_nAdviseReadYOff = nYOff;
49✔
1733
    m_nAdviseReadXSize = nXSize;
49✔
1734
    m_nAdviseReadYSize = nYSize;
49✔
1735
    m_nAdviseReadBufXSize = nBufXSize;
49✔
1736
    m_nAdviseReadBufYSize = nBufYSize;
49✔
1737
    m_nAdviseReadBandCount = nBandCount;
49✔
1738
    CPLFree(m_panAdviseReadBandList);
49✔
1739
    if (panBandList)
49✔
1740
    {
1741
        m_panAdviseReadBandList =
41✔
1742
            static_cast<int *>(CPLMalloc(sizeof(int) * nBandCount));
41✔
1743
        memcpy(m_panAdviseReadBandList, panBandList, sizeof(int) * nBandCount);
41✔
1744
    }
1745
    else
1746
    {
1747
        m_panAdviseReadBandList = nullptr;
8✔
1748
    }
1749

1750
    return CE_None;
49✔
1751
}
1752

1753
/************************************************************************/
1754
/*                        RunDeferredAdviseRead()                        */
1755
/************************************************************************/
1756

1757
CPLErr ECWDataset::RunDeferredAdviseRead()
44✔
1758
{
1759
    CPLAssert(m_nAdviseReadXOff >= 0);
44✔
1760

1761
    const int nXOff = m_nAdviseReadXOff;
44✔
1762
    const int nYOff = m_nAdviseReadYOff;
44✔
1763
    const int nXSize = m_nAdviseReadXSize;
44✔
1764
    const int nYSize = m_nAdviseReadYSize;
44✔
1765
    const int nBufXSize = m_nAdviseReadBufXSize;
44✔
1766
    const int nBufYSize = m_nAdviseReadBufYSize;
44✔
1767
    const int nBandCount = m_nAdviseReadBandCount;
44✔
1768
    int *panBandList = m_panAdviseReadBandList;
44✔
1769

1770
    m_nAdviseReadXOff = -1;
44✔
1771
    m_nAdviseReadYOff = -1;
44✔
1772
    m_nAdviseReadXSize = -1;
44✔
1773
    m_nAdviseReadYSize = -1;
44✔
1774
    m_nAdviseReadBufXSize = -1;
44✔
1775
    m_nAdviseReadBufYSize = -1;
44✔
1776
    m_nAdviseReadBandCount = -1;
44✔
1777
    m_panAdviseReadBandList = nullptr;
44✔
1778

1779
    /* -------------------------------------------------------------------- */
1780
    /*      Adjust band numbers to be zero based.                           */
1781
    /* -------------------------------------------------------------------- */
1782
    UINT32 *panAdjustedBandList =
1783
        (UINT32 *)CPLMalloc(sizeof(UINT32) * nBandCount);
44✔
1784
    nBandIndexToPromoteTo8Bit = -1;
44✔
1785
    for (int ii = 0; ii < nBandCount; ii++)
97✔
1786
    {
1787
        const int nIdx = (panBandList != nullptr) ? panBandList[ii] - 1 : ii;
53✔
1788
        ;
1789
        panAdjustedBandList[ii] = nIdx;
53✔
1790
        if (cpl::down_cast<ECWRasterBand *>(GetRasterBand(nIdx + 1))
53✔
1791
                ->bPromoteTo8Bit)
53✔
1792
            nBandIndexToPromoteTo8Bit = ii;
1✔
1793
    }
1794

1795
    /* -------------------------------------------------------------------- */
1796
    /*      Cleanup old window cache information.                           */
1797
    /* -------------------------------------------------------------------- */
1798
    CleanupWindow();
44✔
1799

1800
    /* -------------------------------------------------------------------- */
1801
    /*      Set the new requested window.                                   */
1802
    /* -------------------------------------------------------------------- */
1803
    CNCSError oErr = poFileView->SetView(
44✔
1804
        nBandCount, panAdjustedBandList, nXOff, nYOff, nXOff + nXSize - 1,
44✔
1805
        nYOff + nYSize - 1, nBufXSize, nBufYSize);
88✔
1806

1807
    CPLFree(panAdjustedBandList);
44✔
1808
    if (oErr.GetErrorNumber() != NCS_SUCCESS)
44✔
1809
    {
1810
        ECWReportError(oErr);
×
1811

1812
        bWinActive = FALSE;
×
1813
        CPLFree(panBandList);
×
1814
        return CE_Failure;
×
1815
    }
1816

1817
    bWinActive = TRUE;
44✔
1818

1819
    /* -------------------------------------------------------------------- */
1820
    /*      Record selected window.                                         */
1821
    /* -------------------------------------------------------------------- */
1822
    nWinXOff = nXOff;
44✔
1823
    nWinYOff = nYOff;
44✔
1824
    nWinXSize = nXSize;
44✔
1825
    nWinYSize = nYSize;
44✔
1826
    nWinBufXSize = nBufXSize;
44✔
1827
    nWinBufYSize = nBufYSize;
44✔
1828

1829
    panWinBandList = (int *)CPLMalloc(sizeof(int) * nBandCount);
44✔
1830
    if (panBandList != nullptr)
44✔
1831
        memcpy(panWinBandList, panBandList, sizeof(int) * nBandCount);
40✔
1832
    else
1833
    {
1834
        for (int ii = 0; ii < nBandCount; ii++)
17✔
1835
        {
1836
            panWinBandList[ii] = ii + 1;
13✔
1837
        }
1838
    }
1839
    nWinBandCount = nBandCount;
44✔
1840

1841
    nWinBufLoaded = -1;
44✔
1842

1843
    /* -------------------------------------------------------------------- */
1844
    /*      Allocate current scanline buffer.                               */
1845
    /* -------------------------------------------------------------------- */
1846
    papCurLineBuf = (void **)CPLMalloc(sizeof(void *) * nWinBandCount);
44✔
1847
    for (int iBand = 0; iBand < nWinBandCount; iBand++)
97✔
1848
        papCurLineBuf[iBand] =
106✔
1849
            CPLMalloc(static_cast<size_t>(nBufXSize) *
53✔
1850
                      GDALGetDataTypeSizeBytes(eRasterDataType));
53✔
1851

1852
    CPLFree(panBandList);
44✔
1853

1854
    return CE_None;
44✔
1855
}
1856

1857
/************************************************************************/
1858
/*                           TryWinRasterIO()                           */
1859
/*                                                                      */
1860
/*      Try to satisfy the given request based on the currently         */
1861
/*      defined window.  Return TRUE on success or FALSE on             */
1862
/*      failure.  On failure, the caller should satisfy the request     */
1863
/*      another way (not report an error).                              */
1864
/************************************************************************/
1865

1866
int ECWDataset::TryWinRasterIO(CPL_UNUSED GDALRWFlag eFlag, int nXOff,
1,931✔
1867
                               int nYOff, int nXSize, int nYSize,
1868
                               GByte *pabyData, int nBufXSize, int nBufYSize,
1869
                               GDALDataType eDT, int nBandCount,
1870
                               const int *panBandList, GSpacing nPixelSpace,
1871
                               GSpacing nLineSpace, GSpacing nBandSpace,
1872
                               GDALRasterIOExtraArg *psExtraArg)
1873
{
1874
    int iBand, i;
1875

1876
    /* -------------------------------------------------------------------- */
1877
    /*      Provide default buffer organization.                            */
1878
    /* -------------------------------------------------------------------- */
1879
    if (nPixelSpace == 0)
1,931✔
1880
        nPixelSpace = GDALGetDataTypeSizeBytes(eDT);
×
1881
    if (nLineSpace == 0)
1,931✔
1882
        nLineSpace = nPixelSpace * nBufXSize;
×
1883
    if (nBandSpace == 0)
1,931✔
1884
        nBandSpace = nLineSpace * nBufYSize;
1✔
1885

1886
/* -------------------------------------------------------------------- */
1887
/*      Do some simple tests to see if the current window can           */
1888
/*      satisfy our requirement.                                        */
1889
/* -------------------------------------------------------------------- */
1890
#ifdef NOISY_DEBUG
1891
    CPLDebug("ECW", "TryWinRasterIO(%d,%d,%d,%d,%d,%d)", nXOff, nYOff, nXSize,
1892
             nYSize, nBufXSize, nBufYSize);
1893
#endif
1894

1895
    if (!bWinActive)
1,931✔
1896
    {
1897
        if (nXOff == m_nAdviseReadXOff && nXSize == m_nAdviseReadXSize &&
528✔
1898
            nBufXSize == m_nAdviseReadBufXSize)
44✔
1899
        {
1900
            if (RunDeferredAdviseRead() != CE_None)
44✔
1901
                return FALSE;
×
1902
        }
1903
        if (!bWinActive)
528✔
1904
        {
1905
            return FALSE;
484✔
1906
        }
1907
    }
1908

1909
    if (nXOff != nWinXOff || nXSize != nWinXSize)
1,447✔
1910
        return FALSE;
×
1911

1912
    if (nBufXSize != nWinBufXSize)
1,447✔
1913
        return FALSE;
×
1914

1915
    for (iBand = 0; iBand < nBandCount; iBand++)
2,907✔
1916
    {
1917
        for (i = 0; i < nWinBandCount; i++)
1,488✔
1918
        {
1919
            if (panWinBandList[i] == panBandList[iBand])
1,486✔
1920
                break;
1,460✔
1921
        }
1922

1923
        if (i == nWinBandCount)
1,462✔
1924
            return FALSE;
2✔
1925
    }
1926

1927
    if (nYOff < nWinYOff || nYOff + nYSize > nWinYOff + nWinYSize)
1,445✔
1928
        return FALSE;
×
1929

1930
    /* -------------------------------------------------------------------- */
1931
    /*      Now we try more subtle tests.                                   */
1932
    /* -------------------------------------------------------------------- */
1933
    {
1934
        static int nDebugCount = 0;
1935

1936
        if (nDebugCount < 30)
1,445✔
1937
            CPLDebug(
30✔
1938
                "ECW",
1939
                "TryWinRasterIO(%d,%d,%d,%d -> %dx%d) - doing advised read.",
1940
                nXOff, nYOff, nXSize, nYSize, nBufXSize, nBufYSize);
1941

1942
        if (nDebugCount == 29)
1,445✔
1943
            CPLDebug("ECW", "No more TryWinRasterIO messages will be reported");
1✔
1944

1945
        nDebugCount++;
1,445✔
1946
    }
1947

1948
    /* -------------------------------------------------------------------- */
1949
    /*      Actually load data one buffer line at a time.                   */
1950
    /* -------------------------------------------------------------------- */
1951
    int iBufLine;
1952

1953
    for (iBufLine = 0; iBufLine < nBufYSize; iBufLine++)
4,165✔
1954
    {
1955
        double fFileLine = ((iBufLine + 0.5) / nBufYSize) * nYSize + nYOff;
2,788✔
1956
        int iWinLine =
2,788✔
1957
            (int)(((fFileLine - nWinYOff) / nWinYSize) * nWinBufYSize);
2,788✔
1958

1959
        if (iWinLine == nWinBufLoaded + 1)
2,788✔
1960
            LoadNextLine();
2,720✔
1961

1962
        if (iWinLine != nWinBufLoaded)
2,788✔
1963
            return FALSE;
68✔
1964

1965
        /* --------------------------------------------------------------------
1966
         */
1967
        /*      Copy out all our target bands. */
1968
        /* --------------------------------------------------------------------
1969
         */
1970
        int iWinBand;
1971
        for (iBand = 0; iBand < nBandCount; iBand++)
8,290✔
1972
        {
1973
            for (iWinBand = 0; iWinBand < nWinBandCount; iWinBand++)
10,070✔
1974
            {
1975
                if (panWinBandList[iWinBand] == panBandList[iBand])
10,070✔
1976
                    break;
5,570✔
1977
            }
1978

1979
            GDALCopyWords(papCurLineBuf[iWinBand], eRasterDataType,
5,570✔
1980
                          GDALGetDataTypeSizeBytes(eRasterDataType),
1981
                          pabyData + nBandSpace * iBand + iBufLine * nLineSpace,
5,570✔
1982
                          eDT, (int)nPixelSpace, nBufXSize);
1983
        }
1984

1985
        if (psExtraArg->pfnProgress != nullptr &&
2,870✔
1986
            !psExtraArg->pfnProgress(1.0 * (iBufLine + 1) / nBufYSize, "",
150✔
1987
                                     psExtraArg->pProgressData))
1988
        {
1989
            return -1;
×
1990
        }
1991
    }
1992

1993
    return TRUE;
1,377✔
1994
}
1995

1996
/************************************************************************/
1997
/*                            LoadNextLine()                            */
1998
/************************************************************************/
1999

2000
CPLErr ECWDataset::LoadNextLine()
2,720✔
2001

2002
{
2003
    if (!bWinActive)
2,720✔
2004
        return CE_Failure;
×
2005

2006
    if (nWinBufLoaded == nWinBufYSize - 1)
2,720✔
2007
    {
2008
        CleanupWindow();
×
2009
        return CE_Failure;
×
2010
    }
2011

2012
    NCSEcwReadStatus eRStatus;
2013
    eRStatus = poFileView->ReadLineBIL(eNCSRequestDataType,
5,440✔
2014
                                       (UINT16)nWinBandCount, papCurLineBuf);
2,720✔
2015
    if (eRStatus != NCSECW_READ_OK)
2,720✔
2016
        return CE_Failure;
×
2017

2018
    if (nBandIndexToPromoteTo8Bit >= 0)
2,720✔
2019
    {
2020
        for (int iX = 0; iX < nWinBufXSize; iX++)
24,450✔
2021
        {
2022
            ((GByte *)papCurLineBuf[nBandIndexToPromoteTo8Bit])[iX] *= 255;
24,300✔
2023
        }
2024
    }
2025

2026
    nWinBufLoaded++;
2,720✔
2027

2028
    return CE_None;
2,720✔
2029
}
2030

2031
/************************************************************************/
2032
/*                           CleanupWindow()                            */
2033
/************************************************************************/
2034

2035
void ECWDataset::CleanupWindow()
479✔
2036

2037
{
2038
    if (!bWinActive)
479✔
2039
        return;
435✔
2040

2041
    bWinActive = FALSE;
44✔
2042
    CPLFree(panWinBandList);
44✔
2043
    panWinBandList = nullptr;
44✔
2044

2045
    for (int iBand = 0; iBand < nWinBandCount; iBand++)
97✔
2046
        CPLFree(papCurLineBuf[iBand]);
53✔
2047
    CPLFree(papCurLineBuf);
44✔
2048
    papCurLineBuf = nullptr;
44✔
2049
}
2050

2051
/************************************************************************/
2052
/*                             IRasterIO()                              */
2053
/************************************************************************/
2054

2055
CPLErr ECWDataset::IRasterIO(GDALRWFlag eRWFlag, int nXOff, int nYOff,
1,699✔
2056
                             int nXSize, int nYSize, void *pData, int nBufXSize,
2057
                             int nBufYSize, GDALDataType eBufType,
2058
                             int nBandCount, BANDMAP_TYPE panBandMap,
2059
                             GSpacing nPixelSpace, GSpacing nLineSpace,
2060
                             GSpacing nBandSpace,
2061
                             GDALRasterIOExtraArg *psExtraArg)
2062

2063
{
2064
    if (eRWFlag == GF_Write)
1,699✔
2065
        return CE_Failure;
×
2066

2067
    if (nBandCount > 100)
1,699✔
2068
        return CE_Failure;
×
2069

2070
    if (bUseOldBandRasterIOImplementation)
1,699✔
2071
        /* Sanity check. Should not happen */
2072
        return CE_Failure;
×
2073
    int nDataTypeSize = GDALGetDataTypeSizeBytes(eRasterDataType);
1,699✔
2074

2075
    if (nPixelSpace == 0)
1,699✔
2076
    {
2077
        nPixelSpace = nDataTypeSize;
×
2078
    }
2079

2080
    if (nLineSpace == 0)
1,699✔
2081
    {
2082
        nLineSpace = nPixelSpace * nBufXSize;
×
2083
    }
2084
    if (nBandSpace == 0)
1,699✔
2085
    {
2086
        nBandSpace =
18✔
2087
            static_cast<GSpacing>(nDataTypeSize) * nBufXSize * nBufYSize;
18✔
2088
    }
2089

2090
    // Use GDAL upsampling if non nearest
2091
    if ((nBufXSize > nXSize || nBufYSize > nYSize) &&
1,699✔
2092
        psExtraArg->eResampleAlg != GRIORA_NearestNeighbour)
3✔
2093
    {
2094
        const int nBufDataTypeSize = GDALGetDataTypeSizeBytes(eBufType);
2✔
2095
        GByte *pabyTemp = (GByte *)VSI_MALLOC3_VERBOSE(
2✔
2096
            nXSize, nYSize, nBufDataTypeSize * nBandCount);
2097
        if (pabyTemp == nullptr)
2✔
2098
        {
2099
            return CE_Failure;
×
2100
        }
2101

2102
        GDALRasterIOExtraArg sExtraArgDefault;
2103
        INIT_RASTERIO_EXTRA_ARG(sExtraArgDefault);
2✔
2104
        sExtraArgDefault.pfnProgress = psExtraArg->pfnProgress;
2✔
2105
        sExtraArgDefault.pProgressData = psExtraArg->pProgressData;
2✔
2106

2107
        CPLErr eErr = IRasterIO(
4✔
2108
            eRWFlag, nXOff, nYOff, nXSize, nYSize, pabyTemp, nXSize, nYSize,
2109
            eBufType, nBandCount, panBandMap, nBufDataTypeSize,
2110
            (GIntBig)nBufDataTypeSize * nXSize,
2✔
2111
            (GIntBig)nBufDataTypeSize * nXSize * nYSize, &sExtraArgDefault);
2✔
2112

2113
        if (eErr == CE_None)
2✔
2114
        {
2115
            /* Create a MEM dataset that wraps the input buffer */
2116
            auto poMEMDS = std::unique_ptr<MEMDataset>(
2117
                MEMDataset::Create("", nXSize, nYSize, 0, eBufType, nullptr));
2✔
2118

2119
            for (int i = 0; i < nBandCount; i++)
7✔
2120
            {
2121
                auto hBand = MEMCreateRasterBandEx(
5✔
2122
                    poMEMDS.get(), i + 1,
5✔
2123
                    pabyTemp + static_cast<size_t>(i) * nBufDataTypeSize *
5✔
2124
                                   nXSize * nYSize,
5✔
2125
                    eBufType, 0, 0, false);
2126
                poMEMDS->AddMEMBand(hBand);
5✔
2127

2128
                const char *pszNBITS = GetRasterBand(i + 1)->GetMetadataItem(
5✔
2129
                    "NBITS", "IMAGE_STRUCTURE");
5✔
2130
                if (pszNBITS)
5✔
2131
                    poMEMDS->GetRasterBand(i + 1)->SetMetadataItem(
×
2132
                        "NBITS", pszNBITS, "IMAGE_STRUCTURE");
×
2133
            }
2134

2135
            GDALRasterIOExtraArg sExtraArgTmp;
2136
            INIT_RASTERIO_EXTRA_ARG(sExtraArgTmp);
2✔
2137
            CPL_IGNORE_RET_VAL(sExtraArgTmp.eResampleAlg);
2✔
2138
            sExtraArgTmp.eResampleAlg = psExtraArg->eResampleAlg;
2✔
2139

2140
            CPL_IGNORE_RET_VAL(poMEMDS->RasterIO(
2✔
2141
                GF_Read, 0, 0, nXSize, nYSize, pData, nBufXSize, nBufYSize,
2142
                eBufType, nBandCount, nullptr, nPixelSpace, nLineSpace,
2143
                nBandSpace, &sExtraArgTmp));
2144
        }
2145

2146
        VSIFree(pabyTemp);
2✔
2147

2148
        return eErr;
2✔
2149
    }
2150

2151
/* -------------------------------------------------------------------- */
2152
/*      ECW SDK 3.3 has a bug with the ECW format when we query the     */
2153
/*      number of bands of the dataset, but not in the "natural order". */
2154
/*      It ignores the content of panBandMap. (#4234)                   */
2155
/* -------------------------------------------------------------------- */
2156
#if ECWSDK_VERSION < 40
2157
    if (!bIsJPEG2000 && nBandCount == nBands)
1,697✔
2158
    {
2159
        int i;
2160
        int bDoBandIRasterIO = FALSE;
11✔
2161
        for (i = 0; i < nBandCount; i++)
44✔
2162
        {
2163
            if (panBandMap[i] != i + 1)
33✔
2164
            {
2165
                bDoBandIRasterIO = TRUE;
2✔
2166
            }
2167
        }
2168
        if (bDoBandIRasterIO)
11✔
2169
        {
2170
            return GDALDataset::IRasterIO(eRWFlag, nXOff, nYOff, nXSize, nYSize,
1✔
2171
                                          pData, nBufXSize, nBufYSize, eBufType,
2172
                                          nBandCount, panBandMap, nPixelSpace,
2173
                                          nLineSpace, nBandSpace, psExtraArg);
1✔
2174
        }
2175
    }
2176
#endif
2177

2178
    /* -------------------------------------------------------------------- */
2179
    /*      Check if we can directly return the data in case we have cached */
2180
    /*      it from a previous call in a multi-band reading pattern.        */
2181
    /* -------------------------------------------------------------------- */
2182
    if (nBandCount == 1 && *panBandMap > 1 && *panBandMap <= nBands &&
1,696✔
2183
        sCachedMultiBandIO.nXOff == nXOff &&
67✔
2184
        sCachedMultiBandIO.nYOff == nYOff &&
67✔
2185
        sCachedMultiBandIO.nXSize == nXSize &&
30✔
2186
        sCachedMultiBandIO.nYSize == nYSize &&
22✔
2187
        sCachedMultiBandIO.nBufXSize == nBufXSize &&
22✔
2188
        sCachedMultiBandIO.nBufYSize == nBufYSize &&
22✔
2189
        sCachedMultiBandIO.eBufType == eBufType)
22✔
2190
    {
2191
        sCachedMultiBandIO.nBandsTried++;
22✔
2192

2193
        if (sCachedMultiBandIO.bEnabled &&
22✔
2194
            sCachedMultiBandIO.pabyData != nullptr)
7✔
2195
        {
2196
            int j;
2197
            const int nBufTypeSize = GDALGetDataTypeSizeBytes(eBufType);
7✔
2198
            for (j = 0; j < nBufYSize; j++)
357✔
2199
            {
2200
                GDALCopyWords(
350✔
2201
                    sCachedMultiBandIO.pabyData +
350✔
2202
                        static_cast<size_t>(*panBandMap - 1) * nBufXSize *
350✔
2203
                            nBufYSize * nBufTypeSize +
350✔
2204
                        static_cast<size_t>(j) * nBufXSize * nBufTypeSize,
350✔
2205
                    eBufType, nBufTypeSize, ((GByte *)pData) + j * nLineSpace,
350✔
2206
                    eBufType, (int)nPixelSpace, nBufXSize);
2207
            }
2208
            return CE_None;
7✔
2209
        }
2210

2211
        if (!(sCachedMultiBandIO.bEnabled) &&
45✔
2212
            sCachedMultiBandIO.nBandsTried == nBands &&
22✔
2213
            CPLTestBool(CPLGetConfigOption("ECW_CLEVER", "YES")))
7✔
2214
        {
2215
            sCachedMultiBandIO.bEnabled = TRUE;
7✔
2216
            CPLDebug(
7✔
2217
                "ECW",
2218
                "Detecting successive band reading pattern (for next time)");
2219
        }
2220
    }
2221

2222
    /* -------------------------------------------------------------------- */
2223
    /*      Try to do it based on existing "advised" access.                */
2224
    /* -------------------------------------------------------------------- */
2225
    int nRet =
2226
        TryWinRasterIO(eRWFlag, nXOff, nYOff, nXSize, nYSize, (GByte *)pData,
1,689✔
2227
                       nBufXSize, nBufYSize, eBufType, nBandCount, panBandMap,
2228
                       nPixelSpace, nLineSpace, nBandSpace, psExtraArg);
2229
    if (nRet == TRUE)
1,689✔
2230
        return CE_None;
1,371✔
2231
    else if (nRet < 0)
318✔
2232
        return CE_Failure;
×
2233

2234
    /* -------------------------------------------------------------------- */
2235
    /*      If we are requesting a single line at 1:1, we do a multi-band   */
2236
    /*      AdviseRead() and then TryWinRasterIO() again.                   */
2237
    /*                                                                      */
2238
    /*      Except for reading a 1x1 window when reading a scanline might   */
2239
    /*      be longer.                                                      */
2240
    /* -------------------------------------------------------------------- */
2241
    if (nXSize == 1 && nYSize == 1 && nBufXSize == 1 && nBufYSize == 1)
318✔
2242
    {
2243
        /* do nothing */
2244
    }
2245

2246
#if !defined(SDK_CAN_DO_SUPERSAMPLING)
2247
    /* -------------------------------------------------------------------- */
2248
    /*      If we are supersampling we need to fall into the general        */
2249
    /*      purpose logic.                                                  */
2250
    /* -------------------------------------------------------------------- */
2251
    else if (nXSize < nBufXSize || nYSize < nBufYSize)
314✔
2252
    {
2253
        bUseOldBandRasterIOImplementation = TRUE;
1✔
2254
        CPLErr eErr = GDALDataset::IRasterIO(
1✔
2255
            eRWFlag, nXOff, nYOff, nXSize, nYSize, pData, nBufXSize, nBufYSize,
2256
            eBufType, nBandCount, panBandMap, nPixelSpace, nLineSpace,
2257
            nBandSpace, psExtraArg);
2258
        bUseOldBandRasterIOImplementation = FALSE;
1✔
2259
        return eErr;
1✔
2260
    }
2261
#endif
2262

2263
    else if (nBufYSize == 1)
313✔
2264
    {
2265
        // This is tricky, because it expects the rest of the image
2266
        // with this buffer width to be read. The preferred way to
2267
        // achieve this behavior would be to call AdviseRead before
2268
        // call IRasterIO.  The logic could be improved to detect
2269
        // successive pattern of single line reading before doing an
2270
        // AdviseRead.
2271
        CPLErr eErr;
2272

2273
        eErr = AdviseRead(nXOff, nYOff, nXSize, GetRasterYSize() - nYOff,
241✔
2274
                          nBufXSize, (nRasterYSize - nYOff) / nYSize, eBufType,
241✔
2275
                          nBandCount, const_cast<int *>(panBandMap), nullptr);
2276
        if (eErr == CE_None &&
482✔
2277
            TryWinRasterIO(eRWFlag, nXOff, nYOff, nXSize, nYSize,
241✔
2278
                           (GByte *)pData, nBufXSize, nBufYSize, eBufType,
2279
                           nBandCount, panBandMap, nPixelSpace, nLineSpace,
2280
                           nBandSpace, psExtraArg))
2281
            return CE_None;
6✔
2282
    }
2283

2284
    CPLDebug("ECW", "RasterIO(%d,%d,%d,%d -> %dx%d) - doing interleaved read.",
311✔
2285
             nXOff, nYOff, nXSize, nYSize, nBufXSize, nBufYSize);
2286

2287
    /* -------------------------------------------------------------------- */
2288
    /*      Setup view.                                                     */
2289
    /* -------------------------------------------------------------------- */
2290
    UINT32 anBandIndices[100];
2291
    int i;
2292
    NCSError eNCSErr;
2293
    CNCSError oErr(GetCNCSError(NCS_SUCCESS));
622✔
2294

2295
    for (i = 0; i < nBandCount; i++)
641✔
2296
        anBandIndices[i] = panBandMap[i] - 1;
330✔
2297

2298
    CleanupWindow();
311✔
2299

2300
    /* -------------------------------------------------------------------- */
2301
    /*      Cache data in the context of a multi-band reading pattern.      */
2302
    /* -------------------------------------------------------------------- */
2303
    if (nBandCount == 1 && *panBandMap == 1 && (nBands == 3 || nBands == 4))
311✔
2304
    {
2305
        if (sCachedMultiBandIO.bEnabled &&
219✔
2306
            sCachedMultiBandIO.nBandsTried != nBands)
4✔
2307
        {
2308
            sCachedMultiBandIO.bEnabled = FALSE;
1✔
2309
            CPLDebug("ECW", "Disabling successive band reading pattern");
1✔
2310
        }
2311

2312
        sCachedMultiBandIO.nXOff = nXOff;
219✔
2313
        sCachedMultiBandIO.nYOff = nYOff;
219✔
2314
        sCachedMultiBandIO.nXSize = nXSize;
219✔
2315
        sCachedMultiBandIO.nYSize = nYSize;
219✔
2316
        sCachedMultiBandIO.nBufXSize = nBufXSize;
219✔
2317
        sCachedMultiBandIO.nBufYSize = nBufYSize;
219✔
2318
        sCachedMultiBandIO.eBufType = eBufType;
219✔
2319
        sCachedMultiBandIO.nBandsTried = 1;
219✔
2320

2321
        const int nBufTypeSize = GDALGetDataTypeSizeBytes(eBufType);
219✔
2322

2323
        if (sCachedMultiBandIO.bEnabled)
219✔
2324
        {
2325
            GByte *pNew =
2326
                (GByte *)VSIRealloc(sCachedMultiBandIO.pabyData,
6✔
2327
                                    static_cast<size_t>(nBufXSize) * nBufYSize *
3✔
2328
                                        nBands * nBufTypeSize);
3✔
2329
            if (pNew == nullptr)
3✔
2330
                CPLFree(sCachedMultiBandIO.pabyData);
×
2331
            sCachedMultiBandIO.pabyData = pNew;
3✔
2332
        }
2333

2334
        if (sCachedMultiBandIO.bEnabled &&
219✔
2335
            sCachedMultiBandIO.pabyData != nullptr)
3✔
2336
        {
2337
            nBandIndexToPromoteTo8Bit = -1;
3✔
2338
            for (i = 0; i < nBands; i++)
12✔
2339
            {
2340
                if (cpl::down_cast<ECWRasterBand *>(GetRasterBand(i + 1))
9✔
2341
                        ->bPromoteTo8Bit)
9✔
2342
                    nBandIndexToPromoteTo8Bit = i;
×
2343
                anBandIndices[i] = i;
9✔
2344
            }
2345

2346
            oErr = poFileView->SetView(nBands, anBandIndices, nXOff, nYOff,
3✔
2347
                                       nXOff + nXSize - 1, nYOff + nYSize - 1,
3✔
2348
                                       nBufXSize, nBufYSize);
3✔
2349
            eNCSErr = oErr.GetErrorNumber();
3✔
2350

2351
            if (eNCSErr != NCS_SUCCESS)
3✔
2352
            {
2353
                CPLError(CE_Failure, CPLE_AppDefined, "%s",
×
2354
                         NCSGetErrorText(eNCSErr));
2355

2356
                return CE_Failure;
×
2357
            }
2358

2359
            CPLErr eErr = ReadBands(
6✔
2360
                sCachedMultiBandIO.pabyData, nBufXSize, nBufYSize, eBufType,
3✔
2361
                nBands, nBufTypeSize, nBufXSize * nBufTypeSize,
3✔
2362
                nBufXSize * nBufYSize * nBufTypeSize, psExtraArg);
3✔
2363
            if (eErr != CE_None)
3✔
2364
                return eErr;
×
2365

2366
            int j;
2367
            for (j = 0; j < nBufYSize; j++)
153✔
2368
            {
2369
                GDALCopyWords(
150✔
2370
                    sCachedMultiBandIO.pabyData +
150✔
2371
                        static_cast<size_t>(j) * nBufXSize * nBufTypeSize,
150✔
2372
                    eBufType, nBufTypeSize, ((GByte *)pData) + j * nLineSpace,
150✔
2373
                    eBufType, (int)nPixelSpace, nBufXSize);
2374
            }
2375
            return CE_None;
3✔
2376
        }
2377
    }
2378

2379
    nBandIndexToPromoteTo8Bit = -1;
308✔
2380
    for (i = 0; i < nBandCount; i++)
635✔
2381
    {
2382
        if (((ECWRasterBand *)GetRasterBand(anBandIndices[i] + 1))
327✔
2383
                ->bPromoteTo8Bit)
327✔
2384
            nBandIndexToPromoteTo8Bit = i;
4✔
2385
    }
2386
    oErr = poFileView->SetView(nBandCount, anBandIndices, nXOff, nYOff,
308✔
2387
                               nXOff + nXSize - 1, nYOff + nYSize - 1,
308✔
2388
                               nBufXSize, nBufYSize);
308✔
2389
    eNCSErr = oErr.GetErrorNumber();
308✔
2390

2391
    if (eNCSErr != NCS_SUCCESS)
308✔
2392
    {
2393
        CPLError(CE_Failure, CPLE_AppDefined, "%s", NCSGetErrorText(eNCSErr));
×
2394

2395
        return CE_Failure;
×
2396
    }
2397

2398
    return ReadBands(pData, nBufXSize, nBufYSize, eBufType, nBandCount,
308✔
2399
                     nPixelSpace, nLineSpace, nBandSpace, psExtraArg);
308✔
2400
}
2401

2402
/************************************************************************/
2403
/*                        ReadBandsDirectly()                           */
2404
/************************************************************************/
2405

2406
CPLErr ECWDataset::ReadBandsDirectly(void *pData, int nBufXSize, int nBufYSize,
39✔
2407
                                     CPL_UNUSED GDALDataType eBufType,
2408
                                     int nBandCount,
2409
                                     CPL_UNUSED GSpacing nPixelSpace,
2410
                                     GSpacing nLineSpace, GSpacing nBandSpace,
2411
                                     GDALRasterIOExtraArg *psExtraArg)
2412
{
2413
    CPLDebug("ECW", "ReadBandsDirectly(-> %dx%d) - reading lines directly.",
39✔
2414
             nBufXSize, nBufYSize);
2415

2416
    UINT8 **pBIL = (UINT8 **)NCSMalloc(nBandCount * sizeof(UINT8 *), FALSE);
39✔
2417

2418
    for (int nB = 0; nB < nBandCount; nB++)
101✔
2419
    {
2420
        pBIL[nB] = ((UINT8 *)pData) + (nBandSpace * nB);  // for any bit depth
62✔
2421
    }
2422

2423
    CPLErr eErr = CE_None;
39✔
2424
    for (int nR = 0; nR < nBufYSize; nR++)
5,574✔
2425
    {
2426
        if (poFileView->ReadLineBIL(eNCSRequestDataType, (UINT16)nBandCount,
11,070✔
2427
                                    (void **)pBIL) != NCSECW_READ_OK)
5,535✔
2428
        {
2429
            eErr = CE_Failure;
×
2430
            break;
×
2431
        }
2432
        for (int nB = 0; nB < nBandCount; nB++)
13,722✔
2433
        {
2434
            if (nB == nBandIndexToPromoteTo8Bit)
8,187✔
2435
            {
2436
                for (int iX = 0; iX < nBufXSize; iX++)
48,999✔
2437
                {
2438
                    pBIL[nB][iX] *= 255;
48,690✔
2439
                }
2440
            }
2441
            pBIL[nB] += nLineSpace;
8,187✔
2442
        }
2443

2444
        if (psExtraArg->pfnProgress != nullptr &&
5,535✔
2445
            !psExtraArg->pfnProgress(1.0 * (nR + 1) / nBufYSize, "",
×
2446
                                     psExtraArg->pProgressData))
2447
        {
2448
            eErr = CE_Failure;
×
2449
            break;
×
2450
        }
2451
    }
2452
    if (pBIL)
39✔
2453
    {
2454
        NCSFree(pBIL);
39✔
2455
    }
2456
    return eErr;
39✔
2457
}
2458

2459
/************************************************************************/
2460
/*                            ReadBands()                               */
2461
/************************************************************************/
2462

2463
CPLErr ECWDataset::ReadBands(void *pData, int nBufXSize, int nBufYSize,
311✔
2464
                             GDALDataType eBufType, int nBandCount,
2465
                             GSpacing nPixelSpace, GSpacing nLineSpace,
2466
                             GSpacing nBandSpace,
2467
                             GDALRasterIOExtraArg *psExtraArg)
2468
{
2469
    int i;
2470
    /* -------------------------------------------------------------------- */
2471
    /*      Setup working scanline, and the pointers into it.               */
2472
    /* -------------------------------------------------------------------- */
2473
    const int nDataTypeSizeBytes = GDALGetDataTypeSizeBytes(eRasterDataType);
311✔
2474
    bool bDirect =
311✔
2475
        (eBufType == eRasterDataType) && nDataTypeSizeBytes == nPixelSpace &&
45✔
2476
        nLineSpace == (nPixelSpace * nBufXSize) &&
395✔
2477
        nBandSpace ==
2478
            (static_cast<GSpacing>(nDataTypeSizeBytes) * nBufXSize * nBufYSize);
39✔
2479
    if (bDirect)
311✔
2480
    {
2481
        return ReadBandsDirectly(pData, nBufXSize, nBufYSize, eBufType,
39✔
2482
                                 nBandCount, nPixelSpace, nLineSpace,
2483
                                 nBandSpace, psExtraArg);
39✔
2484
    }
2485
    CPLDebug("ECW", "ReadBands(-> %dx%d) - reading lines using GDALCopyWords.",
272✔
2486
             nBufXSize, nBufYSize);
2487
    CPLErr eErr = CE_None;
272✔
2488
    GByte *pabyBILScanline = (GByte *)CPLMalloc(
544✔
2489
        static_cast<size_t>(nBufXSize) * nDataTypeSizeBytes * nBandCount);
272✔
2490
    GByte **papabyBIL = (GByte **)CPLMalloc(nBandCount * sizeof(void *));
272✔
2491

2492
    for (i = 0; i < nBandCount; i++)
546✔
2493
        papabyBIL[i] = pabyBILScanline +
274✔
2494
                       static_cast<size_t>(i) * nBufXSize * nDataTypeSizeBytes;
274✔
2495

2496
    /* -------------------------------------------------------------------- */
2497
    /*      Read back all the data for the requested view.                  */
2498
    /* -------------------------------------------------------------------- */
2499
    for (int iScanline = 0; iScanline < nBufYSize; iScanline++)
4,272✔
2500
    {
2501
        NCSEcwReadStatus eRStatus;
2502

2503
        eRStatus = poFileView->ReadLineBIL(
8,000✔
2504
            eNCSRequestDataType, (UINT16)nBandCount, (void **)papabyBIL);
4,000✔
2505
        if (eRStatus != NCSECW_READ_OK)
4,000✔
2506
        {
2507
            eErr = CE_Failure;
×
2508
            CPLError(CE_Failure, CPLE_AppDefined,
×
2509
                     "NCScbmReadViewLineBIL failed.");
2510
            break;
×
2511
        }
2512

2513
        for (i = 0; i < nBandCount; i++)
8,080✔
2514
        {
2515
            if (i == nBandIndexToPromoteTo8Bit)
4,080✔
2516
            {
2517
                for (int iX = 0; iX < nBufXSize; iX++)
24,450✔
2518
                {
2519
                    papabyBIL[i][iX] *= 255;
24,300✔
2520
                }
2521
            }
2522

2523
            GDALCopyWords(pabyBILScanline + static_cast<size_t>(i) *
4,080✔
2524
                                                nDataTypeSizeBytes * nBufXSize,
4,080✔
2525
                          eRasterDataType, nDataTypeSizeBytes,
2526
                          ((GByte *)pData) + nLineSpace * iScanline +
4,080✔
2527
                              nBandSpace * i,
4,080✔
2528
                          eBufType, (int)nPixelSpace, nBufXSize);
2529
        }
2530

2531
        if (psExtraArg->pfnProgress != nullptr &&
4,000✔
2532
            !psExtraArg->pfnProgress(1.0 * (iScanline + 1) / nBufYSize, "",
×
2533
                                     psExtraArg->pProgressData))
2534
        {
2535
            eErr = CE_Failure;
×
2536
            break;
×
2537
        }
2538
    }
2539

2540
    CPLFree(pabyBILScanline);
272✔
2541
    CPLFree(papabyBIL);
272✔
2542

2543
    return eErr;
272✔
2544
}
2545

2546
/************************************************************************/
2547
/*                            OpenJPEG2000()                            */
2548
/*                                                                      */
2549
/*          Open method that only supports JPEG2000 files.              */
2550
/************************************************************************/
2551

2552
GDALDataset *ECWDataset::OpenJPEG2000(GDALOpenInfo *poOpenInfo)
89✔
2553

2554
{
2555
    if (!ECWDatasetIdentifyJPEG2000(poOpenInfo))
89✔
2556
        return nullptr;
×
2557

2558
    return Open(poOpenInfo, TRUE);
89✔
2559
}
2560

2561
/************************************************************************/
2562
/*                              OpenECW()                               */
2563
/*                                                                      */
2564
/*      Open method that only supports ECW files.                       */
2565
/************************************************************************/
2566

2567
GDALDataset *ECWDataset::OpenECW(GDALOpenInfo *poOpenInfo)
35✔
2568

2569
{
2570
    if (!ECWDatasetIdentifyECW(poOpenInfo))
35✔
2571
        return nullptr;
×
2572

2573
    return Open(poOpenInfo, FALSE);
35✔
2574
}
2575

2576
/************************************************************************/
2577
/*                            OpenFileView()                            */
2578
/************************************************************************/
2579

2580
CNCSJP2FileView *ECWDataset::OpenFileView(const char *pszDatasetName,
124✔
2581
                                          bool bProgressive,
2582
                                          int &bUsingCustomStream,
2583
                                          CPL_UNUSED bool bWrite)
2584
{
2585
    /* -------------------------------------------------------------------- */
2586
    /*      First we try to open it as a normal CNCSFile, letting the       */
2587
    /*      ECW SDK manage the IO itself.   This will only work for real    */
2588
    /*      files, and ecwp: or ecwps: sources.                             */
2589
    /* -------------------------------------------------------------------- */
2590
    CNCSJP2FileView *poFileView = nullptr;
124✔
2591
    NCSError eErr;
2592
    CNCSError oErr(GetCNCSError(NCS_SUCCESS));
248✔
2593

2594
    bUsingCustomStream = FALSE;
124✔
2595
    poFileView = new CNCSFile();
124✔
2596
    // we always open in read only mode. This should be improved in the future.
2597
    try
2598
    {
2599
#ifdef _WIN32
2600
        if (CPLTestBool(CPLGetConfigOption("GDAL_FILENAME_IS_UTF8", "YES")))
2601
        {
2602
            wchar_t *pwszDatasetName =
2603
                CPLRecodeToWChar(pszDatasetName, CPL_ENC_UTF8, CPL_ENC_UCS2);
2604
            oErr = poFileView->Open(pwszDatasetName, bProgressive, false);
2605
            CPLFree(pwszDatasetName);
2606
        }
2607
        else
2608
#endif
2609
        {
2610
            oErr =
2611
                poFileView->Open((char *)pszDatasetName, bProgressive, false);
124✔
2612
        }
2613
    }
2614
    catch (...)
×
2615
    {
2616
        CPLError(CE_Failure, CPLE_AppDefined,
×
2617
                 "Unexpected exception occurred in ECW SDK");
2618
        delete poFileView;
×
2619
        return nullptr;
×
2620
    }
2621
    eErr = oErr.GetErrorNumber();
124✔
2622

2623
    /* -------------------------------------------------------------------- */
2624
    /*      If that did not work, trying opening as a virtual file.         */
2625
    /* -------------------------------------------------------------------- */
2626
    if (eErr != NCS_SUCCESS)
124✔
2627
    {
2628
        CPLDebug("ECW",
53✔
2629
                 "NCScbmOpenFileView(%s): eErr=%d, will try VSIL stream.",
2630
                 pszDatasetName, (int)eErr);
2631

2632
        delete poFileView;
53✔
2633

2634
        VSILFILE *fpVSIL = VSIFOpenL(pszDatasetName, "rb");
53✔
2635
        if (fpVSIL == nullptr)
53✔
2636
        {
2637
            CPLError(CE_Failure, CPLE_OpenFailed, "Failed to open %s.",
×
2638
                     pszDatasetName);
2639
            return nullptr;
×
2640
        }
2641

2642
        if (hECWDatasetMutex == nullptr)
53✔
2643
        {
2644
            hECWDatasetMutex = CPLCreateMutex();
×
2645
        }
2646
        else if (!CPLAcquireMutex(hECWDatasetMutex, 60.0))
53✔
2647
        {
2648
            CPLDebug("ECW", "Failed to acquire mutex in 60s.");
×
2649
        }
2650
        else
2651
        {
2652
            CPLDebug("ECW", "Got mutex.");
53✔
2653
        }
2654

2655
        poFileView = new CNCSJP2FileView();
53✔
2656

2657
#if ECWSDK_VERSION >= 55
2658
        NCS::CString streamName(pszDatasetName);
2659
        auto vsiIoStream =
2660
            NCS::CView::FindSteamByStreamNameFromOpenDatasets(streamName);
2661
        if (!vsiIoStream)
2662
        {
2663
            vsiIoStream = std::make_shared<VSIIOStream>();
2664
            std::static_pointer_cast<VSIIOStream>(vsiIoStream)
2665
                ->Access(fpVSIL, FALSE, TRUE, pszDatasetName, 0, -1);
2666
            bUsingCustomStream = TRUE;
2667
        }
2668
        oErr = poFileView->Open(vsiIoStream, bProgressive);
2669
#else
2670
        auto vsiIoStream = new VSIIOStream();
53✔
2671
        vsiIoStream->Access(fpVSIL, FALSE, TRUE, pszDatasetName, 0, -1);
53✔
2672
        oErr = poFileView->Open(vsiIoStream, bProgressive);
53✔
2673

2674
        // The CNCSJP2FileView (poFileView) object may not use the iostream
2675
        // (poIOStream) passed to the CNCSJP2FileView::Open() method if an
2676
        // iostream is already available to the ECW JPEG 2000 SDK for a given
2677
        // file.  Consequently, if the iostream passed to
2678
        // CNCSJP2FileView::Open() does not become the underlying iostream
2679
        // of the CNCSJP2FileView object, then it should be deleted.
2680
        //
2681
        // In addition, the underlying iostream of the CNCSJP2FileView object
2682
        // should not be deleted until all CNCSJP2FileView objects using the
2683
        // underlying iostream are deleted. Consequently, each time a
2684
        // CNCSJP2FileView object is created, the nFileViewCount attribute
2685
        // of the underlying VSIIOStream object must be incremented for use
2686
        // in the ECWDataset destructor.
2687

2688
        VSIIOStream *poUnderlyingIOStream =
2689
            ((VSIIOStream *)(poFileView->GetStream()));
53✔
2690

2691
        if (poUnderlyingIOStream)
53✔
2692
            poUnderlyingIOStream->nFileViewCount++;
52✔
2693

2694
        if (vsiIoStream != poUnderlyingIOStream)
53✔
2695
        {
2696
            delete vsiIoStream;
1✔
2697
        }
2698
        else
2699
        {
2700
            bUsingCustomStream = TRUE;
52✔
2701
        }
2702
#endif
2703

2704
        CPLReleaseMutex(hECWDatasetMutex);
53✔
2705

2706
        if (oErr.GetErrorNumber() != NCS_SUCCESS)
53✔
2707
        {
2708
            delete poFileView;
1✔
2709
            ECWReportError(oErr);
1✔
2710

2711
            return nullptr;
1✔
2712
        }
2713
    }
2714

2715
    return poFileView;
123✔
2716
}
2717

2718
/************************************************************************/
2719
/*                                Open()                                */
2720
/************************************************************************/
2721

2722
GDALDataset *ECWDataset::Open(GDALOpenInfo *poOpenInfo, int bIsJPEG2000)
124✔
2723

2724
{
2725
    CNCSJP2FileView *poFileView = nullptr;
124✔
2726
    int i;
2727
    int bUsingCustomStream = FALSE;
124✔
2728
    CPLString osFilename = poOpenInfo->pszFilename;
248✔
2729

2730
    ECWInitialize();
124✔
2731

2732
    /* Note: J2K_SUBFILE is somehow an obsolete concept that predates
2733
     * /vsisubfile/ */
2734
    /* syntax and was used mainly(only?) by the NITF driver before its switch */
2735
    /* to /vsisubfile */
2736

2737
    /* -------------------------------------------------------------------- */
2738
    /*      If we get a J2K_SUBFILE style name, convert it into the         */
2739
    /*      corresponding /vsisubfile/ path.                                */
2740
    /*                                                                      */
2741
    /*      From: J2K_SUBFILE:offset,size,filename                           */
2742
    /*      To: /vsisubfile/offset_size,filename                            */
2743
    /* -------------------------------------------------------------------- */
2744
    if (STARTS_WITH_CI(osFilename, "J2K_SUBFILE:"))
124✔
2745
    {
2746
        char **papszTokens =
2747
            CSLTokenizeString2(osFilename.c_str() + 12, ",", 0);
×
2748
        if (CSLCount(papszTokens) >= 3)
×
2749
        {
2750
            osFilename.Printf("/vsisubfile/%s_%s,%s", papszTokens[0],
2751
                              papszTokens[1], papszTokens[2]);
×
2752
        }
2753
        else
2754
        {
2755
            CPLError(CE_Failure, CPLE_OpenFailed,
×
2756
                     "Failed to parse J2K_SUBFILE specification.");
2757
            CSLDestroy(papszTokens);
×
2758
            return nullptr;
×
2759
        }
2760
        CSLDestroy(papszTokens);
×
2761
    }
2762

2763
    /* -------------------------------------------------------------------- */
2764
    /*      Open the client interface.                                      */
2765
    /* -------------------------------------------------------------------- */
2766
    poFileView = OpenFileView(osFilename.c_str(), false, bUsingCustomStream,
124✔
2767
                              poOpenInfo->eAccess == GA_Update);
124✔
2768
    if (poFileView == nullptr)
124✔
2769
    {
2770
#if ECWSDK_VERSION < 50
2771
        /* Detect what is apparently the ECW v3 file format signature */
2772
        if (EQUAL(CPLGetExtensionSafe(osFilename).c_str(), "ECW") &&
2✔
2773
            poOpenInfo->nHeaderBytes > 0x30 &&
3✔
2774
            STARTS_WITH_CI((const char *)(poOpenInfo->pabyHeader + 0x20),
1✔
2775
                           "ecw ECW3"))
2776
        {
2777
            CPLError(CE_Failure, CPLE_AppDefined,
1✔
2778
                     "Cannot open %s which looks like a ECW format v3 file, "
2779
                     "that requires ECW SDK 5.0 or later",
2780
                     osFilename.c_str());
2781
        }
2782
#endif
2783
        return nullptr;
1✔
2784
    }
2785

2786
    /* -------------------------------------------------------------------- */
2787
    /*      Create a corresponding GDALDataset.                             */
2788
    /* -------------------------------------------------------------------- */
2789
    ECWDataset *poDS = new ECWDataset(bIsJPEG2000);
123✔
2790
    poDS->poFileView = poFileView;
123✔
2791
    poDS->eAccess = poOpenInfo->eAccess;
123✔
2792

2793
    // Disable .aux.xml writing for subfiles and such.  Unfortunately
2794
    // this will also disable it in some cases where it might be
2795
    // applicable.
2796
    if (bUsingCustomStream)
123✔
2797
        poDS->nPamFlags |= GPF_DISABLED;
52✔
2798

2799
    poDS->bUsingCustomStream = bUsingCustomStream;
123✔
2800

2801
    /* -------------------------------------------------------------------- */
2802
    /*      Fetch general file information.                                 */
2803
    /* -------------------------------------------------------------------- */
2804
    poDS->psFileInfo = poFileView->GetFileInfo();
123✔
2805

2806
    CPLDebug("ECW",
123✔
2807
             "FileInfo: SizeXY=%d,%d Bands=%d\n"
2808
             "       OriginXY=%g,%g  CellIncrementXY=%g,%g\n"
2809
             "       ColorSpace=%d, eCellType=%d\n",
2810
             poDS->psFileInfo->nSizeX, poDS->psFileInfo->nSizeY,
123✔
2811
             poDS->psFileInfo->nBands, poDS->psFileInfo->fOriginX,
123✔
2812
             poDS->psFileInfo->fOriginY, poDS->psFileInfo->fCellIncrementX,
123✔
2813
             poDS->psFileInfo->fCellIncrementY,
123✔
2814
             (int)poDS->psFileInfo->eColorSpace,
123✔
2815
             (int)poDS->psFileInfo->eCellType);
123✔
2816

2817
    /* -------------------------------------------------------------------- */
2818
    /*      Establish raster info.                                          */
2819
    /* -------------------------------------------------------------------- */
2820
    poDS->nRasterXSize = poDS->psFileInfo->nSizeX;
123✔
2821
    poDS->nRasterYSize = poDS->psFileInfo->nSizeY;
123✔
2822

2823
    /* -------------------------------------------------------------------- */
2824
    /*      Establish the GDAL data type that corresponds.  A few NCS       */
2825
    /*      data types have no direct corresponding value in GDAL so we     */
2826
    /*      will coerce to something sufficiently similar.                  */
2827
    /* -------------------------------------------------------------------- */
2828
    poDS->eNCSRequestDataType = poDS->psFileInfo->eCellType;
123✔
2829
    switch (poDS->psFileInfo->eCellType)
123✔
2830
    {
2831
        case NCSCT_UINT8:
103✔
2832
            poDS->eRasterDataType = GDT_Byte;
103✔
2833
            break;
103✔
2834

2835
        case NCSCT_UINT16:
10✔
2836
            poDS->eRasterDataType = GDT_UInt16;
10✔
2837
            break;
10✔
2838

2839
        case NCSCT_UINT32:
2✔
2840
        case NCSCT_UINT64:
2841
            poDS->eRasterDataType = GDT_UInt32;
2✔
2842
            poDS->eNCSRequestDataType = NCSCT_UINT32;
2✔
2843
            break;
2✔
2844

2845
        case NCSCT_INT8:
6✔
2846
        case NCSCT_INT16:
2847
            poDS->eRasterDataType = GDT_Int16;
6✔
2848
            poDS->eNCSRequestDataType = NCSCT_INT16;
6✔
2849
            break;
6✔
2850

2851
        case NCSCT_INT32:
2✔
2852
        case NCSCT_INT64:
2853
            poDS->eRasterDataType = GDT_Int32;
2✔
2854
            poDS->eNCSRequestDataType = NCSCT_INT32;
2✔
2855
            break;
2✔
2856

2857
        case NCSCT_IEEE4:
×
2858
            poDS->eRasterDataType = GDT_Float32;
×
2859
            break;
×
2860

2861
        case NCSCT_IEEE8:
×
2862
            poDS->eRasterDataType = GDT_Float64;
×
2863
            break;
×
2864

2865
        default:
×
2866
            CPLDebug("ECW", "Unhandled case : eCellType = %d",
×
2867
                     (int)poDS->psFileInfo->eCellType);
×
2868
            break;
×
2869
    }
2870

2871
/* -------------------------------------------------------------------- */
2872
/*      If decoding a UInt32 image, check that the SDK is not buggy     */
2873
/*      There are issues at least in the 5.x series.                    */
2874
/* -------------------------------------------------------------------- */
2875
#if ECWSDK_VERSION >= 40
2876
    constexpr const char *szDETECT_BUG_FILENAME =
2877
        "__detect_ecw_uint32_bug__.j2k";
2878
    if (bIsJPEG2000 && poDS->eNCSRequestDataType == NCSCT_UINT32 &&
2879
        CPLTestBool(CPLGetConfigOption("ECW_CHECK_CORRECT_DECODING", "TRUE")) &&
2880
        strstr(poOpenInfo->pszFilename, szDETECT_BUG_FILENAME) == nullptr)
2881
    {
2882
        static bool bUINT32_Ok = false;
2883
        {
2884
            CPLMutexHolder oHolder(&hECWDatasetMutex);
2885
            static bool bTestDone = false;
2886
            if (!bTestDone)
2887
            {
2888
                bTestDone = true;
2889
                // Minimal J2K 2x2 image with NBITS=20, unsigned, reversible
2890
                // compression and following values 0 1048575 1048574 524288
2891

2892
                static const GByte abyTestUInt32ImageData[] = {
2893
                    0xFF, 0x4F, 0xFF, 0x51, 0x00, 0x29, 0x00, 0x02, 0x00, 0x00,
2894
                    0x00, 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00,
2895
                    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00,
2896
                    0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
2897
                    0x00, 0x01, 0x13, 0x01, 0x01, 0xFF, 0x52, 0x00, 0x0D, 0x01,
2898
                    0x00, 0x00, 0x01, 0x00, 0x00, 0x04, 0x04, 0x00, 0x01, 0x99,
2899
                    0xFF, 0x5C, 0x00, 0x04, 0x40, 0xA0, 0xFF, 0x90, 0x00, 0x0A,
2900
                    0x00, 0x00, 0x00, 0x00, 0x00, 0x1C, 0x00, 0x01, 0xFF, 0x93,
2901
                    0xDF, 0xF9, 0x40, 0x50, 0x07, 0x68, 0xE0, 0x12, 0xD2, 0xDA,
2902
                    0xDF, 0xFF, 0x7F, 0x5F, 0xFF, 0xD9};
2903

2904
                const std::string osTmpFilename =
2905
                    VSIMemGenerateHiddenFilename(szDETECT_BUG_FILENAME);
2906
                VSIFCloseL(VSIFileFromMemBuffer(
2907
                    osTmpFilename.c_str(),
2908
                    const_cast<GByte *>(abyTestUInt32ImageData),
2909
                    sizeof(abyTestUInt32ImageData), false));
2910
                GDALOpenInfo oOpenInfo(osTmpFilename.c_str(), GA_ReadOnly);
2911
                auto poTmpDS =
2912
                    std::unique_ptr<GDALDataset>(Open(&oOpenInfo, true));
2913
                if (poTmpDS)
2914
                {
2915
                    uint32_t anValues[4] = {0};
2916
                    if (poTmpDS->GetRasterBand(1)->RasterIO(
2917
                            GF_Read, 0, 0, 2, 2, anValues, 2, 2, GDT_UInt32, 0,
2918
                            0, nullptr) == CE_None &&
2919
                        anValues[0] == 0 && anValues[1] == 1048575 &&
2920
                        anValues[2] == 1048574 && anValues[3] == 524288)
2921
                    {
2922
                        bUINT32_Ok = true;
2923
                    }
2924
                }
2925
                VSIUnlink(osTmpFilename.c_str());
2926
            }
2927
        }
2928

2929
        if (!bUINT32_Ok)
2930
        {
2931
            CPLDebug("ECW", "ECW SDK used cannot correctly decode UInt32 "
2932
                            "images. Giving up");
2933
            delete poDS;
2934
            return nullptr;
2935
        }
2936
    }
2937
#endif
2938

2939
    /* -------------------------------------------------------------------- */
2940
    /*      Create band information objects.                                */
2941
    /* -------------------------------------------------------------------- */
2942
    for (i = 0; i < poDS->psFileInfo->nBands; i++)
336✔
2943
        poDS->SetBand(i + 1, new ECWRasterBand(poDS, i + 1, -1,
213✔
2944
                                               poOpenInfo->papszOpenOptions));
213✔
2945

2946
    /* -------------------------------------------------------------------- */
2947
    /*      Look for supporting coordinate system information.              */
2948
    /* -------------------------------------------------------------------- */
2949
    if (bIsJPEG2000)
123✔
2950
    {
2951
        poDS->LoadJP2Metadata(poOpenInfo, osFilename);
89✔
2952
    }
2953
    else
2954
    {
2955
        poDS->ECW2WKTProjection();
34✔
2956

2957
        /* --------------------------------------------------------------------
2958
         */
2959
        /*      Check for world file. */
2960
        /* --------------------------------------------------------------------
2961
         */
2962
        if (!poDS->bGeoTransformValid)
34✔
2963
        {
2964
            poDS->bGeoTransformValid |=
×
2965
                GDALReadWorldFile2(osFilename, nullptr, poDS->m_gt,
×
2966
                                   poOpenInfo->GetSiblingFiles(), nullptr) ||
×
2967
                GDALReadWorldFile2(osFilename, ".wld", poDS->m_gt,
×
2968
                                   poOpenInfo->GetSiblingFiles(), nullptr);
×
2969
        }
2970
    }
2971

2972
    if (poDS->psFileInfo->nCompressionRate > 0)
123✔
2973
        poDS->GDALDataset::SetMetadataItem(
67✔
2974
            "COMPRESSION_RATE_TARGET",
2975
            CPLString().Printf("%d", poDS->psFileInfo->nCompressionRate));
134✔
2976
    poDS->GDALDataset::SetMetadataItem(
123✔
2977
        "COLORSPACE", ECWGetColorSpaceName(poDS->psFileInfo->eColorSpace));
123✔
2978
#if ECWSDK_VERSION >= 50
2979
    if (!bIsJPEG2000)
2980
        poDS->GDALDataset::SetMetadataItem(
2981
            "VERSION",
2982
            CPLString().Printf("%d", poDS->psFileInfo->nFormatVersion));
2983
#if ECWSDK_VERSION >= 51
2984
    // output jp2 header info
2985
    if (bIsJPEG2000 && poDS->poFileView)
2986
    {
2987
        // comments
2988
        char *csComments = nullptr;
2989
        poDS->poFileView->GetParameter((char *)"JPC:DECOMPRESS:COMMENTS",
2990
                                       &csComments);
2991
        if (csComments)
2992
        {
2993
            std::string osComments(csComments);
2994

2995
            // Strip off boring Kakadu COM content
2996
            if (STARTS_WITH(osComments.c_str(), "Kakadu-"))
2997
            {
2998
                const auto nEOLPos = osComments.find('\n');
2999
                if (nEOLPos == std::string::npos)
3000
                    osComments.clear();
3001
                osComments = osComments.substr(nEOLPos + 1);
3002
            }
3003
            if (STARTS_WITH(
3004
                    osComments.c_str(),
3005
                    "Kdu-Layer-Info: "
3006
                    "log_2{Delta-D(squared-error)/Delta-L(bytes)}, L(bytes)\n"))
3007
            {
3008
                while (true)
3009
                {
3010
                    const auto nEOLPos = osComments.find('\n');
3011
                    if (nEOLPos == std::string::npos)
3012
                    {
3013
                        osComments.clear();
3014
                        break;
3015
                    }
3016
                    osComments = osComments.substr(nEOLPos + 1);
3017
                    if (osComments.find(",  ") == std::string::npos)
3018
                        break;
3019
                }
3020
            }
3021

3022
            // Strip off boring OpenJPEG COM content
3023
            if (STARTS_WITH(osComments.c_str(),
3024
                            "Created by OpenJPEG version ") &&
3025
                osComments.find('\n') == std::string::npos)
3026
            {
3027
                osComments.clear();
3028
            }
3029

3030
            if (!osComments.empty())
3031
                poDS->GDALDataset::SetMetadataItem("ALL_COMMENTS",
3032
                                                   osComments.c_str());
3033
            NCSFree(csComments);
3034
        }
3035

3036
        // Profile
3037
        UINT32 nProfile = 2;
3038
        UINT32 nRsiz = 0;
3039
        poDS->poFileView->GetParameter((char *)"JP2:COMPLIANCE:PROFILE:TYPE",
3040
                                       &nRsiz);
3041
        if (nRsiz == 0)
3042
            nProfile = 2;  // Profile 2 (no restrictions)
3043
        else if (nRsiz == 1)
3044
            nProfile = 0;  // Profile 0
3045
        else if (nRsiz == 2)
3046
            nProfile = 1;  // Profile 1, NITF_BIIF_NPJE, NITF_BIIF_EPJE
3047
        poDS->GDALDataset::SetMetadataItem("PROFILE",
3048
                                           CPLString().Printf("%d", nProfile),
3049
                                           JPEG2000_DOMAIN_NAME);
3050

3051
        // number of tiles on X axis
3052
        UINT32 nTileNrX = 1;
3053
        poDS->poFileView->GetParameter((char *)"JPC:DECOMPRESS:TILENR:X",
3054
                                       &nTileNrX);
3055
        poDS->GDALDataset::SetMetadataItem("TILES_X",
3056
                                           CPLString().Printf("%d", nTileNrX),
3057
                                           JPEG2000_DOMAIN_NAME);
3058

3059
        // number of tiles on X axis
3060
        UINT32 nTileNrY = 1;
3061
        poDS->poFileView->GetParameter((char *)"JPC:DECOMPRESS:TILENR:Y",
3062
                                       &nTileNrY);
3063
        poDS->GDALDataset::SetMetadataItem("TILES_Y",
3064
                                           CPLString().Printf("%d", nTileNrY),
3065
                                           JPEG2000_DOMAIN_NAME);
3066

3067
        // Tile Width
3068
        UINT32 nTileSizeX = 0;
3069
        poDS->poFileView->GetParameter((char *)"JPC:DECOMPRESS:TILESIZE:X",
3070
                                       &nTileSizeX);
3071
        poDS->GDALDataset::SetMetadataItem("TILE_WIDTH",
3072
                                           CPLString().Printf("%d", nTileSizeX),
3073
                                           JPEG2000_DOMAIN_NAME);
3074

3075
        // Tile Height
3076
        UINT32 nTileSizeY = 0;
3077
        poDS->poFileView->GetParameter((char *)"JPC:DECOMPRESS:TILESIZE:Y",
3078
                                       &nTileSizeY);
3079
        poDS->GDALDataset::SetMetadataItem("TILE_HEIGHT",
3080
                                           CPLString().Printf("%d", nTileSizeY),
3081
                                           JPEG2000_DOMAIN_NAME);
3082

3083
        // Precinct Sizes on X axis
3084
        char *csPreSizeX = nullptr;
3085
        poDS->poFileView->GetParameter((char *)"JPC:DECOMPRESS:PRECINCTSIZE:X",
3086
                                       &csPreSizeX);
3087
        if (csPreSizeX)
3088
        {
3089
            poDS->GDALDataset::SetMetadataItem("PRECINCT_SIZE_X", csPreSizeX,
3090
                                               JPEG2000_DOMAIN_NAME);
3091
            NCSFree(csPreSizeX);
3092
        }
3093

3094
        // Precinct Sizes on Y axis
3095
        char *csPreSizeY = nullptr;
3096
        poDS->poFileView->GetParameter((char *)"JPC:DECOMPRESS:PRECINCTSIZE:Y",
3097
                                       &csPreSizeY);
3098
        if (csPreSizeY)
3099
        {
3100
            poDS->GDALDataset::SetMetadataItem("PRECINCT_SIZE_Y", csPreSizeY,
3101
                                               JPEG2000_DOMAIN_NAME);
3102
            NCSFree(csPreSizeY);
3103
        }
3104

3105
        // Code Block Size on X axis
3106
        UINT32 nCodeBlockSizeX = 0;
3107
        poDS->poFileView->GetParameter((char *)"JPC:DECOMPRESS:CODEBLOCK:X",
3108
                                       &nCodeBlockSizeX);
3109
        poDS->GDALDataset::SetMetadataItem(
3110
            "CODE_BLOCK_SIZE_X", CPLString().Printf("%d", nCodeBlockSizeX),
3111
            JPEG2000_DOMAIN_NAME);
3112

3113
        // Code Block Size on Y axis
3114
        UINT32 nCodeBlockSizeY = 0;
3115
        poDS->poFileView->GetParameter((char *)"JPC:DECOMPRESS:CODEBLOCK:Y",
3116
                                       &nCodeBlockSizeY);
3117
        poDS->GDALDataset::SetMetadataItem(
3118
            "CODE_BLOCK_SIZE_Y", CPLString().Printf("%d", nCodeBlockSizeY),
3119
            JPEG2000_DOMAIN_NAME);
3120

3121
        // Bitdepth
3122
        char *csBitdepth = nullptr;
3123
        poDS->poFileView->GetParameter((char *)"JPC:DECOMPRESS:BITDEPTH",
3124
                                       &csBitdepth);
3125
        if (csBitdepth)
3126
        {
3127
            poDS->GDALDataset::SetMetadataItem("PRECISION", csBitdepth,
3128
                                               JPEG2000_DOMAIN_NAME);
3129
            NCSFree(csBitdepth);
3130
        }
3131

3132
        // Resolution Levels
3133
        UINT32 nLevels = 0;
3134
        poDS->poFileView->GetParameter(
3135
            (char *)"JPC:DECOMPRESS:RESOLUTION:LEVELS", &nLevels);
3136
        poDS->GDALDataset::SetMetadataItem("RESOLUTION_LEVELS",
3137
                                           CPLString().Printf("%d", nLevels),
3138
                                           JPEG2000_DOMAIN_NAME);
3139

3140
        // Qualaity Layers
3141
        UINT32 nLayers = 0;
3142
        poDS->poFileView->GetParameter((char *)"JP2:DECOMPRESS:LAYERS",
3143
                                       &nLayers);
3144
        poDS->GDALDataset::SetMetadataItem("QUALITY_LAYERS",
3145
                                           CPLString().Printf("%d", nLayers),
3146
                                           JPEG2000_DOMAIN_NAME);
3147

3148
        // Progression Order
3149
        char *csOrder = nullptr;
3150
        poDS->poFileView->GetParameter(
3151
            (char *)"JPC:DECOMPRESS:PROGRESSION:ORDER", &csOrder);
3152
        if (csOrder)
3153
        {
3154
            poDS->GDALDataset::SetMetadataItem("PROGRESSION_ORDER", csOrder,
3155
                                               JPEG2000_DOMAIN_NAME);
3156
            NCSFree(csOrder);
3157
        }
3158

3159
        // DWT Filter
3160
        const char *csFilter = nullptr;
3161
        UINT32 nFilter;
3162
        poDS->poFileView->GetParameter((char *)"JP2:TRANSFORMATION:TYPE",
3163
                                       &nFilter);
3164
        if (nFilter)
3165
            csFilter = "5x3";
3166
        else
3167
            csFilter = "9x7";
3168
        poDS->GDALDataset::SetMetadataItem("TRANSFORMATION_TYPE", csFilter,
3169
                                           JPEG2000_DOMAIN_NAME);
3170

3171
        // SOP used?
3172
        bool bSOP = 0;
3173
        poDS->poFileView->GetParameter((char *)"JP2:DECOMPRESS:SOP:EXISTS",
3174
                                       &bSOP);
3175
        poDS->SetMetadataItem("USE_SOP", (bSOP) ? "TRUE" : "FALSE",
3176
                              JPEG2000_DOMAIN_NAME);
3177

3178
        // EPH used?
3179
        bool bEPH = 0;
3180
        poDS->poFileView->GetParameter((char *)"JP2:DECOMPRESS:EPH:EXISTS",
3181
                                       &bEPH);
3182
        poDS->SetMetadataItem("USE_EPH", (bEPH) ? "TRUE" : "FALSE",
3183
                              JPEG2000_DOMAIN_NAME);
3184

3185
        // GML JP2 data contained?
3186
        bool bGML = 0;
3187
        poDS->poFileView->GetParameter((char *)"JP2:GML:JP2:BOX:EXISTS", &bGML);
3188
        poDS->SetMetadataItem("GML_JP2_DATA", (bGML) ? "TRUE" : "FALSE",
3189
                              JPEG2000_DOMAIN_NAME);
3190
    }
3191
#endif  // ECWSDK_VERSION>=51
3192
    if (!bIsJPEG2000 && poDS->psFileInfo->nFormatVersion >= 3)
3193
    {
3194
        poDS->GDALDataset::SetMetadataItem(
3195
            "COMPRESSION_RATE_ACTUAL",
3196
            CPLString().Printf("%f", poDS->psFileInfo->fActualCompressionRate));
3197
        poDS->GDALDataset::SetMetadataItem(
3198
            "CLOCKWISE_ROTATION_DEG",
3199
            CPLString().Printf("%f", poDS->psFileInfo->fCWRotationDegrees));
3200
        poDS->GDALDataset::SetMetadataItem("COMPRESSION_DATE",
3201
                                           poDS->psFileInfo->sCompressionDate);
3202
        // Get file metadata.
3203
        poDS->ReadFileMetaDataFromFile();
3204
    }
3205
#else
3206
    poDS->GDALDataset::SetMetadataItem(
123✔
3207
        "VERSION", CPLString().Printf("%d", bIsJPEG2000 ? 1 : 2));
246✔
3208
#endif
3209

3210
    /* -------------------------------------------------------------------- */
3211
    /*      Initialize any PAM information.                                 */
3212
    /* -------------------------------------------------------------------- */
3213
    poDS->SetDescription(osFilename);
123✔
3214
    poDS->TryLoadXML(poOpenInfo->GetSiblingFiles());
123✔
3215

3216
    /* -------------------------------------------------------------------- */
3217
    /*      Vector layers                                                   */
3218
    /* -------------------------------------------------------------------- */
3219
    if (bIsJPEG2000 && poOpenInfo->nOpenFlags & GDAL_OF_VECTOR)
123✔
3220
    {
3221
        poDS->LoadVectorLayers(CPLFetchBool(poOpenInfo->papszOpenOptions,
1✔
3222
                                            "OPEN_REMOTE_GML", false));
3223

3224
        // If file opened in vector-only mode and there's no vector,
3225
        // return
3226
        if ((poOpenInfo->nOpenFlags & GDAL_OF_RASTER) == 0 &&
1✔
3227
            poDS->GetLayerCount() == 0)
×
3228
        {
3229
            delete poDS;
×
3230
            return nullptr;
×
3231
        }
3232
    }
3233

3234
    return poDS;
123✔
3235
}
3236

3237
/************************************************************************/
3238
/*                      GetMetadataDomainList()                         */
3239
/************************************************************************/
3240

3241
char **ECWDataset::GetMetadataDomainList()
4✔
3242
{
3243
    return BuildMetadataDomainList(
4✔
3244
        GDALJP2AbstractDataset::GetMetadataDomainList(), TRUE, "ECW", "GML",
3245
        nullptr);
4✔
3246
}
3247

3248
/************************************************************************/
3249
/*                           GetMetadataItem()                          */
3250
/************************************************************************/
3251

3252
const char *ECWDataset::GetMetadataItem(const char *pszName,
55✔
3253
                                        const char *pszDomain)
3254
{
3255
    if (!bIsJPEG2000 && pszDomain != nullptr && EQUAL(pszDomain, "ECW") &&
55✔
3256
        pszName != nullptr)
3257
    {
3258
        if (EQUAL(pszName, "PROJ"))
6✔
3259
            return m_osProjCode.size() ? m_osProjCode.c_str() : "RAW";
2✔
3260
        if (EQUAL(pszName, "DATUM"))
4✔
3261
            return m_osDatumCode.size() ? m_osDatumCode.c_str() : "RAW";
2✔
3262
        if (EQUAL(pszName, "UNITS"))
2✔
3263
            return m_osUnitsCode.size() ? m_osUnitsCode.c_str() : "METERS";
2✔
3264
    }
3265
    return GDALJP2AbstractDataset::GetMetadataItem(pszName, pszDomain);
49✔
3266
}
3267

3268
/************************************************************************/
3269
/*                            GetMetadata()                             */
3270
/************************************************************************/
3271

3272
char **ECWDataset::GetMetadata(const char *pszDomain)
90✔
3273

3274
{
3275
    if (!bIsJPEG2000 && pszDomain != nullptr && EQUAL(pszDomain, "ECW"))
90✔
3276
    {
3277
        oECWMetadataList.Clear();
×
3278
        oECWMetadataList.AddString(
3279
            CPLSPrintf("%s=%s", "PROJ", GetMetadataItem("PROJ", "ECW")));
×
3280
        oECWMetadataList.AddString(
3281
            CPLSPrintf("%s=%s", "DATUM", GetMetadataItem("DATUM", "ECW")));
×
3282
        oECWMetadataList.AddString(
3283
            CPLSPrintf("%s=%s", "UNITS", GetMetadataItem("UNITS", "ECW")));
×
3284
        return oECWMetadataList.List();
×
3285
    }
3286
    else if (pszDomain == nullptr || !EQUAL(pszDomain, "GML"))
90✔
3287
        return GDALJP2AbstractDataset::GetMetadata(pszDomain);
86✔
3288
    else
3289
        return papszGMLMetadata;
4✔
3290
}
3291

3292
/************************************************************************/
3293
/*                   ReadFileMetaDataFromFile()                         */
3294
/*                                                                      */
3295
/* Gets relevant information from NCSFileMetadata and populates         */
3296
/* GDAL metadata.                                                       */
3297
/*                                                                      */
3298
/************************************************************************/
3299
#if ECWSDK_VERSION >= 50
3300
void ECWDataset::ReadFileMetaDataFromFile()
3301
{
3302
    if (psFileInfo->pFileMetaData == nullptr)
3303
        return;
3304

3305
    if (psFileInfo->pFileMetaData->sClassification != nullptr)
3306
        GDALDataset::SetMetadataItem(
3307
            "FILE_METADATA_CLASSIFICATION",
3308
            NCS::CString(psFileInfo->pFileMetaData->sClassification));
3309
    if (psFileInfo->pFileMetaData->sAcquisitionDate != nullptr)
3310
        GDALDataset::SetMetadataItem(
3311
            "FILE_METADATA_ACQUISITION_DATE",
3312
            NCS::CString(psFileInfo->pFileMetaData->sAcquisitionDate));
3313
    if (psFileInfo->pFileMetaData->sAcquisitionSensorName != nullptr)
3314
        GDALDataset::SetMetadataItem(
3315
            "FILE_METADATA_ACQUISITION_SENSOR_NAME",
3316
            NCS::CString(psFileInfo->pFileMetaData->sAcquisitionSensorName));
3317
    if (psFileInfo->pFileMetaData->sCompressionSoftware != nullptr)
3318
        GDALDataset::SetMetadataItem(
3319
            "FILE_METADATA_COMPRESSION_SOFTWARE",
3320
            NCS::CString(psFileInfo->pFileMetaData->sCompressionSoftware));
3321
    if (psFileInfo->pFileMetaData->sAuthor != nullptr)
3322
        GDALDataset::SetMetadataItem(
3323
            "FILE_METADATA_AUTHOR",
3324
            NCS::CString(psFileInfo->pFileMetaData->sAuthor));
3325
    if (psFileInfo->pFileMetaData->sCopyright != nullptr)
3326
        GDALDataset::SetMetadataItem(
3327
            "FILE_METADATA_COPYRIGHT",
3328
            NCS::CString(psFileInfo->pFileMetaData->sCopyright));
3329
    if (psFileInfo->pFileMetaData->sCompany != nullptr)
3330
        GDALDataset::SetMetadataItem(
3331
            "FILE_METADATA_COMPANY",
3332
            NCS::CString(psFileInfo->pFileMetaData->sCompany));
3333
    if (psFileInfo->pFileMetaData->sEmail != nullptr)
3334
        GDALDataset::SetMetadataItem(
3335
            "FILE_METADATA_EMAIL",
3336
            NCS::CString(psFileInfo->pFileMetaData->sEmail));
3337
    if (psFileInfo->pFileMetaData->sAddress != nullptr)
3338
        GDALDataset::SetMetadataItem(
3339
            "FILE_METADATA_ADDRESS",
3340
            NCS::CString(psFileInfo->pFileMetaData->sAddress));
3341
    if (psFileInfo->pFileMetaData->sTelephone != nullptr)
3342
        GDALDataset::SetMetadataItem(
3343
            "FILE_METADATA_TELEPHONE",
3344
            NCS::CString(psFileInfo->pFileMetaData->sTelephone));
3345
}
3346

3347
/************************************************************************/
3348
/*                       WriteFileMetaData()                            */
3349
/************************************************************************/
3350

3351
void ECWDataset::WriteFileMetaData(NCSFileMetaData *pFileMetaDataCopy)
3352
{
3353
    if (!bFileMetaDataDirty)
3354
        return;
3355

3356
    CPLAssert(eAccess == GA_Update);
3357
    CPLAssert(!bIsJPEG2000);
3358

3359
    bFileMetaDataDirty = FALSE;
3360

3361
    NCSFileView *psFileView = nullptr;
3362
    NCSError eErr;
3363

3364
    psFileView = NCSEditOpen(GetDescription());
3365
    if (psFileView == nullptr)
3366
    {
3367
        CPLError(CE_Failure, CPLE_AppDefined, "NCSEditOpen() failed");
3368
        return;
3369
    }
3370

3371
    eErr = NCSEditSetFileMetaData(psFileView, pFileMetaDataCopy);
3372
    if (eErr != NCS_SUCCESS)
3373
    {
3374
        CPLError(CE_Failure, CPLE_AppDefined,
3375
                 "NCSEditSetFileMetaData() failed : %s",
3376
                 NCSGetLastErrorText(eErr));
3377
    }
3378

3379
    NCSEditFlushAll(psFileView);
3380
    NCSEditClose(psFileView);
3381
}
3382

3383
#endif
3384
/************************************************************************/
3385
/*                         ECW2WKTProjection()                          */
3386
/*                                                                      */
3387
/*      Set the dataset pszProjection string in OGC WKT format by       */
3388
/*      looking up the ECW (GDT) coordinate system info in              */
3389
/*      ecw_cs.wkt support data file.                                   */
3390
/*                                                                      */
3391
/*      This code is likely still broken in some circumstances.  For    */
3392
/*      instance, I haven't been careful about changing the linear      */
3393
/*      projection parameters (false easting/northing) if the units     */
3394
/*      is feet.  Lots of cases missing here, and in ecw_cs.wkt.        */
3395
/************************************************************************/
3396

3397
void ECWDataset::ECW2WKTProjection()
34✔
3398

3399
{
3400
    if (psFileInfo == nullptr)
34✔
3401
        return;
20✔
3402

3403
    /* -------------------------------------------------------------------- */
3404
    /*      Capture Geotransform.                                           */
3405
    /*                                                                      */
3406
    /*      We will try to ignore the provided file information if it is    */
3407
    /*      origin (0,0) and pixel size (1,1).  I think sometimes I have    */
3408
    /*      also seen pixel increments of 0 on invalid datasets.            */
3409
    /* -------------------------------------------------------------------- */
3410
    if (psFileInfo->fOriginX != 0.0 || psFileInfo->fOriginY != 0.0 ||
34✔
3411
        (psFileInfo->fCellIncrementX != 0.0 &&
×
3412
         psFileInfo->fCellIncrementX != 1.0) ||
×
3413
        (psFileInfo->fCellIncrementY != 0.0 &&
×
3414
         psFileInfo->fCellIncrementY != 1.0))
×
3415
    {
3416
        bGeoTransformValid = TRUE;
34✔
3417

3418
        m_gt[0] = psFileInfo->fOriginX;
34✔
3419
        m_gt[1] = psFileInfo->fCellIncrementX;
34✔
3420
        m_gt[2] = 0.0;
34✔
3421

3422
        m_gt[3] = psFileInfo->fOriginY;
34✔
3423
        m_gt[4] = 0.0;
34✔
3424

3425
        /* By default, set Y-resolution negative assuming images always */
3426
        /* have "Upward" orientation (Y coordinates increase "Upward"). */
3427
        /* Setting ECW_ALWAYS_UPWARD=FALSE option relexes that policy   */
3428
        /* and makes the driver rely on the actual Y-resolution         */
3429
        /* value (sign) of an image. This allows correctly processing   */
3430
        /* rare images with "Downward" orientation, where Y coordinates */
3431
        /* increase "Downward" and Y-resolution is positive.            */
3432
        if (CPLTestBool(CPLGetConfigOption("ECW_ALWAYS_UPWARD", "TRUE")))
34✔
3433
            m_gt[5] = -fabs(psFileInfo->fCellIncrementY);
33✔
3434
        else
3435
            m_gt[5] = psFileInfo->fCellIncrementY;
1✔
3436
    }
3437

3438
    /* -------------------------------------------------------------------- */
3439
    /*      do we have projection and datum?                                */
3440
    /* -------------------------------------------------------------------- */
3441
    CPLString osUnits =
3442
        ECWTranslateFromCellSizeUnits(psFileInfo->eCellSizeUnits);
34✔
3443

3444
    CPLDebug("ECW", "projection=%s, datum=%s, units=%s",
34✔
3445
             psFileInfo->szProjection, psFileInfo->szDatum, osUnits.c_str());
34✔
3446

3447
    if (EQUAL(psFileInfo->szProjection, "RAW"))
34✔
3448
        return;
20✔
3449

3450
    /* -------------------------------------------------------------------- */
3451
    /*      Set projection if we have it.                                   */
3452
    /* -------------------------------------------------------------------- */
3453
    OGRSpatialReference oSRS;
28✔
3454

3455
    /* For backward-compatible with previous behavior. Should we only */
3456
    /* restrict to those 2 values ? */
3457
    if (psFileInfo->eCellSizeUnits != ECW_CELL_UNITS_METERS &&
14✔
3458
        psFileInfo->eCellSizeUnits != ECW_CELL_UNITS_FEET)
4✔
3459
        osUnits = ECWTranslateFromCellSizeUnits(ECW_CELL_UNITS_METERS);
2✔
3460

3461
    m_osDatumCode = psFileInfo->szDatum;
14✔
3462
    m_osProjCode = psFileInfo->szProjection;
14✔
3463
    m_osUnitsCode = osUnits;
14✔
3464
    if (oSRS.importFromERM(psFileInfo->szProjection, psFileInfo->szDatum,
14✔
3465
                           osUnits) == OGRERR_NONE)
14✔
3466
    {
3467
        m_oSRS = std::move(oSRS);
14✔
3468
        m_oSRS.SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
14✔
3469
    }
3470

3471
    CPLErrorReset(); /* see #4187 */
14✔
3472
}
3473

3474
/************************************************************************/
3475
/*                        ECWTranslateFromWKT()                         */
3476
/************************************************************************/
3477

3478
int ECWTranslateFromWKT(const OGRSpatialReference *poSRS, char *pszProjection,
27✔
3479
                        int nProjectionLen, char *pszDatum, int nDatumLen,
3480
                        char *pszUnits)
3481

3482
{
3483
    OGRSpatialReference oSRS;
54✔
3484

3485
    strcpy(pszProjection, "RAW");
27✔
3486
    strcpy(pszDatum, "RAW");
27✔
3487
    strcpy(pszUnits, "METERS");
27✔
3488

3489
    if (poSRS == nullptr || poSRS->IsEmpty())
27✔
3490
        return FALSE;
×
3491

3492
    oSRS = *poSRS;
27✔
3493

3494
    if (oSRS.IsLocal())
27✔
3495
        return TRUE;
×
3496

3497
    /* -------------------------------------------------------------------- */
3498
    /*      Do we have an overall EPSG number for this coordinate system?   */
3499
    /* -------------------------------------------------------------------- */
3500
    const char *pszAuthorityCode = nullptr;
27✔
3501
    const char *pszAuthorityName = nullptr;
27✔
3502
    UINT32 nEPSGCode = 0;
27✔
3503

3504
    if (oSRS.IsProjected())
27✔
3505
    {
3506
        pszAuthorityCode = oSRS.GetAuthorityCode("PROJCS");
8✔
3507
        pszAuthorityName = oSRS.GetAuthorityName("PROJCS");
8✔
3508
    }
3509
    else if (oSRS.IsGeographic())
19✔
3510
    {
3511
        pszAuthorityCode = oSRS.GetAuthorityCode("GEOGCS");
19✔
3512
        pszAuthorityName = oSRS.GetAuthorityName("GEOGCS");
19✔
3513
    }
3514

3515
    if (pszAuthorityName != nullptr && EQUAL(pszAuthorityName, "EPSG") &&
27✔
3516
        pszAuthorityCode != nullptr && atoi(pszAuthorityCode) > 0)
11✔
3517
        nEPSGCode = (UINT32)atoi(pszAuthorityCode);
11✔
3518

3519
    if (nEPSGCode != 0)
27✔
3520
    {
3521
        char *pszEPSGProj = nullptr, *pszEPSGDatum = nullptr;
11✔
3522
        CNCSError oErr = CNCSJP2FileView::GetProjectionAndDatum(
3523
            atoi(pszAuthorityCode), &pszEPSGProj, &pszEPSGDatum);
11✔
3524

3525
        CPLDebug("ECW", "GetGDTProjDat(%d) = %s/%s", atoi(pszAuthorityCode),
22✔
3526
                 pszEPSGProj ? pszEPSGProj : "(null)",
11✔
3527
                 pszEPSGDatum ? pszEPSGDatum : "(null)");
11✔
3528

3529
        if (oErr.GetErrorNumber() == NCS_SUCCESS && pszEPSGProj != nullptr &&
22✔
3530
            pszEPSGDatum != nullptr)
11✔
3531
        {
3532
            strncpy(pszProjection, pszEPSGProj, nProjectionLen);
11✔
3533
            strncpy(pszDatum, pszEPSGDatum, nDatumLen);
11✔
3534
            pszProjection[nProjectionLen - 1] = 0;
11✔
3535
            pszDatum[nDatumLen - 1] = 0;
11✔
3536
            NCSFree(pszEPSGProj);
11✔
3537
            NCSFree(pszEPSGDatum);
11✔
3538
            return TRUE;
11✔
3539
        }
3540

3541
        NCSFree(pszEPSGProj);
×
3542
        NCSFree(pszEPSGDatum);
×
3543
    }
3544

3545
    /* -------------------------------------------------------------------- */
3546
    /*      Fallback to translating based on the ecw_cs.wkt file, and       */
3547
    /*      various jiffy rules.                                            */
3548
    /* -------------------------------------------------------------------- */
3549

3550
    return oSRS.exportToERM(pszProjection, pszDatum, pszUnits) == OGRERR_NONE;
16✔
3551
}
3552

3553
/************************************************************************/
3554
/*                    ECWTranslateToCellSizeUnits()                     */
3555
/************************************************************************/
3556

3557
CellSizeUnits ECWTranslateToCellSizeUnits(const char *pszUnits)
28✔
3558
{
3559
    if (EQUAL(pszUnits, "METERS"))
28✔
3560
        return ECW_CELL_UNITS_METERS;
26✔
3561
    else if (EQUAL(pszUnits, "DEGREES"))
2✔
3562
        return ECW_CELL_UNITS_DEGREES;
×
3563
    else if (EQUAL(pszUnits, "FEET"))
2✔
3564
        return ECW_CELL_UNITS_FEET;
2✔
3565
    else if (EQUAL(pszUnits, "UNKNOWN"))
×
3566
        return ECW_CELL_UNITS_UNKNOWN;
×
3567
    else if (EQUAL(pszUnits, "INVALID"))
×
3568
        return ECW_CELL_UNITS_INVALID;
×
3569
    else
3570
    {
3571
        CPLError(CE_Warning, CPLE_AppDefined,
×
3572
                 "Unrecognized value for UNITS : %s", pszUnits);
3573
        return ECW_CELL_UNITS_INVALID;
×
3574
    }
3575
}
3576

3577
/************************************************************************/
3578
/*                   ECWGetColorInterpretationByName()                  */
3579
/************************************************************************/
3580

3581
GDALColorInterp ECWGetColorInterpretationByName(const char *pszName)
228✔
3582
{
3583
    if (EQUAL(pszName, NCS_BANDDESC_AllOpacity))
228✔
3584
        return GCI_AlphaBand;
7✔
3585
    else if (EQUAL(pszName, NCS_BANDDESC_Blue))
221✔
3586
        return GCI_BlueBand;
51✔
3587
    else if (EQUAL(pszName, NCS_BANDDESC_Green))
170✔
3588
        return GCI_GreenBand;
51✔
3589
    else if (EQUAL(pszName, NCS_BANDDESC_Red))
119✔
3590
        return GCI_RedBand;
51✔
3591
    else if (EQUAL(pszName, NCS_BANDDESC_Greyscale))
68✔
3592
        return GCI_GrayIndex;
×
3593
    else if (EQUAL(pszName, NCS_BANDDESC_GreyscaleOpacity))
68✔
3594
        return GCI_AlphaBand;
×
3595
    return GCI_Undefined;
68✔
3596
}
3597

3598
/************************************************************************/
3599
/*                    ECWGetColorInterpretationName()                   */
3600
/************************************************************************/
3601

3602
const char *ECWGetColorInterpretationName(GDALColorInterp eColorInterpretation,
59✔
3603
                                          int nBandNumber)
3604
{
3605
    const char *pszResult = nullptr;
59✔
3606
    switch (eColorInterpretation)
59✔
3607
    {
3608
        case GCI_AlphaBand:
×
3609
            pszResult = NCS_BANDDESC_AllOpacity;
×
3610
            break;
×
3611
        case GCI_GrayIndex:
17✔
3612
            pszResult = NCS_BANDDESC_Greyscale;
17✔
3613
            break;
17✔
3614
        case GCI_RedBand:
12✔
3615
        case GCI_GreenBand:
3616
        case GCI_BlueBand:
3617
            pszResult = GDALGetColorInterpretationName(eColorInterpretation);
12✔
3618
            break;
12✔
3619
        case GCI_Undefined:
30✔
3620
            if (nBandNumber == 0)
30✔
3621
            {
3622
                pszResult = "Red";
20✔
3623
            }
3624
            else if (nBandNumber == 1)
10✔
3625
            {
3626
                pszResult = "Green";
4✔
3627
            }
3628
            else if (nBandNumber == 2)
6✔
3629
            {
3630
                pszResult = "Blue";
3✔
3631
            }
3632
            else
3633
            {
3634
                pszResult = CPLSPrintf(NCS_BANDDESC_Band, nBandNumber + 1);
3✔
3635
            }
3636
            break;
30✔
3637
        default:
×
3638
            pszResult = CPLSPrintf(NCS_BANDDESC_Band, nBandNumber + 1);
×
3639
            break;
×
3640
    }
3641
    return pszResult;
59✔
3642
}
3643

3644
/************************************************************************/
3645
/*                         ECWGetColorSpaceName()                       */
3646
/************************************************************************/
3647

3648
const char *ECWGetColorSpaceName(NCSFileColorSpace colorSpace)
123✔
3649
{
3650
    switch (colorSpace)
123✔
3651
    {
3652
        case NCSCS_NONE:
×
3653
            return "NONE";
×
3654
            break;
3655
        case NCSCS_GREYSCALE:
54✔
3656
            return "GREYSCALE";
54✔
3657
            break;
3658
        case NCSCS_YUV:
×
3659
            return "YUV";
×
3660
            break;
3661
        case NCSCS_MULTIBAND:
36✔
3662
            return "MULTIBAND";
36✔
3663
            break;
3664
        case NCSCS_sRGB:
33✔
3665
            return "RGB";
33✔
3666
            break;
3667
        case NCSCS_YCbCr:
×
3668
            return "YCbCr";
×
3669
            break;
3670
        default:
×
3671
            return "unrecognized";
×
3672
    }
3673
}
3674

3675
/************************************************************************/
3676
/*                     ECWTranslateFromCellSizeUnits()                  */
3677
/************************************************************************/
3678

3679
const char *ECWTranslateFromCellSizeUnits(CellSizeUnits eUnits)
77✔
3680
{
3681
    if (eUnits == ECW_CELL_UNITS_METERS)
77✔
3682
        return "METERS";
71✔
3683
    else if (eUnits == ECW_CELL_UNITS_DEGREES)
6✔
3684
        return "DEGREES";
2✔
3685
    else if (eUnits == ECW_CELL_UNITS_FEET)
4✔
3686
        return "FEET";
4✔
3687
    else if (eUnits == ECW_CELL_UNITS_UNKNOWN)
×
3688
        return "UNKNOWN";
×
3689
    else
3690
        return "INVALID";
×
3691
}
3692

3693
/************************************************************************/
3694
/*                           ECWInitialize()                            */
3695
/*                                                                      */
3696
/*      Initialize NCS library.  We try to defer this as late as        */
3697
/*      possible since de-initializing it seems to be expensive/slow    */
3698
/*      on some system.                                                 */
3699
/************************************************************************/
3700

3701
void ECWInitialize()
193✔
3702

3703
{
3704
    CPLMutexHolder oHolder(&hECWDatasetMutex);
193✔
3705

3706
    if (bNCSInitialized)
193✔
3707
        return;
187✔
3708

3709
#ifndef _WIN32
3710
    NCSecwInit();
6✔
3711
#endif
3712
    bNCSInitialized = TRUE;
6✔
3713

3714
    /* -------------------------------------------------------------------- */
3715
    /*      This will disable automatic conversion of YCbCr to RGB by       */
3716
    /*      the toolkit.                                                    */
3717
    /* -------------------------------------------------------------------- */
3718
    if (!CPLTestBool(CPLGetConfigOption("CONVERT_YCBCR_TO_RGB", "YES")))
6✔
3719
        NCSecwSetConfig(NCSCFG_JP2_MANAGE_ICC, FALSE);
×
3720
#if ECWSDK_VERSION >= 50
3721
    NCSecwSetConfig(NCSCFG_ECWP_CLIENT_HTTP_USER_AGENT,
3722
                    "ECW GDAL Driver/" NCS_ECWJP2_FULL_VERSION_STRING_DOT_DEL);
3723
#endif
3724
    /* -------------------------------------------------------------------- */
3725
    /*      Initialize cache memory limit.  Default is apparently 1/4 RAM.  */
3726
    /* -------------------------------------------------------------------- */
3727
    const char *pszEcwCacheSize =
3728
        CPLGetConfigOption("GDAL_ECW_CACHE_MAXMEM", nullptr);
6✔
3729
    if (pszEcwCacheSize == nullptr)
6✔
3730
        pszEcwCacheSize = CPLGetConfigOption("ECW_CACHE_MAXMEM", nullptr);
6✔
3731

3732
    if (pszEcwCacheSize != nullptr)
6✔
3733
        NCSecwSetConfig(NCSCFG_CACHE_MAXMEM, (UINT32)atoi(pszEcwCacheSize));
×
3734

3735
/* -------------------------------------------------------------------- */
3736
/*      Version 3.x and 4.x of the ECWJP2 SDK did not resolve datum and */
3737
/*      projection to EPSG code using internal mapping.                 */
3738
/*      Version 5.x do so we provide means to achieve old               */
3739
/*      behavior.                                                      */
3740
/* -------------------------------------------------------------------- */
3741
#if ECWSDK_VERSION >= 50
3742
    if (CPLTestBool(CPLGetConfigOption("ECW_DO_NOT_RESOLVE_DATUM_PROJECTION",
3743
                                       "NO")) == TRUE)
3744
        NCSecwSetConfig(NCSCFG_PROJECTION_FORMAT,
3745
                        NCS_PROJECTION_ERMAPPER_FORMAT);
3746
#endif
3747
    /* -------------------------------------------------------------------- */
3748
    /*      Allow configuration of a local cache based on configuration     */
3749
    /*      options.  Setting the location turns things on.                 */
3750
    /* -------------------------------------------------------------------- */
3751
    const char *pszOpt = nullptr;
6✔
3752
    CPL_IGNORE_RET_VAL(pszOpt);
6✔
3753

3754
#if ECWSDK_VERSION >= 40
3755
    pszOpt = CPLGetConfigOption("ECWP_CACHE_SIZE_MB", nullptr);
3756
    if (pszOpt)
3757
        NCSecwSetConfig(NCSCFG_ECWP_CACHE_SIZE_MB, (INT32)atoi(pszOpt));
3758

3759
    pszOpt = CPLGetConfigOption("ECWP_CACHE_LOCATION", nullptr);
3760
    if (pszOpt)
3761
    {
3762
        NCSecwSetConfig(NCSCFG_ECWP_CACHE_LOCATION, pszOpt);
3763
        NCSecwSetConfig(NCSCFG_ECWP_CACHE_ENABLED, (BOOLEAN)TRUE);
3764
    }
3765
#endif
3766

3767
    /* -------------------------------------------------------------------- */
3768
    /*      Various other configuration items.                              */
3769
    /* -------------------------------------------------------------------- */
3770
    pszOpt = CPLGetConfigOption("ECWP_BLOCKING_TIME_MS", nullptr);
6✔
3771
    if (pszOpt)
6✔
3772
        NCSecwSetConfig(NCSCFG_BLOCKING_TIME_MS, (NCSTimeStampMs)atoi(pszOpt));
×
3773

3774
    // I believe 10s means we wait for complete data back from
3775
    // ECWP almost all the time which is good for our blocking model.
3776
    pszOpt = CPLGetConfigOption("ECWP_REFRESH_TIME_MS", "10000");
6✔
3777
    if (pszOpt)
6✔
3778
        NCSecwSetConfig(NCSCFG_REFRESH_TIME_MS, (NCSTimeStampMs)atoi(pszOpt));
6✔
3779

3780
    pszOpt = CPLGetConfigOption("ECW_TEXTURE_DITHER", nullptr);
6✔
3781
    if (pszOpt)
6✔
3782
        NCSecwSetConfig(NCSCFG_TEXTURE_DITHER, (BOOLEAN)CPLTestBool(pszOpt));
×
3783

3784
    pszOpt = CPLGetConfigOption("ECW_FORCE_FILE_REOPEN", nullptr);
6✔
3785
    if (pszOpt)
6✔
3786
        NCSecwSetConfig(NCSCFG_FORCE_FILE_REOPEN, (BOOLEAN)CPLTestBool(pszOpt));
×
3787

3788
    pszOpt = CPLGetConfigOption("ECW_CACHE_MAXOPEN", nullptr);
6✔
3789
    if (pszOpt)
6✔
3790
        NCSecwSetConfig(NCSCFG_CACHE_MAXOPEN, (UINT32)atoi(pszOpt));
×
3791

3792
#if ECWSDK_VERSION >= 40
3793
    pszOpt = CPLGetConfigOption("ECW_AUTOGEN_J2I", nullptr);
3794
    if (pszOpt)
3795
        NCSecwSetConfig(NCSCFG_JP2_AUTOGEN_J2I, (BOOLEAN)CPLTestBool(pszOpt));
3796

3797
    pszOpt = CPLGetConfigOption("ECW_OPTIMIZE_USE_NEAREST_NEIGHBOUR", nullptr);
3798
    if (pszOpt)
3799
        NCSecwSetConfig(NCSCFG_OPTIMIZE_USE_NEAREST_NEIGHBOUR,
3800
                        (BOOLEAN)CPLTestBool(pszOpt));
3801

3802
    pszOpt = CPLGetConfigOption("ECW_RESILIENT_DECODING", nullptr);
3803
    if (pszOpt)
3804
        NCSecwSetConfig(NCSCFG_RESILIENT_DECODING,
3805
                        (BOOLEAN)CPLTestBool(pszOpt));
3806
#endif
3807
}
3808

3809
/************************************************************************/
3810
/*                         GDALDeregister_ECW()                         */
3811
/************************************************************************/
3812

3813
static void GDALDeregister_ECW(GDALDriver *)
9✔
3814

3815
{
3816
    /* For unknown reason, this cleanup can take up to 3 seconds (see #3134) for
3817
     * SDK 3.3. */
3818
    /* Not worth it */
3819
#if ECWSDK_VERSION >= 50
3820
#ifndef _WIN32
3821
    if (bNCSInitialized)
3822
    {
3823
        bNCSInitialized = FALSE;
3824
        NCSecwShutdown();
3825
    }
3826
#endif
3827
#endif
3828

3829
    if (hECWDatasetMutex != nullptr)
9✔
3830
    {
3831
        CPLDestroyMutex(hECWDatasetMutex);
4✔
3832
        hECWDatasetMutex = nullptr;
4✔
3833
    }
3834
}
9✔
3835

3836
#if ECWSDK_VERSION < 40
3837
namespace
3838
{
3839
NCSError NCS_CALL EcwFileOpenForReadACB(char *szFileName, void **ppClientData)
82✔
3840
{
3841
    *ppClientData = VSIFOpenL(szFileName, "rb");
82✔
3842
    if (*ppClientData == nullptr)
82✔
3843
    {
3844
        return NCS_FILE_OPEN_FAILED;
15✔
3845
    }
3846
    else
3847
    {
3848
        return NCS_SUCCESS;
67✔
3849
    }
3850
}
3851

3852
NCSError NCS_CALL EcwFileOpenForReadWCB(wchar_t *wszFileName,
×
3853
                                        void **ppClientData)
3854
{
3855
    char *szFileName =
3856
        CPLRecodeFromWChar(wszFileName, CPL_ENC_UCS2, CPL_ENC_UTF8);
×
3857
    *ppClientData = VSIFOpenL(szFileName, "rb");
×
3858
    CPLFree(szFileName);
×
3859
    if (*ppClientData == nullptr)
×
3860
    {
3861
        return NCS_FILE_OPEN_FAILED;
×
3862
    }
3863
    else
3864
    {
3865
        return NCS_SUCCESS;
×
3866
    }
3867
}
3868

3869
NCSError NCS_CALL EcwFileCloseCB(void *pClientData)
67✔
3870
{
3871
    if (0 == VSIFCloseL(reinterpret_cast<VSILFILE *>(pClientData)))
67✔
3872
    {
3873
        return NCS_SUCCESS;
67✔
3874
    }
3875
    else
3876
    {
3877
        return NCS_FILE_CLOSE_ERROR;
×
3878
    }
3879
}
3880

3881
NCSError NCS_CALL EcwFileReadCB(void *pClientData, void *pBuffer,
1,557✔
3882
                                UINT32 nLength)
3883
{
3884
    if (nLength == VSIFReadL(pBuffer, 1, nLength,
1,557✔
3885
                             reinterpret_cast<VSILFILE *>(pClientData)))
3886
    {
3887
        return NCS_SUCCESS;
1,557✔
3888
    }
3889
    else
3890
    {
3891
        return NCS_FILE_IO_ERROR;
×
3892
    }
3893
}
3894

3895
NCSError NCS_CALL EcwFileSeekCB(void *pClientData, UINT64 nOffset)
208✔
3896
{
3897
    if (0 ==
208✔
3898
        VSIFSeekL(reinterpret_cast<VSILFILE *>(pClientData), nOffset, SEEK_SET))
208✔
3899
    {
3900
        return NCS_SUCCESS;
208✔
3901
    }
3902
    else
3903
    {
3904
        return NCS_FILE_SEEK_ERROR;
×
3905
    }
3906
}
3907

3908
NCSError NCS_CALL EcwFileTellCB(void *pClientData, UINT64 *pOffset)
108✔
3909
{
3910
    *pOffset = VSIFTellL(reinterpret_cast<VSILFILE *>(pClientData));
108✔
3911
    return NCS_SUCCESS;
108✔
3912
}
3913
}  // namespace
3914
#endif  // ECWSDK_VERSION < 40
3915

3916
/************************************************************************/
3917
/*                          GDALRegister_ECW()                          */
3918
/************************************************************************/
3919

3920
void GDALRegister_ECW()
14✔
3921

3922
{
3923
    if (!GDAL_CHECK_VERSION("ECW driver"))
14✔
3924
        return;
×
3925

3926
    if (GDALGetDriverByName(ECW_DRIVER_NAME) != nullptr)
14✔
3927
        return;
×
3928
#if ECWSDK_VERSION < 40
3929
    CNCSJPCFileIOStream::SetIOCallbacks(
14✔
3930
        EcwFileOpenForReadACB, EcwFileOpenForReadWCB, EcwFileCloseCB,
3931
        EcwFileReadCB, EcwFileSeekCB, EcwFileTellCB);
3932
#endif  // ECWSDK_VERSION < 40
3933
    GDALDriver *poDriver = new GDALDriver();
14✔
3934

3935
    ECWDriverSetCommonMetadata(poDriver);
14✔
3936
    poDriver->pfnOpen = ECWDataset::OpenECW;
14✔
3937
    poDriver->pfnUnloadDriver = GDALDeregister_ECW;
14✔
3938
#ifdef HAVE_COMPRESS
3939
    // The create method does not work with SDK 3.3 ( crash in
3940
    // CNCSJP2FileView::WriteLineBIL() due to m_pFile being nullptr ).
3941
#if ECWSDK_VERSION >= 50
3942
    poDriver->pfnCreate = ECWCreateECW;
3943
#endif
3944
    poDriver->pfnCreateCopy = ECWCreateCopyECW;
14✔
3945
#endif
3946

3947
    GetGDALDriverManager()->RegisterDriver(poDriver);
14✔
3948
}
3949

3950
/************************************************************************/
3951
/*                      GDALRegister_ECW_JP2ECW()                       */
3952
/*                                                                      */
3953
/*      This function exists so that when built as a plugin, there      */
3954
/*      is a function that will register both drivers.                  */
3955
/************************************************************************/
3956

3957
void GDALRegister_ECW_JP2ECW()
14✔
3958

3959
{
3960
    GDALRegister_ECW();
14✔
3961
    GDALRegister_JP2ECW();
14✔
3962
}
14✔
3963

3964
/************************************************************************/
3965
/*                     ECWDatasetOpenJPEG2000()                         */
3966
/************************************************************************/
3967
GDALDataset *ECWDatasetOpenJPEG2000(GDALOpenInfo *poOpenInfo)
29✔
3968
{
3969
    return ECWDataset::OpenJPEG2000(poOpenInfo);
29✔
3970
}
3971

3972
/************************************************************************/
3973
/*                        GDALRegister_JP2ECW()                         */
3974
/************************************************************************/
3975
void GDALRegister_JP2ECW()
14✔
3976

3977
{
3978
    if (!GDAL_CHECK_VERSION("JP2ECW driver"))
14✔
3979
        return;
×
3980

3981
    if (GDALGetDriverByName(JP2ECW_DRIVER_NAME) != nullptr)
14✔
3982
        return;
×
3983

3984
    GDALDriver *poDriver = new GDALDriver();
14✔
3985
    JP2ECWDriverSetCommonMetadata(poDriver);
14✔
3986
    poDriver->pfnOpen = ECWDataset::OpenJPEG2000;
14✔
3987
#ifdef HAVE_COMPRESS
3988
    poDriver->pfnCreate = ECWCreateJPEG2000;
14✔
3989
    poDriver->pfnCreateCopy = ECWCreateCopyJPEG2000;
14✔
3990
#endif
3991

3992
    GetGDALDriverManager()->RegisterDriver(poDriver);
14✔
3993
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc