• 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

86.56
/frmts/opjlike/jp2opjlikedataset.cpp
1
/******************************************************************************
2
 *
3
 * Project:  JPEG2000 driver based on OpenJPEG or Grok library
4
 * Purpose:  JPEG2000 driver based on OpenJPEG or Grok library
5
 * Authors:  Even Rouault, <even dot rouault at spatialys dot com>
6
 *           Aaron Boxer, <boxerab at protonmail dot com>
7
 *
8
 ******************************************************************************
9
 * Copyright (c) 2010-2014, Even Rouault <even dot rouault at spatialys dot com>
10
 * Copyright (c) 2015, European Union (European Environment Agency)
11
 * Copyright (c) 2023, Grok Image Compression Inc.
12
 *
13
 * SPDX-License-Identifier: MIT
14
 ****************************************************************************/
15

16
#include <cassert>
17
#include <vector>
18

19
#include "cpl_atomic_ops.h"
20
#include "cpl_multiproc.h"
21
#include "cpl_string.h"
22
#include "cpl_worker_thread_pool.h"
23
#include "gdal_frmts.h"
24
#include "gdaljp2abstractdataset.h"
25
#include "gdaljp2metadata.h"
26
#include "vrt/vrtdataset.h"
27

28
#include <algorithm>
29

30
#include "jp2opjlikedataset.h"
31

32
JP2DatasetBase::~JP2DatasetBase() = default;
33

34
/************************************************************************/
35
/*                        JP2OPJLikeRasterBand()                        */
36
/************************************************************************/
37

38
template <typename CODEC, typename BASE>
39
JP2OPJLikeRasterBand<CODEC, BASE>::JP2OPJLikeRasterBand(
1,146✔
40
    JP2OPJLikeDataset<CODEC, BASE> *poDSIn, int nBandIn,
41
    GDALDataType eDataTypeIn, int nBits, int bPromoteTo8BitIn,
42
    int nBlockXSizeIn, int nBlockYSizeIn)
1,146✔
43

44
{
45
    this->eDataType = eDataTypeIn;
1,146✔
46
    this->nBlockXSize = nBlockXSizeIn;
1,146✔
47
    this->nBlockYSize = nBlockYSizeIn;
1,146✔
48
    this->bPromoteTo8Bit = bPromoteTo8BitIn;
1,146✔
49
    poCT = nullptr;
1,146✔
50

51
    if ((nBits % 8) != 0)
1,146✔
52
        GDALRasterBand::SetMetadataItem(
41✔
53
            "NBITS", CPLString().Printf("%d", nBits), "IMAGE_STRUCTURE");
82✔
54
    GDALRasterBand::SetMetadataItem("COMPRESSION", "JPEG2000",
1,146✔
55
                                    "IMAGE_STRUCTURE");
56
    this->poDS = poDSIn;
1,146✔
57
    this->nBand = nBandIn;
1,146✔
58
}
1,146✔
59

60
template <typename CODEC, typename BASE>
61
GDALColorTable *JP2OPJLikeRasterBand<CODEC, BASE>::GetColorTable()
50✔
62
{
63
    return poCT;
50✔
64
}
65

66
template <typename CODEC, typename BASE>
67
int JP2OPJLikeRasterBand<CODEC, BASE>::HasArbitraryOverviews()
×
68
{
69
    return poCT == nullptr;
×
70
}
71

72
/************************************************************************/
73
/*                      ~JP2OPJLikeRasterBand()                         */
74
/************************************************************************/
75

76
template <typename CODEC, typename BASE>
77
JP2OPJLikeRasterBand<CODEC, BASE>::~JP2OPJLikeRasterBand()
2,292✔
78
{
79
    delete poCT;
24✔
80
}
2,316✔
81

82
/************************************************************************/
83
/*                            CLAMP_0_255()                             */
84
/************************************************************************/
85

86
static CPL_INLINE GByte CLAMP_0_255(int val)
134,000✔
87
{
88
    return static_cast<GByte>(std::clamp(val, 0, 255));
134,000✔
89
}
90

91
/************************************************************************/
92
/*                             IReadBlock()                             */
93
/************************************************************************/
94

95
template <typename CODEC, typename BASE>
96
CPLErr JP2OPJLikeRasterBand<CODEC, BASE>::IReadBlock(int nBlockXOff,
254✔
97
                                                     int nBlockYOff,
98
                                                     void *pImage)
99
{
100
    auto poGDS = cpl::down_cast<JP2OPJLikeDataset<CODEC, BASE> *>(poDS);
254✔
101

102
#ifdef DEBUG_VERBOSE
103
    int nXOff = nBlockXOff * nBlockXSize;
104
    int nYOff = nBlockYOff * nBlockYSize;
105
    int nXSize = std::min(nBlockXSize, nRasterXSize - nXOff);
106
    int nYSize = std::min(nBlockYSize, nRasterYSize - nYOff);
107
    if (poGDS->iLevel == 0)
108
    {
109
        CPLDebug(CODEC::debugId(),
110
                 "ds.GetRasterBand(%d).ReadRaster(%d,%d,%d,%d)", nBand, nXOff,
111
                 nYOff, nXSize, nYSize);
112
    }
113
    else
114
    {
115
        CPLDebug(CODEC::debugId(),
116
                 "ds.GetRasterBand(%d).GetOverview(%d).ReadRaster(%d,%d,%d,%d)",
117
                 nBand, poGDS->iLevel - 1, nXOff, nYOff, nXSize, nYSize);
118
    }
119
#endif
120

121
    if (poGDS->bEnoughMemoryToLoadOtherBands)
254✔
122
        return poGDS->ReadBlock(nBand, poGDS->fp_, nBlockXOff, nBlockYOff,
123
                                pImage, poGDS->nBands, nullptr);
254✔
124
    else
125
        return poGDS->ReadBlock(nBand, poGDS->fp_, nBlockXOff, nBlockYOff,
126
                                pImage, 1, &nBand);
×
127
}
128

129
/************************************************************************/
130
/*                             IRasterIO()                              */
131
/************************************************************************/
132

133
template <typename CODEC, typename BASE>
134
CPLErr JP2OPJLikeRasterBand<CODEC, BASE>::IRasterIO(
227✔
135
    GDALRWFlag eRWFlag, int nXOff, int nYOff, int nXSize, int nYSize,
136
    void *pData, int nBufXSize, int nBufYSize, GDALDataType eBufType,
137
    GSpacing nPixelSpace, GSpacing nLineSpace, GDALRasterIOExtraArg *psExtraArg)
138
{
139
    auto poGDS = cpl::down_cast<JP2OPJLikeDataset<CODEC, BASE> *>(poDS);
227✔
140

141
    if (eRWFlag != GF_Read)
227✔
142
        return CE_Failure;
×
143

144
    /* ==================================================================== */
145
    /*      Do we have overviews that would be appropriate to satisfy       */
146
    /*      this request?                                                   */
147
    /* ==================================================================== */
148
    if ((nBufXSize < nXSize || nBufYSize < nYSize) && GetOverviewCount() > 0)
227✔
149
    {
150
        int bTried;
151
        CPLErr eErr = TryOverviewRasterIO(
1✔
152
            eRWFlag, nXOff, nYOff, nXSize, nYSize, pData, nBufXSize, nBufYSize,
153
            eBufType, nPixelSpace, nLineSpace, psExtraArg, &bTried);
154
        if (bTried)
1✔
155
            return eErr;
1✔
156
    }
157

158
    int nRet =
226✔
159
        poGDS->PreloadBlocks(this, nXOff, nYOff, nXSize, nYSize, 0, nullptr);
160
    if (nRet < 0)
226✔
161
        return CE_Failure;
×
162
    poGDS->bEnoughMemoryToLoadOtherBands = nRet;
226✔
163

164
    CPLErr eErr = GDALPamRasterBand::IRasterIO(
226✔
165
        eRWFlag, nXOff, nYOff, nXSize, nYSize, pData, nBufXSize, nBufYSize,
166
        eBufType, nPixelSpace, nLineSpace, psExtraArg);
167

168
    // cppcheck-suppress redundantAssignment
169
    poGDS->bEnoughMemoryToLoadOtherBands = TRUE;
226✔
170
    return eErr;
226✔
171
}
172

173
template <typename CODEC, typename BASE> struct JP2JobStruct
174
{
175
  public:
176
    JP2OPJLikeDataset<CODEC, BASE> *poGDS_ = nullptr;
177
    int nBand = 0;
178
    std::vector<std::pair<int, int>> oPairs{};
179
    volatile int nCurPair = 0;
180
    int nBandCount = 0;
181
    const int *panBandMap = nullptr;
182
    volatile bool bSuccess = false;
183
};
184

185
/************************************************************************/
186
/*                   GetFileHandle()                                    */
187
/************************************************************************/
188

189
template <typename CODEC, typename BASE>
190
VSILFILE *JP2OPJLikeDataset<CODEC, BASE>::GetFileHandle()
8✔
191
{
192
    return this->fp_;
8✔
193
}
194

195
/************************************************************************/
196
/*                   ReadBlockInThread()                                */
197
/************************************************************************/
198

199
template <typename CODEC, typename BASE>
200
void JP2OPJLikeDataset<CODEC, BASE>::ReadBlockInThread(void *userdata)
17✔
201
{
202
    int nPair;
203
    auto poJob = static_cast<JP2JobStruct<CODEC, BASE> *>(userdata);
17✔
204

205
    JP2OPJLikeDataset *poGDS = poJob->poGDS_;
17✔
206
    const int nBand = poJob->nBand;
17✔
207
    const int nPairs = static_cast<int>(poJob->oPairs.size());
17✔
208
    const int nBandCount = poJob->nBandCount;
17✔
209
    const int *panBandMap = poJob->panBandMap;
17✔
210
    VSILFILE *fp = VSIFOpenL(poGDS->m_osFilename.c_str(), "rb");
17✔
211
    if (fp == nullptr)
17✔
212
    {
213
        CPLDebug(CODEC::debugId(), "Cannot open %s",
×
214
                 poGDS->m_osFilename.c_str());
215
        poJob->bSuccess = false;
×
216
        // VSIFree(pDummy);
217
        return;
×
218
    }
219

220
    while ((nPair = CPLAtomicInc(&(poJob->nCurPair))) < nPairs &&
289✔
221
           poJob->bSuccess)
136✔
222
    {
223
        int nBlockXOff = poJob->oPairs[nPair].first;
137✔
224
        int nBlockYOff = poJob->oPairs[nPair].second;
136✔
225
        poGDS->AcquireMutex();
136✔
226
        GDALRasterBlock *poBlock =
227
            poGDS->GetRasterBand(nBand)->GetLockedBlockRef(nBlockXOff,
137✔
228
                                                           nBlockYOff, TRUE);
229
        poGDS->ReleaseMutex();
137✔
230
        if (poBlock == nullptr)
137✔
231
        {
232
            poJob->bSuccess = false;
×
233
            break;
×
234
        }
235

236
        void *pDstBuffer = poBlock->GetDataRef();
137✔
237
        if (poGDS->ReadBlock(nBand, fp, nBlockXOff, nBlockYOff, pDstBuffer,
137✔
238
                             nBandCount, panBandMap) != CE_None)
137✔
239
        {
240
            poJob->bSuccess = false;
×
241
        }
242

243
        poBlock->DropLock();
137✔
244
    }
245

246
    VSIFCloseL(fp);
16✔
247
    // VSIFree(pDummy);
248
}
249

250
/************************************************************************/
251
/*                           PreloadBlocks()                            */
252
/************************************************************************/
253

254
template <typename CODEC, typename BASE>
255
int JP2OPJLikeDataset<CODEC, BASE>::PreloadBlocks(
240✔
256
    JP2OPJLikeRasterBand<CODEC, BASE> *poBand, int nXOff, int nYOff, int nXSize,
257
    int nYSize, int nBandCount, const int *panBandMap)
258
{
259
    int bRet = TRUE;
240✔
260
    const int nXStart = nXOff / poBand->nBlockXSize;
240✔
261
    const int nXEnd = (nXOff + nXSize - 1) / poBand->nBlockXSize;
240✔
262
    const int nYStart = nYOff / poBand->nBlockYSize;
240✔
263
    const int nYEnd = (nYOff + nYSize - 1) / poBand->nBlockYSize;
240✔
264
    const GIntBig nReqMem = static_cast<GIntBig>(nXEnd - nXStart + 1) *
480✔
265
                            (nYEnd - nYStart + 1) * poBand->nBlockXSize *
240✔
266
                            poBand->nBlockYSize *
240✔
267
                            GDALGetDataTypeSizeBytes(poBand->eDataType);
240✔
268

269
    const int nMaxThreads = this->GetNumThreads();
240✔
270
    if (!this->bUseSetDecodeArea && nMaxThreads > 1)
240✔
271
    {
272
        if (nReqMem > GDALGetCacheMax64() / (nBandCount == 0 ? 1 : nBandCount))
96✔
273
            return FALSE;
×
274

275
        JP2JobStruct<CODEC, BASE> oJob;
96✔
276
        this->m_nBlocksToLoad = 0;
96✔
277
        try
278
        {
279
            for (int nBlockXOff = nXStart; nBlockXOff <= nXEnd; ++nBlockXOff)
226✔
280
            {
281
                for (int nBlockYOff = nYStart; nBlockYOff <= nYEnd;
484✔
282
                     ++nBlockYOff)
283
                {
284
                    GDALRasterBlock *poBlock =
354✔
285
                        poBand->TryGetLockedBlockRef(nBlockXOff, nBlockYOff);
286
                    if (poBlock != nullptr)
354✔
287
                    {
288
                        poBlock->DropLock();
156✔
289
                        continue;
156✔
290
                    }
291
                    oJob.oPairs.push_back(
198✔
292
                        std::pair<int, int>(nBlockXOff, nBlockYOff));
293
                    this->m_nBlocksToLoad++;
198✔
294
                }
295
            }
296
        }
297
        catch (const std::bad_alloc &)
×
298
        {
299
            CPLError(CE_Failure, CPLE_OutOfMemory, "Out of memory error");
×
300
            this->m_nBlocksToLoad = 0;
×
301
            return -1;
×
302
        }
303

304
        if (this->m_nBlocksToLoad > 1)
96✔
305
        {
306
            const int l_nThreads = std::min(this->m_nBlocksToLoad, nMaxThreads);
5✔
307
            CPLJoinableThread **pahThreads = static_cast<CPLJoinableThread **>(
308
                VSI_CALLOC_VERBOSE(sizeof(CPLJoinableThread *), l_nThreads));
5✔
309
            if (pahThreads == nullptr)
5✔
310
            {
311
                this->m_nBlocksToLoad = 0;
×
312
                return -1;
×
313
            }
314
            int i;
315

316
            CPLDebug(CODEC::debugId(), "%d blocks to load (%d threads)",
5✔
317
                     this->m_nBlocksToLoad, l_nThreads);
318

319
            oJob.poGDS_ = this;
5✔
320
            oJob.nBand = poBand->GetBand();
5✔
321
            oJob.nCurPair = -1;
5✔
322
            if (nBandCount > 0)
5✔
323
            {
324
                oJob.nBandCount = nBandCount;
2✔
325
                oJob.panBandMap = panBandMap;
2✔
326
            }
327
            else
328
            {
329
                if (nReqMem <= GDALGetCacheMax64() / nBands)
3✔
330
                {
331
                    oJob.nBandCount = nBands;
3✔
332
                    oJob.panBandMap = nullptr;
3✔
333
                }
334
                else
335
                {
336
                    bRet = FALSE;
×
337
                    oJob.nBandCount = 1;
×
338
                    oJob.panBandMap = &oJob.nBand;
×
339
                }
340
            }
341
            oJob.bSuccess = true;
5✔
342

343
            /* Flushes all dirty blocks from cache to disk to avoid them */
344
            /* to be flushed randomly, and simultaneously, from our worker
345
             * threads, */
346
            /* which might cause races in the output driver. */
347
            /* This is a workaround to a design defect of the block cache */
348
            GDALRasterBlock::FlushDirtyBlocks();
5✔
349

350
            for (i = 0; i < l_nThreads; i++)
22✔
351
            {
352
                pahThreads[i] =
34✔
353
                    CPLCreateJoinableThread(ReadBlockInThread, &oJob);
17✔
354
                if (pahThreads[i] == nullptr)
17✔
355
                    oJob.bSuccess = false;
×
356
            }
357
            TemporarilyDropReadWriteLock();
5✔
358
            for (i = 0; i < l_nThreads; i++)
22✔
359
                CPLJoinThread(pahThreads[i]);
17✔
360
            ReacquireReadWriteLock();
5✔
361
            CPLFree(pahThreads);
5✔
362
            if (!oJob.bSuccess)
5✔
363
            {
364
                this->m_nBlocksToLoad = 0;
×
365
                return -1;
×
366
            }
367
            this->m_nBlocksToLoad = 0;
5✔
368
        }
369
    }
370

371
    return bRet;
240✔
372
}
373

374
/************************************************************************/
375
/*                      GetEstimatedRAMUsage()                          */
376
/************************************************************************/
377

378
template <typename CODEC, typename BASE>
379
GIntBig JP2OPJLikeDataset<CODEC, BASE>::GetEstimatedRAMUsage()
100✔
380
{
381
    // libopenjp2 holds the code block values in a uint32_t array.
382
    GIntBig nVal = static_cast<GIntBig>(this->m_nTileWidth) *
100✔
383
                   this->m_nTileHeight * this->nBands * sizeof(uint32_t);
100✔
384
    if (this->bSingleTiled)
100✔
385
    {
386
        // libopenjp2 ingests the codestream for a whole tile. So for a
387
        // single-tiled image, this is roughly the size of the file.
388
        const auto nCurPos = VSIFTellL(this->fp_);
100✔
389
        VSIFSeekL(this->fp_, 0, SEEK_END);
100✔
390
        nVal += VSIFTellL(this->fp_);
100✔
391
        VSIFSeekL(this->fp_, nCurPos, SEEK_SET);
100✔
392
    }
393
    CPLDebug(CODEC::debugId(), "Estimated RAM usage for %s: %.2f GB",
100✔
394
             GetDescription(), static_cast<double>(nVal * 1e-9));
100✔
395
    return nVal;
100✔
396
}
397

398
/************************************************************************/
399
/*                             IRasterIO()                              */
400
/************************************************************************/
401

402
template <typename CODEC, typename BASE>
403
CPLErr JP2OPJLikeDataset<CODEC, BASE>::IRasterIO(
15✔
404
    GDALRWFlag eRWFlag, int nXOff, int nYOff, int nXSize, int nYSize,
405
    void *pData, int nBufXSize, int nBufYSize, GDALDataType eBufType,
406
    int nBandCount, BANDMAP_TYPE panBandMap, GSpacing nPixelSpace,
407
    GSpacing nLineSpace, GSpacing nBandSpace, GDALRasterIOExtraArg *psExtraArg)
408
{
409
    if (eRWFlag != GF_Read)
15✔
410
        return CE_Failure;
×
411

412
    if (nBandCount < 1)
15✔
413
        return CE_Failure;
×
414

415
    auto poBand = cpl::down_cast<JP2OPJLikeRasterBand<CODEC, BASE> *>(
15✔
416
        GetRasterBand(panBandMap[0]));
417

418
    /* ==================================================================== */
419
    /*      Do we have overviews that would be appropriate to satisfy       */
420
    /*      this request?                                                   */
421
    /* ==================================================================== */
422

423
    if ((nBufXSize < nXSize || nBufYSize < nYSize) &&
16✔
424
        poBand->GetOverviewCount() > 0)
1✔
425
    {
426
        int bTried;
427
        CPLErr eErr = TryOverviewRasterIO(
1✔
428
            eRWFlag, nXOff, nYOff, nXSize, nYSize, pData, nBufXSize, nBufYSize,
429
            eBufType, nBandCount, panBandMap, nPixelSpace, nLineSpace,
430
            nBandSpace, psExtraArg, &bTried);
431
        if (bTried)
1✔
432
            return eErr;
1✔
433
    }
434

435
    this->bEnoughMemoryToLoadOtherBands = PreloadBlocks(
14✔
436
        poBand, nXOff, nYOff, nXSize, nYSize, nBandCount, panBandMap);
437

438
    CPLErr eErr = GDALPamDataset::IRasterIO(
14✔
439
        eRWFlag, nXOff, nYOff, nXSize, nYSize, pData, nBufXSize, nBufYSize,
440
        eBufType, nBandCount, panBandMap, nPixelSpace, nLineSpace, nBandSpace,
441
        psExtraArg);
442

443
    this->bEnoughMemoryToLoadOtherBands = TRUE;
14✔
444
    return eErr;
14✔
445
}
446

447
/************************************************************************/
448
/*                          IBuildOverviews()                           */
449
/************************************************************************/
450

451
template <typename CODEC, typename BASE>
452
CPLErr JP2OPJLikeDataset<CODEC, BASE>::IBuildOverviews(
3✔
453
    const char *pszResampling, int nOverviews, const int *panOverviewList,
454
    int nListBands, const int *panBandList, GDALProgressFunc pfnProgress,
455
    void *pProgressData, CSLConstList papszOptions)
456

457
{
458
    // In order for building external overviews to work properly, we
459
    // discard any concept of internal overviews when the user
460
    // first requests to build external overviews.
461
    for (int i = 0; i < this->nOverviewCount; i++)
5✔
462
    {
463
        delete papoOverviewDS[i];
2✔
464
    }
465
    CPLFree(papoOverviewDS);
3✔
466
    papoOverviewDS = nullptr;
3✔
467
    this->nOverviewCount = 0;
3✔
468

469
    return GDALPamDataset::IBuildOverviews(
3✔
470
        pszResampling, nOverviews, panOverviewList, nListBands, panBandList,
471
        pfnProgress, pProgressData, papszOptions);
3✔
472
}
473

474
/************************************************************************/
475
/*                             ReadBlock()                              */
476
/************************************************************************/
477

478
template <typename CODEC, typename BASE>
479
CPLErr JP2OPJLikeDataset<CODEC, BASE>::ReadBlock(int nBand, VSILFILE *fpIn,
391✔
480
                                                 int nBlockXOff, int nBlockYOff,
481
                                                 void *pImage, int nBandCount,
482
                                                 const int *panBandMap)
