• 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

77.16
/frmts/ecw/ecwcreatecopy.cpp
1
/******************************************************************************
2
 *
3
 * Project:  GDAL ECW Driver
4
 * Purpose:  ECW CreateCopy method implementation.
5
 * Author:   Frank Warmerdam, warmerdam@pobox.com
6
 *
7
 ******************************************************************************
8
 * Copyright (c) 2001, 2004, Frank Warmerdam <warmerdam@pobox.com>
9
 * Copyright (c) 2008-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 "gdal_ecw.h"
18
#include "gdaljp2metadata.h"
19
#include "ogr_spatialref.h"
20

21
#if defined(HAVE_COMPRESS)
22

23
#define OPTIMIZED_FOR_GDALWARP
24

25
#if ECWSDK_VERSION >= 50
26
static CPLString GetCompressionSoftwareName()
27
{
28
    CPLString osRet;
29
    char szProcessName[2048];
30

31
    /* For privacy reason, allow the user to not write the software name in the
32
     * ECW */
33
    if (!CPLTestBool(
34
            CPLGetConfigOption("GDAL_ECW_WRITE_COMPRESSION_SOFTWARE", "YES")))
35
        return osRet;
36

37
    if (CPLGetExecPath(szProcessName, sizeof(szProcessName) - 1))
38
    {
39
        szProcessName[sizeof(szProcessName) - 1] = 0;
40
#ifdef _WIN32
41
        char *szLastSlash = strrchr(szProcessName, '\\');
42
#else
43
        char *szLastSlash = strrchr(szProcessName, '/');
44
#endif
45
        if (szLastSlash != nullptr)
46
            memmove(szProcessName, szLastSlash + 1,
47
                    strlen(szLastSlash + 1) + 1);
48
    }
49
    else
50
        strcpy(szProcessName, "Unknown");
51

52
    osRet.Printf("%s/GDAL v%d.%d.%d.%d/ECWJP2 SDK v%s", szProcessName,
53
                 GDAL_VERSION_MAJOR, GDAL_VERSION_MINOR, GDAL_VERSION_REV,
54
                 GDAL_VERSION_BUILD, NCS_ECWJP2_FULL_VERSION_STRING_DOT_DEL);
55
    return osRet;
56
}
57
#endif
58

59
class GDALECWCompressor final : public CNCSFile
60
{
61

62
  public:
63
    GDALECWCompressor();
64
    virtual ~GDALECWCompressor();
65
    virtual CNCSError WriteReadLine(UINT32 nNextLine,
66
                                    void **ppInputArray) override;
67
#if ECWSDK_VERSION >= 50
68
    virtual void WriteStatus(IEEE4 fPercentComplete,
69
                             const NCS::CString &sStatusText,
70
                             const CompressionCounters &Counters) override;
71
#else
72
    virtual void WriteStatus(UINT32 nCurrentLine) override;
73
#endif
74

75
    virtual bool WriteCancel() override;
76

77
    CPLErr Initialize(const char *pszFilename, char **papszOptions, int nXSize,
78
                      int nYSize, int nBands,
79
                      const char *const *papszBandDescriptions,
80
                      int bRGBColorSpace, GDALDataType eType,
81
                      const OGRSpatialReference *poSRS,
82
                      const GDALGeoTransform &gt, int nGCPCount,
83
                      const GDAL_GCP *pasGCPList, int bIsJPEG2000,
84
                      int bPixelIsPoint, char **papszRPCMD,
85
                      GDALDataset *poSrcDS = nullptr);
86
    CPLErr CloseDown();
87

88
    CPLErr WriteJP2Box(GDALJP2Box *);
89
    void WriteXMLBoxes();
90
    CPLErr ourWriteLineBIL(UINT16 nBands, void **ppOutputLine,
91
                           UINT32 *pLineSteps = nullptr);
92
#if ECWSDK_VERSION >= 50
93
    virtual NCSEcwCellType WriteReadLineGetCellType() override
94
    {
95
        return sFileInfo.eCellType;
96
    }
97
#endif
98
#ifdef ECW_FW
99
    CNCSJP2File::CNCSJPXAssocBox m_oGMLAssoc;
100
#endif
101

102
    // Data
103

104
    GDALDataset *m_poSrcDS;
105

106
    std::shared_ptr<VSIIOStream> m_OStream;
107
    int m_nPercentComplete;
108

109
    int m_bCanceled;
110

111
    GDALProgressFunc pfnProgress;
112
    void *pProgressData;
113

114
    GDALDataType eWorkDT;
115
    int m_nSwathLines;
116
    UINT32 m_nSwathOffset;
117
    GByte *m_pabySwathBuf;
118
    JP2UserBox **papoJP2UserBox;
119
    int nJP2UserBox;
120
    std::vector<int> m_anBandMap{};
121

122
  private:
123
    NCSFileViewFileInfoEx sFileInfo;
124

125
    /* To fix 'warning: ‘virtual NCS::CView& NCS::CView::operator=(const
126
     * NCS::CView&)’ was hidden ' with SDK 5 */
127
#if ECWSDK_VERSION >= 50
128
    using CNCSFile::operator=;
129
#endif
130
    CPL_DISALLOW_COPY_ASSIGN(GDALECWCompressor)
131
};
132

133
/************************************************************************/
134
/*                         GDALECWCompressor()                          */
135
/************************************************************************/
136

137
GDALECWCompressor::GDALECWCompressor()
69✔
138
    : m_OStream(std::make_shared<VSIIOStream>()), eWorkDT(GDT_Unknown),
139
      m_nSwathLines(0), m_nSwathOffset(0), m_pabySwathBuf(nullptr)
69✔
140
{
141
    m_poSrcDS = nullptr;
69✔
142
    m_nPercentComplete = -1;
69✔
143
    m_bCanceled = FALSE;
69✔
144
    pfnProgress = GDALDummyProgress;
69✔
145
    pProgressData = nullptr;
69✔
146
    papoJP2UserBox = nullptr;
69✔
147
    nJP2UserBox = 0;
69✔
148
#if ECWSDK_VERSION >= 50
149
    NCSInitFileInfo(&sFileInfo);
150
#else
151
    NCSInitFileInfoEx(&sFileInfo);
69✔
152
#endif
153
    m_anBandMap.resize(sFileInfo.nBands);
69✔
154
    for (int iBand = 0; iBand < sFileInfo.nBands; iBand++)
69✔
155
        m_anBandMap[iBand] = iBand + 1;
×
156
}
69✔
157

158
/************************************************************************/
159
/*                         ~GDALECWCompressor()                         */
160
/************************************************************************/
161

162
GDALECWCompressor::~GDALECWCompressor()
69✔
163

164
{
165
    int i;
166
    for (i = 0; i < nJP2UserBox; i++)
131✔
167
        delete papoJP2UserBox[i];
62✔
168
    CPLFree(papoJP2UserBox);
69✔
169

170
#if ECWSDK_VERSION >= 50
171
    NCSFreeFileInfo(&sFileInfo);
172
#else
173
    NCSFreeFileInfoEx(&sFileInfo);
69✔
174
#endif
175
    CPLFree(m_pabySwathBuf);
69✔
176
}
69✔
177

178
/************************************************************************/
179
/*                             CloseDown()                              */
180
/************************************************************************/
181

182
CPLErr GDALECWCompressor::CloseDown()
34✔
183

184
{
185
    Close(true);
34✔
186
    m_OStream->Close();
34✔
187

188
    return CE_None;
34✔
189
}
190

191
/************************************************************************/
192
/*                           WriteReadLine()                            */
193
/************************************************************************/
194

195
CNCSError GDALECWCompressor::WriteReadLine(UINT32 nNextLine,
3,979✔
196
                                           void **ppInputArray)
197

198
{
199
    CPLErr eErr;
200

201
#ifdef DEBUG_VERBOSE
202
    CPLDebug("ECW", "nNextLine = %d", nNextLine);
203
#endif
204

205
    if (m_poSrcDS == nullptr || m_poSrcDS->GetRasterBand(1) == nullptr)
3,979✔
206
    {
207
        return GetCNCSError(NCS_FILEIO_ERROR);
×
208
    }
209
    if (m_nSwathLines <= 0)
3,979✔
210
    {
211
        int nBlockX;
212
        constexpr int MIN_SWATH_LINES = 256;
32✔
213
        m_poSrcDS->GetRasterBand(1)->GetBlockSize(&nBlockX, &m_nSwathLines);
32✔
214
        if (m_nSwathLines < MIN_SWATH_LINES)
32✔
215
            m_nSwathLines = MIN_SWATH_LINES;
30✔
216
    }
217

218
    const GSpacing nPixelSpace = GDALGetDataTypeSizeBytes(eWorkDT);
3,979✔
219
    const GSpacing nLineSpace = sFileInfo.nSizeX * nPixelSpace;
3,979✔
220
    const GSpacing nBandSpace = nLineSpace * m_nSwathLines;
3,979✔
221

222
    if (m_pabySwathBuf == nullptr)
3,979✔
223
    {
224
        size_t nBufSize = static_cast<size_t>(nBandSpace * sFileInfo.nBands);
32✔
225
        m_pabySwathBuf = (GByte *)VSI_MALLOC_VERBOSE(nBufSize);
32✔
226
    }
227
    if (m_pabySwathBuf == nullptr)
3,979✔
228
    {
229
        return GetCNCSError(NCS_FILE_NO_MEMORY);
×
230
    }
231

232
    if (nNextLine == 0 || nNextLine >= m_nSwathOffset + m_nSwathLines)
3,979✔
233
    {
234
        int nSwathLines = m_nSwathLines;
41✔
235
        if (nNextLine + nSwathLines > sFileInfo.nSizeY)
41✔
236
        {
237
            nSwathLines = sFileInfo.nSizeY - nNextLine;
29✔
238
        }
239
        eErr = m_poSrcDS->RasterIO(
123✔
240
            GF_Read, 0, nNextLine, sFileInfo.nSizeX, nSwathLines,
41✔
241
            m_pabySwathBuf, sFileInfo.nSizeX, nSwathLines, eWorkDT,
41✔
242
            sFileInfo.nBands, &m_anBandMap[0], nPixelSpace, nLineSpace,
41✔
243
            nBandSpace, nullptr);
244
        m_nSwathOffset = nNextLine;
41✔
245
        UINT32 nNextSwathLine = nNextLine + nSwathLines;
41✔
246
        if (nNextSwathLine < sFileInfo.nSizeY)
41✔
247
        {
248
            if (nNextSwathLine + nSwathLines > sFileInfo.nSizeY)
9✔
249
            {
250
                nSwathLines = sFileInfo.nSizeY - nNextSwathLine;
3✔
251
            }
252
            m_poSrcDS->AdviseRead(0, nNextSwathLine, sFileInfo.nSizeX,
18✔
253
                                  nSwathLines, sFileInfo.nSizeX, nSwathLines,
9✔
254
                                  eWorkDT, sFileInfo.nBands, &m_anBandMap[0],
9✔
255
                                  nullptr);
9✔
256
        }
41✔
257
    }
258
    else
259
    {
260
        eErr = CE_None;
3,938✔
261
    }
262

263
    for (int iBand = 0; iBand < (int)sFileInfo.nBands; iBand++)
9,928✔
264
    {
265
        memcpy(ppInputArray[iBand],
5,949✔
266
               m_pabySwathBuf + nLineSpace * (nNextLine - m_nSwathOffset) +
5,949✔
267
                   nBandSpace * iBand,
5,949✔
268
               static_cast<size_t>(nPixelSpace * sFileInfo.nSizeX));
5,949✔
269
    }
270

271
    if (eErr == CE_None)
3,979✔
272
        return GetCNCSError(NCS_SUCCESS);
3,979✔
273
    else
274
        return GetCNCSError(NCS_FILEIO_ERROR);
×
275
}
276