483
{
484
    CPLErr eErr = CE_None;
391✔
485
    CODEC localctx;
×
486

487
    auto poBand = cpl::down_cast<JP2OPJLikeRasterBand<CODEC, BASE> *>(
391✔
488
        GetRasterBand(nBand));
489
    const int nBlockXSize = poBand->nBlockXSize;
391✔
490
    const int nBlockYSize = poBand->nBlockYSize;
391✔
491
    const GDALDataType eDataType = poBand->eDataType;
391✔
492

493
    const int nDataTypeSize = GDALGetDataTypeSizeBytes(eDataType);
391✔
494

495
    const int nTileNumber = nBlockXOff + nBlockYOff * poBand->nBlocksPerRow;
391✔
496
    const int nWidthToRead =
391✔
497
        std::min(nBlockXSize, nRasterXSize - nBlockXOff * nBlockXSize);
391✔
498
    const int nHeightToRead =
391✔
499
        std::min(nBlockYSize, nRasterYSize - nBlockYOff * nBlockYSize);
391✔
500

501
    eErr = this->readBlockInit(fpIn, &localctx, nBlockXOff, nBlockYOff,
391✔
502
                               this->nRasterXSize, this->nRasterYSize,
503
                               nBlockXSize, nBlockYSize, nTileNumber);
504
    if (eErr != CE_None)
391✔
505
        goto end;
×
506

507
    for (unsigned int iBand = 0; iBand < localctx.psImage->numcomps; iBand++)
841✔
508
    {
509
        if (localctx.psImage->comps[iBand].data == nullptr)
450✔
510
        {
511
            CPLError(CE_Failure, CPLE_AppDefined,
×
512
                     "localctx.psImage->comps[%d].data == nullptr", iBand);
513
            eErr = CE_Failure;
×
514
            goto end;
×
515
        }
516
    }
517

518
    for (int xBand = 0; xBand < nBandCount; xBand++)
841✔
519
    {
520
        GDALRasterBlock *poBlock = nullptr;
450✔
521
        int iBand = (panBandMap) ? panBandMap[xBand] : xBand + 1;
450✔
522
        int bPromoteTo8Bit =
449✔
523
            (cpl::down_cast<JP2OPJLikeRasterBand<CODEC, BASE> *>(
450✔
524
                 GetRasterBand(iBand)))
525
                ->bPromoteTo8Bit;
526

527
        void *pDstBuffer = nullptr;
449✔
528
        if (iBand == nBand)
449✔
529
            pDstBuffer = pImage;
391✔
530
        else
531
        {
532
            AcquireMutex();
58✔
533
            poBlock = (cpl::down_cast<JP2OPJLikeRasterBand<CODEC, BASE> *>(
59✔
534
                           GetRasterBand(iBand)))
535
                          ->TryGetLockedBlockRef(nBlockXOff, nBlockYOff);
536
            if (poBlock != nullptr)
59✔
537
            {
538
                ReleaseMutex();
×
539
                poBlock->DropLock();
×
540
                continue;
×
541
            }
542

543
            poBlock = GetRasterBand(iBand)->GetLockedBlockRef(nBlockXOff,
59✔
544
                                                              nBlockYOff, TRUE);
545
            ReleaseMutex();
59✔
546
            if (poBlock == nullptr)
59✔
547
            {
548
                continue;
×
549
            }
550

551
            pDstBuffer = poBlock->GetDataRef();
59✔
552
        }
553

554
        if (this->bIs420)
449✔
555
        {
556
            if (static_cast<int>(localctx.psImage->comps[0].w) < nWidthToRead ||
7✔
557
                static_cast<int>(localctx.psImage->comps[0].h) <
7✔
558
                    nHeightToRead ||
7✔
559
                localctx.psImage->comps[1].w !=
7✔
560
                    (localctx.psImage->comps[0].w + 1) / 2 ||
7✔
561
                localctx.psImage->comps[1].h !=
7✔
562
                    (localctx.psImage->comps[0].h + 1) / 2 ||
7✔
563
                localctx.psImage->comps[2].w !=
7✔
564
                    (localctx.psImage->comps[0].w + 1) / 2 ||
7✔
565
                localctx.psImage->comps[2].h !=
7✔
566
                    (localctx.psImage->comps[0].h + 1) / 2 ||
7✔
567
                (nBands == 4 &&
7✔
568
                 (static_cast<int>(localctx.psImage->comps[3].w) <
4✔
569
                      nWidthToRead ||
4✔
570
                  static_cast<int>(localctx.psImage->comps[3].h) <
4✔
571
                      nHeightToRead)))
572
            {
573
                CPLError(CE_Failure, CPLE_AssertionFailed,
×
574
                         "Assertion at line %d of %s failed", __LINE__,
575
                         __FILE__);
576
                if (poBlock != nullptr)
×
577
                    poBlock->DropLock();
×
578
                eErr = CE_Failure;
×
579
                goto end;
×
580
            }
581

582
            GByte *pDst = static_cast<GByte *>(pDstBuffer);
7✔
583
            if (iBand == 4)
7✔
584
            {
585
                const auto pSrcA = localctx.psImage->comps[3].data;
1✔
586
                for (GPtrDiff_t j = 0; j < nHeightToRead; j++)
151✔
587
                {
588
                    memcpy(pDst + j * nBlockXSize,
300✔
589
                           pSrcA + j * localctx.stride(localctx.psImage->comps),
150✔
590
                           nWidthToRead);
591
                }
592
            }
593
            else
594
            {
595
                const auto pSrcY = localctx.psImage->comps[0].data;
6✔
596
                const auto pSrcCb = localctx.psImage->comps[1].data;
6✔
597
                const auto pSrcCr = localctx.psImage->comps[2].data;
6✔
598
                for (GPtrDiff_t j = 0; j < nHeightToRead; j++)
606✔
599
                {
600
                    for (int i = 0; i < nWidthToRead; i++)
81,000✔
601
                    {
602
                        int Y =
80,400✔
603
                            pSrcY[j * localctx.stride(localctx.psImage->comps) +
80,400✔
604
                                  i];
80,400✔
605
                        int Cb =
80,400✔
606
                            pSrcCb[(j / 2) * localctx.stride(
160,800✔
607
                                                 localctx.psImage->comps + 1) +
80,400✔
608
                                   (i / 2)];
80,400✔
609
                        int Cr =
80,400✔
610
                            pSrcCr[(j / 2) * localctx.stride(
160,800✔
611
                                                 localctx.psImage->comps + 2) +
80,400✔
612
                                   (i / 2)];
80,400✔
613
                        if (iBand == 1)
80,400✔
614
                            pDst[j * nBlockXSize + i] = CLAMP_0_255(
26,800✔
615
                                static_cast<int>(Y + 1.402 * (Cr - 128)));
26,800✔
616
                        else if (iBand == 2)
53,600✔
617
                            pDst[j * nBlockXSize + i] = CLAMP_0_255(
26,800✔
618
                                static_cast<int>(Y - 0.34414 * (Cb - 128) -
26,800✔
619
                                                 0.71414 * (Cr - 128)));
26,800✔
620
                        else if (iBand == 3)
26,800✔
621
                            pDst[j * nBlockXSize + i] = CLAMP_0_255(
26,800✔
622
                                static_cast<int>(Y + 1.772 * (Cb - 128)));
26,800✔
623
                    }
624
                }
625
            }
626

627
            if (bPromoteTo8Bit)
7✔
628
            {
629
                for (GPtrDiff_t j = 0; j < nHeightToRead; j++)
×
630
                {
631
                    for (int i = 0; i < nWidthToRead; i++)
×
632
                    {
633
                        pDst[j * nBlockXSize + i] *= 255;
×
634
                    }
635
                }
636
            }
637
        }
638
        else
639
        {
640
            if (static_cast<int>(localctx.psImage->comps[iBand - 1].w) <
442✔
641
                    nWidthToRead ||
443✔
642
                static_cast<int>(localctx.psImage->comps[iBand - 1].h) <
443✔
643
                    nHeightToRead)
644
            {
645
                CPLError(CE_Failure, CPLE_AssertionFailed,
×
646
                         "Assertion at line %d of %s failed", __LINE__,
647
                         __FILE__);
648
                if (poBlock != nullptr)
×
649
                    poBlock->DropLock();
×
650
                eErr = CE_Failure;
×
651
                goto end;
×
652
            }
653

654
            if (bPromoteTo8Bit)
442✔
655
            {
656
                for (GPtrDiff_t j = 0; j < nHeightToRead; j++)
529✔
657
                {
658
                    for (int i = 0; i < nWidthToRead; i++)
79,500✔
659
                    {
660
                        localctx.psImage->comps[iBand - 1]
157,950✔
661
                            .data[j * localctx.stride(localctx.psImage->comps +
157,950✔
662
                                                      iBand - 1) +
78,975✔
663
                                  i] *= 255;
78,975✔
664
                    }
665
                }
666
            }
667

668
            if (static_cast<int>(localctx.stride(localctx.psImage->comps +
885✔
669
                                                 iBand - 1)) == nBlockXSize &&
856✔
670
                static_cast<int>(localctx.psImage->comps[iBand - 1].h) ==
414✔
671
                    nBlockYSize)
672
            {
673
                GDALCopyWords64(
393✔
674
                    localctx.psImage->comps[iBand - 1].data, GDT_Int32, 4,
393✔
675
                    pDstBuffer, eDataType, nDataTypeSize,
676
                    static_cast<GPtrDiff_t>(nBlockXSize) * nBlockYSize);
393✔
677
            }
678
            else
679
            {
680
                for (GPtrDiff_t j = 0; j < nHeightToRead; j++)
12,249✔
681
                {
682
                    GDALCopyWords(
24,398✔
683
                        localctx.psImage->comps[iBand - 1].data +
24,398✔
684
                            j * localctx.stride(localctx.psImage->comps +
12,199✔
685
                                                iBand - 1),
12,199✔
686
                        GDT_Int32, 4,
687
                        static_cast<GByte *>(pDstBuffer) +
688
                            j * nBlockXSize * nDataTypeSize,
12,199✔
689
                        eDataType, nDataTypeSize, nWidthToRead);
690
                }
691
            }
692
        }
693

694
        if (poBlock != nullptr)
450✔
695
            poBlock->DropLock();
59✔
696
    }
697

698
end:
391✔
699
    this->cache(&localctx);
391✔
700

701
    return eErr;
782✔
702
}
703

704
/************************************************************************/
705
/*                         GetOverviewCount()                           */
706
/************************************************************************/
707

708
template <typename CODEC, typename BASE>
709
int JP2OPJLikeRasterBand<CODEC, BASE>::GetOverviewCount()
119✔
710
{
711
    auto poGDS = cpl::down_cast<JP2OPJLikeDataset<CODEC, BASE> *>(poDS);
119✔
712
    if (!poGDS->AreOverviewsEnabled())
119✔
713
        return 0;
×
714

715
    if (GDALPamRasterBand::GetOverviewCount() > 0)
119✔
716
        return GDALPamRasterBand::GetOverviewCount();
4✔
717

718
    return poGDS->nOverviewCount;
115✔
719
}
720

721
/************************************************************************/
722
/*                            GetOverview()                             */
723
/************************************************************************/
724

725
template <typename CODEC, typename BASE>
726
GDALRasterBand *JP2OPJLikeRasterBand<CODEC, BASE>::GetOverview(int iOvrLevel)
22✔
727
{
728
    if (GDALPamRasterBand::GetOverviewCount() > 0)
22✔
729
        return GDALPamRasterBand::GetOverview(iOvrLevel);
6✔
730

731
    auto poGDS = cpl::down_cast<JP2OPJLikeDataset<CODEC, BASE> *>(poDS);
16✔
732
    if (iOvrLevel < 0 || iOvrLevel >= poGDS->nOverviewCount)
16✔
733
        return nullptr;
×
734

735
    return poGDS->papoOverviewDS[iOvrLevel]->GetRasterBand(nBand);
16✔
736
}
737

738
/************************************************************************/
739
/*                       GetColorInterpretation()                       */
740
/************************************************************************/
741

742
template <typename CODEC, typename BASE>
743
GDALColorInterp JP2OPJLikeRasterBand<CODEC, BASE>::GetColorInterpretation()
570✔
744
{
745
    auto poGDS = cpl::down_cast<JP2OPJLikeDataset<CODEC, BASE> *>(poDS);
570✔
746

747
    if (poCT)
570✔
748
        return GCI_PaletteIndex;
6✔
749

750
    if (nBand == poGDS->nAlphaIndex + 1)
564✔
751
        return GCI_AlphaBand;
22✔
752

753
    if (poGDS->nBands <= 2 &&
949✔
754
        poGDS->eColorSpace == CODEC::cvtenum(JP2_CLRSPC_GRAY))
407✔
755
        return GCI_GrayIndex;
386✔
756
    else if (poGDS->eColorSpace == CODEC::cvtenum(JP2_CLRSPC_SRGB) ||
247✔
757
             poGDS->eColorSpace == CODEC::cvtenum(JP2_CLRSPC_SYCC))
91✔
758
    {
759
        if (nBand == poGDS->nRedIndex + 1)
77✔
760
            return GCI_RedBand;
26✔
761
        if (nBand == poGDS->nGreenIndex + 1)
51✔
762
            return GCI_GreenBand;
25✔
763
        if (nBand == poGDS->nBlueIndex + 1)
26✔
764
            return GCI_BlueBand;
25✔
765
    }
766

767
    return GCI_Undefined;
80✔
768
}
769

770
/************************************************************************/
771
/* ==================================================================== */
772
/*                           JP2OPJLikeDataset                          */
773
/* ==================================================================== */
774
/************************************************************************/
775

776
/************************************************************************/
777
/*                        JP2OPJLikeDataset()                           */
778
/************************************************************************/
779

780
template <typename CODEC, typename BASE>
781
JP2OPJLikeDataset<CODEC, BASE>::JP2OPJLikeDataset()
1,902✔
782
{
783
    this->init();
1,902✔
784
}
1,902✔
785

786
/************************************************************************/
787
/*                         ~JP2OPJLikeDataset()                         */
788
/************************************************************************/
789

790
template <typename CODEC, typename BASE>
791
JP2OPJLikeDataset<CODEC, BASE>::~JP2OPJLikeDataset()
2,778✔
792

793
{
794
    JP2OPJLikeDataset::Close();
1,902✔
795
    this->deinit();
1,902✔
796
}
4,680✔
797

798
/************************************************************************/
799
/*                              Close()                                 */
800
/************************************************************************/
801

802
template <typename CODEC, typename BASE>
803
CPLErr JP2OPJLikeDataset<CODEC, BASE>::Close()
2,567✔
804
{
805
    CPLErr eErr = CE_None;
2,567✔
806
    if (nOpenFlags != OPEN_FLAGS_CLOSED)
2,567✔
807
    {
808
        if (JP2OPJLikeDataset::FlushCache(true) != CE_None)
1,902✔
809
            eErr = CE_Failure;
×
810

811
        this->closeJP2();
1,902✔
812
        if (this->iLevel == 0 && this->fp_ != nullptr)
1,902✔
813
        {
814
            if (this->bRewrite)
759✔
815
            {
816
                GDALJP2Box oBox(this->fp_);
18✔
817
                vsi_l_offset nOffsetJP2C = 0;
9✔
818
                vsi_l_offset nLengthJP2C = 0;
9✔
819
                vsi_l_offset nOffsetXML = 0;
9✔
820
                vsi_l_offset nOffsetASOC = 0;
9✔
821
                vsi_l_offset nOffsetUUID = 0;
9✔
822
                vsi_l_offset nOffsetIHDR = 0;
9✔
823
                vsi_l_offset nLengthIHDR = 0;
9✔
824
                int bMSIBox = FALSE;
9✔
825
                int bGMLData = FALSE;
9✔
826
                int bUnsupportedConfiguration = FALSE;
9✔
827
                if (oBox.ReadFirst())
9✔
828
                {
829
                    while (strlen(oBox.GetType()) > 0)
47✔
830
                    {
831
                        if (EQUAL(oBox.GetType(), "jp2c"))
47✔
832
                        {
833
                            if (nOffsetJP2C == 0)
9✔
834
                            {
835
                                nOffsetJP2C = VSIFTellL(this->fp_);
9✔
836
                                nLengthJP2C = oBox.GetDataLength();
9✔
837
                            }
838
                            else
839
                                bUnsupportedConfiguration = TRUE;
×
840
                        }
841
                        else if (EQUAL(oBox.GetType(), "jp2h"))
38✔
842
                        {
843
                            GDALJP2Box oSubBox(this->fp_);
18✔
844
                            if (oSubBox.ReadFirstChild(&oBox) &&
18✔
845
                                EQUAL(oSubBox.GetType(), "ihdr"))
9✔
846
                            {
847
                                nOffsetIHDR = VSIFTellL(this->fp_);
9✔
848
                                nLengthIHDR = oSubBox.GetDataLength();
9✔
849
                            }
850
                        }
851
                        else if (EQUAL(oBox.GetType(), "xml "))
29✔
852
                        {
853
                            if (nOffsetXML == 0)
2✔
854
                                nOffsetXML = VSIFTellL(this->fp_);
2✔
855
                        }
856
                        else if (EQUAL(oBox.GetType(), "asoc"))
27✔
857
                        {
858
                            if (nOffsetASOC == 0)
3✔
859
                                nOffsetASOC = VSIFTellL(this->fp_);
3✔
860

861
                            GDALJP2Box oSubBox(this->fp_);
6✔
862
                            if (oSubBox.ReadFirstChild(&oBox) &&
6✔
863
                                EQUAL(oSubBox.GetType(), "lbl "))
3✔
864
                            {
865
                                char *pszLabel = reinterpret_cast<char *>(
866
                                    oSubBox.ReadBoxData());
3✔
867
                                if (pszLabel != nullptr &&
3✔
868
                                    EQUAL(pszLabel, "gml.data"))
3✔
869
                                {
870
                                    bGMLData = TRUE;
3✔
871
                                }
872
                                else
873
                                    bUnsupportedConfiguration = TRUE;
×
874
                                CPLFree(pszLabel);
3✔
875
                            }
876
                            else
877
                                bUnsupportedConfiguration = TRUE;
×
878
                        }
879
                        else if (EQUAL(oBox.GetType(), "uuid"))
24✔
880
                        {
881
                            if (nOffsetUUID == 0)
4✔
882
                                nOffsetUUID = VSIFTellL(this->fp_);
4✔
883
                            if (GDALJP2Metadata::IsUUID_MSI(oBox.GetUUID()))
4✔
884
                                bMSIBox = TRUE;
4✔
885
                            else if (!GDALJP2Metadata::IsUUID_XMP(
×
886
                                         oBox.GetUUID()))
887
                                bUnsupportedConfiguration = TRUE;
×
888
                        }
889
                        else if (!EQUAL(oBox.GetType(), "jP  ") &&
20✔
890
                                 !EQUAL(oBox.GetType(), "ftyp") &&
11✔
891
                                 !EQUAL(oBox.GetType(), "rreq") &&
2✔
892
                                 !EQUAL(oBox.GetType(), "jp2h") &&
31✔
893
                                 !EQUAL(oBox.GetType(), "jp2i"))
×
894
                        {
895
                            bUnsupportedConfiguration = TRUE;
×
896
                        }
897

898
                        if (bUnsupportedConfiguration || !oBox.ReadNext())
47✔
899
                            break;
9✔
900
                    }
901
                }
902

903
                const char *pszGMLJP2;
904
                int bGeoreferencingCompatOfGMLJP2 =
9✔
905
                    (!m_oSRS.IsEmpty() && bGeoTransformValid && nGCPCount == 0);
9✔
906
                if (bGeoreferencingCompatOfGMLJP2 &&
9✔
907
                    ((this->bHasGeoreferencingAtOpening && bGMLData) ||
3✔
908
                     (!this->bHasGeoreferencingAtOpening)))
2✔
909
                    pszGMLJP2 = "GMLJP2=YES";
2✔
910
                else
911
                    pszGMLJP2 = "GMLJP2=NO";
7✔
912

913
                const char *pszGeoJP2;
914
                int bGeoreferencingCompatOfGeoJP2 =
9✔
915
                    (!m_oSRS.IsEmpty() || nGCPCount != 0 || bGeoTransformValid);
9✔
916
                if (bGeoreferencingCompatOfGeoJP2 &&
9✔
917
                    ((this->bHasGeoreferencingAtOpening && bMSIBox) ||
6✔
918
                     (!this->bHasGeoreferencingAtOpening) ||
4✔
919
                     this->nGCPCount > 0))
2✔
920
                    pszGeoJP2 = "GeoJP2=YES";
5✔
921
                else
922
                    pszGeoJP2 = "GeoJP2=NO";
4✔
923

924
                /* Test that the length of the JP2C box is not 0 */
925
                int bJP2CBoxOKForRewriteInPlace = TRUE;
9✔
926
                if (nOffsetJP2C > 16 && !bUnsupportedConfiguration)
9✔
927
                {
928
                    VSIFSeekL(this->fp_, nOffsetJP2C - 8, SEEK_SET);
9✔
929
                    GByte abyBuffer[8];
930
                    VSIFReadL(abyBuffer, 1, 8, this->fp_);
9✔
931
                    if (memcmp(abyBuffer + 4, "jp2c", 4) == 0 &&
9✔
932
                        abyBuffer[0] == 0 && abyBuffer[1] == 0 &&
9✔
933
                        abyBuffer[2] == 0 && abyBuffer[3] == 0)
9✔
934
                    {
935
                        if (nLengthJP2C + 8 < UINT32_MAX)
1✔
936
                        {
937
                            CPLDebug(
1✔
938
                                CODEC::debugId(),
939
                                "Patching length of JP2C box with real length");
940
                            VSIFSeekL(this->fp_, nOffsetJP2C - 8, SEEK_SET);
1✔
941
                            GUInt32 nLength =
1✔
942
                                static_cast<GUInt32>(nLengthJP2C) + 8;
1✔
943
                            CPL_MSBPTR32(&nLength);
1✔
944
                            if (VSIFWriteL(&nLength, 1, 4, this->fp_) != 1)
1✔
945
                                eErr = CE_Failure;
1✔
946
                        }
947
                        else
948
                            bJP2CBoxOKForRewriteInPlace = FALSE;
×
949
                    }
950
                }
951

952
                if (nOffsetJP2C == 0 || bUnsupportedConfiguration)
9✔
953
                {
954
                    eErr = CE_Failure;
×
955
                    CPLError(CE_Failure, CPLE_AppDefined,
×
956
                             "Cannot rewrite file due to unsupported JP2 box "
957
                             "configuration");
958
                    VSIFCloseL(this->fp_);
×
959
                }
960
                else if (bJP2CBoxOKForRewriteInPlace &&
9✔
961
                         (nOffsetXML == 0 || nOffsetXML > nOffsetJP2C) &&
9✔
962
                         (nOffsetASOC == 0 || nOffsetASOC > nOffsetJP2C) &&
9✔
963
                         (nOffsetUUID == 0 || nOffsetUUID > nOffsetJP2C))
4✔
964
                {
965
                    CPLDebug(CODEC::debugId(),
5✔
966
                             "Rewriting boxes after codestream");
967

968
                    /* Update IPR flag */
969
                    if (nLengthIHDR == 14)
5✔
970
                    {
971
                        VSIFSeekL(this->fp_, nOffsetIHDR + nLengthIHDR - 1,
5✔
972
                                  SEEK_SET);
973
                        GByte bIPR = GetMetadata("xml:IPR") != nullptr;
5✔
974
                        if (VSIFWriteL(&bIPR, 1, 1, this->fp_) != 1)
5✔
975
                            eErr = CE_Failure;
×
976
                    }
977

978
                    VSIFSeekL(this->fp_, nOffsetJP2C + nLengthJP2C, SEEK_SET);
5✔
979

980
                    GDALJP2Metadata oJP2MD;
10✔
981
                    if (GetGCPCount() > 0)
5✔
982
                    {
983
                        oJP2MD.SetGCPs(GetGCPCount(), GetGCPs());
1✔
984
                        oJP2MD.SetSpatialRef(GetGCPSpatialRef());
1✔
985
                    }
986
                    else
987
                    {
988
                        const OGRSpatialReference *poSRS = GetSpatialRef();
4✔
989
                        if (poSRS != nullptr)
4✔
990
                        {
991
                            oJP2MD.SetSpatialRef(poSRS);
1✔
992
                        }
993
                        if (bGeoTransformValid)
4✔
994
                        {
995
                            oJP2MD.SetGeoTransform(m_gt);
1✔
996
                        }
997
                    }
998

999
                    const char *pszAreaOrPoint =
1000
                        GetMetadataItem(GDALMD_AREA_OR_POINT);
5✔
1001
                    oJP2MD.bPixelIsPoint =
5✔
1002
                        pszAreaOrPoint != nullptr &&
5✔
1003
                        EQUAL(pszAreaOrPoint, GDALMD_AOP_POINT);
×
1004

1005
                    if (!WriteIPRBox(this->fp_, this))
5✔
1006
                        eErr = CE_Failure;
×
1007

1008
                    if (bGeoreferencingCompatOfGMLJP2 &&
5✔
1009
                        EQUAL(pszGMLJP2, "GMLJP2=YES"))
1✔
1010
                    {
1011
                        GDALJP2Box *poBox =
1012
                            oJP2MD.CreateGMLJP2(nRasterXSize, nRasterYSize);
1✔
1013
                        if (!WriteBox(this->fp_, poBox))
1✔
1014
                            eErr = CE_Failure;
×
1015
                        delete poBox;
1✔
1016
                    }
1017

1018
                    if (!WriteXMLBoxes(this->fp_, this) ||
10✔
1019
                        !WriteGDALMetadataBox(this->fp_, this, nullptr))
5✔
1020
                        eErr = CE_Failure;
×
1021

1022
                    if (bGeoreferencingCompatOfGeoJP2 &&
5✔
1023
                        EQUAL(pszGeoJP2, "GeoJP2=YES"))
2✔
1024
                    {
1025
                        GDALJP2Box *poBox = oJP2MD.CreateJP2GeoTIFF();
2✔
1026
                        if (!WriteBox(this->fp_, poBox))
2✔
1027
                            eErr = CE_Failure;
×
1028
                        delete poBox;
2✔
1029
                    }
1030

1031
                    if (!WriteXMPBox(this->fp_, this))
5✔
1032
                        eErr = CE_Failure;
×
1033

1034
                    if (VSIFTruncateL(this->fp_, VSIFTellL(this->fp_)) != 0)
5✔
1035
                        eErr = CE_Failure;
×
1036

1037
                    if (VSIFCloseL(this->fp_) != 0)
5✔
1038
                        eErr = CE_Failure;
5✔
1039
                }
1040
                else
1041
                {
1042
                    VSIFCloseL(this->fp_);
4✔
1043

1044
                    CPLDebug(CODEC::debugId(), "Rewriting whole file");
4✔
1045

1046
                    const char *const apszOptions[] = {"USE_SRC_CODESTREAM=YES",
4✔
1047
                                                       "CODEC=JP2",
1048
                                                       "WRITE_METADATA=YES",
1049
                                                       pszGMLJP2,
1050
                                                       pszGeoJP2,
1051
                                                       nullptr};
1052
                    CPLString osTmpFilename(
8✔
1053
                        CPLSPrintf("%s.tmp", GetDescription()));
4✔
1054
                    GDALDataset *poOutDS =
1055
                        CreateCopy(osTmpFilename, this, FALSE,
4✔
1056
                                   const_cast<char **>(apszOptions),
1057
                                   GDALDummyProgress, nullptr);
1058
                    if (poOutDS)
4✔
1059
                    {
1060
                        if (GDALClose(poOutDS) != CE_None)
4✔
1061
                            eErr = CE_Failure;
×
1062
                        if (VSIRename(osTmpFilename, GetDescription()) != 0)
4✔
1063
                            eErr = CE_Failure;
×
1064
                    }
1065
                    else
1066
                    {
1067
                        eErr = CE_Failure;
×
1068
                        VSIUnlink(osTmpFilename);
×
1069
                    }
1070
                    VSIUnlink(CPLSPrintf("%s.tmp.aux.xml", GetDescription()));
4✔
1071
                }
1072
            }
1073
            else
1074
                VSIFCloseL(this->fp_);
750✔
1075
        }
1076

1077
        JP2OPJLikeDataset::CloseDependentDatasets();
1,902✔
1078

1079
        if (GDALPamDataset::Close() != CE_None)
1,902✔
1080
            eErr = CE_Failure;
×
1081
    }
1082
    return eErr;
2,567✔
1083
}
1084

1085
/************************************************************************/
1086
/*                      CloseDependentDatasets()                        */
1087
/************************************************************************/
1088

1089
template <typename CODEC, typename BASE>
1090
int JP2OPJLikeDataset<CODEC, BASE>::CloseDependentDatasets()
1,916✔
1091
{
1092
    int bRet = GDALJP2AbstractDataset::CloseDependentDatasets();
1,916✔
1093
    if (papoOverviewDS)
1,916✔
1094
    {
1095
        for (int i = 0; i < this->nOverviewCount; i++)
178✔
1096
            delete papoOverviewDS[i];
115✔
1097
        CPLFree(papoOverviewDS);
63✔
1098
        papoOverviewDS = nullptr;
63✔
1099
        bRet = TRUE;
63✔
1100
    }
1101
    return bRet;
1,916✔
1102
}
1103

1104
/************************************************************************/
1105
/*                           SetSpatialRef()                            */
1106
/************************************************************************/
1107

1108
template <typename CODEC, typename BASE>
1109
CPLErr
1110
JP2OPJLikeDataset<CODEC, BASE>::SetSpatialRef(const OGRSpatialReference *poSRS)
18✔
1111
{
1112
    if (eAccess == GA_Update)
18✔
1113
    {
1114
        this->bRewrite = TRUE;
2✔
1115
        m_oSRS.Clear();
2✔
1116
        if (poSRS)
2✔
1117
            m_oSRS = *poSRS;
1✔
1118
        return CE_None;
2✔
1119
    }
1120
    else
1121
        return GDALJP2AbstractDataset::SetSpatialRef(poSRS);
16✔
1122
}
1123

1124
/************************************************************************/
1125
/*                           SetGeoTransform()                          */
1126
/************************************************************************/
1127

1128
template <typename CODEC, typename BASE>
1129
CPLErr
1130
JP2OPJLikeDataset<CODEC, BASE>::SetGeoTransform(const GDALGeoTransform &gt)
24✔
1131
{
1132
    if (eAccess == GA_Update)
24✔
1133
    {
1134
        this->bRewrite = TRUE;
4✔
1135
        m_gt = gt;
4✔
1136
        bGeoTransformValid = m_gt != GDALGeoTransform();
4✔
1137
        return CE_None;
4✔
1138
    }
1139
    else
1140
        return GDALJP2AbstractDataset::SetGeoTransform(gt);
20✔
1141
}
1142

1143
/************************************************************************/
1144
/*                           SetGCPs()                                  */
1145
/************************************************************************/
1146

1147
template <typename CODEC, typename BASE>
1148
CPLErr JP2OPJLikeDataset<CODEC, BASE>::SetGCPs(int nGCPCountIn,
4✔
1149
                                               const GDAL_GCP *pasGCPListIn,
1150
                                               const OGRSpatialReference *poSRS)
1151
{
1152
    if (eAccess == GA_Update)
4✔
1153
    {
1154
        this->bRewrite = TRUE;
3✔
1155
        if (nGCPCount > 0)
3✔
1156
        {
1157
            GDALDeinitGCPs(nGCPCount, pasGCPList);
1✔
1158
            CPLFree(pasGCPList);
1✔
1159
        }
1160

1161
        m_oSRS.Clear();
3✔
1162
        if (poSRS)
3✔
1163
            m_oSRS = *poSRS;
2✔
1164

1165
        nGCPCount = nGCPCountIn;
3✔
1166
        pasGCPList = GDALDuplicateGCPs(nGCPCount, pasGCPListIn);
3✔
1167

1168
        return CE_None;
3✔
1169
    }
1170
    else
1171
        return GDALJP2AbstractDataset::SetGCPs(nGCPCountIn, pasGCPListIn,
1✔
1172
                                               poSRS);
1✔
1173
}
1174

1175
/************************************************************************/
1176
/*                            SetMetadata()                             */
1177
/************************************************************************/
1178

1179
template <typename CODEC, typename BASE>
1180
CPLErr JP2OPJLikeDataset<CODEC, BASE>::SetMetadata(char **papszMetadata,
11✔
1181
                                                   const char *pszDomain)
1182
{
1183
    if (eAccess == GA_Update)
11✔
1184
    {
1185
        this->bRewrite = TRUE;
2✔
1186
        if (pszDomain == nullptr || EQUAL(pszDomain, ""))
2✔
1187
        {
1188
            CSLDestroy(m_papszMainMD);
1✔
1189
            m_papszMainMD = CSLDuplicate(papszMetadata);
1✔
1190
        }
1191
        return GDALDataset::SetMetadata(papszMetadata, pszDomain);
2✔
1192
    }
1193
    return GDALJP2AbstractDataset::SetMetadata(papszMetadata, pszDomain);
9✔
1194
}
1195

1196
/************************************************************************/
1197
/*                            SetMetadata()                             */
1198
/************************************************************************/
1199

1200
template <typename CODEC, typename BASE>
1201
CPLErr JP2OPJLikeDataset<CODEC, BASE>::SetMetadataItem(const char *pszName,
3✔
1202
                                                       const char *pszValue,
1203
                                                       const char *pszDomain)
1204
{
1205
    if (eAccess == GA_Update)
3✔
1206
    {
1207
        this->bRewrite = TRUE;
1✔
1208
        if (pszDomain == nullptr || EQUAL(pszDomain, ""))
1✔
1209
        {
1210
            m_papszMainMD = CSLSetNameValue(GetMetadata(), pszName, pszValue);
1✔
1211
        }
1212
        return GDALDataset::SetMetadataItem(pszName, pszValue, pszDomain);
1✔
1213
    }
1214
    return GDALJP2AbstractDataset::SetMetadataItem(pszName, pszValue,
2✔
1215
                                                   pszDomain);
2✔
1216
}
1217

1218
/************************************************************************/
1219
/*                            Identify()                                */
1220
/************************************************************************/
1221

1222
#ifndef jpc_header_defined
1223
#define jpc_header_defined
1224
static const unsigned char jpc_header[] = {0xff, 0x4f, 0xff,
1225
                                           0x51};  // SOC + RSIZ markers
1226
static const unsigned char jp2_box_jp[] = {0x6a, 0x50, 0x20, 0x20}; /* 'jP  ' */
1227
#endif
1228

1229
template <typename CODEC, typename BASE>
1230
int JP2OPJLikeDataset<CODEC, BASE>::Identify(GDALOpenInfo *poOpenInfo)
766✔
1231

1232
{
1233
    if (poOpenInfo->nHeaderBytes >= 16 &&
766✔
1234
        (memcmp(poOpenInfo->pabyHeader, jpc_header, sizeof(jpc_header)) == 0 ||
766✔
1235
         memcmp(poOpenInfo->pabyHeader + 4, jp2_box_jp, sizeof(jp2_box_jp)) ==
683✔
1236
             0))
1237
        return TRUE;
766✔
1238

1239
    else
1240
        return FALSE;
×
1241
}
1242

1243
/************************************************************************/
1244
/*                        JP2FindCodeStream()                           */
1245
/************************************************************************/
1246

1247
static vsi_l_offset JP2FindCodeStream(VSILFILE *fp, vsi_l_offset *pnLength)
773✔
1248
{
1249
    vsi_l_offset nCodeStreamStart = 0;
773✔
1250
    vsi_l_offset nCodeStreamLength = 0;
773✔
1251

1252
    VSIFSeekL(fp, 0, SEEK_SET);
773✔
1253
    GByte abyHeader[16];
1254
    VSIFReadL(abyHeader, 1, 16, fp);
773✔
1255

1256
    if (memcmp(abyHeader, jpc_header, sizeof(jpc_header)) == 0)
773✔
1257
    {
1258
        VSIFSeekL(fp, 0, SEEK_END);
83✔
1259
        nCodeStreamLength = VSIFTellL(fp);
83✔
1260
    }
1261
    else if (memcmp(abyHeader + 4, jp2_box_jp, sizeof(jp2_box_jp)) == 0)
690✔
1262
    {
1263
        /* Find offset of first jp2c box */
1264
        GDALJP2Box oBox(fp);
1,378✔
1265
        if (oBox.ReadFirst())
689✔
1266
        {
1267
            while (strlen(oBox.GetType()) > 0)
3,585✔
1268
            {
1269
                if (EQUAL(oBox.GetType(), "jp2c"))
3,585✔
1270
                {
1271
                    nCodeStreamStart = VSIFTellL(fp);
688✔
1272
                    nCodeStreamLength = oBox.GetDataLength();
688✔
1273
                    break;
688✔
1274
                }
1275

1276
                if (!oBox.ReadNext())
2,897✔
1277
                    break;
1✔
1278
            }
1279
        }
1280
    }
1281
    *pnLength = nCodeStreamLength;
773✔
1282
    return nCodeStreamStart;
773✔
1283
}
1284

1285
/************************************************************************/
1286
/*                                Open()                                */
1287
/************************************************************************/
1288

1289
template <typename CODEC, typename BASE>
1290
GDALDataset *JP2OPJLikeDataset<CODEC, BASE>::Open(GDALOpenInfo *poOpenInfo)
766✔
1291