277
/************************************************************************/
278
/*                            WriteStatus()                             */
279
/************************************************************************/
280
#if ECWSDK_VERSION >= 50
281
void GDALECWCompressor::WriteStatus(IEEE4 fPercentComplete,
282
                                    const NCS::CString &sStatusText,
283
                                    const CompressionCounters &Counters)
284
{
285
    std::string sStatusUTF8;
286
    sStatusText.utf8_str(sStatusUTF8);
287

288
    m_bCanceled = !pfnProgress(fPercentComplete / 100.0, sStatusUTF8.c_str(),
289
                               pProgressData);
290
}
291
#else
292

293
void GDALECWCompressor::WriteStatus(UINT32 nCurrentLine)
3,979✔
294

295
{
296
    m_bCanceled = !pfnProgress(nCurrentLine / (float)sFileInfo.nSizeY, nullptr,
3,979✔
297
                               pProgressData);
298
}
3,979✔
299
#endif
300
/************************************************************************/
301
/*                            WriteCancel()                             */
302
/************************************************************************/
303

304
bool GDALECWCompressor::WriteCancel()
3,979✔
305

306
{
307
    return (bool)m_bCanceled;
3,979✔
308
}
309

310
/************************************************************************/
311
/*                            WriteJP2Box()                             */
312
/************************************************************************/
313

314
CPLErr GDALECWCompressor::WriteJP2Box(GDALJP2Box *poBox)
70✔
315

316
{
317
    JP2UserBox *poECWBox;
318

319
    if (poBox == nullptr)
70✔
320
        return CE_None;
8✔
321

322
    poECWBox = new JP2UserBox();
62✔
323
    memcpy(&(poECWBox->m_nTBox), poBox->GetType(), 4);
62✔
324
    CPL_MSBPTR32(&(poECWBox->m_nTBox));
62✔
325

326
    poECWBox->SetData((int)poBox->GetDataLength(), poBox->GetWritableData());
62✔
327

328
    AddBox(poECWBox);
62✔
329

330
    delete poBox;
62✔
331

332
    papoJP2UserBox = (JP2UserBox **)CPLRealloc(
124✔
333
        papoJP2UserBox, (nJP2UserBox + 1) * sizeof(JP2UserBox *));
62✔
334
    papoJP2UserBox[nJP2UserBox] = poECWBox;
62✔
335
    nJP2UserBox++;
62✔
336

337
    return CE_None;
62✔
338
}
339

340
/************************************************************************/
341
/*                         WriteXMLBoxes()                              */
342
/************************************************************************/
343

344
void GDALECWCompressor::WriteXMLBoxes()
6✔
345
{
346
    int nBoxes = 0;
6✔
347
    GDALJP2Box **papoBoxes =
348
        GDALJP2Metadata::CreateXMLBoxes(m_poSrcDS, &nBoxes);
6✔
349
    for (int i = 0; i < nBoxes; i++)
7✔
350
    {
351
        WriteJP2Box(papoBoxes[i]);
1✔
352
    }
353
    CPLFree(papoBoxes);
6✔
354
}
6✔
355

356
/************************************************************************/
357
/*                          ourWriteLineBIL()                           */
358
/************************************************************************/
359

360
CPLErr GDALECWCompressor::ourWriteLineBIL(UINT16 nBands, void **ppOutputLine,
200✔
361
                                          UINT32 *pLineSteps)
362
{
363

364
    CNCSError oError = CNCSFile::WriteLineBIL(sFileInfo.eCellType, nBands,
365
                                              ppOutputLine, pLineSteps);
400✔
366

367
    if (oError.GetErrorNumber() != NCS_SUCCESS)
200✔
368
    {
369
        ECWReportError(oError, "Scanline write write failed.\n");
×
370
        return CE_Failure;
×
371
    }
372
    return CE_None;
200✔
373
}
374

375
/************************************************************************/
376
/*                             Initialize()                             */
377
/*                                                                      */
378
/*      Initialize compressor output.                                   */
379
/************************************************************************/
380

381
CPLErr GDALECWCompressor::Initialize(
38✔
382
    const char *pszFilename, char **papszOptions, int nXSize, int nYSize,
383
    int nBands, const char *const *papszBandDescriptions, int bRGBColorSpace,
384
    GDALDataType eType, const OGRSpatialReference *poSRS,
385
    const GDALGeoTransform &gt, int nGCPCount, const GDAL_GCP *pasGCPList,
386
    int bIsJPEG2000, int bPixelIsPoint, char **papszRPCMD, GDALDataset *poSrcDS)
387