1292
{
1293
    if (!Identify(poOpenInfo) || poOpenInfo->fpL == nullptr)
766✔
1294
        return nullptr;
×
1295

1296
    /* Detect which codec to use : J2K or JP2 ? */
1297
    vsi_l_offset nCodeStreamLength = 0;
766✔
1298
    vsi_l_offset nCodeStreamStart =
1299
        JP2FindCodeStream(poOpenInfo->fpL, &nCodeStreamLength);
766✔
1300

1301
    if (nCodeStreamStart == 0 && nCodeStreamLength == 0)
766✔
1302
    {
1303
        CPLError(CE_Failure, CPLE_AppDefined, "No code-stream in JP2 file");
1✔
1304
        return nullptr;
1✔
1305
    }
1306
    JP2OPJLikeDataset oTmpDS;
1,530✔
1307
    int numThreads = oTmpDS.GetNumThreads();
765✔
1308
    auto eCodecFormat = (nCodeStreamStart == 0) ? CODEC::cvtenum(JP2_CODEC_J2K)
765✔
1309
                                                : CODEC::cvtenum(JP2_CODEC_JP2);
682✔
1310

1311
    uint32_t nTileW = 0, nTileH = 0;
765✔
1312
    int numResolutions = 0;
765✔
1313
    CODEC localctx;
765✔
1314
    localctx.open(poOpenInfo->fpL, nCodeStreamStart);
765✔
1315
    if (!localctx.setUpDecompress(numThreads, nCodeStreamLength, &nTileW,
765✔
1316
                                  &nTileH, &numResolutions))
1317
        return nullptr;
6✔
1318

1319
    GDALDataType eDataType = GDT_Byte;
759✔
1320
    if (localctx.psImage->comps[0].prec > 16)
759✔
1321
    {
1322
        if (localctx.psImage->comps[0].sgnd)
×
1323
            eDataType = GDT_Int32;
×
1324
        else
1325
            eDataType = GDT_UInt32;
×
1326
    }
1327
    else if (localctx.psImage->comps[0].prec > 8)
759✔
1328
    {
1329
        if (localctx.psImage->comps[0].sgnd)
26✔
1330
            eDataType = GDT_Int16;
7✔
1331
        else
1332
            eDataType = GDT_UInt16;
19✔
1333
    }
1334

1335
    int bIs420 =
759✔
1336
        (localctx.psImage->color_space != CODEC::cvtenum(JP2_CLRSPC_SRGB) &&
1,518✔
1337
         eDataType == GDT_Byte &&
733✔
1338
         (localctx.psImage->numcomps == 3 || localctx.psImage->numcomps == 4) &&
733✔
1339
         localctx.psImage->comps[1].w == localctx.psImage->comps[0].w / 2 &&
58✔
1340
         localctx.psImage->comps[1].h == localctx.psImage->comps[0].h / 2 &&
3✔
1341
         localctx.psImage->comps[2].w == localctx.psImage->comps[0].w / 2 &&
3✔
1342
         localctx.psImage->comps[2].h == localctx.psImage->comps[0].h / 2) &&
1,521✔
1343
        (localctx.psImage->numcomps == 3 ||
3✔
1344
         (localctx.psImage->numcomps == 4 &&
2✔
1345
          localctx.psImage->comps[3].w == localctx.psImage->comps[0].w &&
2✔
1346
          localctx.psImage->comps[3].h == localctx.psImage->comps[0].h));
2✔
1347

1348
    if (bIs420)
759✔
1349
    {
1350
        CPLDebug(CODEC::debugId(), "420 format");
3✔
1351
    }
1352
    else
1353
    {
1354
        for (unsigned iBand = 2; iBand <= localctx.psImage->numcomps; iBand++)
917✔
1355
        {
1356
            if (localctx.psImage->comps[iBand - 1].w !=
161✔
1357
                    localctx.psImage->comps[0].w ||
161✔
1358
                localctx.psImage->comps[iBand - 1].h !=
161✔
1359
                    localctx.psImage->comps[0].h)
161✔
1360
            {
1361
                CPLDebug(CODEC::debugId(), "Unable to handle that image (2)");
×
1362
                localctx.free();
×
1363
                return nullptr;
×
1364
            }
1365
        }
1366
    }
1367

1368
    /* -------------------------------------------------------------------- */
1369
    /*      Create a corresponding GDALDataset.                             */
1370
    /* -------------------------------------------------------------------- */
1371
    JP2OPJLikeDataset *poDS;
1372
    int iBand;
1373

1374
    poDS = new JP2OPJLikeDataset();
759✔
1375
    poDS->m_osFilename = poOpenInfo->pszFilename;
759✔
1376
    if (eCodecFormat == CODEC::cvtenum(JP2_CODEC_JP2))
759✔
1377
        poDS->eAccess = poOpenInfo->eAccess;
678✔
1378
    poDS->eColorSpace = localctx.psImage->color_space;
759✔
1379
    poDS->nRasterXSize = localctx.psImage->x1 - localctx.psImage->x0;
759✔
1380
    poDS->nRasterYSize = localctx.psImage->y1 - localctx.psImage->y0;
759✔
1381
    poDS->nBands = localctx.psImage->numcomps;
759✔
1382
    poDS->fp_ = poOpenInfo->fpL;
759✔
1383
    poOpenInfo->fpL = nullptr;
759✔
1384
    poDS->nCodeStreamStart = nCodeStreamStart;
759✔
1385
    poDS->nCodeStreamLength = nCodeStreamLength;
759✔
1386
    poDS->bIs420 = bIs420;
759✔
1387
    poDS->bSingleTiled = (poDS->nRasterXSize == static_cast<int>(nTileW) &&
1,468✔
1388
                          poDS->nRasterYSize == static_cast<int>(nTileH));
709✔
1389
    poDS->m_nX0 = localctx.psImage->x0;
759✔
1390
    poDS->m_nY0 = localctx.psImage->y0;
759✔
1391
    poDS->m_nTileWidth = nTileW;
759✔
1392
    poDS->m_nTileHeight = nTileH;
759✔
1393

1394
    int nBlockXSize = static_cast<int>(nTileW);
759✔
1395
    int nBlockYSize = static_cast<int>(nTileH);
759✔
1396

1397
    if (CPLFetchBool(poOpenInfo->papszOpenOptions, "USE_TILE_AS_BLOCK", false))
759✔
1398
    {
1399
        poDS->bUseSetDecodeArea = false;
×
1400
    }
1401

1402
    poDS->m_bStrict = CPLTestBool(
759✔
1403
        CSLFetchNameValueDef(poOpenInfo->papszOpenOptions, "STRICT", "YES"));
759✔
1404
    localctx.updateStrict(poDS->m_bStrict);
759✔
1405

1406
    if (localctx.preferPerBlockDeCompress())
759✔
1407
    {
1408
        /* Some Sentinel2 preview datasets are 343x343 large, but with 8x8 blocks */
1409
        /* Using the tile API for that is super slow, so expose a single block */
1410
        if (poDS->nRasterXSize <= 1024 && poDS->nRasterYSize <= 1024 &&
759✔
1411
            nTileW < 32 && nTileH < 32)
743✔
1412
        {
1413
            poDS->bUseSetDecodeArea = true;
574✔
1414
            nBlockXSize = poDS->nRasterXSize;
574✔
1415
            nBlockYSize = poDS->nRasterYSize;
574✔
1416
        }
1417
        else
1418
        {
1419
            poDS->bUseSetDecodeArea =
185✔
1420
                poDS->bSingleTiled &&
322✔
1421
                (poDS->nRasterXSize > 1024 || poDS->nRasterYSize > 1024);
137✔
1422

1423
            /* Other Sentinel2 preview datasets are 343x343 and 60m are 1830x1830,
1424
             * but they */
1425
            /* are tiled with tile dimensions 2048x2048. It would be a waste of */
1426
            /* memory to allocate such big blocks */
1427
            if (poDS->nRasterXSize < static_cast<int>(nTileW) &&
185✔
1428
                poDS->nRasterYSize < static_cast<int>(nTileH))
18✔
1429
            {
1430
                poDS->bUseSetDecodeArea = TRUE;
18✔
1431
                nBlockXSize = poDS->nRasterXSize;
18✔
1432
                nBlockYSize = poDS->nRasterYSize;
18✔
1433
                if (nBlockXSize > 2048)
18✔
1434
                    nBlockXSize = 2048;
×
1435
                if (nBlockYSize > 2048)
18✔
1436
                    nBlockYSize = 2048;
×
1437
            }
1438
            else if (poDS->bUseSetDecodeArea)
167✔
1439
            {
1440
                // Arbitrary threshold... ~4 million at least needed for the GRIB2
1441
                // images mentioned below.
1442
                if (nTileH == 1 && nTileW < 20 * 1024 * 1024)
7✔
1443
                {
1444
                    // Some GRIB2 JPEG2000 compressed images are a 2D image
1445
                    // organized as a single line image...
1446
                }
1447
                else
1448
                {
1449
                    if (nBlockXSize > 1024)
7✔
1450
                        nBlockXSize = 1024;
7✔
1451
                    if (nBlockYSize > 1024)
7✔
1452
                        nBlockYSize = 1024;
7✔
1453
                }
1454
            }
1455
        }
1456
    }
1457

1458
    GDALColorTable *poCT = nullptr;
759✔
1459

1460
    /* -------------------------------------------------------------------- */
1461
    /*      Look for color table or cdef box                                */
1462
    /* -------------------------------------------------------------------- */
1463
    if (eCodecFormat == CODEC::cvtenum(JP2_CODEC_JP2))
759✔
1464
    {
1465
        vsi_l_offset nCurOffset = VSIFTellL(poDS->fp_);
678✔
1466

1467
        GDALJP2Box oBox(poDS->fp_);
1,356✔
1468
        if (oBox.ReadFirst())
678✔
1469
        {
1470
            while (strlen(oBox.GetType()) > 0)
3,625✔
1471
            {
1472
                if (EQUAL(oBox.GetType(), "jp2h"))
3,625✔
1473
                {
1474
                    GDALJP2Box oSubBox(poDS->fp_);
1,356✔
1475

1476
                    for (oSubBox.ReadFirstChild(&oBox);
2,146✔
1477
                         strlen(oSubBox.GetType()) > 0;
2,146✔
1478
                         oSubBox.ReadNextChild(&oBox))
1,468✔
1479
                    {
1480
                        GIntBig nDataLength = oSubBox.GetDataLength();
1,468✔
1481
                        if (poCT == nullptr &&
2,911✔
1482
                            EQUAL(oSubBox.GetType(), "pclr") &&
1,443✔
1483
                            nDataLength >= 3 &&
2,911✔
1484
                            nDataLength <= 2 + 1 + 4 + 4 * 256)
1485
                        {
1486
                            GByte *pabyCT = oSubBox.ReadBoxData();
24✔
1487
                            if (pabyCT != nullptr)
24✔
1488
                            {
1489
                                int nEntries = (pabyCT[0] << 8) | pabyCT[1];
24✔
1490
                                int nComponents = pabyCT[2];
24✔
1491
                                /* CPLDebug(CODEC::debugId(), "Color table found"); */
1492
                                if (nEntries <= 256 && nComponents == 3)
24✔
1493
                                {
1494
                                    /*CPLDebug(CODEC::debugId(), "resol[0] = %d",
1495
                                    pabyCT[3]); CPLDebug(CODEC::debugId(), "resol[1] =
1496
                                    %d", pabyCT[4]); CPLDebug(CODEC::debugId(),
1497
                                    "resol[2] = %d", pabyCT[5]);*/
1498
                                    if (pabyCT[3] == 7 && pabyCT[4] == 7 &&
19✔
1499
                                        pabyCT[5] == 7 &&
19✔
1500
                                        nDataLength == 2 + 1 + 3 + 3 * nEntries)
19✔
1501
                                    {
1502
                                        poCT = new GDALColorTable();
19✔
1503
                                        for (int i = 0; i < nEntries; i++)
1,103✔
1504
                                        {
1505
                                            GDALColorEntry sEntry;
1506
                                            sEntry.c1 = pabyCT[6 + 3 * i];
1,084✔
1507
                                            sEntry.c2 = pabyCT[6 + 3 * i + 1];
1,084✔
1508
                                            sEntry.c3 = pabyCT[6 + 3 * i + 2];
1,084✔
1509
                                            sEntry.c4 = 255;
1,084✔
1510
                                            poCT->SetColorEntry(i, &sEntry);
1,084✔
1511
                                        }
1512
                                    }
19✔
1513
                                }
1514
                                else if (nEntries <= 256 && nComponents == 4)
5✔
1515
                                {
1516
                                    if (pabyCT[3] == 7 && pabyCT[4] == 7 &&
5✔
1517
                                        pabyCT[5] == 7 && pabyCT[6] == 7 &&
5✔
1518
                                        nDataLength == 2 + 1 + 4 + 4 * nEntries)
5✔
1519
                                    {
1520
                                        poCT = new GDALColorTable();
5✔
1521
                                        for (int i = 0; i < nEntries; i++)
17✔
1522
                                        {
1523
                                            GDALColorEntry sEntry;
1524
                                            sEntry.c1 = pabyCT[7 + 4 * i];
12✔
1525
                                            sEntry.c2 = pabyCT[7 + 4 * i + 1];
12✔
1526
                                            sEntry.c3 = pabyCT[7 + 4 * i + 2];
12✔
1527
                                            sEntry.c4 = pabyCT[7 + 4 * i + 3];
12✔
1528
                                            poCT->SetColorEntry(i, &sEntry);
12✔
1529
                                        }
1530
                                    }
1531
                                }
1532
                                CPLFree(pabyCT);
24✔
1533
                            }
1534
                        }
1535
                        /* There's a bug/misfeature in openjpeg: the color_space
1536
                           only gets set at read tile time */
1537
                        else if (EQUAL(oSubBox.GetType(), "colr") &&
1,444✔
1538
                                 nDataLength == 7)
1539
                        {
1540
                            GByte *pabyContent = oSubBox.ReadBoxData();
678✔
1541
                            if (pabyContent != nullptr)
678✔
1542
                            {
1543
                                if (pabyContent[0] ==
678✔
1544
                                    1 /* enumerated colourspace */)
1545
                                {
1546
                                    GUInt32 enumcs = (pabyContent[3] << 24) |
678✔
1547
                                                     (pabyContent[4] << 16) |
678✔
1548
                                                     (pabyContent[5] << 8) |
678✔
1549
                                                     (pabyContent[6]);
1550
                                    if (enumcs == 16)
678✔
1551
                                    {
1552
                                        poDS->eColorSpace =
53✔
1553
                                            CODEC::cvtenum(JP2_CLRSPC_SRGB);
53✔
1554
                                        CPLDebug(CODEC::debugId(),
53✔
1555
                                                 "SRGB color space");
1556
                                    }
1557
                                    else if (enumcs == 17)
625✔
1558
                                    {
1559
                                        poDS->eColorSpace =
618✔
1560
                                            CODEC::cvtenum(JP2_CLRSPC_GRAY);
618✔
1561
                                        CPLDebug(CODEC::debugId(),
618✔
1562
                                                 "Grayscale color space");
1563
                                    }
1564
                                    else if (enumcs == 18)
7✔
1565
                                    {
1566
                                        poDS->eColorSpace =
3✔
1567
                                            CODEC::cvtenum(JP2_CLRSPC_SYCC);
3✔
1568
                                        CPLDebug(CODEC::debugId(),
3✔
1569
                                                 "SYCC color space");
1570
                                    }
1571
                                    else if (enumcs == 20)
4✔
1572
                                    {
1573
                                        /* Used by
1574
                                         * J2KP4files/testfiles_jp2/file7.jp2 */
1575
                                        poDS->eColorSpace =
×
1576
                                            CODEC::cvtenum(JP2_CLRSPC_SRGB);
×
1577
                                        CPLDebug(CODEC::debugId(),
×
1578
                                                 "e-sRGB color space");
1579
                                    }
1580
                                    else if (enumcs == 21)
4✔
1581
                                    {
1582
                                        /* Used by
1583
                                         * J2KP4files/testfiles_jp2/file5.jp2 */
1584
                                        poDS->eColorSpace =
×
1585
                                            CODEC::cvtenum(JP2_CLRSPC_SRGB);
×
1586
                                        CPLDebug(CODEC::debugId(),
×
1587
                                                 "ROMM-RGB color space");
1588
                                    }
1589
                                    else
1590
                                    {
1591
                                        poDS->eColorSpace =
4✔
1592
                                            CODEC::cvtenum(JP2_CLRSPC_UNKNOWN);
4✔
1593
                                        CPLDebug(CODEC::debugId(),
4✔
1594
                                                 "Unknown color space");
1595
                                    }
1596
                                }
1597
                                CPLFree(pabyContent);
678✔
1598
                            }
1599
                        }
1600
                        /* Check if there's an alpha channel or odd channel
1601
                         * attribution */
1602
                        else if (EQUAL(oSubBox.GetType(), "cdef") &&
796✔
1603
                                 nDataLength == 2 + poDS->nBands * 6)
30✔
1604
                        {
1605
                            GByte *pabyContent = oSubBox.ReadBoxData();
29✔
1606
                            if (pabyContent != nullptr)
29✔
1607
                            {
1608
                                int nEntries =
29✔
1609
                                    (pabyContent[0] << 8) | pabyContent[1];
29✔
1610
                                if (nEntries == poDS->nBands)
29✔
1611
                                {
1612
                                    poDS->nRedIndex = -1;
29✔
1613
                                    poDS->nGreenIndex = -1;
29✔
1614
                                    poDS->nBlueIndex = -1;
29✔
1615
                                    for (int i = 0; i < poDS->nBands; i++)
130✔
1616
                                    {
1617
                                        int CNi =
101✔
1618
                                            (pabyContent[2 + 6 * i] << 8) |
101✔
1619
                                            pabyContent[2 + 6 * i + 1];
101✔
1620
                                        int Typi =
101✔
1621
                                            (pabyContent[2 + 6 * i + 2] << 8) |
101✔
1622
                                            pabyContent[2 + 6 * i + 3];
101✔
1623
                                        int Asoci =
101✔
1624
                                            (pabyContent[2 + 6 * i + 4] << 8) |
101✔
1625
                                            pabyContent[2 + 6 * i + 5];
101✔
1626
                                        if (CNi < 0 || CNi >= poDS->nBands)
101✔
1627
                                        {
1628
                                            CPLError(CE_Failure,
×
1629
                                                     CPLE_AppDefined,
1630
                                                     "Wrong value of CN%d=%d",
1631
                                                     i, CNi);
1632
                                            break;
×
1633
                                        }
1634
                                        if (Typi == 0)
101✔
1635
                                        {
1636
                                            if (Asoci == 1)
75✔
1637
                                                poDS->nRedIndex = CNi;
24✔
1638
                                            else if (Asoci == 2)
51✔
1639
                                                poDS->nGreenIndex = CNi;
18✔
1640
                                            else if (Asoci == 3)
33✔
1641
                                                poDS->nBlueIndex = CNi;
18✔
1642
                                            else if (Asoci < 0 ||
15✔
1643
                                                     (Asoci > poDS->nBands &&
15✔
1644
                                                      Asoci != 65535))
1645
                                            {
1646
                                                CPLError(
×
1647
                                                    CE_Failure, CPLE_AppDefined,
1648
                                                    "Wrong value of Asoc%d=%d",
1649
                                                    i, Asoci);
1650
                                                break;
×
1651
                                            }
1652
                                        }
1653
                                        else if (Typi == 1)
26✔
1654
                                        {
1655
                                            poDS->nAlphaIndex = CNi;
26✔
1656
                                        }
1657
                                    }
1658
                                }
1659
                                else
1660
                                {
1661
                                    CPLDebug(CODEC::debugId(),
×
1662
                                             "Unsupported cdef content");
1663
                                }
1664
                                CPLFree(pabyContent);
29✔
1665
                            }
1666
                        }
1667
                    }
1668
                }
1669

1670
                if (!oBox.ReadNext())
3,625✔
1671
                    break;
678✔
1672
            }
1673
        }
1674

1675
        VSIFSeekL(poDS->fp_, nCurOffset, SEEK_SET);
678✔
1676

1677
        if (poDS->eColorSpace == CODEC::cvtenum(JP2_CLRSPC_GRAY) &&
678✔
1678
            poDS->nBands == 4 && poDS->nRedIndex == 0 &&
618✔
1679
            poDS->nGreenIndex == 1 && poDS->nBlueIndex == 2 &&
1,301✔
1680
            poDS->m_osFilename.find("dop10rgbi") != std::string::npos)
5✔
1681
        {
1682
            CPLDebug(CODEC::debugId(),
×
1683
                     "Autofix wrong colorspace from Greyscale to sRGB");
1684
            // Workaround https://github.com/uclouvain/openjpeg/issues/1464
1685
            // dop10rgbi products from https://www.opengeodata.nrw.de/produkte/geobasis/lusat/dop/dop_jp2_f10/
1686
            // have a wrong color space.
1687
            poDS->eColorSpace = CODEC::cvtenum(JP2_CLRSPC_SRGB);
×
1688
        }
1689
    }
1690

1691
    /* -------------------------------------------------------------------- */
1692
    /*      Create band information objects.                                */
1693
    /* -------------------------------------------------------------------- */
1694
    for (iBand = 1; iBand <= poDS->nBands; iBand++)
1,687✔
1695
    {
1696
        const bool bPromoteTo8Bit =
928✔
1697
            iBand == poDS->nAlphaIndex + 1 &&
954✔
1698
            localctx.psImage
26✔
1699
                    ->comps[(poDS->nAlphaIndex == 0 && poDS->nBands > 1) ? 1
26✔
1700
                                                                         : 0]
1701
                    .prec == 8 &&
26✔
1702
            localctx.psImage->comps[poDS->nAlphaIndex].prec == 1 &&
968✔
1703
            CPLFetchBool(poOpenInfo->papszOpenOptions, "1BIT_ALPHA_PROMOTION",
14✔
1704
                         CPLTestBool(CPLGetConfigOption(
14✔
1705
                             "JP2OPENJPEG_PROMOTE_1BIT_ALPHA_AS_8BIT", "YES")));
1706
        if (bPromoteTo8Bit)
928✔
1707
            CPLDebug(CODEC::debugId(),
10✔
1708
                     "Alpha band is promoted from 1 bit to 8 bit");
1709

1710
        auto poBand = new JP2OPJLikeRasterBand<CODEC, BASE>(
1,846✔
1711
            poDS, iBand, eDataType,
1712
            bPromoteTo8Bit ? 8 : localctx.psImage->comps[iBand - 1].prec,
918✔
1713
            bPromoteTo8Bit, nBlockXSize, nBlockYSize);
1714
        if (iBand == 1 && poCT != nullptr)
928✔
1715
            poBand->poCT = poCT;
24✔
1716
        poDS->SetBand(iBand, poBand);
928✔
1717
    }
1718

1719
    /* -------------------------------------------------------------------- */
1720
    /*      Create overview datasets.                                       */
1721
    /* -------------------------------------------------------------------- */
1722
    int nW = poDS->nRasterXSize;
759✔
1723
    int nH = poDS->nRasterYSize;
759✔
1724
    poDS->nParentXSize = poDS->nRasterXSize;
759✔
1725
    poDS->nParentYSize = poDS->nRasterYSize;
759✔
1726

1727
    /* Lower resolutions are not compatible with a color-table */
1728
    if (poCT != nullptr)
759✔
1729
        numResolutions = 0;
24✔
1730

1731
    if (poDS->bSingleTiled && poDS->bUseSetDecodeArea)
759✔
1732
    {
1733
        poDS->cacheNew(&localctx);
579✔
1734
    }
1735
    poDS->m_pnLastLevel = new int(-1);
759✔
1736

1737
    while (
117✔
1738
        poDS->nOverviewCount + 1 < numResolutions && (nW > 128 || nH > 128) &&
876✔
1739
        (poDS->bUseSetDecodeArea || ((nTileW % 2) == 0 && (nTileH % 2) == 0)))
120✔
1740
    {
1741
        // This must be this exact formula per the JPEG2000 standard
1742
        nW = (nW + 1) / 2;
117✔
1743
        nH = (nH + 1) / 2;
117✔
1744

1745
        poDS->papoOverviewDS = static_cast<JP2OPJLikeDataset<CODEC, BASE> **>(
117✔
1746
            CPLRealloc(poDS->papoOverviewDS,
234✔
1747
                       (poDS->nOverviewCount + 1) *
117✔
1748
                           sizeof(JP2OPJLikeDataset<CODEC, BASE> *)));
1749
        JP2OPJLikeDataset *poODS = new JP2OPJLikeDataset();
117✔
1750
        poODS->m_osFilename = poDS->m_osFilename;
117✔
1751
        poODS->nParentXSize = poDS->nRasterXSize;
117✔
1752
        poODS->nParentYSize = poDS->nRasterYSize;
117✔
1753
        poODS->SetDescription(poOpenInfo->pszFilename);
117✔
1754
        poODS->iLevel = poDS->nOverviewCount + 1;
117✔
1755
        poODS->bSingleTiled = poDS->bSingleTiled;
117✔
1756
        poODS->bUseSetDecodeArea = poDS->bUseSetDecodeArea;
117✔
1757
        poODS->nRedIndex = poDS->nRedIndex;
117✔
1758
        poODS->nGreenIndex = poDS->nGreenIndex;
117✔
1759
        poODS->nBlueIndex = poDS->nBlueIndex;
117✔
1760
        poODS->nAlphaIndex = poDS->nAlphaIndex;
117✔
1761
        if (!poDS->bUseSetDecodeArea)
117✔
1762
        {
1763
            nTileW /= 2;
83✔
1764
            nTileH /= 2;
83✔
1765
            nBlockXSize = static_cast<int>(nTileW);
83✔
1766
            nBlockYSize = static_cast<int>(nTileH);
83✔
1767
        }
1768
        else
1769
        {
1770
            nBlockXSize = std::min(nW, static_cast<int>(nTileW));
34✔
1771
            nBlockYSize = std::min(nH, static_cast<int>(nTileH));
34✔
1772
        }
1773

1774
        poODS->eColorSpace = poDS->eColorSpace;
117✔
1775
        poODS->nRasterXSize = nW;
117✔
1776
        poODS->nRasterYSize = nH;
117✔
1777
        poODS->nBands = poDS->nBands;
117✔
1778
        poODS->fp_ = poDS->fp_;
117✔
1779
        poODS->nCodeStreamStart = nCodeStreamStart;
117✔
1780
        poODS->nCodeStreamLength = nCodeStreamLength;
117✔
1781
        poODS->bIs420 = bIs420;
117✔
1782

1783
        if (poODS->bSingleTiled && poODS->bUseSetDecodeArea)
117✔
1784
        {
1785
            poODS->cache(poDS);
32✔
1786
        }
1787
        poODS->m_pnLastLevel = poDS->m_pnLastLevel;
117✔
1788
        poODS->m_bStrict = poDS->m_bStrict;
117✔
1789

1790
        poODS->m_nX0 = poDS->m_nX0;
117✔
1791
        poODS->m_nY0 = poDS->m_nY0;
117✔
1792

1793
        for (iBand = 1; iBand <= poDS->nBands; iBand++)
335✔
1794
        {
1795
            const bool bPromoteTo8Bit =
218✔
1796
                iBand == poDS->nAlphaIndex + 1 &&
237✔
1797
                localctx.psImage
19✔
1798
                        ->comps[(poDS->nAlphaIndex == 0 && poDS->nBands > 1)
19✔
1799
                                    ? 1
1800
                                    : 0]
1801
                        .prec == 8 &&
19✔
1802
                localctx.psImage->comps[poDS->nAlphaIndex].prec == 1 &&
246✔
1803
                CPLFetchBool(
9✔
1804
                    poOpenInfo->papszOpenOptions, "1BIT_ALPHA_PROMOTION",
9✔
1805
                    CPLTestBool(CPLGetConfigOption(
9✔
1806
                        "JP2OPENJPEG_PROMOTE_1BIT_ALPHA_AS_8BIT", "YES")));
1807

1808
            poODS->SetBand(iBand,
218✔
1809
                           new JP2OPJLikeRasterBand<CODEC, BASE>(
430✔
1810
                               poODS, iBand, eDataType,
1811
                               bPromoteTo8Bit
1812
                                   ? 8
1813
                                   : localctx.psImage->comps[iBand - 1].prec,
212✔
1814
                               bPromoteTo8Bit, nBlockXSize, nBlockYSize));
1815
        }
1816

1817
        poDS->papoOverviewDS[poDS->nOverviewCount++] = poODS;
117✔
1818
    }
1819

1820
    poDS->openCompleteJP2(&localctx);
759✔
1821

1822
    /* -------------------------------------------------------------------- */
1823
    /*      More metadata.                                                  */
1824
    /* -------------------------------------------------------------------- */
1825
    if (poDS->nBands > 1)
759✔
1826
    {
1827
        poDS->GDALDataset::SetMetadataItem("INTERLEAVE", "PIXEL",
72✔
1828
                                           "IMAGE_STRUCTURE");
1829
    }
1830

1831
    poOpenInfo->fpL = poDS->fp_;
759✔
1832
    vsi_l_offset nCurOffset = VSIFTellL(poDS->fp_);
759✔
1833
    poDS->LoadJP2Metadata(poOpenInfo);
759✔
1834
    VSIFSeekL(poDS->fp_, nCurOffset, SEEK_SET);
759✔
1835
    poOpenInfo->fpL = nullptr;
759✔
1836

1837
    poDS->bHasGeoreferencingAtOpening =
759✔
1838
        (!poDS->m_oSRS.IsEmpty() || poDS->nGCPCount != 0 ||
994✔
1839
         poDS->bGeoTransformValid);
235✔
1840

1841
    /* -------------------------------------------------------------------- */
1842
    /*      Vector layers                                                   */
1843
    /* -------------------------------------------------------------------- */
1844
    if (poOpenInfo->nOpenFlags & GDAL_OF_VECTOR)
759✔
1845
    {
1846
        poDS->LoadVectorLayers(CPLFetchBool(poOpenInfo->papszOpenOptions,
66✔
1847
                                            "OPEN_REMOTE_GML", false));
1848

1849
        // If file opened in vector-only mode and there's no vector,
1850
        // return
1851
        if ((poOpenInfo->nOpenFlags & GDAL_OF_RASTER) == 0 &&
70✔
1852
            poDS->GetLayerCount() == 0)
4✔
1853
        {
1854
            delete poDS;
2✔
1855
            return nullptr;
2✔
1856
        }
1857
    }
1858

1859
    /* -------------------------------------------------------------------- */
1860
    /*      Initialize any PAM information.                                 */
1861
    /* -------------------------------------------------------------------- */
1862
    poDS->SetDescription(poOpenInfo->pszFilename);
757✔
1863
    poDS->TryLoadXML(poOpenInfo->GetSiblingFiles());
757✔
1864

1865
    /* -------------------------------------------------------------------- */
1866
    /*      Check for overviews.                                            */
1867
    /* -------------------------------------------------------------------- */
1868
    poDS->oOvManager.Initialize(poDS, poOpenInfo);
757✔
1869

1870
    return poDS;
757✔
1871
}
1872

1873
/************************************************************************/
1874
/*                           WriteBox()                                 */
1875
/************************************************************************/
1876

1877
template <typename CODEC, typename BASE>
1878
bool JP2OPJLikeDataset<CODEC, BASE>::WriteBox(VSILFILE *fp, GDALJP2Box *poBox)
939✔
1879
{
1880
    GUInt32 nLBox;
1881
    GUInt32 nTBox;
1882

1883
    if (poBox == nullptr)
939✔
1884
        return true;
×
1885

1886
    nLBox = static_cast<int>(poBox->GetDataLength()) + 8;
939✔
1887
    nLBox = CPL_MSBWORD32(nLBox);
939✔
1888

1889
    memcpy(&nTBox, poBox->GetType(), 4);
939✔
1890

1891
    return VSIFWriteL(&nLBox, 4, 1, fp) == 1 &&
939✔
1892
           VSIFWriteL(&nTBox, 4, 1, fp) == 1 &&
1,878✔
1893
           VSIFWriteL(poBox->GetWritableData(),
939✔
1894
                      static_cast<int>(poBox->GetDataLength()), 1, fp) == 1;
1,878✔
1895
}
1896

1897
/************************************************************************/
1898
/*                         WriteGDALMetadataBox()                       */
1899
/************************************************************************/
1900

1901
template <typename CODEC, typename BASE>
1902
bool JP2OPJLikeDataset<CODEC, BASE>::WriteGDALMetadataBox(VSILFILE *fp,
19✔
1903
                                                          GDALDataset *poSrcDS,
1904
                                                          char **papszOptions)
1905
{
1906
    bool bRet = true;
19✔
1907
    GDALJP2Box *poBox = GDALJP2Metadata::CreateGDALMultiDomainMetadataXMLBox(
38✔
1908
        poSrcDS, CPLFetchBool(papszOptions, "MAIN_MD_DOMAIN_ONLY", false));
19✔
1909
    if (poBox)
19✔
1910
        bRet = WriteBox(fp, poBox);
6✔
1911
    delete poBox;
19✔
1912
    return bRet;
19✔
1913
}
1914

1915
/************************************************************************/
1916
/*                         WriteXMLBoxes()                              */
1917
/************************************************************************/
1918

1919
template <typename CODEC, typename BASE>
1920
bool JP2OPJLikeDataset<CODEC, BASE>::WriteXMLBoxes(VSILFILE *fp,
19✔
1921
                                                   GDALDataset *poSrcDS)
1922
{
1923
    bool bRet = true;
19✔
1924
    int nBoxes = 0;
19✔
1925
    GDALJP2Box **papoBoxes = GDALJP2Metadata::CreateXMLBoxes(poSrcDS, &nBoxes);
19✔
1926
    for (int i = 0; i < nBoxes; i++)
21✔
1927
    {
1928
        if (!WriteBox(fp, papoBoxes[i]))
2✔
1929
            bRet = false;
×
1930
        delete papoBoxes[i];
2✔
1931
    }
1932
    CPLFree(papoBoxes);
19✔
1933
    return bRet;
19✔
1934
}
1935

1936
/************************************************************************/
1937
/*                           WriteXMPBox()                              */
1938
/************************************************************************/
1939

1940
template <typename CODEC, typename BASE>
1941
bool JP2OPJLikeDataset<CODEC, BASE>::WriteXMPBox(VSILFILE *fp,
19✔
1942
                                                 GDALDataset *poSrcDS)
1943
{
1944
    bool bRet = true;
19✔
1945
    GDALJP2Box *poBox = GDALJP2Metadata::CreateXMPBox(poSrcDS);
19✔
1946
    if (poBox)
19✔
1947
        bRet = WriteBox(fp, poBox);
2✔
1948
    delete poBox;
19✔
1949
    return bRet;
19✔
1950
}
1951

1952
/************************************************************************/
1953
/*                           WriteIPRBox()                              */
1954
/************************************************************************/
1955

1956
template <typename CODEC, typename BASE>
1957
bool JP2OPJLikeDataset<CODEC, BASE>::WriteIPRBox(VSILFILE *fp,
19✔
1958
                                                 GDALDataset *poSrcDS)
1959
{
1960
    bool bRet = true;
19✔
1961
    GDALJP2Box *poBox = GDALJP2Metadata::CreateIPRBox(poSrcDS);
19✔
1962
    if (poBox)
19✔
1963
        bRet = WriteBox(fp, poBox);
2✔
1964
    delete poBox;
19✔
1965
    return bRet;
19✔
1966
}
1967

1968
/************************************************************************/
1969
/*                         FloorPowerOfTwo()                            */
1970
/************************************************************************/
1971

1972
static int FloorPowerOfTwo(int nVal)
542✔
1973
{
1974
    int nBits = 0;
542✔
1975
    while (nVal > 1)
3,791✔
1976
    {
1977
        nBits++;
3,249✔
1978
        nVal >>= 1;
3,249✔
1979
    }
1980
    return 1 << nBits;
542✔
1981
}
1982

1983
/************************************************************************/
1984
/*                          CreateCopy()                                */
1985
/************************************************************************/
1986

1987
template <typename CODEC, typename BASE>
1988
GDALDataset *JP2OPJLikeDataset<CODEC, BASE>::CreateCopy(
281✔
1989
    const char *pszFilename, GDALDataset *poSrcDS, CPL_UNUSED int bStrict,
1990
    char **papszOptions, GDALProgressFunc pfnProgress, void *pProgressData)
1991