388
{
389
/* -------------------------------------------------------------------- */
390
/*      For 4.x and beyond you need a license key to compress data.     */
391
/*      Check for it as a configuration option or a creation option.    */
392
/* -------------------------------------------------------------------- */
393
#if ECWSDK_VERSION >= 40
394
    const char *pszECWKey = CSLFetchNameValue(papszOptions, "ECW_ENCODE_KEY");
395
    if (pszECWKey == nullptr)
396
        pszECWKey = CPLGetConfigOption("ECW_ENCODE_KEY", nullptr);
397

398
    const char *pszECWCompany =
399
        CSLFetchNameValue(papszOptions, "ECW_ENCODE_COMPANY");
400
    if (pszECWCompany == nullptr)
401
        pszECWCompany = CPLGetConfigOption("ECW_ENCODE_COMPANY", nullptr);
402

403
    if (pszECWKey && pszECWCompany)
404
    {
405
        CPLDebug("ECW", "SetOEMKey(%s,%s)", pszECWCompany, pszECWKey);
406
        CNCSFile::SetOEMKey((char *)pszECWCompany, (char *)pszECWKey);
407
    }
408
    else if (pszECWKey || pszECWCompany)
409
    {
410
        CPLError(CE_Failure, CPLE_AppDefined,
411
                 "Only one of ECW_ENCODE_KEY and ECW_ENCODE_COMPANY were "
412
                 "provided.\nBoth are required.");
413
        return CE_Failure;
414
    }
415
    else
416
    {
417
        CPLError(CE_Failure, CPLE_AppDefined,
418
                 "None of ECW_ENCODE_KEY and ECW_ENCODE_COMPANY were "
419
                 "provided.\nBoth are required.");
420
        return CE_Failure;
421
    }
422

423
#endif /* ECWSDK_VERSION >= 40 */
424

425
    /* -------------------------------------------------------------------- */
426
    /*      Do some rudimentary checking in input.                          */
427
    /* -------------------------------------------------------------------- */
428
    if (nBands == 0)
38✔
429
    {
430
        CPLError(CE_Failure, CPLE_NotSupported,
×
431
                 "ECW driver requires at least one band.");
432
        return CE_Failure;
×
433
    }
434

435
    /* -------------------------------------------------------------------- */
436
    /*      Parse out some known options.                                   */
437
    /* -------------------------------------------------------------------- */
438
    float fTargetCompression;
439

440
    // Default compression based on image type per request from Paul Beaty.
441
    if (nBands > 1)
38✔
442
        fTargetCompression = 95.0;
11✔
443
    else
444
        fTargetCompression = 90.0;
27✔
445

446
    if (CSLFetchNameValue(papszOptions, "TARGET") != nullptr)
38✔
447
    {
448
        fTargetCompression =
10✔
449
            (float)CPLAtof(CSLFetchNameValue(papszOptions, "TARGET"));
10✔
450

451
        /* The max allowed value should be 100 - 100 / 65535 = 99.9984740978 */
452
        /* so that nCompressionRate fits on a uint16 (see below) */
453
        /* No need to be so pedantic, so we will limit to 99.99 % */
454
        /* (compression rate = 10 000) */
455
        if (fTargetCompression < 0.0 || fTargetCompression > 99.99)
10✔
456
        {
457
            CPLError(CE_Failure, CPLE_NotSupported,
×
458
                     "TARGET compression of %.3f invalid, should be a\n"
459
                     "value between 0 and 99.99 percent.\n",
460
                     (double)fTargetCompression);
461
            return CE_Failure;
×
462
        }
463
    }
464

465
    /* -------------------------------------------------------------------- */
466
    /*      Create and initialize compressor.                               */
467
    /* -------------------------------------------------------------------- */
468
    NCSFileViewFileInfoEx *psClient = &(sFileInfo);
38✔
469
    const char *pszOption = nullptr;
38✔
470
#if ECWSDK_VERSION >= 50
471
    if (bIsJPEG2000 == FALSE)
472
    {
473
        bool bECWV3 = false;
474
        pszOption = CSLFetchNameValue(papszOptions, "ECW_FORMAT_VERSION");
475
        if (pszOption != nullptr)
476
        {
477
            bECWV3 = (3 == atoi(pszOption));
478
        }
479
        psClient->nFormatVersion = (bECWV3) ? 3 : 2;
480
    }
481
    else
482
    {
483
        psClient->nFormatVersion = 1;
484
    }
485
#endif
486
    psClient->nBands = (UINT16)nBands;
38✔
487
    psClient->nSizeX = nXSize;
38✔
488
    psClient->nSizeY = nYSize;
38✔
489
    psClient->nCompressionRate =
38✔
490
        (UINT16)MAX(1, 100 / (100 - fTargetCompression));
38✔
491
    psClient->eCellSizeUnits = ECW_CELL_UNITS_METERS;
38✔
492

493
    if (nBands == 1)
38✔
494
        psClient->eColorSpace = NCSCS_GREYSCALE;
27✔
495
    else if (nBands == 3 && bRGBColorSpace)
11✔
496
        psClient->eColorSpace = NCSCS_sRGB;
5✔
497
#if ECWSDK_VERSION >= 40
498
    else if (nBands == 4 && bRGBColorSpace)
499
        psClient->eColorSpace = NCSCS_sRGB;
500
#endif
501
    else
502
        psClient->eColorSpace = NCSCS_MULTIBAND;
6✔
503

504
    /* -------------------------------------------------------------------- */
505
    /*      Figure out the data type.                                       */
506
    /* -------------------------------------------------------------------- */
507
    int bSigned = FALSE;
38✔
508
    int nBits = 8;
38✔
509
    eWorkDT = eType;
38✔
510

511
    switch (eWorkDT)
38✔
512
    {
513
        case GDT_Byte:
30✔
514
#if ECWSDK_VERSION >= 50
515
            psClient->nCellBitDepth = 8;
516
#endif
517
            psClient->eCellType = NCSCT_UINT8;
30✔
518
            nBits = 8;
30✔
519
            bSigned = FALSE;
30✔
520
            break;
30✔
521

522
        case GDT_UInt16:
2✔
523
#if ECWSDK_VERSION >= 50
524
            psClient->nCellBitDepth = 16;
525
#endif
526
            psClient->eCellType = NCSCT_UINT16;
2✔
527
            nBits = 16;
2✔
528
            bSigned = FALSE;
2✔
529
            break;
2✔
530

531
        case GDT_UInt32:
1✔
532
#if ECWSDK_VERSION >= 50
533
            psClient->nCellBitDepth = 32;
534
#endif
535
            psClient->eCellType = NCSCT_UINT32;
1✔
536
            nBits = 32;
1✔
537
            bSigned = FALSE;
1✔
538
            break;
1✔
539

540
        case GDT_Int16:
3✔
541
#if ECWSDK_VERSION >= 50
542
            psClient->nCellBitDepth = 16;
543
#endif
544
            psClient->eCellType = NCSCT_INT16;
3✔
545
            nBits = 16;
3✔
546
            bSigned = TRUE;
3✔
547
            break;
3✔
548

549
        case GDT_Int32:
1✔
550
#if ECWSDK_VERSION >= 50
551
            psClient->nCellBitDepth = 32;
552
#endif
553
            psClient->eCellType = NCSCT_INT32;
1✔
554
            nBits = 32;
1✔
555
            bSigned = TRUE;
1✔
556
            break;
1✔
557

558
        case GDT_Float32:
1✔
559
            psClient->eCellType = NCSCT_IEEE4;
1✔
560
            nBits = 32;
1✔
561
            bSigned = TRUE;
1✔
562
            break;
1✔
563

564
#if ECWSDK_VERSION >= 40
565
        case GDT_Float64:
566
            psClient->eCellType = NCSCT_IEEE8;
567
            nBits = 64;
568
            bSigned = TRUE;
569
            break;
570
#endif
571

572
        default:
×
573
            // We treat complex types as float.
574
            psClient->eCellType = NCSCT_IEEE4;
×
575
            nBits = 32;
×
576
            bSigned = TRUE;
×
577
            eWorkDT = GDT_Float32;
×
578
            break;
×
579
    }
580

581
    /* -------------------------------------------------------------------- */
582
    /*      Create band information structures.                             */
583
    /* -------------------------------------------------------------------- */
584
    int iBand;
585

586
    psClient->pBands =
38✔
587
        (NCSFileBandInfo *)NCSMalloc(sizeof(NCSFileBandInfo) * nBands, true);
38✔
588
    for (iBand = 0; iBand < nBands; iBand++)
98✔
589
    {
590
        const char *pszNBITS = CSLFetchNameValue(papszOptions, "NBITS");
60✔
591
        if (pszNBITS && atoi(pszNBITS) > 0)
60✔
592
            psClient->pBands[iBand].nBits = (UINT8)atoi(pszNBITS);
2✔
593
        else
594
            psClient->pBands[iBand].nBits = (UINT8)nBits;
58✔
595
        psClient->pBands[iBand].bSigned = (BOOLEAN)bSigned;
60✔
596
#if ECWSDK_VERSION >= 50
597
        psClient->pBands[iBand].szDesc =
598
            NCSStrDup(papszBandDescriptions[iBand]);
599
#else
600
        psClient->pBands[iBand].szDesc =
120✔
601
            NCSStrDup((char *)papszBandDescriptions[iBand]);
60✔
602
#endif
603
    }
604

605
    /* -------------------------------------------------------------------- */
606
    /*      Allow CNCSFile::SetParameter() requests.                        */
607
    /* -------------------------------------------------------------------- */
608

609
    if (bIsJPEG2000)
38✔
610
    {
611
        pszOption = CSLFetchNameValue(papszOptions, "PROFILE");
33✔
612
        if (pszOption != nullptr && EQUAL(pszOption, "BASELINE_0"))
33✔
613
            SetParameter(CNCSJP2FileView::JP2_COMPRESS_PROFILE_BASELINE_0);
×
614
        else if (pszOption != nullptr && EQUAL(pszOption, "BASELINE_1"))
33✔
615
            SetParameter(CNCSJP2FileView::JP2_COMPRESS_PROFILE_BASELINE_1);
×
616
        else if (pszOption != nullptr && EQUAL(pszOption, "BASELINE_2"))
33✔
617
            SetParameter(CNCSJP2FileView::JP2_COMPRESS_PROFILE_BASELINE_2);
×
618
        else if (pszOption != nullptr && EQUAL(pszOption, "NPJE"))
33✔
619
            SetParameter(CNCSJP2FileView::JP2_COMPRESS_PROFILE_NITF_BIIF_NPJE);
7✔
620
        else if (pszOption != nullptr && EQUAL(pszOption, "EPJE"))
26✔
621
            SetParameter(CNCSJP2FileView::JP2_COMPRESS_PROFILE_NITF_BIIF_EPJE);
×
622

623
        pszOption = CSLFetchNameValue(papszOptions, "CODESTREAM_ONLY");
33✔
624
        if (pszOption == nullptr &&
62✔
625
            EQUAL(CPLGetExtensionSafe(pszFilename).c_str(), "j2k"))
62✔
626
            pszOption = "YES";
2✔
627
        if (pszOption != nullptr)
33✔
628
            SetParameter(CNCSJP2FileView::JP2_COMPRESS_CODESTREAM_ONLY,
12✔
629
                         CPLTestBool(pszOption));
6✔
630

631
        pszOption = CSLFetchNameValue(papszOptions, "LEVELS");
33✔
632
        if (pszOption != nullptr)
33✔
633
            SetParameter(CNCSJP2FileView::JP2_COMPRESS_LEVELS,
×
634
                         (UINT32)atoi(pszOption));
×
635

636
        pszOption = CSLFetchNameValue(papszOptions, "LAYERS");
33✔
637
        if (pszOption != nullptr)
33✔
638
            SetParameter(CNCSJP2FileView::JP2_COMPRESS_LAYERS,
3✔
639
                         (UINT32)atoi(pszOption));
3✔
640

641
        pszOption = CSLFetchNameValue(papszOptions, "PRECINCT_WIDTH");
33✔
642
        if (pszOption != nullptr)
33✔
643
            SetParameter(CNCSJP2FileView::JP2_COMPRESS_PRECINCT_WIDTH,
×
644
                         (UINT32)atoi(pszOption));
×
645

646
        pszOption = CSLFetchNameValue(papszOptions, "PRECINCT_HEIGHT");
33✔
647
        if (pszOption != nullptr)
33✔
648
            SetParameter(CNCSJP2FileView::JP2_COMPRESS_PRECINCT_HEIGHT,
×
649
                         (UINT32)atoi(pszOption));
×
650

651
        pszOption = CSLFetchNameValue(papszOptions, "TILE_WIDTH");
33✔
652
        if (pszOption != nullptr)
33✔
653
            SetParameter(CNCSJP2FileView::JP2_COMPRESS_TILE_WIDTH,
×
654
                         (UINT32)atoi(pszOption));
×
655

656
        pszOption = CSLFetchNameValue(papszOptions, "TILE_HEIGHT");
33✔
657
        if (pszOption != nullptr)
33✔
658
            SetParameter(CNCSJP2FileView::JP2_COMPRESS_TILE_HEIGHT,
×
659
                         (UINT32)atoi(pszOption));
×
660

661
        pszOption = CSLFetchNameValue(papszOptions, "INCLUDE_SOP");
33✔
662
        if (pszOption != nullptr)
33✔
663
            SetParameter(CNCSJP2FileView::JP2_COMPRESS_INCLUDE_SOP,
×
664
                         CPLTestBool(pszOption));
×
665

666
        pszOption = CSLFetchNameValue(papszOptions, "INCLUDE_EPH");
33✔
667
        if (pszOption != nullptr)
33✔
668
            SetParameter(CNCSJP2FileView::JP2_COMPRESS_INCLUDE_EPH,
×
669
                         CPLTestBool(pszOption));
×
670

671
        pszOption = CSLFetchNameValue(papszOptions, "PROGRESSION");
33✔
672
        if (pszOption != nullptr && EQUAL(pszOption, "LRCP"))
33✔
673
            SetParameter(CNCSJP2FileView::JP2_COMPRESS_PROGRESSION_LRCP);
×
674

675
        else if (pszOption != nullptr && EQUAL(pszOption, "RLCP"))
33✔
676
            SetParameter(CNCSJP2FileView::JP2_COMPRESS_PROGRESSION_RLCP);
×
677

678
        else if (pszOption != nullptr && EQUAL(pszOption, "RPCL"))
33✔
679
            SetParameter(CNCSJP2FileView::JP2_COMPRESS_PROGRESSION_RPCL);
×
680

681
        pszOption = CSLFetchNameValue(papszOptions, "GEODATA_USAGE");
33✔
682
        if (pszOption == nullptr)
33✔
683
            // Default to suppressing ECW SDK geodata, just use our own stuff.
684
            SetGeodataUsage(JP2_GEODATA_USE_NONE);
33✔
685
        else if (EQUAL(pszOption, "NONE"))
×
686
            SetGeodataUsage(JP2_GEODATA_USE_NONE);
×
687
        else if (EQUAL(pszOption, "PCS_ONLY"))
×
688
            SetGeodataUsage(JP2_GEODATA_USE_PCS_ONLY);
×
689
        else if (EQUAL(pszOption, "GML_ONLY"))
×
690
            SetGeodataUsage(JP2_GEODATA_USE_GML_ONLY);
×
691
        else if (EQUAL(pszOption, "PCS_GML"))
×
692
            SetGeodataUsage(JP2_GEODATA_USE_PCS_GML);
×
693
        else if (EQUAL(pszOption, "GML_PCS"))
×
694
            SetGeodataUsage(JP2_GEODATA_USE_GML_PCS);
×
695
        else if (EQUAL(pszOption, "ALL"))
×
696
            SetGeodataUsage(JP2_GEODATA_USE_GML_PCS_WLD);
×
697

698
        pszOption = CSLFetchNameValue(papszOptions, "DECOMPRESS_LAYERS");
33✔
699
        if (pszOption != nullptr)
33✔
700
            SetParameter(CNCSJP2FileView::JP2_DECOMPRESS_LAYERS,
×
701
                         (UINT32)atoi(pszOption));
×
702

703
        pszOption = CSLFetchNameValue(papszOptions,
33✔
704
                                      "DECOMPRESS_RECONSTRUCTION_PARAMETER");
705
        if (pszOption != nullptr)
33✔
706
            SetParameter(
×
707
                CNCSJP2FileView::JPC_DECOMPRESS_RECONSTRUCTION_PARAMETER,
708
                (IEEE4)CPLAtof(pszOption));
×
709
    }
710

711
    /* -------------------------------------------------------------------- */
712
    /*      Georeferencing.                                                 */
713
    /* -------------------------------------------------------------------- */
714

715
    psClient->fOriginX = 0.0;
38✔
716
    psClient->fOriginY = psClient->nSizeY;
38✔
717
    psClient->fCellIncrementX = 1.0;
38✔
718
    psClient->fCellIncrementY = -1.0;
38✔
719
    psClient->fCWRotationDegrees = 0.0;
38✔
720

721
    if (gt[2] != 0.0 || gt[4] != 0.0)
38✔
722
        CPLError(CE_Warning, CPLE_NotSupported,
×
723
                 "Rotational coefficients ignored, georeferencing of\n"
724
                 "output ECW file will be incorrect.\n");
725
    else
726
    {
727
        psClient->fOriginX = gt[0];
38✔
728
        psClient->fOriginY = gt[3];
38✔
729
        psClient->fCellIncrementX = gt[1];
38✔
730
        psClient->fCellIncrementY = gt[5];
38✔
731
    }
732

733
    /* -------------------------------------------------------------------- */
734
    /*      Projection.                                                     */
735
    /* -------------------------------------------------------------------- */
736
    char szProjection[128];
737
    char szDatum[128];
738
    char szUnits[128];
739

740
    strcpy(szProjection, "RAW");
38✔
741
    strcpy(szDatum, "RAW");
38✔
742

743
    if (CSLFetchNameValue(papszOptions, "PROJ") != nullptr)
38✔
744
    {
745
        strncpy(szProjection, CSLFetchNameValue(papszOptions, "PROJ"),
×
746
                sizeof(szProjection));
747
        szProjection[sizeof(szProjection) - 1] = 0;
×
748
    }
749

750
    if (CSLFetchNameValue(papszOptions, "DATUM") != nullptr)
38✔
751
    {
752
        strncpy(szDatum, CSLFetchNameValue(papszOptions, "DATUM"),
×
753
                sizeof(szDatum));
754
        szDatum[sizeof(szDatum) - 1] = 0;
×
755
        if (EQUAL(szProjection, "RAW"))
×
756
            strcpy(szProjection, "GEODETIC");
×
757
    }
758

759
    const char *pszUnits = CSLFetchNameValue(papszOptions, "UNITS");
38✔
760
    if (pszUnits != nullptr)
38✔
761
    {
762
        psClient->eCellSizeUnits = ECWTranslateToCellSizeUnits(pszUnits);
×
763
    }
764

765
    if (EQUAL(szProjection, "RAW") && poSRS != nullptr && !poSRS->IsEmpty())
38✔
766
    {
767
        ECWTranslateFromWKT(poSRS, szProjection, sizeof(szProjection), szDatum,
25✔
768
                            sizeof(szDatum), szUnits);
769
        psClient->eCellSizeUnits = ECWTranslateToCellSizeUnits(szUnits);
25✔
770
    }
771

772
    NCSFree(psClient->szDatum);
38✔
773
    psClient->szDatum = NCSStrDup(szDatum);
38✔
774
    NCSFree(psClient->szProjection);
38✔
775
    psClient->szProjection = NCSStrDup(szProjection);
38✔
776

777
    CPLDebug("ECW", "Writing with PROJ=%s, DATUM=%s, UNITS=%s", szProjection,
38✔
778
             szDatum, ECWTranslateFromCellSizeUnits(psClient->eCellSizeUnits));
779

780
    /* -------------------------------------------------------------------- */
781
    /*      Setup GML and GeoTIFF information.                              */
782
    /* -------------------------------------------------------------------- */
783
    if ((poSRS != nullptr && !poSRS->IsEmpty()) || gt != GDALGeoTransform() ||
38✔
784
        nGCPCount > 0 || papszRPCMD != nullptr)
76✔
785
    {
786
        GDALJP2Metadata oJP2MD;
74✔
787

788
        oJP2MD.SetSpatialRef(poSRS);
37✔
789
        oJP2MD.SetGeoTransform(gt);
37✔
790
        oJP2MD.SetGCPs(nGCPCount, pasGCPList);
37✔
791
        oJP2MD.bPixelIsPoint = bPixelIsPoint;
37✔
792
        oJP2MD.SetRPCMD(papszRPCMD);
37✔
793

794
        if (bIsJPEG2000)
37✔
795
        {
796
            if (CPLFetchBool(papszOptions, "WRITE_METADATA", false))
32✔
797
            {
798
                if (!CPLFetchBool(papszOptions, "MAIN_MD_DOMAIN_ONLY", false))
6✔
799
                {
800
                    WriteXMLBoxes();
6✔
801
                }
802
                WriteJP2Box(
6✔
803
                    GDALJP2Metadata::CreateGDALMultiDomainMetadataXMLBox(
804
                        m_poSrcDS, CPLFetchBool(papszOptions,
6✔
805
                                                "MAIN_MD_DOMAIN_ONLY", false)));
806
            }
807
            if (CPLFetchBool(papszOptions, "GMLJP2", true))
32✔
808
            {
809
                const char *pszGMLJP2V2Def =
810
                    CSLFetchNameValue(papszOptions, "GMLJP2V2_DEF");
29✔
811
                if (pszGMLJP2V2Def != nullptr)
29✔
812
                {
813
                    WriteJP2Box(oJP2MD.CreateGMLJP2V2(nXSize, nYSize,
×
814
                                                      pszGMLJP2V2Def, poSrcDS));
815
                }
816
                else
817
                {
818
                    if (!poSRS || poSRS->IsEmpty() ||
48✔
819
                        GDALJP2Metadata::IsSRSCompatible(poSRS))
19✔
820
                    {
821
                        WriteJP2Box(oJP2MD.CreateGMLJP2(nXSize, nYSize));
28✔
822
                    }
823
                    else if (CSLFetchNameValue(papszOptions, "GMLJP2"))
1✔
824
                    {
825
                        CPLError(CE_Warning, CPLE_AppDefined,
×
826
                                 "GMLJP2 box was explicitly required but "
827
                                 "cannot be written due "
828
                                 "to lack of georeferencing and/or unsupported "
829
                                 "georeferencing "
830
                                 "for GMLJP2");
831
                    }
832
                    else
833
                    {
834
                        CPLDebug(
1✔
835
                            "JP2ECW",
836
                            "Cannot write GMLJP2 box due to unsupported SRS");
837
                    }
838
                }
839
            }
840
            if (CPLFetchBool(papszOptions, "GeoJP2", true))
32✔
841
                WriteJP2Box(oJP2MD.CreateJP2GeoTIFF());
29✔
842
            if (CPLFetchBool(papszOptions, "WRITE_METADATA", false) &&
38✔
843
                !CPLFetchBool(papszOptions, "MAIN_MD_DOMAIN_ONLY", false))
6✔
844
            {
845
                WriteJP2Box(GDALJP2Metadata::CreateXMPBox(m_poSrcDS));
6✔
846
            }
847
        }
848
    }
849
    /* -------------------------------------------------------------------- */
850
    /*      We handle all jpeg2000 files via the VSIIOStream, but ECW       */
851
    /*      files cannot be done this way for some reason.                  */
852
    /* -------------------------------------------------------------------- */
853
    VSILFILE *fpVSIL = nullptr;
38✔
854

855
    if (bIsJPEG2000)
38✔
856
    {
857
        int bSeekable = !(STARTS_WITH(pszFilename, "/vsistdout/") ||
66✔
858
                          STARTS_WITH(pszFilename, "/vsizip/") ||
33✔
859
                          STARTS_WITH(pszFilename, "/vsigzip/"));
33✔
860
        fpVSIL = VSIFOpenL(pszFilename, (bSeekable) ? "wb+" : "wb");
33✔
861
        if (fpVSIL == nullptr)
33✔
862
        {
863
            CPLError(CE_Failure, CPLE_OpenFailed, "Failed to open/create %s.",
2✔
864
                     pszFilename);
865
            return CE_Failure;
2✔
866
        }
867

868
        m_OStream->Access(fpVSIL, TRUE, (BOOLEAN)bSeekable, pszFilename, 0, -1);
31✔
869
    }
870
    else
871
    {
872
        if (!STARTS_WITH(pszFilename, "/vsi"))
5✔
873
        {
874
            // Try now to create the file to avoid memory leaks if it is
875
            // the SDK that fails to do it.
876
            fpVSIL = VSIFOpenL(pszFilename, "wb");
5✔
877
            if (fpVSIL == nullptr)
5✔
878
            {
879
                CPLError(CE_Failure, CPLE_OpenFailed,
2✔
880
                         "Failed to open/create %s.", pszFilename);
881
                return CE_Failure;
2✔
882
            }
883
            VSIFCloseL(fpVSIL);
3✔
884
            VSIUnlink(pszFilename);
3✔
885
            fpVSIL = nullptr;
3✔
886
        }
887
    }
888

889
/* -------------------------------------------------------------------- */
890
/*      Check if we can enable large files.  This option should only    */
891
/*      be set when the application is adhering to one of the           */
892
/*      ERMapper options for licensing larger than 500MB input          */
893
/*      files.  See Bug 767.  This option no longer exists with         */
894
/*      version 4+.                                                     */
895
/* -------------------------------------------------------------------- */
896
#if ECWSDK_VERSION < 40
897
    const char *pszLargeOK = CSLFetchNameValue(papszOptions, "LARGE_OK");
34✔
898
    if (pszLargeOK == nullptr)
34✔
899
        pszLargeOK = "NO";
34✔
900

901
    pszLargeOK = CPLGetConfigOption("ECW_LARGE_OK", pszLargeOK);
34✔
902

903
    if (CPLTestBool(pszLargeOK))
34✔
904
    {
905
        CNCSFile::SetKeySize();
×
906
        CPLDebug("ECW", "Large file generation enabled.");
×
907
    }
908
#endif /* ECWSDK_VERSION < 40 */
909
/* -------------------------------------------------------------------- */
910
/*      Infer metadata information from source dataset if possible      */
911
/* -------------------------------------------------------------------- */
912
#if ECWSDK_VERSION >= 50
913
    if (psClient->nFormatVersion > 2)
914
    {
915
        if (psClient->pFileMetaData == nullptr)
916
        {
917
            NCSEcwInitMetaData(&psClient->pFileMetaData);
918
        }
919
        if (m_poSrcDS && m_poSrcDS->GetMetadataItem(
920
                             "FILE_METADATA_ACQUISITION_DATE") != nullptr)
921
        {
922
            psClient->pFileMetaData->sAcquisitionDate =
923
                NCSStrDupT(NCS::CString(m_poSrcDS->GetMetadataItem(
924
                                            "FILE_METADATA_ACQUISITION_DATE"))
925
                               .c_str());
926
        }
927

928
        if (m_poSrcDS &&
929
            m_poSrcDS->GetMetadataItem(
930
                "FILE_METADATA_ACQUISITION_SENSOR_NAME") != nullptr)
931
        {
932
            psClient->pFileMetaData->sAcquisitionSensorName = NCSStrDupT(
933
                NCS::CString(m_poSrcDS->GetMetadataItem(
934
                                 "FILE_METADATA_ACQUISITION_SENSOR_NAME"))
935
                    .c_str());
936
        }
937
        if (m_poSrcDS &&
938
            m_poSrcDS->GetMetadataItem("FILE_METADATA_ADDRESS") != nullptr)
939
        {
940
            psClient->pFileMetaData->sAddress =
941
                NCSStrDupT(NCS::CString(m_poSrcDS->GetMetadataItem(
942
                                            "FILE_METADATA_ADDRESS"))
943
                               .c_str());
944
        }
945
        if (m_poSrcDS &&
946
            m_poSrcDS->GetMetadataItem("FILE_METADATA_AUTHOR") != nullptr)
947
        {
948
            psClient->pFileMetaData->sAuthor = NCSStrDupT(
949
                NCS::CString(m_poSrcDS->GetMetadataItem("FILE_METADATA_AUTHOR"))
950
                    .c_str());
951
        }
952
        if (m_poSrcDS && m_poSrcDS->GetMetadataItem(
953
                             "FILE_METADATA_CLASSIFICATION") != nullptr)
954
        {
955
            psClient->pFileMetaData->sClassification =
956
                NCSStrDupT(NCS::CString(m_poSrcDS->GetMetadataItem(
957
                                            "FILE_METADATA_CLASSIFICATION"))
958
                               .c_str());
959
        }
960
        if (pszECWCompany != nullptr &&
961
            CPLTestBool(CPLGetConfigOption("GDAL_ECW_WRITE_COMPANY", "YES")))
962
        {
963
            psClient->pFileMetaData->sCompany =
964
                NCSStrDupT(NCS::CString(pszECWCompany).c_str());
965
        }
966
        CPLString osCompressionSoftware = GetCompressionSoftwareName();
967
        if (!osCompressionSoftware.empty())
968
        {
969
            psClient->pFileMetaData->sCompressionSoftware =
970
                NCSStrDupT(NCS::CString(osCompressionSoftware.c_str()).c_str());
971
        }
972
        if (m_poSrcDS &&
973
            m_poSrcDS->GetMetadataItem("FILE_METADATA_COPYRIGHT") != nullptr)
974
        {
975
            psClient->pFileMetaData->sCopyright =
976
                NCSStrDupT(NCS::CString(m_poSrcDS->GetMetadataItem(
977
                                            "FILE_METADATA_COPYRIGHT"))
978
                               .c_str());
979
        }
980
        if (m_poSrcDS &&
981
            m_poSrcDS->GetMetadataItem("FILE_METADATA_EMAIL") != nullptr)
982
        {
983
            psClient->pFileMetaData->sEmail = NCSStrDupT(
984
                NCS::CString(m_poSrcDS->GetMetadataItem("FILE_METADATA_EMAIL"))
985
                    .c_str());
986
        }
987
        if (m_poSrcDS &&
988
            m_poSrcDS->GetMetadataItem("FILE_METADATA_TELEPHONE") != nullptr)
989
        {
990
            psClient->pFileMetaData->sTelephone =
991
                NCSStrDupT(NCS::CString(m_poSrcDS->GetMetadataItem(
992
                                            "FILE_METADATA_TELEPHONE"))
993
                               .c_str());
994
        }
995
    }
996
#endif
997
    /* -------------------------------------------------------------------- */
998
    /*      Set the file info.                                              */
999
    /* -------------------------------------------------------------------- */
1000
    CNCSError oError = SetFileInfo(sFileInfo);
68✔
1001

1002
    if (oError.GetErrorNumber() == NCS_SUCCESS)
34✔
1003
    {
1004
        if (fpVSIL == nullptr)
34✔
1005
        {
1006
#if ECWSDK_VERSION >= 40 && defined(_WIN32)
1007
            if (CPLTestBool(CPLGetConfigOption("GDAL_FILENAME_IS_UTF8", "YES")))
1008
            {
1009
                wchar_t *pwszFilename =
1010
                    CPLRecodeToWChar(pszFilename, CPL_ENC_UTF8, CPL_ENC_UCS2);
1011
                oError = GetCNCSError(Open(pwszFilename, false, true));
1012
                CPLFree(pwszFilename);
1013
            }
1014
            else
1015
#endif
1016
            {
1017
                oError = GetCNCSError(Open((char *)pszFilename, false, true));
3✔
1018
            }
1019
        }
1020
        else
1021
        {
1022
#if ECWSDK_VERSION >= 55
1023
            oError = CNCSJP2FileView::Open(m_OStream);
1024
#else
1025
            oError = CNCSJP2FileView::Open(m_OStream.get());
31✔
1026
#endif
1027
        }
1028
    }
1029

1030
    if (oError.GetErrorNumber() == NCS_SUCCESS)
34✔
1031
        return CE_None;
34✔
1032
    else if (oError.GetErrorNumber() == NCS_INPUT_SIZE_EXCEEDED)
×
1033
    {
1034
        CPLError(CE_Failure, CPLE_AppDefined,
×
1035
                 "ECW SDK compress limit exceeded.");
1036
        return CE_Failure;
×
1037
    }
1038
    else
1039
    {
1040
        ECWReportError(oError);
×
1041

1042
        return CE_Failure;
×
1043
    }
1044
}
1045