1992
{
1993
    int nBands = poSrcDS->GetRasterCount();
281✔
1994
    int nXSize = poSrcDS->GetRasterXSize();
281✔
1995
    int nYSize = poSrcDS->GetRasterYSize();
281✔
1996

1997
    if (nBands == 0 || nBands > 16384)
281✔
1998
    {
1999
        CPLError(
2✔
2000
            CE_Failure, CPLE_NotSupported,
2001
            "Unable to export files with %d bands. Must be >= 1 and <= 16384",
2002
            nBands);
2003
        return nullptr;
2✔
2004
    }
2005

2006
    GDALColorTable *poCT = poSrcDS->GetRasterBand(1)->GetColorTable();
279✔
2007
    if (poCT != nullptr && nBands != 1)
279✔
2008
    {
2009
        CPLError(CE_Failure, CPLE_NotSupported,
1✔
2010
                 "JP2 driver only supports a color table for a "
2011
                 "single-band dataset");
2012
        return nullptr;
1✔
2013
    }
2014

2015
    GDALDataType eDataType = poSrcDS->GetRasterBand(1)->GetRasterDataType();
278✔
2016
    const int nDataTypeSize = GDALGetDataTypeSizeBytes(eDataType);
278✔
2017
    if (eDataType != GDT_Byte && eDataType != GDT_Int16 &&
278✔
2018
        eDataType != GDT_UInt16 && eDataType != GDT_Int32 &&
8✔
2019
        eDataType != GDT_UInt32)
2020
    {
2021
        CPLError(CE_Failure, CPLE_NotSupported,
6✔
2022
                 "JP2 driver only supports creating Byte, GDT_Int16, "
2023
                 "GDT_UInt16, GDT_Int32, GDT_UInt32");
2024
        return nullptr;
6✔
2025
    }
2026

2027
    const bool bInspireTG = CPLFetchBool(papszOptions, "INSPIRE_TG", false);
272✔
2028

2029
    /* -------------------------------------------------------------------- */
2030
    /*      Analyze creation options.                                       */
2031
    /* -------------------------------------------------------------------- */
2032
    auto eCodecFormat = CODEC::cvtenum(JP2_CODEC_J2K);
272✔
2033
    const char *pszCodec = CSLFetchNameValueDef(papszOptions, "CODEC", nullptr);
272✔
2034
    if (pszCodec)
272✔
2035
    {
2036
        if (EQUAL(pszCodec, "JP2"))
14✔
2037
            eCodecFormat = CODEC::cvtenum(JP2_CODEC_JP2);
5✔
2038
        else if (EQUAL(pszCodec, "J2K"))
9✔
2039
            eCodecFormat = CODEC::cvtenum(JP2_CODEC_J2K);
9✔
2040
        else
2041
        {
2042
            CPLError(CE_Warning, CPLE_NotSupported,
×
2043
                     "Unsupported value for CODEC : %s. Defaulting to J2K",
2044
                     pszCodec);
2045
        }
2046
    }
2047
    else
2048
    {
2049
        if (strlen(pszFilename) > 4 &&
258✔
2050
            EQUAL(pszFilename + strlen(pszFilename) - 4, ".JP2"))
258✔
2051
        {
2052
            eCodecFormat = CODEC::cvtenum(JP2_CODEC_JP2);
223✔
2053
        }
2054
    }
2055
    if (eCodecFormat != CODEC::cvtenum(JP2_CODEC_JP2) && bInspireTG)
272✔
2056
    {
2057
        CPLError(CE_Warning, CPLE_NotSupported,
1✔
2058
                 "INSPIRE_TG=YES mandates CODEC=JP2 (TG requirement 21)");
2059
        return nullptr;
1✔
2060
    }
2061

2062
    // NOTE: if changing the default block size, the logic in nitfdataset.cpp
2063
    // CreateCopy() will have to be changed as well.
2064
    int nBlockXSize =
271✔
2065
        atoi(CSLFetchNameValueDef(papszOptions, "BLOCKXSIZE", "1024"));
271✔
2066
    int nBlockYSize =
271✔
2067
        atoi(CSLFetchNameValueDef(papszOptions, "BLOCKYSIZE", "1024"));
271✔
2068
    if (nBlockXSize <= 0 || nBlockYSize <= 0)
271✔
2069
    {
2070
        CPLError(CE_Failure, CPLE_NotSupported, "Invalid block size");
×
2071
        return nullptr;
×
2072
    }
2073

2074
    // By default do not generate tile sizes larger than the dataset
2075
    // dimensions
2076
    if (!CPLFetchBool(papszOptions, "BLOCKSIZE_STRICT", false) &&
542✔
2077
        !CPLFetchBool(papszOptions, "@BLOCKSIZE_STRICT", false))
271✔
2078
    {
2079
        if (nBlockXSize < 32 || nBlockYSize < 32)
267✔
2080
        {
2081
            CPLError(CE_Failure, CPLE_NotSupported, "Invalid block size");
×
2082
            return nullptr;
×
2083
        }
2084

2085
        if (nXSize < nBlockXSize)
267✔
2086
        {
2087
            CPLDebug(CODEC::debugId(), "Adjusting block width from %d to %d",
242✔
2088
                     nBlockXSize, nXSize);
2089
            nBlockXSize = nXSize;
242✔
2090
        }
2091
        if (nYSize < nBlockYSize)
267✔
2092
        {
2093
            CPLDebug(CODEC::debugId(), "Adjusting block width from %d to %d",
243✔
2094
                     nBlockYSize, nYSize);
2095
            nBlockYSize = nYSize;
243✔
2096
        }
2097
    }
2098

2099
    JP2_PROG_ORDER eProgOrder = JP2_LRCP;
271✔
2100
    const char *pszPROGORDER =
2101
        CSLFetchNameValueDef(papszOptions, "PROGRESSION", "LRCP");
271✔
2102
    if (EQUAL(pszPROGORDER, "LRCP"))
271✔
2103
        eProgOrder = JP2_LRCP;
271✔
2104
    else if (EQUAL(pszPROGORDER, "RLCP"))
×
2105
        eProgOrder = JP2_RLCP;
×
2106
    else if (EQUAL(pszPROGORDER, "RPCL"))
×
2107
        eProgOrder = JP2_RPCL;
×
2108
    else if (EQUAL(pszPROGORDER, "PCRL"))
×
2109
        eProgOrder = JP2_PCRL;
×
2110
    else if (EQUAL(pszPROGORDER, "CPRL"))
×
2111
        eProgOrder = JP2_CPRL;
×
2112
    else
2113
    {
2114
        CPLError(CE_Warning, CPLE_NotSupported,
×
2115
                 "Unsupported value for PROGRESSION : %s. Defaulting to LRCP",
2116
                 pszPROGORDER);
2117
    }
2118

2119
    const bool bIsIrreversible =
271✔
2120
        !CPLFetchBool(papszOptions, "REVERSIBLE", poCT != nullptr);
271✔
2121

2122
    std::vector<double> adfRates;
542✔
2123
    const char *pszQuality =
2124
        CSLFetchNameValueDef(papszOptions, "QUALITY", nullptr);
271✔
2125
    double dfDefaultQuality = (poCT != nullptr) ? 100.0 : 25.0;
271✔
2126
    if (pszQuality)
271✔
2127
    {
2128
        char **papszTokens =
2129
            CSLTokenizeStringComplex(pszQuality, ",", FALSE, FALSE);
41✔
2130
        for (int i = 0; papszTokens[i] != nullptr; i++)
158✔
2131
        {
2132
            double dfQuality = CPLAtof(papszTokens[i]);
117✔
2133
            if (dfQuality > 0 && dfQuality <= 100)
117✔
2134
            {
2135
                double dfRate = 100 / dfQuality;
117✔
2136
                adfRates.push_back(dfRate);
117✔
2137
            }
2138
            else
2139
            {
2140
                CPLError(CE_Warning, CPLE_NotSupported,
×
2141
                         "Unsupported value for QUALITY: %s. Defaulting to "
2142
                         "single-layer, with quality=%.0f",
2143
                         papszTokens[i], dfDefaultQuality);
×
2144
                adfRates.resize(0);
×
2145
                break;
×
2146
            }
2147
        }
2148
        if (papszTokens[0] == nullptr)
41✔
2149
        {
2150
            CPLError(CE_Warning, CPLE_NotSupported,
×
2151
                     "Unsupported value for QUALITY: %s. Defaulting to "
2152
                     "single-layer, with quality=%.0f",
2153
                     pszQuality, dfDefaultQuality);
2154
        }
2155
        CSLDestroy(papszTokens);
41✔
2156
    }
2157
    if (adfRates.empty())
271✔
2158
    {
2159
        adfRates.push_back(100. / dfDefaultQuality);
230✔
2160
        assert(!adfRates.empty());
230✔
2161
    }
2162

2163
    if (poCT != nullptr && (bIsIrreversible || adfRates.back() != 1.0))
271✔
2164
    {
2165
        CPLError(CE_Warning, CPLE_AppDefined,
2✔
2166
                 "Encoding a dataset with a color table with REVERSIBLE != YES "
2167
                 "or QUALITY != 100 will likely lead to bad visual results");
2168
    }
2169

2170
    const int nMaxTileDim = std::max(nBlockXSize, nBlockYSize);
271✔
2171
    const int nMinTileDim = std::min(nBlockXSize, nBlockYSize);
271✔
2172
    int nNumResolutions = 1;
271✔
2173
    /* Pickup a reasonable value compatible with PROFILE_1 requirements */
2174
    while ((nMaxTileDim >> (nNumResolutions - 1)) > 128 &&
362✔
2175
           (nMinTileDim >> nNumResolutions) > 0)
92✔
2176
        nNumResolutions++;
91✔
2177
    int nMinProfile1Resolutions = nNumResolutions;
271✔
2178
    const char *pszResolutions =
2179
        CSLFetchNameValueDef(papszOptions, "RESOLUTIONS", nullptr);
271✔
2180
    if (pszResolutions)
271✔
2181
    {
2182
        nNumResolutions = atoi(pszResolutions);
10✔
2183
        if (nNumResolutions <= 0 || nNumResolutions >= 32 ||
10✔
2184
            (nMinTileDim >> nNumResolutions) == 0 ||
9✔
2185
            (nMaxTileDim >> nNumResolutions) == 0)
9✔
2186
        {
2187
            CPLError(CE_Warning, CPLE_NotSupported,
1✔
2188
                     "Unsupported value for RESOLUTIONS : %s. Defaulting to %d",
2189
                     pszResolutions, nMinProfile1Resolutions);
2190
            nNumResolutions = nMinProfile1Resolutions;
1✔
2191
        }
2192
    }
2193
    int nRedBandIndex = -1;
271✔
2194
    int nGreenBandIndex = -1;
271✔
2195
    int nBlueBandIndex = -1;
271✔
2196
    int nAlphaBandIndex = -1;
271✔
2197
    for (int i = 0; i < nBands; i++)
606✔
2198
    {
2199
        GDALColorInterp eInterp =
2200
            poSrcDS->GetRasterBand(i + 1)->GetColorInterpretation();
335✔
2201
        if (eInterp == GCI_RedBand)
335✔
2202
            nRedBandIndex = i;
15✔
2203
        else if (eInterp == GCI_GreenBand)
320✔
2204
            nGreenBandIndex = i;
15✔
2205
        else if (eInterp == GCI_BlueBand)
305✔
2206
            nBlueBandIndex = i;
15✔
2207
        else if (eInterp == GCI_AlphaBand)
290✔
2208
            nAlphaBandIndex = i;
7✔
2209
    }
2210
    const char *pszAlpha = CSLFetchNameValue(papszOptions, "ALPHA");
271✔
2211
    if (nAlphaBandIndex < 0 && nBands > 1 && pszAlpha != nullptr &&
273✔
2212
        CPLTestBool(pszAlpha))
2✔
2213
    {
2214
        nAlphaBandIndex = nBands - 1;
2✔
2215
    }
2216

2217
    const char *pszYCBCR420 = CSLFetchNameValue(papszOptions, "YCBCR420");
271✔
2218
    int bYCBCR420 = FALSE;
271✔
2219
    if (pszYCBCR420 && CPLTestBool(pszYCBCR420))
271✔
2220
    {
2221
        if ((nBands == 3 || nBands == 4) && eDataType == GDT_Byte &&
2✔
2222
            nRedBandIndex == 0 && nGreenBandIndex == 1 && nBlueBandIndex == 2)
2✔
2223
        {
2224
            if (((nXSize % 2) == 0 && (nYSize % 2) == 0 &&
2✔
2225
                 (nBlockXSize % 2) == 0 && (nBlockYSize % 2) == 0))
2✔
2226
            {
2227
                bYCBCR420 = TRUE;
2✔
2228
            }
2229
            else
2230
            {
2231
                CPLError(CE_Warning, CPLE_NotSupported,
×
2232
                         "YCBCR420 unsupported when image size and/or tile "
2233
                         "size are not multiple of 2");
2234
            }
2235
        }
2236
        else
2237
        {
2238
            CPLError(CE_Warning, CPLE_NotSupported,
×
2239
                     "YCBCR420 unsupported with this image band count and/or "
2240
                     "data byte");
2241
        }
2242
    }
2243

2244
    const char *pszYCC = CSLFetchNameValue(papszOptions, "YCC");
271✔
2245
    int bYCC = ((nBands == 3 || nBands == 4) &&
292✔
2246
                CPLTestBool(CSLFetchNameValueDef(papszOptions, "YCC", "TRUE")));
21✔
2247

2248
    if (bYCBCR420 && bYCC)
271✔
2249
    {
2250
        if (pszYCC != nullptr)
2✔
2251
        {
2252
            CPLError(CE_Warning, CPLE_NotSupported,
×
2253
                     "YCC unsupported when YCbCr requesting");
2254
        }
2255
        bYCC = FALSE;
2✔
2256
    }
2257

2258
    /* -------------------------------------------------------------------- */
2259
    /*      Deal with codeblocks size                                       */
2260
    /* -------------------------------------------------------------------- */
2261

2262
    int nCblockW =
2263
        atoi(CSLFetchNameValueDef(papszOptions, "CODEBLOCK_WIDTH", "64"));
271✔
2264
    int nCblockH =
2265
        atoi(CSLFetchNameValueDef(papszOptions, "CODEBLOCK_HEIGHT", "64"));
271✔
2266
    if (nCblockW < 4 || nCblockW > 1024 || nCblockH < 4 || nCblockH > 1024)
271✔
2267
    {
2268
        CPLError(CE_Warning, CPLE_NotSupported,
4✔
2269
                 "Invalid values for codeblock size. Defaulting to 64x64");
2270
        nCblockW = 64;
4✔
2271
        nCblockH = 64;
4✔
2272
    }
2273
    else if (nCblockW * nCblockH > 4096)
267✔
2274
    {
2275
        CPLError(CE_Warning, CPLE_NotSupported,
1✔
2276
                 "Invalid values for codeblock size. "
2277
                 "CODEBLOCK_WIDTH * CODEBLOCK_HEIGHT should be <= 4096. "
2278
                 "Defaulting to 64x64");
2279
        nCblockW = 64;
1✔
2280
        nCblockH = 64;
1✔
2281
    }
2282
    int nCblockW_po2 = FloorPowerOfTwo(nCblockW);
271✔
2283
    int nCblockH_po2 = FloorPowerOfTwo(nCblockH);
271✔
2284
    if (nCblockW_po2 != nCblockW || nCblockH_po2 != nCblockH)
271✔
2285
    {
2286
        CPLError(CE_Warning, CPLE_NotSupported,
1✔
2287
                 "Non power of two values used for codeblock size. "
2288
                 "Using to %dx%d",
2289
                 nCblockW_po2, nCblockH_po2);
2290
    }
2291
    nCblockW = nCblockW_po2;
271✔
2292
    nCblockH = nCblockH_po2;
271✔
2293

2294
    /* -------------------------------------------------------------------- */
2295
    /*      Deal with codestream PROFILE                                    */
2296
    /* -------------------------------------------------------------------- */
2297
    const char *pszProfile =
2298
        CSLFetchNameValueDef(papszOptions, "PROFILE", "AUTO");
271✔
2299
    int bProfile1 = FALSE;
271✔
2300
    if (EQUAL(pszProfile, "UNRESTRICTED"))
271✔
2301
    {
2302
        bProfile1 = FALSE;
1✔
2303
        if (bInspireTG)
1✔
2304
        {
2305
            CPLError(CE_Failure, CPLE_NotSupported,
1✔
2306
                     "INSPIRE_TG=YES mandates PROFILE=PROFILE_1 (TG "
2307
                     "requirement 21)");
2308
            return nullptr;
1✔
2309
        }
2310
    }
2311
    else if (EQUAL(pszProfile, "UNRESTRICTED_FORCED"))
270✔
2312
    {
2313
        bProfile1 = FALSE;
×
2314
    }
2315
    else if (EQUAL(pszProfile,
270✔
2316
                   "PROFILE_1_FORCED")) /* For debug only: can produce
2317
                                           inconsistent codestream */
2318
    {
2319
        bProfile1 = TRUE;
×
2320
    }
2321
    else
2322
    {
2323
        if (!(EQUAL(pszProfile, "PROFILE_1") || EQUAL(pszProfile, "AUTO")))
270✔
2324
        {
2325
            CPLError(CE_Warning, CPLE_NotSupported,
×
2326
                     "Unsupported value for PROFILE : %s. Defaulting to AUTO",
2327
                     pszProfile);
2328
            pszProfile = "AUTO";
×
2329
        }
2330

2331
        bProfile1 = TRUE;
270✔
2332
        const char *pszReq21OrEmpty = bInspireTG ? " (TG requirement 21)" : "";
270✔
2333
        if ((nBlockXSize != nXSize || nBlockYSize != nYSize) &&
270✔
2334
            (nBlockXSize != nBlockYSize || nBlockXSize > 1024 ||
24✔
2335
             nBlockYSize > 1024))
19✔
2336
        {
2337
            bProfile1 = FALSE;
5✔
2338
            if (bInspireTG || EQUAL(pszProfile, "PROFILE_1"))
5✔
2339
            {
2340
                CPLError(
2✔
2341
                    CE_Failure, CPLE_NotSupported,
2342
                    "Tile dimensions incompatible with PROFILE_1%s. "
2343
                    "Should be whole image or square with dimension <= 1024.",
2344
                    pszReq21OrEmpty);
2345
                return nullptr;
2✔
2346
            }
2347
        }
2348
        if ((nMaxTileDim >> (nNumResolutions - 1)) > 128)
268✔
2349
        {
2350
            bProfile1 = FALSE;
4✔
2351
            if (bInspireTG || EQUAL(pszProfile, "PROFILE_1"))
4✔
2352
            {
2353
                CPLError(CE_Failure, CPLE_NotSupported,
1✔
2354
                         "Number of resolutions incompatible with PROFILE_1%s. "
2355
                         "Should be at least %d.",
2356
                         pszReq21OrEmpty, nMinProfile1Resolutions);
2357
                return nullptr;
1✔
2358
            }
2359
        }
2360
        if (nCblockW > 64 || nCblockH > 64)
267✔
2361
        {
2362
            bProfile1 = FALSE;
2✔
2363
            if (bInspireTG || EQUAL(pszProfile, "PROFILE_1"))
2✔
2364
            {
2365
                CPLError(CE_Failure, CPLE_NotSupported,
2✔
2366
                         "Codeblock width incompatible with PROFILE_1%s. "
2367
                         "Codeblock width or height should be <= 64.",
2368
                         pszReq21OrEmpty);
2369
                return nullptr;
2✔
2370
            }
2371
        }
2372
    }
2373

2374
    /* -------------------------------------------------------------------- */
2375
    /*      Work out the precision.                                         */
2376
    /* -------------------------------------------------------------------- */
2377
    int nBits;
2378
    const int nDTBits = GDALGetDataTypeSizeBits(eDataType);
265✔
2379

2380
    if (CSLFetchNameValue(papszOptions, "NBITS") != nullptr)
265✔
2381
    {
2382
        nBits = atoi(CSLFetchNameValue(papszOptions, "NBITS"));
22✔
2383
        if (bInspireTG &&
22✔
2384
            !(nBits == 1 || nBits == 8 || nBits == 16 || nBits == 32))
1✔
2385
        {
2386
            CPLError(CE_Failure, CPLE_NotSupported,
1✔
2387
                     "INSPIRE_TG=YES mandates NBITS=1,8,16 or 32 (TG "
2388
                     "requirement 24)");
2389
            return nullptr;
1✔
2390
        }
2391
    }
2392
    else if (poSrcDS->GetRasterBand(1)->GetMetadataItem(
243✔
2393
                 "NBITS", "IMAGE_STRUCTURE") != nullptr)
243✔
2394
    {
2395
        nBits = atoi(poSrcDS->GetRasterBand(1)->GetMetadataItem(
3✔
2396
            "NBITS", "IMAGE_STRUCTURE"));
2397
        if (bInspireTG &&
3✔
2398
            !(nBits == 1 || nBits == 8 || nBits == 16 || nBits == 32))
1✔
2399
        {
2400
            /* Implements "NOTE If the original data do not satisfy this "
2401
               "requirement, they will be converted in a representation using "
2402
               "the next higher power of 2" */
2403
            nBits = nDTBits;
1✔
2404
        }
2405
    }
2406
    else
2407
    {
2408
        nBits = nDTBits;
240✔
2409
    }
2410

2411
    if ((nDTBits == 8 && nBits > 8) ||
264✔
2412
        (nDTBits == 16 && (nBits <= 8 || nBits > 16)) ||
264✔
2413
        (nDTBits == 32 && (nBits <= 16 || nBits > 32)))
2✔
2414
    {
2415
        CPLError(CE_Warning, CPLE_NotSupported,
×
2416
                 "Inconsistent NBITS value with data type. Using %d", nDTBits);
2417
    }
2418

2419
    /* -------------------------------------------------------------------- */
2420
    /*      Georeferencing options                                          */
2421
    /* -------------------------------------------------------------------- */
2422

2423
    bool bGMLJP2Option = CPLFetchBool(papszOptions, "GMLJP2", true);
264✔
2424
    int nGMLJP2Version = 1;
264✔
2425
    const char *pszGMLJP2V2Def =
2426
        CSLFetchNameValue(papszOptions, "GMLJP2V2_DEF");
264✔
2427
    if (pszGMLJP2V2Def != nullptr)
264✔
2428
    {
2429
        bGMLJP2Option = true;
28✔
2430
        nGMLJP2Version = 2;
28✔
2431
        if (bInspireTG)
28✔
2432
        {
2433
            CPLError(CE_Warning, CPLE_NotSupported,
×
2434
                     "INSPIRE_TG=YES is only compatible with GMLJP2 v1");
2435
            return nullptr;
×
2436
        }
2437
    }
2438
    const bool bGeoJP2Option = CPLFetchBool(papszOptions, "GeoJP2", true);
264✔
2439

2440
    GDALJP2Metadata oJP2MD;
528✔
2441

2442
    int bGeoreferencingCompatOfGeoJP2 = FALSE;
264✔
2443
    int bGeoreferencingCompatOfGMLJP2 = FALSE;
264✔
2444
    if (eCodecFormat == CODEC::cvtenum(JP2_CODEC_JP2) &&
270✔
2445
        (bGMLJP2Option || bGeoJP2Option))
6✔
2446
    {
2447
        if (poSrcDS->GetGCPCount() > 0)
219✔
2448
        {
2449
            bGeoreferencingCompatOfGeoJP2 = TRUE;
3✔
2450
            oJP2MD.SetGCPs(poSrcDS->GetGCPCount(), poSrcDS->GetGCPs());
3✔
2451
            oJP2MD.SetSpatialRef(poSrcDS->GetGCPSpatialRef());
3✔
2452
        }
2453
        else
2454
        {
2455
            const OGRSpatialReference *poSRS = poSrcDS->GetSpatialRef();
216✔
2456
            if (poSRS)
216✔
2457
            {
2458
                bGeoreferencingCompatOfGeoJP2 = TRUE;
57✔
2459
                oJP2MD.SetSpatialRef(poSRS);
57✔
2460
            }
2461
            GDALGeoTransform gt;
216✔
2462
            if (poSrcDS->GetGeoTransform(gt) == CE_None)
216✔
2463
            {
2464
                bGeoreferencingCompatOfGeoJP2 = TRUE;
164✔
2465
                oJP2MD.SetGeoTransform(gt);
164✔
2466
                if (poSRS && !poSRS->IsEmpty())
164✔
2467
                {
2468
                    bGeoreferencingCompatOfGMLJP2 =
57✔
2469
                        GDALJP2Metadata::IsSRSCompatible(poSRS);
57✔
2470
                    if (!bGeoreferencingCompatOfGMLJP2)
57✔
2471
                    {
2472
                        CPLDebug(
1✔
2473
                            CODEC::debugId(),
2474
                            "Cannot write GMLJP2 box due to unsupported SRS");
2475
                    }
2476
                }
2477
            }
2478
        }
2479
        if (poSrcDS->GetMetadata("RPC") != nullptr)
219✔
2480
        {
2481
            oJP2MD.SetRPCMD(poSrcDS->GetMetadata("RPC"));
1✔
2482
            bGeoreferencingCompatOfGeoJP2 = TRUE;
1✔
2483
        }
2484

2485
        const char *pszAreaOrPoint =
2486
            poSrcDS->GetMetadataItem(GDALMD_AREA_OR_POINT);
219✔
2487
        oJP2MD.bPixelIsPoint = pszAreaOrPoint != nullptr &&
260✔
2488
                               EQUAL(pszAreaOrPoint, GDALMD_AOP_POINT);
41✔
2489

2490
        if (bGMLJP2Option &&
434✔
2491
            CPLGetConfigOption("GMLJP2OVERRIDE", nullptr) != nullptr)
215✔
2492
        {
2493
            // Force V1 since this is the branch in which the hack is
2494
            // implemented
2495
            nGMLJP2Version = 1;
7✔
2496
            bGeoreferencingCompatOfGMLJP2 = TRUE;
7✔
2497
        }
2498
    }
2499

2500
    if (CSLFetchNameValue(papszOptions, "GMLJP2") != nullptr && bGMLJP2Option &&
264✔
2501
        !bGeoreferencingCompatOfGMLJP2)
2502
    {
2503
        CPLError(CE_Warning, CPLE_AppDefined,
×
2504
                 "GMLJP2 box was explicitly required but cannot be written due "
2505
                 "to lack of georeferencing and/or unsupported georeferencing "
2506
                 "for GMLJP2");
2507
    }
2508

2509
    if (CSLFetchNameValue(papszOptions, "GeoJP2") != nullptr && bGeoJP2Option &&
264✔
2510
        !bGeoreferencingCompatOfGeoJP2)
2511
    {
2512
        CPLError(CE_Warning, CPLE_AppDefined,
×
2513
                 "GeoJP2 box was explicitly required but cannot be written due "
2514
                 "to lack of georeferencing");
2515
    }
2516
    const bool bGeoBoxesAfter =
2517
        CPLFetchBool(papszOptions, "GEOBOXES_AFTER_JP2C", bInspireTG);
264✔
2518
    GDALJP2Box *poGMLJP2Box = nullptr;
264✔
2519
    if (eCodecFormat == CODEC::cvtenum(JP2_CODEC_JP2) && bGMLJP2Option &&
264✔
2520
        bGeoreferencingCompatOfGMLJP2)
2521
    {
2522
        if (nGMLJP2Version == 1)
61✔
2523
            poGMLJP2Box = oJP2MD.CreateGMLJP2(nXSize, nYSize);
33✔
2524
        else
2525
            poGMLJP2Box =
2526
                oJP2MD.CreateGMLJP2V2(nXSize, nYSize, pszGMLJP2V2Def, poSrcDS);
28✔
2527
        if (poGMLJP2Box == nullptr)
61✔
2528
            return nullptr;
3✔
2529
    }
2530

2531
    /* ---------------------------------------------------------------- */
2532
    /* If the input driver is identifed as "GEORASTER" the following    */
2533
    /* section will try to dump a ORACLE GeoRaster JP2 BLOB into a file */
2534
    /* ---------------------------------------------------------------- */
2535

2536
    if (EQUAL(poSrcDS->GetDriverName(), "GEORASTER"))
261✔
2537
    {
2538
        const char *pszGEOR_compress =
2539
            poSrcDS->GetMetadataItem("COMPRESSION", "IMAGE_STRUCTURE");
×
2540

2541
        if (pszGEOR_compress == nullptr)
×
2542
        {
2543
            pszGEOR_compress = "NONE";
×
2544
        }
2545

2546
        /* Check if the JP2 BLOB needs re-shaping */
2547

2548
        bool bGEOR_reshape = false;
×
2549

2550
        const char *apszIgnoredOptions[] = {"BLOCKXSIZE",
×
2551
                                            "BLOCKYSIZE",
2552
                                            "QUALITY",
2553
                                            "REVERSIBLE",
2554
                                            "RESOLUTIONS",
2555
                                            "PROGRESSION",
2556
                                            "SOP",
2557
                                            "EPH",
2558
                                            "YCBCR420",
2559
                                            "YCC",
2560
                                            "NBITS",
2561
                                            "1BIT_ALPHA",
2562
                                            "PRECINCTS",
2563
                                            "TILEPARTS",
2564
                                            "CODEBLOCK_WIDTH",
2565
                                            "CODEBLOCK_HEIGHT",
2566
                                            "PLT",
2567
                                            "TLM",
2568
                                            nullptr};
2569

2570
        for (int i = 0; apszIgnoredOptions[i]; i++)
×
2571
        {
2572
            if (CSLFetchNameValue(papszOptions, apszIgnoredOptions[i]))
×
2573
            {
2574
                bGEOR_reshape = true;
×
2575
            }
2576
        }
2577

2578
        if (CSLFetchNameValue(papszOptions, "USE_SRC_CODESTREAM"))
×
2579
        {
2580
            bGEOR_reshape = false;
×
2581
        }
2582

2583
        char **papszGEOR_files = poSrcDS->GetFileList();
×
2584

2585
        if (EQUAL(pszGEOR_compress, "JP2-F") && CSLCount(papszGEOR_files) > 0 &&
×
2586
            bGEOR_reshape == false)
×
2587
        {
2588

2589
            const char *pszVsiOciLob = papszGEOR_files[0];
×
2590

2591
            VSILFILE *fpBlob = VSIFOpenL(pszVsiOciLob, "r");
×
2592
            if (fpBlob == nullptr)
×
2593
            {
2594
                CPLError(CE_Failure, CPLE_AppDefined, "Cannot open %s",
×
2595
                         pszVsiOciLob);
2596
                delete poGMLJP2Box;
×
2597
                return nullptr;
×
2598
            }
2599
            VSILFILE *fp = VSIFOpenL(pszFilename, "w+b");
×
2600
            if (fp == nullptr)
×
2601
            {
2602
                CPLError(CE_Failure, CPLE_AppDefined, "Cannot create %s",
×
2603
                         pszFilename);
2604
                delete poGMLJP2Box;
×
2605
                VSIFCloseL(fpBlob);
×
2606
                return nullptr;
×
2607
            }
2608

2609
            VSIFSeekL(fpBlob, 0, SEEK_END);
×
2610

2611
            size_t nBlobSize = static_cast<size_t>(VSIFTellL(fpBlob));
×
2612
            size_t nChunk = GDALGetCacheMax() / 4;
×
2613
            size_t nSize = 0;
×
2614
            size_t nCount = 0;
×
2615

2616
            void *pBuffer = VSI_MALLOC_VERBOSE(nChunk);
×
2617
            if (pBuffer == nullptr)
×
2618
            {
2619
                delete poGMLJP2Box;
×
2620
                VSIFCloseL(fpBlob);
×
2621
                VSIFCloseL(fp);
×
2622
                return nullptr;
×
2623
            }
2624

2625
            VSIFSeekL(fpBlob, 0, SEEK_SET);
×
2626

2627
            while ((nSize = VSIFReadL(pBuffer, 1, nChunk, fpBlob)) > 0)
×
2628
            {
2629
                VSIFWriteL(pBuffer, 1, nSize, fp);
×
2630
                nCount += nSize;
×
2631
                pfnProgress(static_cast<double>(nCount) /
×
2632
                                static_cast<double>(nBlobSize),
2633
                            nullptr, pProgressData);
2634
            }
2635

2636
            CPLFree(pBuffer);
×
2637
            VSIFCloseL(fpBlob);
×
2638

2639
            VSIFCloseL(fp);
×
2640

2641
            /* Return the GDALDaset object */
2642

2643
            GDALOpenInfo oOpenInfo(pszFilename, GA_Update);
×
2644
            GDALDataset *poDS = JP2OPJLikeDataset::Open(&oOpenInfo);
×
2645

2646
            /* Copy essential metadata */
2647

2648
            GDALGeoTransform gt;
×
2649
            if (poSrcDS->GetGeoTransform(gt) == CE_None)
×
2650
            {
2651
                poDS->SetGeoTransform(gt);
×
2652
            }
2653

2654
            const OGRSpatialReference *poSRS = poSrcDS->GetSpatialRef();
×
2655
            if (poSRS)
×
2656
            {
2657
                poDS->SetSpatialRef(poSRS);
×
2658
            }
2659

2660
            delete poGMLJP2Box;
×
2661
            return poDS;
×
2662
        }
2663
    }
2664

2665
    /* -------------------------------------------------------------------- */
2666
    /*      Setup encoder                                                   */
2667
    /* -------------------------------------------------------------------- */
2668

2669
    JP2OPJLikeDataset oTmpDS;
522✔
2670
    int numThreads = oTmpDS.GetNumThreads();
261✔
2671

2672
    CODEC localctx;
261✔
2673
    localctx.allocComponentParams(nBands);
261✔
2674
    int iBand;
2675
    int bSamePrecision = TRUE;
261✔
2676
    int b1BitAlpha = FALSE;
261✔
2677
    for (iBand = 0; iBand < nBands; iBand++)
586✔
2678
    {
2679
        localctx.pasBandParams[iBand].x0 = 0;
325✔
2680
        localctx.pasBandParams[iBand].y0 = 0;
325✔
2681
        if (bYCBCR420 && (iBand == 1 || iBand == 2))
325✔
2682
        {
2683
            localctx.pasBandParams[iBand].dx = 2;
4✔
2684
            localctx.pasBandParams[iBand].dy = 2;
4✔
2685
            localctx.pasBandParams[iBand].w = nXSize / 2;
4✔
2686
            localctx.pasBandParams[iBand].h = nYSize / 2;
4✔
2687
        }
2688
        else
2689
        {
2690
            localctx.pasBandParams[iBand].dx = 1;
321✔
2691
            localctx.pasBandParams[iBand].dy = 1;
321✔
2692
            localctx.pasBandParams[iBand].w = nXSize;
321✔
2693
            localctx.pasBandParams[iBand].h = nYSize;
321✔
2694
        }
2695

2696
        localctx.pasBandParams[iBand].sgnd =
325✔
2697
            (eDataType == GDT_Int16 || eDataType == GDT_Int32);
325✔
2698
        localctx.pasBandParams[iBand].prec = nBits;
325✔
2699

2700
        const char *pszNBits =
2701
            poSrcDS->GetRasterBand(iBand + 1)->GetMetadataItem(
325✔
2702
                "NBITS", "IMAGE_STRUCTURE");
2703
        /* Recommendation 38 In the case of an opacity channel, the bit depth
2704
         * should be 1-bit. */
2705
        if (iBand == nAlphaBandIndex &&
334✔
2706
            ((pszNBits != nullptr && EQUAL(pszNBits, "1")) ||
×
2707
             CPLFetchBool(papszOptions, "1BIT_ALPHA", bInspireTG)))
9✔
2708
        {
2709
            if (iBand != nBands - 1 && nBits != 1)
3✔
2710
            {
2711
                /* Might be a bug in openjpeg, but it seems that if the alpha */
2712
                /* band is the first one, it would select 1-bit for all
2713
                 * channels... */
2714
                CPLError(CE_Warning, CPLE_NotSupported,
×
2715
                         "Cannot output 1-bit alpha channel if it is not the "
2716
                         "last one");
2717
            }
2718
            else
2719
            {
2720
                CPLDebug(CODEC::debugId(), "Using 1-bit alpha channel");
3✔
2721
                localctx.pasBandParams[iBand].sgnd = 0;
3✔
2722
                localctx.pasBandParams[iBand].prec = 1;
3✔
2723
                bSamePrecision = FALSE;
3✔
2724
                b1BitAlpha = TRUE;
3✔
2725
            }
2726
        }
2727
    }
2728

2729
    if (bInspireTG && nAlphaBandIndex >= 0 && !b1BitAlpha)
261✔
2730
    {
2731
        CPLError(
×
2732
            CE_Warning, CPLE_NotSupported,
2733
            "INSPIRE_TG=YES recommends 1BIT_ALPHA=YES (Recommendation 38)");
2734
    }
2735
    auto eColorSpace = CODEC::cvtenum(JP2_CLRSPC_GRAY);
261✔
2736

2737
    if (bYCBCR420)
261✔
2738
    {
2739
        eColorSpace = CODEC::cvtenum(JP2_CLRSPC_SYCC);
2✔
2740
    }
2741
    else if ((nBands == 3 || nBands == 4) && nRedBandIndex >= 0 &&
259✔
2742
             nGreenBandIndex >= 0 && nBlueBandIndex >= 0)
13✔
2743
    {
2744
        eColorSpace = CODEC::cvtenum(JP2_CLRSPC_SRGB);
13✔
2745
    }
2746
    else if (poCT != nullptr)
246✔
2747
    {
2748
        eColorSpace = CODEC::cvtenum(JP2_CLRSPC_SRGB);
6✔
2749
    }
2750

2751
    /* -------------------------------------------------------------------- */
2752
    /*      Create the dataset.                                             */
2753
    /* -------------------------------------------------------------------- */
2754

2755
    const char *pszAccess =
261✔
2756
        STARTS_WITH_CI(pszFilename, "/vsisubfile/") ? "r+b" : "w+b";
261✔
2757
    VSILFILE *fp = VSIFOpenL(pszFilename, pszAccess);
261✔
2758
    if (fp == nullptr)
261✔
2759
    {
2760
        CPLError(CE_Failure, CPLE_AppDefined, "Cannot create file");
3✔
2761
        CPLFree(localctx.pasBandParams);
3✔
2762
        localctx.pasBandParams = nullptr;
3✔
2763
        delete poGMLJP2Box;
3✔
2764
        return nullptr;
3✔
2765
    }
2766

2767
    /* -------------------------------------------------------------------- */
2768
    /*      Add JP2 boxes.                                                  */
2769
    /* -------------------------------------------------------------------- */
2770
    vsi_l_offset nStartJP2C = 0;
258✔
2771
    int bUseXLBoxes = FALSE;
258✔
2772

2773
    if (eCodecFormat == CODEC::cvtenum(JP2_CODEC_JP2))
258✔
2774
    {
2775
        GDALJP2Box jPBox(fp);
436✔
2776
        jPBox.SetType("jP  ");
218✔
2777
        jPBox.AppendWritableData(4, "\x0D\x0A\x87\x0A");
218✔
2778
        WriteBox(fp, &jPBox);
218✔
2779

2780
        GDALJP2Box ftypBox(fp);
436✔
2781
        ftypBox.SetType("ftyp");
218✔
2782
        // http://docs.opengeospatial.org/is/08-085r5/08-085r5.html Req 19
2783
        const bool bJPXOption = CPLFetchBool(papszOptions, "JPX", true);
218✔
2784
        if (nGMLJP2Version == 2 && bJPXOption)
218✔
2785
            ftypBox.AppendWritableData(4, "jpx "); /* Branding */
25✔
2786
        else
2787
            ftypBox.AppendWritableData(4, "jp2 "); /* Branding */
193✔
2788
        ftypBox.AppendUInt32(0);                   /* minimum version */
218✔
2789
        ftypBox.AppendWritableData(
218✔
2790
            4, "jp2 "); /* Compatibility list: first value */
2791

2792
        if (bInspireTG && poGMLJP2Box != nullptr && !bJPXOption)
218✔
2793
        {
2794
            CPLError(
1✔
2795
                CE_Warning, CPLE_AppDefined,
2796
                "INSPIRE_TG=YES implies following GMLJP2 specification which "
2797
                "recommends advertise reader requirement 67 feature, and thus "
2798
                "JPX capability");
2799
        }
2800
        else if (poGMLJP2Box != nullptr && bJPXOption)
217✔
2801
        {
2802
            /* GMLJP2 uses lbl and asoc boxes, which are JPEG2000 Part II spec
2803
             */
2804
            /* advertizing jpx is required per 8.1 of 05-047r3 GMLJP2 */
2805
            ftypBox.AppendWritableData(
57✔
2806
                4, "jpx "); /* Compatibility list: second value */
2807
        }
2808
        WriteBox(fp, &ftypBox);
218✔
2809

2810
        const bool bIPR = poSrcDS->GetMetadata("xml:IPR") != nullptr &&
220✔
2811
                          CPLFetchBool(papszOptions, "WRITE_METADATA", false);
2✔
2812

2813
        /* Reader requirement box */
2814
        if (poGMLJP2Box != nullptr && bJPXOption)
218✔
2815
        {
2816
            GDALJP2Box rreqBox(fp);
114✔
2817
            rreqBox.SetType("rreq");
57✔
2818
            rreqBox.AppendUInt8(1); /* ML = 1 byte for mask length */
57✔
2819

2820
            rreqBox.AppendUInt8(0x80 | 0x40 | (bIPR ? 0x20 : 0)); /* FUAM */
57✔
2821
            rreqBox.AppendUInt8(0x80);                            /* DCM */
57✔
2822

2823
            rreqBox.AppendUInt16(
57✔
2824
                2 + (bIPR ? 1 : 0)); /* NSF: Number of standard features */
2825

2826
            rreqBox.AppendUInt16(
57✔
2827
                (bProfile1) ? 4 : 5);  /* SF0 : PROFILE 1 or PROFILE 2 */
2828
            rreqBox.AppendUInt8(0x80); /* SM0 */
57✔
2829

2830
            rreqBox.AppendUInt16(67);  /* SF1 : GMLJP2 box */
57✔
2831
            rreqBox.AppendUInt8(0x40); /* SM1 */
57✔
2832

2833
            if (bIPR)
57✔
2834
            {
2835
                rreqBox.AppendUInt16(35);  /* SF2 : IPR metadata */
×
2836
                rreqBox.AppendUInt8(0x20); /* SM2 */
×
2837
            }
2838
            rreqBox.AppendUInt16(0); /* NVF */
57✔
2839
            WriteBox(fp, &rreqBox);
57✔
2840
        }
2841

2842
        GDALJP2Box ihdrBox(fp);
436✔
2843
        ihdrBox.SetType("ihdr");
218✔
2844
        ihdrBox.AppendUInt32(nYSize);
218✔
2845
        ihdrBox.AppendUInt32(nXSize);
218✔
2846
        ihdrBox.AppendUInt16(static_cast<GUInt16>(nBands));
218✔
2847
        GByte BPC;
2848
        if (bSamePrecision)
218✔
2849
            BPC = static_cast<GByte>((localctx.pasBandParams[0].prec - 1) |
215✔
2850
                                     (localctx.pasBandParams[0].sgnd << 7));
215✔
2851
        else
2852
            BPC = 255;
3✔
2853
        ihdrBox.AppendUInt8(BPC);
218✔
2854
        ihdrBox.AppendUInt8(7); /* C=Compression type: fixed value */
218✔
2855
        ihdrBox.AppendUInt8(0); /* UnkC: 0= colourspace of the image is known */
218✔
2856
        /*and correctly specified in the Colourspace Specification boxes within
2857
         * the file */
2858
        ihdrBox.AppendUInt8(
218✔
2859
            bIPR ? 1 : 0); /* IPR: 0=no intellectual property, 1=IPR box */
2860

2861
        GDALJP2Box bpccBox(fp);
436✔
2862
        if (!bSamePrecision)
218✔
2863
        {
2864
            bpccBox.SetType("bpcc");
3✔
2865
            for (int i = 0; i < nBands; i++)
13✔
2866
                bpccBox.AppendUInt8(
10✔
2867
                    static_cast<GByte>((localctx.pasBandParams[i].prec - 1) |
10✔
2868
                                       (localctx.pasBandParams[i].sgnd << 7)));
10✔
2869
        }
2870

2871
        GDALJP2Box colrBox(fp);
436✔
2872
        colrBox.SetType("colr");
218✔
2873
        colrBox.AppendUInt8(1); /* METHOD: 1=Enumerated Colourspace */
218✔
2874
        colrBox.AppendUInt8(
218✔
2875
            0); /* PREC: Precedence. 0=(field reserved for ISO use) */
2876
        colrBox.AppendUInt8(0); /* APPROX: Colourspace approximation. */
218✔
2877
        GUInt32 enumcs = 16;
218✔
2878
        if (eColorSpace == CODEC::cvtenum(JP2_CLRSPC_SRGB))
218✔
2879
            enumcs = 16;
16✔
2880
        else if (eColorSpace == CODEC::cvtenum(JP2_CLRSPC_GRAY))
202✔
2881
            enumcs = 17;
200✔
2882
        else if (eColorSpace == CODEC::cvtenum(JP2_CLRSPC_SYCC))
2✔
2883
            enumcs = 18;
2✔
2884
        colrBox.AppendUInt32(enumcs); /* EnumCS: Enumerated colourspace */
218✔
2885

2886
        GDALJP2Box pclrBox(fp);
436✔
2887
        GDALJP2Box cmapBox(fp);
436✔
2888
        int nCTComponentCount = 0;
218✔
2889
        if (poCT != nullptr)
218✔
2890
        {
2891
            pclrBox.SetType("pclr");
6✔
2892
            const int nEntries = std::min(256, poCT->GetColorEntryCount());
6✔
2893
            nCTComponentCount =
2894
                atoi(CSLFetchNameValueDef(papszOptions, "CT_COMPONENTS", "0"));
6✔
2895
            if (bInspireTG)
6✔
2896
            {
2897
                if (nCTComponentCount != 0 && nCTComponentCount != 3)
×
2898
                    CPLError(
×
2899
                        CE_Warning, CPLE_AppDefined,
2900
                        "Inspire TG mandates 3 components for color table");
2901
                else
2902
                    nCTComponentCount = 3;
×
2903
            }
2904
            else if (nCTComponentCount != 3 && nCTComponentCount != 4)
6✔
2905
            {
2906
                nCTComponentCount = 3;
5✔
2907
                for (int i = 0; i < nEntries; i++)
21✔
2908
                {
2909
                    const GDALColorEntry *psEntry = poCT->GetColorEntry(i);
17✔
2910
                    if (psEntry->c4 != 255)
17✔
2911
                    {
2912
                        CPLDebug(
1✔
2913
                            CODEC::debugId(),
2914
                            "Color table has at least one non-opaque value. "
2915
                            "This may cause compatibility problems with some "
2916
                            "readers. "
2917
                            "In which case use CT_COMPONENTS=3 creation "
2918
                            "option");
2919
                        nCTComponentCount = 4;
1✔
2920
                        break;
1✔
2921
                    }
2922
                }
2923
            }
2924
            nRedBandIndex = 0;
6✔
2925
            nGreenBandIndex = 1;
6✔
2926
            nBlueBandIndex = 2;
6✔
2927
            nAlphaBandIndex = (nCTComponentCount == 4) ? 3 : -1;
6✔
2928

2929
            pclrBox.AppendUInt16(static_cast<GUInt16>(nEntries));
6✔
2930
            pclrBox.AppendUInt8(static_cast<GByte>(
6✔
2931
                nCTComponentCount)); /* NPC: Number of components */
2932
            for (int i = 0; i < nCTComponentCount; i++)
25✔
2933
            {
2934
                pclrBox.AppendUInt8(7); /* Bi: unsigned 8 bits */
19✔
2935
            }
2936
            for (int i = 0; i < nEntries; i++)
30✔
2937
            {
2938
                const GDALColorEntry *psEntry = poCT->GetColorEntry(i);
24✔
2939
                pclrBox.AppendUInt8(static_cast<GByte>(psEntry->c1));
24✔
2940
                pclrBox.AppendUInt8(static_cast<GByte>(psEntry->c2));
24✔
2941
                pclrBox.AppendUInt8(static_cast<GByte>(psEntry->c3));
24✔
2942
                if (nCTComponentCount == 4)
24✔
2943
                    pclrBox.AppendUInt8(static_cast<GByte>(psEntry->c4));
4✔
2944
            }
2945

2946
            cmapBox.SetType("cmap");
6✔
2947
            for (int i = 0; i < nCTComponentCount; i++)
25✔
2948
            {
2949
                cmapBox.AppendUInt16(0); /* CMPi: code stream component index */
19✔
2950
                cmapBox.AppendUInt8(1);  /* MYTPi: 1=palette mapping */
19✔
2951
                cmapBox.AppendUInt8(static_cast<GByte>(
19✔
2952
                    i)); /* PCOLi: index component from the map */
2953
            }
2954
        }
2955

2956
        GDALJP2Box cdefBox(fp);
436✔
2957
        if (((nBands == 3 || nBands == 4) &&
228✔
2958
             (eColorSpace == CODEC::cvtenum(JP2_CLRSPC_SRGB) ||
22✔
2959
              eColorSpace == CODEC::cvtenum(JP2_CLRSPC_SYCC)) &&
18✔
2960
             (nRedBandIndex != 0 || nGreenBandIndex != 1 ||
10✔
2961
              nBlueBandIndex != 2)) ||
436✔
2962
            nAlphaBandIndex >= 0)
2963
        {
2964
            cdefBox.SetType("cdef");
11✔
2965
            int nComponents = (nCTComponentCount == 4) ? 4 : nBands;
11✔
2966
            cdefBox.AppendUInt16(static_cast<GUInt16>(nComponents));
11✔
2967
            for (int i = 0; i < nComponents; i++)
50✔
2968
            {
2969
                cdefBox.AppendUInt16(
39✔
2970
                    static_cast<GUInt16>(i)); /* Component number */
2971
                if (i != nAlphaBandIndex)
39✔
2972
                {
2973
                    cdefBox.AppendUInt16(
29✔
2974
                        0); /* Signification: This channel is the colour image
2975
                               data for the associated colour */
2976
                    if (eColorSpace == CODEC::cvtenum(JP2_CLRSPC_GRAY) &&
29✔
2977
                        nComponents == 2)
2978
                        cdefBox.AppendUInt16(
2✔
2979
                            1); /* Colour of the component: associated with a
2980
                                   particular colour */
2981
                    else if ((eColorSpace == CODEC::cvtenum(JP2_CLRSPC_SRGB) ||
33✔
2982
                              eColorSpace == CODEC::cvtenum(JP2_CLRSPC_SYCC)) &&
54✔
2983
                             (nComponents == 3 || nComponents == 4))
21✔
2984
                    {
2985
                        if (i == nRedBandIndex)
24✔
2986
                            cdefBox.AppendUInt16(1);
8✔
2987
                        else if (i == nGreenBandIndex)
16✔
2988
                            cdefBox.AppendUInt16(2);
8✔
2989
                        else if (i == nBlueBandIndex)
8✔
2990
                            cdefBox.AppendUInt16(3);
8✔
2991
                        else
2992
                        {
2993
                            CPLError(CE_Warning, CPLE_AppDefined,
×
2994
                                     "Could not associate band %d with a "
2995
                                     "red/green/blue channel",
2996
                                     i + 1);
2997
                            cdefBox.AppendUInt16(65535);
×
2998
                        }
2999
                    }
3000
                    else
3001
                        cdefBox.AppendUInt16(
3✔
3002
                            65535); /* Colour of the component: not associated
3003
                                       with any particular colour */
3004
                }
3005
                else
3006
                {
3007
                    cdefBox.AppendUInt16(
10✔
3008
                        1); /* Signification: Non pre-multiplied alpha */
3009
                    cdefBox.AppendUInt16(
10✔
3010
                        0); /* Colour of the component: This channel is
3011
                               associated as the image as a whole */
3012
                }
3013
            }
3014
        }
3015

3016
        // Add res box if needed
3017
        GDALJP2Box *poRes = nullptr;
218✔
3018
        if (poSrcDS->GetMetadataItem("TIFFTAG_XRESOLUTION") != nullptr &&
218✔
3019
            poSrcDS->GetMetadataItem("TIFFTAG_YRESOLUTION") != nullptr &&
223✔
3020
            poSrcDS->GetMetadataItem("TIFFTAG_RESOLUTIONUNIT") != nullptr)
5✔
3021
        {
3022
            double dfXRes =
3023
                CPLAtof(poSrcDS->GetMetadataItem("TIFFTAG_XRESOLUTION"));
5✔
3024
            double dfYRes =
3025
                CPLAtof(poSrcDS->GetMetadataItem("TIFFTAG_YRESOLUTION"));
5✔
3026
            int nResUnit =
3027
                atoi(poSrcDS->GetMetadataItem("TIFFTAG_RESOLUTIONUNIT"));
5✔
3028
#define PIXELS_PER_INCH 2
3029
#define PIXELS_PER_CM 3
3030

3031
            if (nResUnit == PIXELS_PER_INCH)
5✔
3032
            {
3033
                // convert pixels per inch to pixels per cm.
3034
                dfXRes = dfXRes * 39.37 / 100.0;
2✔
3035
                dfYRes = dfYRes * 39.37 / 100.0;
2✔
3036
                nResUnit = PIXELS_PER_CM;
2✔
3037
            }
3038

3039
            if (nResUnit == PIXELS_PER_CM && dfXRes > 0 && dfYRes > 0 &&
5✔
3040
                dfXRes < 65535 && dfYRes < 65535)
5✔
3041
            {
3042
                /* Format a resd box and embed it inside a res box */
3043
                GDALJP2Box oResd;
10✔
3044
                oResd.SetType("resd");
5✔
3045

3046
                int nYDenom = 1;
5✔
3047
                while (nYDenom < 32767 && dfYRes < 32767)
58✔
3048
                {
3049
                    dfYRes *= 2;
53✔
3050
                    nYDenom *= 2;
53✔
3051
                }
3052
                int nXDenom = 1;
5✔
3053
                while (nXDenom < 32767 && dfXRes < 32767)
56✔
3054
                {
3055
                    dfXRes *= 2;
51✔
3056
                    nXDenom *= 2;
51✔
3057
                }
3058

3059
                oResd.AppendUInt16(static_cast<GUInt16>(dfYRes));
5✔
3060
                oResd.AppendUInt16(static_cast<GUInt16>(nYDenom));
5✔
3061
                oResd.AppendUInt16(static_cast<GUInt16>(dfXRes));
5✔
3062
                oResd.AppendUInt16(static_cast<GUInt16>(nXDenom));
5✔
3063
                oResd.AppendUInt8(2); /* vertical exponent */
5✔
3064
                oResd.AppendUInt8(2); /* horizontal exponent */
5✔
3065

3066
                GDALJP2Box *poResd = &oResd;
5✔
3067
                poRes = GDALJP2Box::CreateAsocBox(1, &poResd);
5✔
3068
                poRes->SetType("res ");
5✔
3069
            }
3070
        }
3071

3072
        /* Build and write jp2h super box now */
3073
        GDALJP2Box *apoBoxes[7];
3074
        int nBoxes = 1;
218✔
3075
        apoBoxes[0] = &ihdrBox;
218✔
3076
        if (bpccBox.GetDataLength())
218✔
3077
            apoBoxes[nBoxes++] = &bpccBox;
3✔
3078
        apoBoxes[nBoxes++] = &colrBox;
218✔
3079
        if (pclrBox.GetDataLength())
218✔
3080
            apoBoxes[nBoxes++] = &pclrBox;
6✔
3081
        if (cmapBox.GetDataLength())
218✔
3082
            apoBoxes[nBoxes++] = &cmapBox;
6✔
3083
        if (cdefBox.GetDataLength())
218✔
3084
            apoBoxes[nBoxes++] = &cdefBox;
11✔
3085
        if (poRes)
218✔
3086
            apoBoxes[nBoxes++] = poRes;
5✔
3087
        GDALJP2Box *psJP2HBox =
3088
            GDALJP2Box::CreateSuperBox("jp2h", nBoxes, apoBoxes);
218✔
3089
        WriteBox(fp, psJP2HBox);
218✔
3090
        delete psJP2HBox;
218✔
3091
        delete poRes;
218✔
3092

3093
        if (!bGeoBoxesAfter)
218✔
3094
        {
3095
            if (bGeoJP2Option && bGeoreferencingCompatOfGeoJP2)
207✔
3096
            {
3097
                GDALJP2Box *poBox = oJP2MD.CreateJP2GeoTIFF();
150✔
3098
                WriteBox(fp, poBox);
150✔
3099
                delete poBox;
150✔
3100
            }
3101

3102
            if (CPLFetchBool(papszOptions, "WRITE_METADATA", false) &&
218✔
3103
                !CPLFetchBool(papszOptions, "MAIN_MD_DOMAIN_ONLY", false))
11✔
3104
            {
3105
                WriteXMPBox(fp, poSrcDS);
11✔
3106
            }
3107

3108
            if (CPLFetchBool(papszOptions, "WRITE_METADATA", false))
207✔
3109
            {
3110
                if (!CPLFetchBool(papszOptions, "MAIN_MD_DOMAIN_ONLY", false))
11✔
3111
                    WriteXMLBoxes(fp, poSrcDS);
11✔
3112
                WriteGDALMetadataBox(fp, poSrcDS, papszOptions);
11✔
3113
            }
3114

3115
            if (poGMLJP2Box != nullptr)
207✔
3116
            {
3117
                WriteBox(fp, poGMLJP2Box);
53✔
3118
            }
3119
        }
3120
    }
3121

3122
    /* -------------------------------------------------------------------- */
3123
    /*      Try lossless reuse of an existing JPEG2000 codestream           */
3124
    /* -------------------------------------------------------------------- */
3125
    vsi_l_offset nCodeStreamLength = 0;
258✔
3126
    vsi_l_offset nCodeStreamStart = 0;
258✔
3127
    VSILFILE *fpSrc = nullptr;
258✔
3128
    if (CPLFetchBool(papszOptions, "USE_SRC_CODESTREAM", false))
258✔
3129
    {
3130
        CPLString osSrcFilename(poSrcDS->GetDescription());
14✔
3131
        if (poSrcDS->GetDriver() != nullptr &&
14✔
3132
            poSrcDS->GetDriver() == GDALGetDriverByName("VRT"))
7✔
3133
        {
3134
            VRTDataset *poVRTDS = dynamic_cast<VRTDataset *>(poSrcDS);
×
3135
            if (poVRTDS)
×
3136
            {
3137
                GDALDataset *poSimpleSourceDS =
3138
                    poVRTDS->GetSingleSimpleSource();
×
3139
                if (poSimpleSourceDS)
×
3140
                    osSrcFilename = poSimpleSourceDS->GetDescription();
×
3141
            }
3142
        }
3143

3144
        fpSrc = VSIFOpenL(osSrcFilename, "rb");
7✔
3145
        if (fpSrc)
7✔
3146
        {
3147
            nCodeStreamStart = JP2FindCodeStream(fpSrc, &nCodeStreamLength);
7✔
3148
        }
3149
        if (nCodeStreamLength == 0)
7✔
3150
        {
3151
            CPLError(
1✔
3152
                CE_Warning, CPLE_AppDefined,
3153
                "USE_SRC_CODESTREAM=YES specified, but no codestream found");
3154
        }
3155
    }
3156

3157
    if (eCodecFormat == CODEC::cvtenum(JP2_CODEC_JP2))
258✔
3158
    {
3159
        // Start codestream box
3160
        nStartJP2C = VSIFTellL(fp);
218✔
3161
        if (nCodeStreamLength)
218✔
3162
            bUseXLBoxes = nCodeStreamLength > UINT_MAX;
6✔
3163
        else
3164
            bUseXLBoxes = CPLFetchBool(papszOptions, "JP2C_XLBOX",
424✔
3165
                                       false) || /* For debugging */
423✔
3166
                          static_cast<GIntBig>(nXSize) * nYSize * nBands *
211✔
3167
                                  nDataTypeSize / adfRates.back() >
211✔
3168
                              4e9;
3169
        GUInt32 nLBox = (bUseXLBoxes) ? 1 : 0;
218✔
3170
        CPL_MSBPTR32(&nLBox);
218✔
3171
        VSIFWriteL(&nLBox, 1, 4, fp);
218✔
3172
        VSIFWriteL("jp2c", 1, 4, fp);
218✔
3173
        if (bUseXLBoxes)
218✔
3174
        {
3175
            GUIntBig nXLBox = 0;
1✔
3176
            VSIFWriteL(&nXLBox, 1, 8, fp);
1✔
3177
        }
3178
    }
3179

3180
    /* -------------------------------------------------------------------- */
3181
    /*      Do lossless reuse of an existing JPEG2000 codestream            */
3182
    /* -------------------------------------------------------------------- */
3183
    if (fpSrc)
258✔
3184
    {
3185
        const char *apszIgnoredOptions[] = {"BLOCKXSIZE",
7✔
3186
                                            "BLOCKYSIZE",
3187
                                            "QUALITY",
3188
                                            "REVERSIBLE",
3189
                                            "RESOLUTIONS",
3190
                                            "PROGRESSION",
3191
                                            "SOP",
3192
                                            "EPH",
3193
                                            "YCBCR420",
3194
                                            "YCC",
3195
                                            "NBITS",
3196
                                            "1BIT_ALPHA",
3197
                                            "PRECINCTS",
3198
                                            "TILEPARTS",
3199
                                            "CODEBLOCK_WIDTH",
3200
                                            "CODEBLOCK_HEIGHT",
3201
                                            "PLT",
3202
                                            nullptr};
3203
        for (int i = 0; apszIgnoredOptions[i]; i++)
126✔
3204
        {
3205
            if (CSLFetchNameValue(papszOptions, apszIgnoredOptions[i]))
119✔
3206
            {
3207
                CPLError(CE_Warning, CPLE_NotSupported,
1✔
3208
                         "Option %s ignored when USE_SRC_CODESTREAM=YES",
3209
                         apszIgnoredOptions[i]);
3210
            }
3211
        }
3212
        GByte abyBuffer[4096];
3213
        VSIFSeekL(fpSrc, nCodeStreamStart, SEEK_SET);
7✔
3214
        vsi_l_offset nRead = 0;
7✔
3215
        /* coverity[tainted_data] */
3216
        while (nRead < nCodeStreamLength)
17✔
3217
        {
3218
            const size_t nToRead =
10✔
3219
                (nCodeStreamLength - nRead > 4096)
10✔
3220
                    ? 4096
3221
                    : static_cast<size_t>(nCodeStreamLength - nRead);
3222
            if (VSIFReadL(abyBuffer, 1, nToRead, fpSrc) != nToRead)
10✔
3223
            {
3224
                VSIFCloseL(fp);
×
3225
                VSIFCloseL(fpSrc);
×
3226
                delete poGMLJP2Box;
×
3227
                return nullptr;
×
3228
            }
3229
            if (nRead == 0 && (pszProfile || bInspireTG) &&
10✔
3230
                abyBuffer[2] == 0xFF && abyBuffer[3] == 0x51)
6✔
3231
            {
3232
                if (EQUAL(pszProfile, "UNRESTRICTED"))
6✔
3233
                {
3234
                    abyBuffer[6] = 0;
×
3235
                    abyBuffer[7] = 0;
×
3236
                }
3237
                else if (EQUAL(pszProfile, "PROFILE_1") || bInspireTG)
6✔
3238
                {
3239
                    // TODO: ultimately we should check that we can really set
3240
                    // Profile 1
3241
                    abyBuffer[6] = 0;
1✔
3242
                    abyBuffer[7] = 2;
1✔
3243
                }
3244
            }
3245
            if (VSIFWriteL(abyBuffer, 1, nToRead, fp) != nToRead ||
20✔
3246
                !pfnProgress((nRead + nToRead) * 1.0 / nCodeStreamLength,
10✔
3247
                             nullptr, pProgressData))
3248
            {
3249
                VSIFCloseL(fp);
×
3250
                VSIFCloseL(fpSrc);
×
3251
                delete poGMLJP2Box;
×
3252
                return nullptr;
×
3253
            }
3254
            nRead += nToRead;
10✔
3255
        }
3256

3257
        VSIFCloseL(fpSrc);
7✔
3258
    }
3259
    else
3260
    {
3261
        localctx.open(fp);
251✔
3262
        if (!localctx.initCompress(papszOptions, adfRates, nBlockXSize,
251✔
3263
                                   nBlockYSize, bIsIrreversible,
3264
                                   nNumResolutions, eProgOrder, bYCC, nCblockW,
3265
                                   nCblockH, bYCBCR420, bProfile1, nBands,
3266
                                   nXSize, nYSize, eColorSpace, numThreads))
3267
        {
3268
            CPLError(CE_Failure, CPLE_AppDefined, "init compress failed");
×
3269
            localctx.free();
×
3270
            VSIFCloseL(fp);
×
3271
            delete poGMLJP2Box;
×
3272
            return nullptr;
11✔
3273
        }
3274
        const int nTilesX = DIV_ROUND_UP(nXSize, nBlockXSize);
251✔
3275
        const int nTilesY = DIV_ROUND_UP(nYSize, nBlockYSize);
251✔
3276

3277
        const GUIntBig nTileSize = static_cast<GUIntBig>(nBlockXSize) *
251✔
3278
                                   nBlockYSize * nBands * nDataTypeSize;
251✔
3279
        GByte *pTempBuffer = nullptr;
251✔
3280

3281
        const bool bUseIOThread =
251✔
3282
            CODEC::preferPerTileCompress() && (nTilesX > 1 || nTilesY > 1) &&
502✔
3283
            nTileSize < 10 * 1024 * 1024 &&
11✔
3284
            strcmp(CPLGetThreadingModel(), "stub") != 0 &&
513✔
3285
            CPLTestBool(
11✔
3286
                CPLGetConfigOption("JP2OPENJPEG_USE_THREADED_IO", "YES"));
3287

3288
        if (nTileSize > UINT_MAX)
251✔
3289
        {
3290
            CPLError(CE_Failure, CPLE_NotSupported, "Tile size exceeds 4GB");
1✔
3291
            pTempBuffer = nullptr;
1✔
3292
        }
3293
        else
3294
        {
3295
            // Double memory buffer when using threaded I/O
3296
            const size_t nBufferSize =
250✔
3297
                static_cast<size_t>(bUseIOThread ? nTileSize * 2 : nTileSize);
3298
            pTempBuffer = static_cast<GByte *>(VSI_MALLOC_VERBOSE(nBufferSize));
250✔
3299
        }
3300
        if (pTempBuffer == nullptr)
251✔
3301
        {
3302
            localctx.free();
1✔
3303
            VSIFCloseL(fp);
1✔
3304
            delete poGMLJP2Box;
1✔
3305
            return nullptr;
1✔
3306
        }
3307

3308
        GByte *pYUV420Buffer = nullptr;
250✔
3309
        if (bYCBCR420)
250✔
3310
        {
3311
            pYUV420Buffer = static_cast<GByte *>(VSI_MALLOC_VERBOSE(
2✔
3312
                nBlockXSize * nBlockYSize + nBlockXSize * nBlockYSize / 2 +
3313
                ((nBands == 4) ? nBlockXSize * nBlockYSize : 0)));
3314
            if (pYUV420Buffer == nullptr)
2✔
3315
            {
3316
                localctx.free();
×
3317
                CPLFree(pTempBuffer);
×
3318
                VSIFCloseL(fp);
×
3319
                delete poGMLJP2Box;
×
3320
                return nullptr;
×
3321
            }
3322
        }
3323

3324
        /* --------------------------------------------------------------------
3325
         */
3326
        /*      Iterate over the tiles */
3327
        /* --------------------------------------------------------------------
3328
         */
3329
        pfnProgress(0.0, nullptr, pProgressData);
250✔
3330

3331
        struct ReadRasterJob
3332
        {
3333
            GDALDataset *poSrcDS;
3334
            int nXOff;
3335
            int nYOff;
3336
            int nWidthToRead;
3337
            int nHeightToRead;
3338
            GDALDataType eDataType;
3339
            GByte *pBuffer;
3340
            int nBands;
3341
            CPLErr eErr;
3342
        };
3343

3344
        const auto ReadRasterFunction = [](void *threadData)
812✔
3345
        {
3346
            ReadRasterJob *job = static_cast<ReadRasterJob *>(threadData);
406✔
3347
            job->eErr = job->poSrcDS->RasterIO(
812✔
3348
                GF_Read, job->nXOff, job->nYOff, job->nWidthToRead,
3349
                job->nHeightToRead, job->pBuffer, job->nWidthToRead,
406✔
3350
                job->nHeightToRead, job->eDataType, job->nBands, nullptr, 0, 0,
3351
                0, nullptr);
3352
        };
3353

3354
        CPLWorkerThreadPool oPool;
250✔
3355
        if (bUseIOThread)
250✔
3356
        {
3357
            oPool.Setup(1, nullptr, nullptr);
10✔
3358
        }
3359

3360
        GByte *pabyActiveBuffer = pTempBuffer;
250✔
3361
        GByte *pabyBackgroundBuffer =
250✔
3362
            pTempBuffer + static_cast<size_t>(nTileSize);
250✔
3363

3364
        CPLErr eErr = CE_None;
250✔
3365
        int iTile = 0;
250✔
3366

3367
        ReadRasterJob job;
3368
        job.eDataType = eDataType;
250✔
3369
        job.pBuffer = pabyActiveBuffer;
250✔
3370
        job.nBands = nBands;
250✔
3371
        job.eErr = CE_Failure;
250✔
3372
        job.poSrcDS = poSrcDS;
250✔
3373

3374
        if (bUseIOThread)
250✔
3375
        {
3376
            job.nXOff = 0;
10✔
3377
            job.nYOff = 0;
10✔
3378
            job.nWidthToRead = std::min(nBlockXSize, nXSize);
10✔
3379
            job.nHeightToRead = std::min(nBlockYSize, nYSize);
10✔
3380
            job.pBuffer = pabyBackgroundBuffer;
10✔
3381
            ReadRasterFunction(&job);
10✔
3382
            eErr = job.eErr;
10✔
3383
        }
3384

3385
        for (int nBlockYOff = 0; eErr == CE_None && nBlockYOff < nTilesY;
524✔
3386
             nBlockYOff++)
3387
        {
3388
            for (int nBlockXOff = 0; eErr == CE_None && nBlockXOff < nTilesX;
680✔
3389
                 nBlockXOff++)
3390
            {
3391
                const int nWidthToRead =
406✔
3392
                    std::min(nBlockXSize, nXSize - nBlockXOff * nBlockXSize);
406✔
3393
                const int nHeightToRead =
406✔
3394
                    std::min(nBlockYSize, nYSize - nBlockYOff * nBlockYSize);
406✔
3395

3396
                if (bUseIOThread)
406✔
3397
                {
3398
                    // Wait for previous background I/O task to be finished
3399
                    oPool.WaitCompletion();
100✔
3400
                    eErr = job.eErr;
100✔
3401

3402
                    // Swap buffers
3403
                    std::swap(pabyBackgroundBuffer, pabyActiveBuffer);
100✔
3404

3405
                    // Prepare for next I/O task
3406
                    int nNextBlockXOff = nBlockXOff + 1;
100✔
3407
                    int nNextBlockYOff = nBlockYOff;
100✔
3408
                    if (nNextBlockXOff == nTilesX)
100✔
3409
                    {
3410
                        nNextBlockXOff = 0;
26✔
3411
                        nNextBlockYOff++;
26✔
3412
                    }
3413
                    if (nNextBlockYOff != nTilesY)
100✔
3414
                    {
3415
                        job.nXOff = nNextBlockXOff * nBlockXSize;
90✔
3416
                        job.nYOff = nNextBlockYOff * nBlockYSize;
90✔
3417
                        job.nWidthToRead =
90✔
3418
                            std::min(nBlockXSize, nXSize - job.nXOff);
90✔
3419
                        job.nHeightToRead =
90✔
3420
                            std::min(nBlockYSize, nYSize - job.nYOff);
90✔
3421
                        job.pBuffer = pabyBackgroundBuffer;
90✔
3422

3423
                        // Submit next job
3424
                        oPool.SubmitJob(ReadRasterFunction, &job);
90✔
3425
                    }
3426
                }
3427
                else
3428
                {
3429
                    job.nXOff = nBlockXOff * nBlockXSize;
306✔
3430
                    job.nYOff = nBlockYOff * nBlockYSize;
306✔
3431
                    job.nWidthToRead = nWidthToRead;
306✔
3432
                    job.nHeightToRead = nHeightToRead;
306✔
3433
                    ReadRasterFunction(&job);
306✔
3434
                    eErr = job.eErr;
306✔
3435
                }
3436

3437
                if (b1BitAlpha)
406✔
3438
                {
3439
                    for (int i = 0; i < nWidthToRead * nHeightToRead; i++)
64,987✔
3440
                    {
3441
                        if (pabyActiveBuffer[nAlphaBandIndex * nWidthToRead *
64,984✔
3442
                                                 nHeightToRead +
64,984✔
3443
                                             i])
3444
                            pabyActiveBuffer[nAlphaBandIndex * nWidthToRead *
25,040✔
3445
                                                 nHeightToRead +
25,040✔
3446
                                             i] = 1;
25,040✔
3447
                        else
3448
                            pabyActiveBuffer[nAlphaBandIndex * nWidthToRead *
39,944✔
3449
                                                 nHeightToRead +
39,944✔
3450
                                             i] = 0;
39,944✔
3451
                    }
3452
                }
3453
                if (eErr == CE_None)
406✔
3454
                {
3455
                    if (bYCBCR420)
406✔
3456
                    {
3457
                        for (int j = 0; j < nHeightToRead; j++)
202✔
3458
                        {
3459
                            for (int i = 0; i < nWidthToRead; i++)
27,000✔
3460
                            {
3461
                                const int R =
26,800✔
3462
                                    pabyActiveBuffer[j * nWidthToRead + i];
26,800✔
3463
                                const int G =
26,800✔
3464
                                    pabyActiveBuffer[nHeightToRead *
26,800✔
3465
                                                         nWidthToRead +
26,800✔
3466
                                                     j * nWidthToRead + i];
26,800✔
3467
                                const int B =
26,800✔
3468
                                    pabyActiveBuffer[2 * nHeightToRead *
26,800✔
3469
                                                         nWidthToRead +
26,800✔
3470
                                                     j * nWidthToRead + i];
26,800✔
3471
                                const int Y = static_cast<int>(
26,800✔
3472
                                    0.299 * R + 0.587 * G + 0.114 * B);
26,800✔
3473
                                const int Cb = CLAMP_0_255(static_cast<int>(
53,600✔
3474
                                    -0.1687 * R - 0.3313 * G + 0.5 * B + 128));
26,800✔
3475
                                const int Cr = CLAMP_0_255(static_cast<int>(
53,600✔
3476
                                    0.5 * R - 0.4187 * G - 0.0813 * B + 128));
26,800✔
3477
                                pYUV420Buffer[j * nWidthToRead + i] =
26,800✔
3478
                                    static_cast<GByte>(Y);
3479
                                pYUV420Buffer[nHeightToRead * nWidthToRead +
26,800✔
3480
                                              ((j / 2) * ((nWidthToRead) / 2) +
26,800✔
3481
                                               i / 2)] = static_cast<GByte>(Cb);
26,800✔
3482
                                pYUV420Buffer[5 * nHeightToRead * nWidthToRead /
26,800✔
3483
                                                  4 +
26,800✔
3484
                                              ((j / 2) * ((nWidthToRead) / 2) +
26,800✔
3485
                                               i / 2)] = static_cast<GByte>(Cr);
26,800✔
3486
                                if (nBands == 4)
26,800✔
3487
                                {
3488
                                    pYUV420Buffer[3 * nHeightToRead *
24,300✔
3489
                                                      nWidthToRead / 2 +
24,300✔
3490
                                                  j * nWidthToRead + i] =
24,300✔
3491
                                        static_cast<GByte>(
24,300✔
3492
                                            pabyActiveBuffer[3 * nHeightToRead *
24,300✔
3493
                                                                 nWidthToRead +
24,300✔
3494
                                                             j * nWidthToRead +
24,300✔
3495
                                                             i]);
3496
                                }
3497
                            }
3498
                        }
3499

3500
                        int nBytesToWrite =
2✔
3501
                            3 * nWidthToRead * nHeightToRead / 2;
2✔
3502
                        if (nBands == 4)
2✔
3503
                            nBytesToWrite += nBlockXSize * nBlockYSize;
1✔
3504

3505
                        if (!localctx.compressTile(iTile, pYUV420Buffer,
2✔
3506
                                                   nBytesToWrite))
3507
                        {
3508
                            CPLError(CE_Failure, CPLE_AppDefined,
×
3509
                                     "compress tile failed");
3510
                            eErr = CE_Failure;
×
3511
                        }
3512
                    }
3513
                    else
3514
                    {
3515
                        if (!localctx.compressTile(iTile, pabyActiveBuffer,
404✔
3516
                                                   nWidthToRead *
404✔
3517
                                                       nHeightToRead * nBands *
404✔
3518
                                                       nDataTypeSize))
3519
                        {
3520
                            CPLError(CE_Failure, CPLE_AppDefined,
×
3521
                                     "compress tile failed");
3522
                            eErr = CE_Failure;
×
3523
                        }
3524
                    }
3525
                }
3526

3527
                if (!pfnProgress((iTile + 1) * 1.0 / (nTilesX * nTilesY),
406✔
3528
                                 nullptr, pProgressData))
3529
                    eErr = CE_Failure;
×
3530

3531
                iTile++;
406✔
3532
            }
3533
        }
3534

3535
        if (bUseIOThread && eErr == CE_Failure)
250✔
3536
        {
3537
            // Wait for previous background I/O task to be finished
3538
            // before freeing buffers (pTempBuffer, etc.)
3539
            oPool.WaitCompletion();
×
3540
        }
3541

3542
        VSIFree(pTempBuffer);
250✔
3543
        VSIFree(pYUV420Buffer);
250✔
3544

3545
        if (eErr != CE_None)
250✔
3546
        {
3547
            localctx.free();
×
3548
            VSIFCloseL(fp);
×
3549
            delete poGMLJP2Box;
×
3550
            return nullptr;
×
3551
        }
3552

3553
        if (!localctx.finishCompress())
250✔
3554
        {
3555
            localctx.free();
10✔
3556
            VSIFCloseL(fp);
10✔
3557
            delete poGMLJP2Box;
10✔
3558
            return nullptr;
10✔
3559
        }
3560
        localctx.free();
240✔
3561
    }
3562

3563
    /* -------------------------------------------------------------------- */
3564
    /*      Patch JP2C box length and add trailing JP2 boxes                */
3565
    /* -------------------------------------------------------------------- */
3566
    bool bRet = true;
247✔
3567
    if (eCodecFormat == CODEC::cvtenum(JP2_CODEC_JP2) &&
464✔
3568
        !CPLFetchBool(papszOptions, "JP2C_LENGTH_ZERO",
217✔
3569
                      false) /* debug option */)
3570
    {
3571
        vsi_l_offset nEndJP2C = VSIFTellL(fp);
216✔
3572
        GUIntBig nBoxSize = nEndJP2C - nStartJP2C;
216✔
3573
        if (bUseXLBoxes)
216✔
3574
        {
3575
            VSIFSeekL(fp, nStartJP2C + 8, SEEK_SET);
1✔
3576
            CPL_MSBPTR64(&nBoxSize);
1✔
3577
            if (VSIFWriteL(&nBoxSize, 8, 1, fp) != 1)
1✔
3578
                bRet = false;
×
3579
        }
3580
        else
3581
        {
3582
            if (nBoxSize > UINT_MAX)
215✔
3583
            {
3584
                /*  Should not happen hopefully */
3585
                if ((bGeoreferencingCompatOfGeoJP2 || poGMLJP2Box) &&
×
3586
                    bGeoBoxesAfter)
3587
                {
3588
                    CPLError(CE_Warning, CPLE_AppDefined,
×
3589
                             "Cannot write GMLJP2/GeoJP2 boxes as codestream "
3590
                             "is unexpectedly > 4GB");
3591
                    bGeoreferencingCompatOfGeoJP2 = FALSE;
×
3592
                    delete poGMLJP2Box;
×
3593
                    poGMLJP2Box = nullptr;
×
3594
                }
3595
            }
3596
            else
3597
            {
3598
                VSIFSeekL(fp, nStartJP2C, SEEK_SET);
215✔
3599
                GUInt32 nBoxSize32 = static_cast<GUInt32>(nBoxSize);
215✔
3600
                CPL_MSBPTR32(&nBoxSize32);
215✔
3601
                if (VSIFWriteL(&nBoxSize32, 4, 1, fp) != 1)
215✔
3602
                    bRet = false;
×
3603
            }
3604
        }
3605
        VSIFSeekL(fp, 0, SEEK_END);
216✔
3606

3607
        if (CPLFetchBool(papszOptions, "WRITE_METADATA", false))
216✔
3608
        {
3609
            if (!WriteIPRBox(fp, poSrcDS))
14✔
3610
                bRet = false;
×
3611
        }
3612

3613
        if (bGeoBoxesAfter)
216✔
3614
        {
3615
            if (poGMLJP2Box != nullptr)
11✔
3616
            {
3617
                if (!WriteBox(fp, poGMLJP2Box))
5✔
3618
                    bRet = false;
×
3619
            }
3620

3621
            if (CPLFetchBool(papszOptions, "WRITE_METADATA", false))
11✔
3622
            {
3623
                if (!CPLFetchBool(papszOptions, "MAIN_MD_DOMAIN_ONLY", false))
3✔
3624
                {
3625
                    if (!WriteXMLBoxes(fp, poSrcDS))
3✔
3626
                        bRet = false;
×
3627
                }
3628
                if (!WriteGDALMetadataBox(fp, poSrcDS, papszOptions))
3✔
3629
                    bRet = false;
×
3630
            }
3631

3632
            if (bGeoJP2Option && bGeoreferencingCompatOfGeoJP2)
11✔
3633
            {
3634
                GDALJP2Box *poBox = oJP2MD.CreateJP2GeoTIFF();
5✔
3635
                if (!WriteBox(fp, poBox))
5✔
3636
                    bRet = false;
×
3637
                delete poBox;
5✔
3638
            }
3639

3640
            if (CPLFetchBool(papszOptions, "WRITE_METADATA", false) &&
14✔
3641
                !CPLFetchBool(papszOptions, "MAIN_MD_DOMAIN_ONLY", false))
3✔
3642
            {
3643
                if (!WriteXMPBox(fp, poSrcDS))
3✔
3644
                    bRet = false;
×
3645
            }
3646
        }
3647
    }
3648

3649
    if (VSIFCloseL(fp) != 0)
247✔
3650
        bRet = false;
×
3651
    delete poGMLJP2Box;
247✔
3652
    if (!bRet)
247✔
3653
        return nullptr;
×
3654

3655
    /* -------------------------------------------------------------------- */
3656
    /*      Re-open dataset, and copy any auxiliary pam information.        */
3657
    /* -------------------------------------------------------------------- */
3658

3659
    GDALOpenInfo oOpenInfo(pszFilename, GA_ReadOnly);
247✔
3660
    auto poDS =
3✔
3661
        dynamic_cast<JP2OPJLikeDataset *>(JP2OPJLikeDataset::Open(&oOpenInfo));
247✔
3662

3663
    if (poDS)
247✔
3664
    {
3665
        poDS->CloneInfo(poSrcDS, GCIF_PAM_DEFAULT & (~GCIF_METADATA));
244✔
3666

3667
        /* Only write relevant metadata to PAM, and if needed */
3668
        if (!CPLFetchBool(papszOptions, "WRITE_METADATA", false))
244✔
3669
        {
3670
            char **papszSrcMD = CSLDuplicate(poSrcDS->GetMetadata());
230✔
3671
            papszSrcMD =
3672
                CSLSetNameValue(papszSrcMD, GDALMD_AREA_OR_POINT, nullptr);
230✔
3673
            papszSrcMD = CSLSetNameValue(papszSrcMD, "Corder", nullptr);
230✔
3674
            for (char **papszSrcMDIter = papszSrcMD;
321✔
3675
                 papszSrcMDIter && *papszSrcMDIter;)
321✔
3676
            {
3677
                /* Remove entries like KEY= (without value) */
3678
                if ((*papszSrcMDIter)[0] &&
91✔
3679
                    (*papszSrcMDIter)[strlen((*papszSrcMDIter)) - 1] == '=')
91✔
3680
                {
3681
                    CPLFree(*papszSrcMDIter);
37✔
3682
                    memmove(papszSrcMDIter, papszSrcMDIter + 1,
37✔
3683
                            sizeof(char *) *
3684
                                (CSLCount(papszSrcMDIter + 1) + 1));
37✔
3685
                }
3686
                else
3687
                    ++papszSrcMDIter;
54✔
3688
            }
3689
            char **papszMD = CSLDuplicate(poDS->GetMetadata());
230✔
3690
            papszMD = CSLSetNameValue(papszMD, GDALMD_AREA_OR_POINT, nullptr);
230✔
3691
            if (papszSrcMD && papszSrcMD[0] != nullptr &&
245✔
3692
                CSLCount(papszSrcMD) != CSLCount(papszMD))
15✔
3693
            {
3694
                poDS->SetMetadata(papszSrcMD);
9✔
3695
            }
3696
            CSLDestroy(papszSrcMD);
230✔
3697
            CSLDestroy(papszMD);
230✔
3698
        }
3699
    }
3700

3701
    return poDS;
247✔
3702
}
3703

3704
#ifdef unused
3705
template <typename CODEC, typename BASE>
3706
void GDALRegisterJP2(const std::string &libraryName,
3707
                     const std::string &driverName)
3708

3709
{
3710
    if (!GDAL_CHECK_VERSION((driverName + " driver").c_str()))
3711
        return;
3712

3713
    if (GDALGetDriverByName(driverName.c_str()) != nullptr)
3714
        return;
3715

3716
    GDALDriver *poDriver = new GDALDriver();
3717
    poDriver->SetDescription(driverName.c_str());
3718
    poDriver->SetMetadataItem(GDAL_DCAP_RASTER, "YES");
3719
    poDriver->SetMetadataItem(GDAL_DCAP_VECTOR, "YES");
3720
    poDriver->SetMetadataItem(
3721
        GDAL_DMD_LONGNAME,
3722
        ("JPEG-2000 driver based on " + libraryName + " library").c_str());
3723

3724
    poDriver->SetMetadataItem(
3725
        GDAL_DMD_HELPTOPIC,
3726
        ("drivers/raster/jp2" + CPLString(libraryName).tolower() + ".html")
3727
            .c_str());
3728
    poDriver->SetMetadataItem(GDAL_DMD_MIMETYPE, "image/jp2");
3729
    poDriver->SetMetadataItem(GDAL_DMD_EXTENSION, "jp2");
3730
    poDriver->SetMetadataItem(GDAL_DMD_EXTENSIONS, "jp2 j2k");
3731
    poDriver->SetMetadataItem(GDAL_DMD_CREATIONDATATYPES,
3732
                              "Byte Int16 UInt16 Int32 UInt32");
3733

3734
    poDriver->SetMetadataItem(GDAL_DCAP_VIRTUALIO, "YES");
3735
    BASE::setMetaData(poDriver);
3736

3737
    poDriver->pfnIdentify = JP2OPJLikeDataset<CODEC, BASE>::Identify;
3738
    poDriver->pfnOpen = JP2OPJLikeDataset<CODEC, BASE>::Open;
3739
    poDriver->pfnCreateCopy = JP2OPJLikeDataset<CODEC, BASE>::CreateCopy;
3740

3741
    GetGDALDriverManager()->RegisterDriver(poDriver);
3742
}
3743
#endif
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