1046
/************************************************************************/
1047
/*                      ECWIsInputRGBColorSpace()                       */
1048
/************************************************************************/
1049

1050
static int ECWIsInputRGBColorSpace(GDALDataset *poSrcDS)
38✔
1051
{
1052
    int nBands = poSrcDS->GetRasterCount();
38✔
1053

1054
    /* -------------------------------------------------------------------- */
1055
    /*      Is the input RGB or RGBA?                                       */
1056
    /* -------------------------------------------------------------------- */
1057
    int bRGBColorSpace = FALSE;
38✔
1058
    int bRGB = FALSE;
38✔
1059
    if (nBands >= 3)
38✔
1060
    {
1061
        bRGB = (poSrcDS->GetRasterBand(1)->GetColorInterpretation() ==
8✔
1062
                GCI_RedBand);
1063
        bRGB &= (poSrcDS->GetRasterBand(2)->GetColorInterpretation() ==
8✔
1064
                 GCI_GreenBand);
1065
        bRGB &= (poSrcDS->GetRasterBand(3)->GetColorInterpretation() ==
8✔
1066
                 GCI_BlueBand);
1067
    }
1068
    if (nBands == 3)
38✔
1069
    {
1070
        bRGBColorSpace = bRGB;
6✔
1071
    }
1072
    else if (nBands == 4 && bRGB)
32✔
1073
    {
1074
        bRGBColorSpace = (poSrcDS->GetRasterBand(4)->GetColorInterpretation() ==
×
1075
                          GCI_AlphaBand);
1076
    }
1077

1078
    return bRGBColorSpace;
38✔
1079
}
1080

1081
/************************************************************************/
1082
/*                           ECWCreateCopy()                            */
1083
/************************************************************************/
1084

1085
static GDALDataset *ECWCreateCopy(const char *pszFilename, GDALDataset *poSrcDS,
36✔
1086
                                  int bStrict, char **papszOptions,
1087
                                  GDALProgressFunc pfnProgress,
1088
                                  void *pProgressData, int bIsJPEG2000)
1089

1090
{
1091
    ECWInitialize();
36✔
1092

1093
    /* -------------------------------------------------------------------- */
1094
    /*      Get various values from the source dataset.                     */
1095
    /* -------------------------------------------------------------------- */
1096
    int nBands = poSrcDS->GetRasterCount();
36✔
1097
    int nXSize = poSrcDS->GetRasterXSize();
36✔
1098
    int nYSize = poSrcDS->GetRasterYSize();
36✔
1099

1100
    if (nBands == 0)
36✔
1101
    {
1102
        CPLError(
×
1103
            CE_Failure, CPLE_NotSupported,
1104
            "ECW driver does not support source dataset with zero band.\n");
1105
        return nullptr;
×
1106
    }
1107

1108
    GDALDataType eType = poSrcDS->GetRasterBand(1)->GetRasterDataType();
36✔
1109

1110
    const OGRSpatialReference *poSRS = poSrcDS->GetSpatialRef();
36✔
1111
    GDALGeoTransform gt;
36✔
1112
    poSrcDS->GetGeoTransform(gt);
36✔
1113

1114
    if (poSrcDS->GetGCPCount() > 0)
36✔
1115
        poSRS = poSrcDS->GetGCPSpatialRef();
1✔
1116

1117
        /* --------------------------------------------------------------------
1118
         */
1119
        /*      For ECW, confirm the datatype is 8bit (or uint16 for ECW v3) */
1120
        /* --------------------------------------------------------------------
1121
         */
1122
#if ECWSDK_VERSION >= 50
1123
    bool bECWV3 = false;
1124
    if (bIsJPEG2000 == FALSE)
1125
    {
1126
        const char *pszOption =
1127
            CSLFetchNameValue(papszOptions, "ECW_FORMAT_VERSION");
1128
        if (pszOption != nullptr)
1129
        {
1130
            bECWV3 = (3 == atoi(pszOption));
1131
        }
1132
    }
1133
#endif
1134
    if (!(eType == GDT_Byte ||
36✔
1135
#if ECWSDK_VERSION >= 50
1136
          (bECWV3 && eType == GDT_UInt16) ||
1137
#endif
1138
          bIsJPEG2000))
1139
    {
1140
        if (bStrict)
×
1141
        {
1142
            CPLError(
×
1143
                CE_Failure, CPLE_AppDefined,
1144
                "Attempt to create ECW file with pixel data type %s failed.\n"
1145
                "Only Byte data type supported for ECW version 2 files."
1146
#if ECWSDK_VERSION >= 50
1147
                " ECW version 3 files supports UInt16 as well."
1148
                " Specify ECW_FORMAT_VERSION=3 creation option to write "
1149
                "version 3 file. \n"
1150
#else
1151
                ". \n"
1152
#endif
1153
                ,
1154
                GDALGetDataTypeName(eType));
1155
        }
1156
        else
1157
        {
1158
#if ECWSDK_VERSION >= 50
1159
            if (eType == GDT_UInt16)
1160
            {
1161
                CPLError(CE_Warning, CPLE_AppDefined,
1162
                         "ECW version 2 does not support UInt16 data type, "
1163
                         "truncating to Byte."
1164
                         " Consider specifying ECW_FORMAT_VERSION=3 for full "
1165
                         "UInt16 support available in ECW version 3. \n");
1166
            }
1167
            else
1168
#endif
1169
                CPLError(CE_Warning, CPLE_AppDefined,
×
1170
                         "ECW v2 does not support data type, ignoring request "
1171
                         "for %s. \n",
1172
                         GDALGetDataTypeName(eType));
1173

1174
            eType = GDT_Byte;
×
1175
        }
1176
    }
1177

1178
    /* -------------------------------------------------------------------- */
1179
    /*      Is the input RGB or RGBA?                                       */
1180
    /* -------------------------------------------------------------------- */
1181
    int bRGBColorSpace = ECWIsInputRGBColorSpace(poSrcDS);
36✔
1182

1183
    /* -------------------------------------------------------------------- */
1184
    /*      Setup the compressor.                                           */
1185
    /* -------------------------------------------------------------------- */
1186
    GDALECWCompressor oCompressor;
72✔
1187

1188
    oCompressor.pfnProgress = pfnProgress;
36✔
1189
    oCompressor.pProgressData = pProgressData;
36✔
1190
    oCompressor.m_poSrcDS = poSrcDS;
36✔
1191

1192
    CPLStringList aosBandDescriptions;
72✔
1193
    for (int i = 0; i < nBands; i++)
92✔
1194
    {
1195
        /* Make a copy since ECWGetColorInterpretationName() can return a string
1196
         * generated */
1197
        /* by CPLSPrintf(), which has just a few rotating entries. */
1198
        aosBandDescriptions.AddString(ECWGetColorInterpretationName(
1199
            poSrcDS->GetRasterBand(i + 1)->GetColorInterpretation(), i));
56✔
1200
    }
1201

1202
    const char *pszAreaOrPoint = poSrcDS->GetMetadataItem(GDALMD_AREA_OR_POINT);
36✔
1203
    int bPixelIsPoint =
36✔
1204
        pszAreaOrPoint != nullptr && EQUAL(pszAreaOrPoint, GDALMD_AOP_POINT);
36✔
1205

1206
    if (oCompressor.Initialize(
36✔
1207
            pszFilename, papszOptions, nXSize, nYSize, nBands,
1208
            aosBandDescriptions.List(), bRGBColorSpace, eType, poSRS, gt,
36✔
1209
            poSrcDS->GetGCPCount(), poSrcDS->GetGCPs(), bIsJPEG2000,
36✔
1210
            bPixelIsPoint, poSrcDS->GetMetadata("RPC"), poSrcDS) != CE_None)
72✔
1211
    {
1212
        return nullptr;
4✔
1213
    }
1214

1215
    /* -------------------------------------------------------------------- */
1216
    /*      Start the compression.                                          */
1217
    /* -------------------------------------------------------------------- */
1218

1219
    if (!pfnProgress(0.0, nullptr, pProgressData))
32✔
1220
        return nullptr;
×
1221

1222
    CNCSError oErr = oCompressor.Write();
64✔
1223

1224
    if (oErr.GetErrorNumber() != NCS_SUCCESS)
32✔
1225
    {
1226
        ECWReportError(oErr);
×
1227
        return nullptr;
×
1228
    }
1229

1230
    /* -------------------------------------------------------------------- */
1231
    /*      Cleanup, and return read-only handle.                           */
1232
    /* -------------------------------------------------------------------- */
1233
    oCompressor.CloseDown();
32✔
1234
    pfnProgress(1.0, nullptr, pProgressData);
32✔
1235

1236
    /* -------------------------------------------------------------------- */
1237
    /*      Re-open dataset, and copy any auxiliary pam information.         */
1238
    /* -------------------------------------------------------------------- */
1239
    GDALOpenInfo oOpenInfo(pszFilename, GA_ReadOnly);
32✔
1240
    GDALPamDataset *poDS = nullptr;
32✔
1241

1242
    if (bIsJPEG2000)
32✔
1243
        poDS = cpl::down_cast<GDALPamDataset *>(
29✔
1244
            ECWDatasetOpenJPEG2000(&oOpenInfo));
1245
    else
1246
        poDS =
1247
            cpl::down_cast<GDALPamDataset *>(ECWDataset::OpenECW(&oOpenInfo));
3✔
1248

1249
    if (poDS)
32✔
1250
    {
1251
#if ECWSDK_VERSION >= 50
1252
        for (int i = 1; i <= poSrcDS->GetRasterCount(); i++)
1253
        {
1254
            double dMin, dMax, dMean, dStdDev;
1255
            if (poSrcDS->GetRasterBand(i)->GetStatistics(
1256
                    FALSE, FALSE, &dMin, &dMax, &dMean, &dStdDev) == CE_None)
1257
            {
1258
                poDS->GetRasterBand(i)->SetStatistics(dMin, dMax, dMean,
1259
                                                      dStdDev);
1260
            }
1261
            double dHistMin, dHistMax;
1262
            int nBuckets;
1263
            GUIntBig *pHistogram = nullptr;
1264
            if (poSrcDS->GetRasterBand(i)->GetDefaultHistogram(
1265
                    &dHistMin, &dHistMax, &nBuckets, &pHistogram, FALSE,
1266
                    nullptr, nullptr) == CE_None)
1267
            {
1268
                poDS->GetRasterBand(i)->SetDefaultHistogram(
1269
                    dHistMin, dHistMax, nBuckets, pHistogram);
1270
                VSIFree(pHistogram);
1271
            }
1272
        }
1273
#endif
1274

1275
        cpl::down_cast<ECWDataset *>(poDS)->SetPreventCopyingSomeMetadata(TRUE);
32✔
1276
        int nFlags = GCIF_PAM_DEFAULT;
32✔
1277
        if (bIsJPEG2000 && !CPLFetchBool(papszOptions, "WRITE_METADATA", false))
32✔
1278
            nFlags &= ~GCIF_METADATA;
23✔
1279
        poDS->CloneInfo(poSrcDS, nFlags);
32✔
1280
        cpl::down_cast<ECWDataset *>(poDS)->SetPreventCopyingSomeMetadata(
32✔
1281
            FALSE);
1282
    }
1283

1284
    return poDS;
32✔
1285
}
1286

1287
/************************************************************************/
1288
/*                          ECWCreateCopyECW()                          */
1289
/************************************************************************/
1290

1291
GDALDataset *ECWCreateCopyECW(const char *pszFilename, GDALDataset *poSrcDS,
21✔
1292
                              int bStrict, char **papszOptions,
1293
                              GDALProgressFunc pfnProgress, void *pProgressData)
1294

1295
{
1296
    int nBands = poSrcDS->GetRasterCount();
21✔
1297
    if (nBands == 0)
21✔
1298
    {
1299
        CPLError(
1✔
1300
            CE_Failure, CPLE_NotSupported,
1301
            "ECW driver does not support source dataset with zero band.\n");
1302
        return nullptr;
1✔
1303
    }
1304

1305
    if (!EQUAL(CPLGetExtensionSafe(pszFilename).c_str(), "ecw"))
20✔
1306
    {
1307
        CPLError(CE_Failure, CPLE_AppDefined,
×
1308
                 "ECW driver does not support creating ECW files\n"
1309
                 "with an extension other than .ecw");
1310
        return nullptr;
×
1311
    }
1312

1313
#if ECWSDK_VERSION >= 50
1314
    bool bECWV3 = false;
1315
    const char *pszOption =
1316
        CSLFetchNameValue(papszOptions, "ECW_FORMAT_VERSION");
1317
    if (pszOption != nullptr)
1318
    {
1319
        bECWV3 = (3 == atoi(pszOption));
1320
    }
1321

1322
#endif
1323

1324
    GDALDataType eDataType = poSrcDS->GetRasterBand(1)->GetRasterDataType();
20✔
1325
    if (eDataType != GDT_Byte
20✔
1326
#if ECWSDK_VERSION >= 50
1327
        && !(bECWV3 && (eDataType == GDT_UInt16))
1328
#endif
1329
        && bStrict)
10✔
1330
    {
1331
#if ECWSDK_VERSION >= 50
1332
        if (eDataType == GDT_UInt16)
1333
        {
1334
            CPLError(CE_Failure, CPLE_NotSupported,
1335
                     "ECW v2 does not support UInt16 data type. Consider "
1336
                     " specifying ECW_FORMAT_VERSION=3 for full UInt16 support "
1337
                     "available in ECW v3. \n");
1338
        }
1339
        else
1340
#endif
1341
        {
1342
            CPLError(CE_Failure, CPLE_NotSupported,
10✔
1343
                     "ECW driver doesn't support data type %s. "
1344
                     "Only unsigned eight "
1345
#if ECWSDK_VERSION >= 50
1346
                     "or sixteen "
1347
#endif
1348
                     "bit bands supported. \n",
1349
                     GDALGetDataTypeName(eDataType));
1350
        }
1351

1352
        return nullptr;
10✔
1353
    }
1354

1355
    if (poSrcDS->GetRasterXSize() < 128 || poSrcDS->GetRasterYSize() < 128)
10✔
1356
    {
1357
        CPLError(CE_Failure, CPLE_NotSupported,
5✔
1358
                 "ECW driver requires image to be at least 128x128,\n"
1359
                 "the source image is %dx%d.\n",
1360
                 poSrcDS->GetRasterXSize(), poSrcDS->GetRasterYSize());
1361

1362
        return nullptr;
5✔
1363
    }
1364

1365
    if (poSrcDS->GetRasterBand(1)->GetColorTable() != nullptr)
5✔
1366
    {
1367
        CPLError((bStrict) ? CE_Failure : CE_Warning, CPLE_NotSupported,
×
1368
                 "ECW driver ignores color table. "
1369
                 "The source raster band will be considered as grey level.\n"
1370
                 "Consider using color table expansion (-expand option in "
1371
                 "gdal_translate)\n");
1372
        if (bStrict)
×
1373
            return nullptr;
×
1374
    }
1375

1376
    return ECWCreateCopy(pszFilename, poSrcDS, bStrict, papszOptions,
5✔
1377
                         pfnProgress, pProgressData, FALSE);
5✔
1378
}
1379

1380
/************************************************************************/
1381
/*                       ECWCreateCopyJPEG2000()                        */
1382
/************************************************************************/
1383

1384
GDALDataset *ECWCreateCopyJPEG2000(const char *pszFilename,
37✔
1385
                                   GDALDataset *poSrcDS, int bStrict,
1386
                                   char **papszOptions,
1387
                                   GDALProgressFunc pfnProgress,
1388
                                   void *pProgressData)
1389

1390
{
1391
    int nBands = poSrcDS->GetRasterCount();
37✔
1392
    if (nBands == 0)
37✔
1393
    {
1394
        CPLError(
1✔
1395
            CE_Failure, CPLE_NotSupported,
1396
            "JP2ECW driver does not support source dataset with zero band.\n");
1397
        return nullptr;
1✔
1398
    }
1399

1400
    if (EQUAL(CPLGetExtensionSafe(pszFilename).c_str(), "ecw"))
36✔
1401
    {
1402
        CPLError(CE_Failure, CPLE_AppDefined,
×
1403
                 "JP2ECW driver does not support creating JPEG2000 files\n"
1404
                 "with a .ecw extension.  Please use anything else.");
1405
        return nullptr;
×
1406
    }
1407

1408
    GDALDataType eDataType = poSrcDS->GetRasterBand(1)->GetRasterDataType();
36✔
1409
    if (eDataType != GDT_Byte && eDataType != GDT_Int16 &&
36✔
1410
        eDataType != GDT_UInt16 && eDataType != GDT_Int32 &&
8✔
1411
        eDataType != GDT_UInt32 && eDataType != GDT_Float32
6✔
1412
#if ECWSDK_VERSION >= 40
1413
        && eDataType != GDT_Float64
1414
#endif
1415
        && bStrict)
5✔
1416
    {
1417
        CPLError(CE_Failure, CPLE_NotSupported,
5✔
1418
                 "JP2ECW driver doesn't support data type %s. ",
1419
                 GDALGetDataTypeName(eDataType));
1420

1421
        return nullptr;
5✔
1422
    }
1423

1424
    if (poSrcDS->GetRasterBand(1)->GetColorTable() != nullptr)
31✔
1425
    {
1426
        CPLError((bStrict) ? CE_Failure : CE_Warning, CPLE_NotSupported,
×
1427
                 "JP2ECW driver ignores color table. "
1428
                 "The source raster band will be considered as grey level.\n"
1429
                 "Consider using color table expansion (-expand option in "
1430
                 "gdal_translate)\n");
1431
        if (bStrict)
×
1432
            return nullptr;
×
1433
    }
1434

1435
    return ECWCreateCopy(pszFilename, poSrcDS, bStrict, papszOptions,
31✔
1436
                         pfnProgress, pProgressData, TRUE);
31✔
1437
}
1438

1439
/************************************************************************/
1440
/************************************************************************
1441

1442
               ECW/JPEG200 Create() Support
1443
               ----------------------------
1444

1445
  The remainder of the file is code to implement the Create() method.
1446
  New dataset and raster band classes are defined specifically for the
1447
  purpose of being write-only.  In particular, you cannot read back data
1448
  from these datasets, and writing must occur in a pretty specific order.
1449

1450
  That is, you need to write all metadata (projection, georef, etc) first
1451
  and then write the image data.  All bands data for the first scanline
1452
  should be written followed by all bands for the second scanline and so on.
1453

1454
  Creation supports the same virtual subfile names as CreateCopy() supports.
1455

1456
 ************************************************************************/
1457
/************************************************************************/
1458

1459
/************************************************************************/
1460
/* ==================================================================== */
1461
/*                              ECWWriteDataset                         */
1462
/* ==================================================================== */
1463
/************************************************************************/
1464

1465
class ECWWriteRasterBand;
1466

1467
#ifdef OPTIMIZED_FOR_GDALWARP
1468
class IRasterIORequest
1469
{
1470
  public:
1471
    GDALRasterBand *poBand;
1472
    int nXOff;
1473
    int nYOff;
1474
    int nXSize;
1475
    int nYSize;
1476
    GByte *pabyData;
1477
    int nBufXSize;
1478
    int nBufYSize;
1479

1480
    IRasterIORequest(GDALRasterBand *poBandIn, int nXOffIn, int nYOffIn,
×
1481
                     int nXSizeIn, int nYSizeIn, void *pData, int nBufXSizeIn,
1482
                     int nBufYSizeIn, GDALDataType eBufType,
1483
                     GSpacing nPixelSpace, GSpacing nLineSpace)
1484
        : poBand(poBandIn), nXOff(nXOffIn), nYOff(nYOffIn), nXSize(nXSizeIn),
×
1485
          nYSize(nYSizeIn), pabyData(nullptr), nBufXSize(nBufXSizeIn),
1486
          nBufYSize(nBufYSizeIn)
×
1487
    {
1488
        GDALDataType eDataType = poBand->GetRasterDataType();
×
1489
        const int nDataTypeSize = GDALGetDataTypeSizeBytes(eDataType);
×
1490
        pabyData = (GByte *)CPLMalloc(static_cast<size_t>(nBufXSize) *
×
1491
                                      nBufYSize * nDataTypeSize);
×
1492
        for (int iY = 0; iY < nBufYSize; iY++)
×
1493
        {
1494
            GDALCopyWords((GByte *)pData + iY * nLineSpace, eBufType,
×
1495
                          static_cast<int>(nPixelSpace),
1496
                          pabyData + static_cast<size_t>(iY) * nBufXSize *
×
1497
                                         nDataTypeSize,
×
1498
                          eDataType, nDataTypeSize, nBufXSize);
1499
        }
1500
    }
×
1501

1502
    ~IRasterIORequest()
×
1503
    {
×
1504
        CPLFree(pabyData);
×
1505
    }
×
1506
};
1507
#endif
1508

1509
class ECWWriteDataset final : public GDALDataset
1510
{
1511
    friend class ECWWriteRasterBand;
1512

1513
    char *pszFilename;
1514

1515
    int bIsJPEG2000;
1516
    GDALDataType eDataType;
1517
    char **papszOptions;
1518

1519
    OGRSpatialReference m_oSRS{};
1520
    GDALGeoTransform m_gt{};
1521

1522
    GDALECWCompressor oCompressor;
1523
    int bCrystalized;  // TODO: Spelling.
1524

1525
    int nLoadedLine;
1526
    GByte *pabyBILBuffer;
1527

1528
    int bOutOfOrderWriteOccurred;
1529
#ifdef OPTIMIZED_FOR_GDALWARP
1530
    int nPrevIRasterIOBand;
1531
#endif
1532

1533
    CPLErr Crystalize();  // TODO: Spelling.
1534
    CPLErr FlushLine();
1535

1536
  public:
1537
    ECWWriteDataset(const char *, int, int, int, GDALDataType,
1538
                    char **papszOptions, int);
1539
    ~ECWWriteDataset();
1540

1541
    virtual CPLErr FlushCache(bool bAtClosing) override;
1542

1543
    virtual CPLErr GetGeoTransform(GDALGeoTransform &gt) const override;
1544
    virtual CPLErr SetGeoTransform(const GDALGeoTransform &gt) override;
1545
    const OGRSpatialReference *GetSpatialRef() const override;
1546
    CPLErr SetSpatialRef(const OGRSpatialReference *poSRS) override;
1547

1548
#ifdef OPTIMIZED_FOR_GDALWARP
1549
    virtual CPLErr IRasterIO(GDALRWFlag eRWFlag, int nXOff, int nYOff,
1550
                             int nXSize, int nYSize, void *pData, int nBufXSize,
1551
                             int nBufYSize, GDALDataType eBufType,
1552
                             int nBandCount, BANDMAP_TYPE panBandMap,
1553
                             GSpacing nPixelSpace, GSpacing nLineSpace,
1554
                             GSpacing nBandSpace,
1555
                             GDALRasterIOExtraArg *psExtraArg) override;
1556
#endif
1557
};
1558

1559
/************************************************************************/
1560
/* ==================================================================== */
1561
/*                         ECWWriteRasterBand                           */
1562
/* ==================================================================== */
1563
/************************************************************************/
1564

1565
class ECWWriteRasterBand final : public GDALRasterBand
1566
{
1567
    friend class ECWWriteDataset;
1568

1569
    // NOTE: poDS may be altered for NITF/JPEG2000 files!
1570
    ECWWriteDataset *poGDS;
1571

1572
    GDALColorInterp eInterp;
1573

1574
#ifdef OPTIMIZED_FOR_GDALWARP
1575
    IRasterIORequest *poIORequest;
1576
#endif
1577

1578
  public:
1579
    ECWWriteRasterBand(ECWWriteDataset *, int);
1580
    ~ECWWriteRasterBand();
1581

1582
    virtual CPLErr SetColorInterpretation(GDALColorInterp eInterpIn) override
6✔
1583
    {
1584
        eInterp = eInterpIn;
6✔
1585
        if (strlen(GetDescription()) == 0)
6✔
1586
            SetDescription(ECWGetColorInterpretationName(eInterp, nBand - 1));
3✔
1587
        return CE_None;
6✔
1588
    }
1589

1590
    virtual GDALColorInterp GetColorInterpretation() override
6✔
1591
    {
1592
        return eInterp;
6✔
1593
    }
1594

1595
    virtual CPLErr IReadBlock(int, int, void *) override;
1596
    virtual CPLErr IWriteBlock(int, int, void *) override;
1597

1598
#ifdef OPTIMIZED_FOR_GDALWARP
1599
    virtual CPLErr IRasterIO(GDALRWFlag eRWFlag, int nXOff, int nYOff,
1600
                             int nXSize, int nYSize, void *pData, int nBufXSize,
1601
                             int nBufYSize, GDALDataType eBufType,
1602
                             GSpacing nPixelSpace, GSpacing nLineSpace,
1603
                             GDALRasterIOExtraArg *psExtraArg) override;
1604
#endif
1605
};
1606

1607
/************************************************************************/
1608
/*                          ECWWriteDataset()                           */
1609
/************************************************************************/
1610

1611
ECWWriteDataset::ECWWriteDataset(const char *pszFilenameIn, int nXSize,
33✔
1612
                                 int nYSize, int nBandCount, GDALDataType eType,
1613
                                 char **papszOptionsIn, int bIsJPEG2000In)
33✔
1614

1615
{
1616
    bCrystalized = FALSE;
33✔
1617
    pabyBILBuffer = nullptr;
33✔
1618
    nLoadedLine = -1;
33✔
1619

1620
    eAccess = GA_Update;
33✔
1621

1622
    this->bIsJPEG2000 = bIsJPEG2000In;
33✔
1623
    this->eDataType = eType;
33✔
1624
    this->papszOptions = CSLDuplicate(papszOptionsIn);
33✔
1625
    this->pszFilename = CPLStrdup(pszFilenameIn);
33✔
1626

1627
    nRasterXSize = nXSize;
33✔
1628
    nRasterYSize = nYSize;
33✔
1629

1630
    // create band objects.
1631
    for (int iBand = 1; iBand <= nBandCount; iBand++)
104✔
1632
    {
1633
        SetBand(iBand, new ECWWriteRasterBand(this, iBand));
71✔
1634
    }
1635

1636
    bOutOfOrderWriteOccurred = FALSE;
33✔
1637
#ifdef OPTIMIZED_FOR_GDALWARP
1638
    nPrevIRasterIOBand = -1;
33✔
1639
#endif
1640
}
33✔
1641

1642
/************************************************************************/
1643
/*                          ~ECWWriteDataset()                          */
1644
/************************************************************************/
1645

1646
ECWWriteDataset::~ECWWriteDataset()
66✔
1647

1648
{
1649
    ECWWriteDataset::FlushCache(true);
33✔
1650

1651
    if (bCrystalized)
33✔
1652
    {
1653
        if (bOutOfOrderWriteOccurred)
2✔
1654
        {
1655
            /* Otherwise there's a hang-up in the destruction of the oCompressor
1656
             * object */
1657
            while (nLoadedLine < nRasterYSize - 1)
×
1658
                FlushLine();
×
1659
        }
1660
        if (nLoadedLine == nRasterYSize - 1)
2✔
1661
            FlushLine();
2✔
1662
        oCompressor.CloseDown();
2✔
1663
    }
1664

1665
    CPLFree(pabyBILBuffer);
33✔
1666
    CSLDestroy(papszOptions);
33✔
1667
    CPLFree(pszFilename);
33✔
1668
}
66✔
1669

1670
/************************************************************************/
1671
/*                             FlushCache()                             */
1672
/************************************************************************/
1673

1674
CPLErr ECWWriteDataset::FlushCache(bool bAtClosing)
35✔
1675

1676
{
1677
    return BlockBasedFlushCache(bAtClosing);
35✔
1678
}
1679

1680
/************************************************************************/
1681
/*                         GetSpatialRef()                              */
1682
/************************************************************************/
1683

1684
const OGRSpatialReference *ECWWriteDataset::GetSpatialRef() const
×
1685
{
1686
    return m_oSRS.IsEmpty() ? nullptr : &m_oSRS;
×
1687
}
1688

1689
/************************************************************************/
1690
/*                          GetGeoTransform()                           */
1691
/************************************************************************/
1692

1693
CPLErr ECWWriteDataset::GetGeoTransform(GDALGeoTransform &gt) const
31✔
1694

1695
{
1696
    gt = m_gt;
31✔
1697
    return CE_None;
31✔
1698
}
1699

1700
/************************************************************************/
1701
/*                          SetGeoTransform()                           */
1702
/************************************************************************/
1703

1704
CPLErr ECWWriteDataset::SetGeoTransform(const GDALGeoTransform &gt)
32✔
1705

1706
{
1707
    m_gt = gt;
32✔
1708
    return CE_None;
32✔
1709
}
1710

1711
/************************************************************************/
1712
/*                           SetSpatialRef()                            */
1713
/************************************************************************/
1714

1715
CPLErr ECWWriteDataset::SetSpatialRef(const OGRSpatialReference *poSRS)
32✔
1716

1717
{
1718
    m_oSRS.Clear();
32✔
1719
    if (poSRS)
32✔
1720
        m_oSRS = *poSRS;
32✔
1721

1722
    return CE_None;
32✔
1723
}
1724

1725
/************************************************************************/
1726
/*                             Crystalize()                             */
1727
/************************************************************************/
1728

1729
CPLErr ECWWriteDataset::Crystalize()
2✔
1730

1731
{
1732
    const int nWordSize = GDALGetDataTypeSizeBytes(eDataType);
2✔
1733

1734
    CPLErr eErr;
1735

1736
    if (bCrystalized)
2✔
1737
        return CE_None;
×
1738

1739
    const char **paszBandDescriptions =
1740
        (const char **)CPLMalloc(nBands * sizeof(char *));
2✔
1741
    for (int i = 0; i < nBands; i++)
6✔
1742
    {
1743
        paszBandDescriptions[i] = GetRasterBand(i + 1)->GetDescription();
4✔
1744
    }
1745

1746
    int bRGBColorSpace = ECWIsInputRGBColorSpace(this);
2✔
1747

1748
    eErr = oCompressor.Initialize(pszFilename, papszOptions, nRasterXSize,
4✔
1749
                                  nRasterYSize, nBands, paszBandDescriptions,
1750
                                  bRGBColorSpace, eDataType, &m_oSRS, m_gt, 0,
2✔
1751
                                  nullptr, bIsJPEG2000, FALSE, nullptr);
1752

1753
    if (eErr == CE_None)
2✔
1754
        bCrystalized = TRUE;
2✔
1755

1756
    nLoadedLine = -1;
2✔
1757
    pabyBILBuffer = (GByte *)CPLMalloc(static_cast<size_t>(nWordSize) * nBands *
4✔
1758
                                       nRasterXSize);
2✔
1759

1760
    CPLFree(paszBandDescriptions);
2✔
1761

1762
    return eErr;
2✔
1763
}
1764

1765
/************************************************************************/
1766
/*                             FlushLine()                              */
1767
/************************************************************************/
1768

1769
CPLErr ECWWriteDataset::FlushLine()
202✔
1770

1771
{
1772
    const int nWordSize = GDALGetDataTypeSizeBytes(eDataType);
202✔
1773
    CPLErr eErr;
1774

1775
    /* -------------------------------------------------------------------- */
1776
    /*      Crystallize if not already done.                                */
1777
    /* -------------------------------------------------------------------- */
1778
    if (!bCrystalized)
202✔
1779
    {
1780
        eErr = Crystalize();
2✔
1781

1782
        if (eErr != CE_None)
2✔
1783
            return eErr;
×
1784
    }
1785

1786
    /* -------------------------------------------------------------------- */
1787
    /*      Write out the currently loaded line.                            */
1788
    /* -------------------------------------------------------------------- */
1789
    if (nLoadedLine != -1)
202✔
1790
    {
1791

1792
        void **papOutputLine = (void **)CPLMalloc(sizeof(void *) * nBands);
200✔
1793
        for (int i = 0; i < nBands; i++)
600✔
1794
            papOutputLine[i] =
400✔
1795
                (void *)(pabyBILBuffer +
400✔
1796
                         static_cast<size_t>(i) * nWordSize * nRasterXSize);
400✔
1797

1798
        eErr = oCompressor.ourWriteLineBIL((UINT16)nBands, papOutputLine);
200✔
1799
        CPLFree(papOutputLine);
200✔
1800
        if (eErr != CE_None)
200✔
1801
        {
1802
            return eErr;
×
1803
        }
1804
    }
1805

1806
    /* -------------------------------------------------------------------- */
1807
    /*      Clear the buffer and increment the "current line" indicator.    */
1808
    /* -------------------------------------------------------------------- */
1809
    memset(pabyBILBuffer, 0,
202✔
1810
           static_cast<size_t>(nWordSize) * nRasterXSize * nBands);
202✔
1811
    nLoadedLine++;
202✔
1812

1813
    return CE_None;
202✔
1814
}
1815

1816
#ifdef OPTIMIZED_FOR_GDALWARP
1817
/************************************************************************/
1818
/*                             IRasterIO()                              */
1819
/************************************************************************/
1820

1821
CPLErr ECWWriteDataset::IRasterIO(GDALRWFlag eRWFlag, int nXOff, int nYOff,
200✔
1822
                                  int nXSize, int nYSize, void *pData,
1823
                                  int nBufXSize, int nBufYSize,
1824
                                  GDALDataType eBufType, int nBandCount,
1825
                                  BANDMAP_TYPE panBandMap, GSpacing nPixelSpace,
1826
                                  GSpacing nLineSpace, GSpacing nBandSpace,
1827
                                  GDALRasterIOExtraArg *psExtraArg)
1828
{
1829
    ECWWriteRasterBand *po4thBand = nullptr;
200✔
1830
    IRasterIORequest *poIORequest = nullptr;
200✔
1831

1832
    if (bOutOfOrderWriteOccurred)
200✔
1833
        return CE_Failure;
×
1834

1835
    if (eRWFlag == GF_Write && nBandCount == 3 && nBands == 4)
200✔
1836
    {
1837
        po4thBand = cpl::down_cast<ECWWriteRasterBand *>(GetRasterBand(4));
×
1838
        poIORequest = po4thBand->poIORequest;
×
1839
        if (poIORequest != nullptr)
×
1840
        {
1841
            if (nXOff != poIORequest->nXOff || nYOff != poIORequest->nYOff ||
×
1842
                nXSize != poIORequest->nXSize ||
×
1843
                nYSize != poIORequest->nYSize ||
×
1844
                nBufXSize != poIORequest->nBufXSize ||
×
1845
                nBufYSize != poIORequest->nBufYSize)
×
1846
            {
1847
                CPLError(CE_Failure, CPLE_AppDefined, "Out of order write");
×
1848
                bOutOfOrderWriteOccurred = TRUE;
×
1849
                return CE_Failure;
×
1850
            }
1851
        }
1852
    }
1853

1854
    const int nDataTypeSize = GDALGetDataTypeSizeBytes(eDataType);
200✔
1855
    if (eRWFlag == GF_Write && nXOff == 0 && nXSize == nRasterXSize &&
200✔
1856
        nBufXSize == nXSize && nBufYSize == nYSize && eBufType == eDataType &&
200✔
1857
        (nBandCount == nBands ||
100✔
1858
         (nBandCount == 3 && poIORequest != nullptr && nBands == 4)) &&
×
1859
        nPixelSpace == nDataTypeSize &&
100✔
1860
        nLineSpace == nPixelSpace * nRasterXSize)
100✔
1861
    {
1862
        CPLErr eErr = CE_None;
100✔
1863
        GByte *pabyData = (GByte *)pData;
100✔
1864
        for (int iY = 0; iY < nYSize; iY++)
200✔
1865
        {
1866
            for (int iBand = 0; iBand < nBandCount && eErr == CE_None; iBand++)
200✔
1867
            {
1868
                eErr = GetRasterBand(panBandMap[iBand])
100✔
1869
                           ->WriteBlock(0, iY + nYOff,
200✔
1870
                                        pabyData + iY * nLineSpace +
100✔
1871
                                            iBand * nBandSpace);
100✔
1872
            }
1873

1874
            if (poIORequest != nullptr && eErr == CE_None)
100✔
1875
            {
1876
                eErr = po4thBand->WriteBlock(0, iY + nYOff,
×
1877
                                             poIORequest->pabyData +
×
1878
                                                 iY * nDataTypeSize * nXSize);
×
1879
            }
1880
        }
1881

1882
        if (poIORequest != nullptr)
100✔
1883
        {
1884
            delete poIORequest;
×
1885
            po4thBand->poIORequest = nullptr;
×
1886
        }
1887

1888
        return eErr;
100✔
1889
    }
1890
    else
1891
        return GDALDataset::IRasterIO(eRWFlag, nXOff, nYOff, nXSize, nYSize,
100✔
1892
                                      pData, nBufXSize, nBufYSize, eBufType,
1893
                                      nBandCount, panBandMap, nPixelSpace,
1894
                                      nLineSpace, nBandSpace, psExtraArg);
100✔
1895
}
1896
#endif
1897

1898
/************************************************************************/
1899
/* ==================================================================== */
1900
/*                          ECWWriteRasterBand                          */
1901
/* ==================================================================== */
1902
/************************************************************************/
1903

1904
/************************************************************************/
1905
/*                         ECWWriteRasterBand()                         */
1906
/************************************************************************/
1907

1908
ECWWriteRasterBand::ECWWriteRasterBand(ECWWriteDataset *poDSIn, int nBandIn)
71✔
1909

1910
{
1911
    nBand = nBandIn;
71✔
1912
    poDS = poDSIn;
71✔
1913
    poGDS = poDSIn;
71✔
1914
    nBlockXSize = poDSIn->GetRasterXSize();
71✔
1915
    nBlockYSize = 1;
71✔
1916
    eDataType = poDSIn->eDataType;
71✔
1917
    eInterp = GCI_Undefined;
71✔
1918
#ifdef OPTIMIZED_FOR_GDALWARP
1919
    poIORequest = nullptr;
71✔
1920
#endif
1921
}
71✔
1922

1923
/************************************************************************/
1924
/*                        ~ECWWriteRasterBand()                         */
1925
/************************************************************************/
1926

1927
ECWWriteRasterBand::~ECWWriteRasterBand()
142✔
1928

1929
{
1930
#ifdef OPTIMIZED_FOR_GDALWARP
1931
    delete poIORequest;
71✔
1932
#endif
1933
}
142✔
1934

1935
/************************************************************************/
1936
/*                             IReadBlock()                             */
1937
/************************************************************************/
1938

1939
CPLErr ECWWriteRasterBand::IReadBlock(CPL_UNUSED int nBlockX,
×
1940
                                      CPL_UNUSED int nBlockY, void *pBuffer)
1941
{
1942
    const int nWordSize = GDALGetDataTypeSizeBytes(eDataType);
×
1943

1944
    // We zero stuff out here, but we can't really read stuff from
1945
    // a write only stream.
1946

1947
    memset(pBuffer, 0, static_cast<size_t>(nBlockXSize) * nWordSize);
×
1948

1949
    return CE_None;
×
1950
}
1951

1952
#ifdef OPTIMIZED_FOR_GDALWARP
1953
/************************************************************************/
1954
/*                             IRasterIO()                              */
1955
/************************************************************************/
1956

1957
CPLErr ECWWriteRasterBand::IRasterIO(GDALRWFlag eRWFlag, int nXOff, int nYOff,
300✔
1958
                                     int nXSize, int nYSize, void *pData,
1959
                                     int nBufXSize, int nBufYSize,
1960
                                     GDALDataType eBufType,
1961
                                     GSpacing nPixelSpace, GSpacing nLineSpace,
1962
                                     GDALRasterIOExtraArg *psExtraArg)
1963
{
1964
    if (eRWFlag == GF_Write && nBand == 4 && poGDS->nBands == 4 &&
300✔
1965
        poGDS->nPrevIRasterIOBand < 0)
×
1966
    {
1967
        /* Triggered when gdalwarp outputs an alpha band */
1968
        /* It is called before GDALDatasetRasterIO() on the 3 first bands */
1969
        if (poIORequest != nullptr)
×
1970
            return CE_Failure;
×
1971
        poIORequest = new IRasterIORequest(this, nXOff, nYOff, nXSize, nYSize,
×
1972
                                           pData, nBufXSize, nBufYSize,
1973
                                           eBufType, nPixelSpace, nLineSpace);
×
1974
        return CE_None;
×
1975
    }
1976

1977
    poGDS->nPrevIRasterIOBand = nBand;
300✔
1978
    return GDALRasterBand::IRasterIO(eRWFlag, nXOff, nYOff, nXSize, nYSize,
300✔
1979
                                     pData, nBufXSize, nBufYSize, eBufType,
1980
                                     nPixelSpace, nLineSpace, psExtraArg);
300✔
1981
}
1982
#endif
1983

1984
/************************************************************************/
1985
/*                            IWriteBlock()                             */
1986
/************************************************************************/
1987

1988
CPLErr ECWWriteRasterBand::IWriteBlock(CPL_UNUSED int nBlockX, int nBlockY,
400✔
1989
                                       void *pBuffer)
1990
{
1991
    const int nWordSize = GDALGetDataTypeSizeBytes(eDataType);
400✔
1992
    CPLErr eErr;
1993

1994
    if (poGDS->bOutOfOrderWriteOccurred)
400✔
1995
        return CE_Failure;
×
1996

1997
    /* -------------------------------------------------------------------- */
1998
    /*      Flush previous line if needed.                                  */
1999
    /* -------------------------------------------------------------------- */
2000
    if (nBlockY == poGDS->nLoadedLine + 1)
400✔
2001
    {
2002
        eErr = poGDS->FlushLine();
200✔
2003
        if (eErr != CE_None)
200✔
2004
            return eErr;
×
2005
    }
2006

2007
    /* -------------------------------------------------------------------- */
2008
    /*      Blow a gasket if we have been asked to write something out      */
2009
    /*      of order.                                                       */
2010
    /* -------------------------------------------------------------------- */
2011
    if (nBlockY != poGDS->nLoadedLine)
400✔
2012
    {
2013
        CPLError(CE_Failure, CPLE_AppDefined,
×
2014
                 "Apparent attempt to write to ECW non-sequentially.\n"
2015
                 "Loaded line is %d, but %d of band %d was written to.",
2016
                 poGDS->nLoadedLine, nBlockY, nBand);
×
2017
        poGDS->bOutOfOrderWriteOccurred = TRUE;
×
2018
        return CE_Failure;
×
2019
    }
2020

2021
    /* -------------------------------------------------------------------- */
2022
    /*      Copy passed data into current line buffer.                      */
2023
    /* -------------------------------------------------------------------- */
2024
    memcpy(poGDS->pabyBILBuffer + (nBand - 1) * nWordSize * nRasterXSize,
400✔
2025
           pBuffer, nWordSize * nRasterXSize);
400✔
2026

2027
    return CE_None;
400✔
2028
}
2029

2030
/************************************************************************/
2031
/*                         ECWCreateJPEG2000()                          */
2032
/************************************************************************/
2033

2034
GDALDataset *ECWCreateJPEG2000(const char *pszFilename, int nXSize, int nYSize,
51✔
2035
                               int nBands, GDALDataType eType,
2036
                               char **papszOptions)
2037

2038
{
2039
    if (nBands == 0)
51✔
2040
    {
2041
        CPLError(CE_Failure, CPLE_NotSupported, "0 band not supported");
18✔
2042
        return nullptr;
18✔
2043
    }
2044
    ECWInitialize();
33✔
2045

2046
    return new ECWWriteDataset(pszFilename, nXSize, nYSize, nBands, eType,
2047
                               papszOptions, TRUE);
33✔
2048
}
2049

2050
/************************************************************************/
2051
/*                            ECWCreateECW()                            */
2052
/************************************************************************/
2053

2054
GDALDataset *ECWCreateECW(const char *pszFilename, int nXSize, int nYSize,
×
2055
                          int nBands, GDALDataType eType, char **papszOptions)
2056

2057
{
2058
    if (nBands == 0)
×
2059
    {
2060
        CPLError(CE_Failure, CPLE_NotSupported, "0 band not supported");
×
2061
        return nullptr;
×
2062
    }
2063
    ECWInitialize();
×
2064

2065
    return new ECWWriteDataset(pszFilename, nXSize, nYSize, nBands, eType,
2066
                               papszOptions, FALSE);
×
2067
}
2068

2069
#endif /* def HAVE_COMPRESS */
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