LCOV - code coverage report
Current view: top level - frmts/opjlike - jp2opjlikedataset.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 1613 1903 84.8 %
Date: 2026-06-19 21:24:00 Functions: 40 43 93.0 %

          Line data    Source code
       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_vsi_virtual.h"
      23             : #include "cpl_worker_thread_pool.h"
      24             : #include "gdal_frmts.h"
      25             : #include "gdaljp2abstractdataset.h"
      26             : #include "gdaljp2metadata.h"
      27             : #include "vrt/vrtdataset.h"
      28             : 
      29             : #include <algorithm>
      30             : 
      31             : #include "jp2opjlikedataset.h"
      32             : 
      33             : /************************************************************************/
      34             : /*                        JP2OPJLikeRasterBand()                        */
      35             : /************************************************************************/
      36             : 
      37             : template <typename CODEC, typename BASE>
      38        1150 : JP2OPJLikeRasterBand<CODEC, BASE>::JP2OPJLikeRasterBand(
      39             :     JP2OPJLikeDataset<CODEC, BASE> *poDSIn, int nBandIn,
      40             :     GDALDataType eDataTypeIn, int nBits, int bPromoteTo8BitIn,
      41        1150 :     int nBlockXSizeIn, int nBlockYSizeIn)
      42             : 
      43             : {
      44        1150 :     this->eDataType = eDataTypeIn;
      45        1150 :     this->nBlockXSize = nBlockXSizeIn;
      46        1150 :     this->nBlockYSize = nBlockYSizeIn;
      47        1150 :     this->bPromoteTo8Bit = bPromoteTo8BitIn;
      48        1150 :     poCT = nullptr;
      49             : 
      50        1150 :     if ((nBits % 8) != 0)
      51          41 :         GDALRasterBand::SetMetadataItem(GDALMD_NBITS,
      52          82 :                                         CPLString().Printf("%d", nBits),
      53             :                                         GDAL_MDD_IMAGE_STRUCTURE);
      54        1150 :     GDALRasterBand::SetMetadataItem(GDALMD_COMPRESSION, "JPEG2000",
      55             :                                     GDAL_MDD_IMAGE_STRUCTURE);
      56        1150 :     this->poDS = poDSIn;
      57        1150 :     this->nBand = nBandIn;
      58        1150 : }
      59             : 
      60             : template <typename CODEC, typename BASE>
      61          50 : GDALColorTable *JP2OPJLikeRasterBand<CODEC, BASE>::GetColorTable()
      62             : {
      63          50 :     return poCT;
      64             : }
      65             : 
      66             : template <typename CODEC, typename BASE>
      67           0 : int JP2OPJLikeRasterBand<CODEC, BASE>::HasArbitraryOverviews()
      68             : {
      69           0 :     return poCT == nullptr;
      70             : }
      71             : 
      72             : /************************************************************************/
      73             : /*                MayMultiBlockReadingBeMultiThreaded()                 */
      74             : /************************************************************************/
      75             : 
      76             : template <typename CODEC, typename BASE>
      77           0 : bool JP2OPJLikeRasterBand<CODEC, BASE>::MayMultiBlockReadingBeMultiThreaded()
      78             :     const
      79             : {
      80           0 :     auto poGDS = cpl::down_cast<JP2OPJLikeDataset<CODEC, BASE> *>(poDS);
      81           0 :     return poGDS->GetNumThreads() > 1;
      82             : }
      83             : 
      84             : /************************************************************************/
      85             : /*                       ~JP2OPJLikeRasterBand()                        */
      86             : /************************************************************************/
      87             : 
      88             : template <typename CODEC, typename BASE>
      89        2300 : JP2OPJLikeRasterBand<CODEC, BASE>::~JP2OPJLikeRasterBand()
      90             : {
      91          24 :     delete poCT;
      92        2324 : }
      93             : 
      94             : /************************************************************************/
      95             : /*                            CLAMP_0_255()                             */
      96             : /************************************************************************/
      97             : 
      98      134000 : static CPL_INLINE GByte CLAMP_0_255(int val)
      99             : {
     100      134000 :     return static_cast<GByte>(std::clamp(val, 0, 255));
     101             : }
     102             : 
     103             : /************************************************************************/
     104             : /*                           YCbCr420ToBand()                           */
     105             : /************************************************************************/
     106             : 
     107             : // Convert 4:2:0 YCbCr band to RGB. Supports both 16 and 32 bit source buffers
     108             : template <typename T>
     109           6 : static void YCbCr420ToBand(const T *pSrcY, uint32_t nStrideY, const T *pSrcCb,
     110             :                            uint32_t nStrideCb, const T *pSrcCr,
     111             :                            uint32_t nStrideCr, GByte *pDst, int nBlockXSize,
     112             :                            int nWidthToRead, GPtrDiff_t nHeightToRead,
     113             :                            int iBand)
     114             : {
     115         606 :     for (GPtrDiff_t j = 0; j < nHeightToRead; j++)
     116             :     {
     117       81000 :         for (int i = 0; i < nWidthToRead; i++)
     118             :         {
     119       80400 :             const int Y = pSrcY[j * nStrideY + i];
     120       80400 :             const int Cb = pSrcCb[(j / 2) * nStrideCb + (i / 2)];
     121       80400 :             const int Cr = pSrcCr[(j / 2) * nStrideCr + (i / 2)];
     122       80400 :             if (iBand == 1)
     123       53600 :                 pDst[j * nBlockXSize + i] =
     124       26800 :                     CLAMP_0_255(static_cast<int>(Y + 1.402 * (Cr - 128)));
     125       53600 :             else if (iBand == 2)
     126       26800 :                 pDst[j * nBlockXSize + i] = CLAMP_0_255(static_cast<int>(
     127       26800 :                     Y - 0.34414 * (Cb - 128) - 0.71414 * (Cr - 128)));
     128       26800 :             else if (iBand == 3)
     129       53600 :                 pDst[j * nBlockXSize + i] =
     130       26800 :                     CLAMP_0_255(static_cast<int>(Y + 1.772 * (Cb - 128)));
     131             :         }
     132             :     }
     133           6 : }
     134             : 
     135             : /************************************************************************/
     136             : /*                             IReadBlock()                             */
     137             : /************************************************************************/
     138             : 
     139             : template <typename CODEC, typename BASE>
     140         254 : CPLErr JP2OPJLikeRasterBand<CODEC, BASE>::IReadBlock(int nBlockXOff,
     141             :                                                      int nBlockYOff,
     142             :                                                      void *pImage)
     143             : {
     144         254 :     auto poGDS = cpl::down_cast<JP2OPJLikeDataset<CODEC, BASE> *>(poDS);
     145             : 
     146             : #ifdef DEBUG_VERBOSE
     147             :     int nXOff = nBlockXOff * nBlockXSize;
     148             :     int nYOff = nBlockYOff * nBlockYSize;
     149             :     int nXSize = std::min(nBlockXSize, nRasterXSize - nXOff);
     150             :     int nYSize = std::min(nBlockYSize, nRasterYSize - nYOff);
     151             :     if (poGDS->iLevel == 0)
     152             :     {
     153             :         CPLDebug(CODEC::debugId(),
     154             :                  "ds.GetRasterBand(%d).ReadRaster(%d,%d,%d,%d)", nBand, nXOff,
     155             :                  nYOff, nXSize, nYSize);
     156             :     }
     157             :     else
     158             :     {
     159             :         CPLDebug(CODEC::debugId(),
     160             :                  "ds.GetRasterBand(%d).GetOverview(%d).ReadRaster(%d,%d,%d,%d)",
     161             :                  nBand, poGDS->iLevel - 1, nXOff, nYOff, nXSize, nYSize);
     162             :     }
     163             : #endif
     164             : 
     165         254 :     if (poGDS->bEnoughMemoryToLoadOtherBands)
     166             :         return poGDS->ReadBlock(nBand, poGDS->fp_, nBlockXOff, nBlockYOff,
     167         254 :                                 pImage, poGDS->nBands, nullptr);
     168             :     else
     169             :         return poGDS->ReadBlock(nBand, poGDS->fp_, nBlockXOff, nBlockYOff,
     170           0 :                                 pImage, 1, &nBand);
     171             : }
     172             : 
     173             : /************************************************************************/
     174             : /*                             IRasterIO()                              */
     175             : /************************************************************************/
     176             : 
     177             : template <typename CODEC, typename BASE>
     178         227 : CPLErr JP2OPJLikeRasterBand<CODEC, BASE>::IRasterIO(
     179             :     GDALRWFlag eRWFlag, int nXOff, int nYOff, int nXSize, int nYSize,
     180             :     void *pData, int nBufXSize, int nBufYSize, GDALDataType eBufType,
     181             :     GSpacing nPixelSpace, GSpacing nLineSpace, GDALRasterIOExtraArg *psExtraArg)
     182             : {
     183         227 :     auto poGDS = cpl::down_cast<JP2OPJLikeDataset<CODEC, BASE> *>(poDS);
     184             : 
     185         227 :     if (eRWFlag != GF_Read)
     186           0 :         return CE_Failure;
     187             : 
     188             :     /* ==================================================================== */
     189             :     /*      Do we have overviews that would be appropriate to satisfy       */
     190             :     /*      this request?                                                   */
     191             :     /* ==================================================================== */
     192         227 :     if ((nBufXSize < nXSize || nBufYSize < nYSize) && GetOverviewCount() > 0)
     193             :     {
     194             :         int bTried;
     195           1 :         CPLErr eErr = TryOverviewRasterIO(
     196             :             eRWFlag, nXOff, nYOff, nXSize, nYSize, pData, nBufXSize, nBufYSize,
     197             :             eBufType, nPixelSpace, nLineSpace, psExtraArg, &bTried);
     198           1 :         if (bTried)
     199           1 :             return eErr;
     200             :     }
     201             : 
     202             :     // Check whether to skip out to block based methods.
     203         226 :     if (!poGDS->canPerformDirectIO())
     204             :     {
     205         226 :         int nRet = poGDS->PreloadBlocks(this, nXOff, nYOff, nXSize, nYSize, 0,
     206             :                                         nullptr);
     207         226 :         if (nRet < 0)
     208           0 :             return CE_Failure;
     209         226 :         poGDS->bEnoughMemoryToLoadOtherBands = nRet;
     210             : 
     211         226 :         CPLErr eErr = GDALPamRasterBand::IRasterIO(
     212             :             eRWFlag, nXOff, nYOff, nXSize, nYSize, pData, nBufXSize, nBufYSize,
     213             :             eBufType, nPixelSpace, nLineSpace, psExtraArg);
     214             : 
     215             :         // cppcheck-suppress redundantAssignment
     216         226 :         poGDS->bEnoughMemoryToLoadOtherBands = TRUE;
     217         226 :         return eErr;
     218             :     }
     219             : 
     220             :     return poGDS->DirectRasterIO(eRWFlag, nXOff, nYOff, nXSize, nYSize, pData,
     221           0 :                                  nBufXSize, nBufYSize, eBufType, 1, &nBand,
     222           0 :                                  nPixelSpace, nLineSpace, 0, psExtraArg);
     223             : }
     224             : 
     225             : template <typename CODEC, typename BASE> struct JP2JobStruct
     226             : {
     227             :   public:
     228             :     JP2OPJLikeDataset<CODEC, BASE> *poGDS_ = nullptr;
     229             :     int nBand = 0;
     230             :     std::vector<std::pair<int, int>> oPairs{};
     231             :     volatile int nCurPair = 0;
     232             :     int nBandCount = 0;
     233             :     const int *panBandMap = nullptr;
     234             :     volatile bool bSuccess = false;
     235             : };
     236             : 
     237             : /************************************************************************/
     238             : /*                           GetFileHandle()                            */
     239             : /************************************************************************/
     240             : 
     241             : template <typename CODEC, typename BASE>
     242           8 : VSILFILE *JP2OPJLikeDataset<CODEC, BASE>::GetFileHandle()
     243             : {
     244           8 :     return this->fp_;
     245             : }
     246             : 
     247             : /************************************************************************/
     248             : /*                         ReadBlockInThread()                          */
     249             : /************************************************************************/
     250             : 
     251             : template <typename CODEC, typename BASE>
     252          17 : void JP2OPJLikeDataset<CODEC, BASE>::ReadBlockInThread(void *userdata)
     253             : {
     254             :     int nPair;
     255          17 :     auto poJob = static_cast<JP2JobStruct<CODEC, BASE> *>(userdata);
     256             : 
     257          17 :     JP2OPJLikeDataset *poGDS = poJob->poGDS_;
     258          17 :     const int nBand = poJob->nBand;
     259          17 :     const int nPairs = static_cast<int>(poJob->oPairs.size());
     260          17 :     const int nBandCount = poJob->nBandCount;
     261          17 :     const int *panBandMap = poJob->panBandMap;
     262          17 :     VSILFILE *fp = VSIFOpenL(poGDS->m_osFilename.c_str(), "rb");
     263          17 :     if (fp == nullptr)
     264             :     {
     265           0 :         CPLDebug(CODEC::debugId(), "Cannot open %s",
     266             :                  poGDS->m_osFilename.c_str());
     267           0 :         poJob->bSuccess = false;
     268             :         // VSIFree(pDummy);
     269           0 :         return;
     270             :     }
     271             : 
     272         291 :     while ((nPair = CPLAtomicInc(&(poJob->nCurPair))) < nPairs &&
     273         137 :            poJob->bSuccess)
     274             :     {
     275         137 :         int nBlockXOff = poJob->oPairs[nPair].first;
     276         137 :         int nBlockYOff = poJob->oPairs[nPair].second;
     277         137 :         poGDS->AcquireMutex();
     278             :         GDALRasterBlock *poBlock =
     279         137 :             poGDS->GetRasterBand(nBand)->GetLockedBlockRef(nBlockXOff,
     280             :                                                            nBlockYOff, TRUE);
     281         137 :         poGDS->ReleaseMutex();
     282         137 :         if (poBlock == nullptr)
     283             :         {
     284           0 :             poJob->bSuccess = false;
     285           0 :             break;
     286             :         }
     287             : 
     288         137 :         void *pDstBuffer = poBlock->GetDataRef();
     289         137 :         if (poGDS->ReadBlock(nBand, fp, nBlockXOff, nBlockYOff, pDstBuffer,
     290         137 :                              nBandCount, panBandMap) != CE_None)
     291             :         {
     292           0 :             poJob->bSuccess = false;
     293             :         }
     294             : 
     295         137 :         poBlock->DropLock();
     296             :     }
     297             : 
     298          17 :     VSIFCloseL(fp);
     299             :     // VSIFree(pDummy);
     300             : }
     301             : 
     302             : /************************************************************************/
     303             : /*                           PreloadBlocks()                            */
     304             : /************************************************************************/
     305             : 
     306             : template <typename CODEC, typename BASE>
     307         240 : int JP2OPJLikeDataset<CODEC, BASE>::PreloadBlocks(
     308             :     JP2OPJLikeRasterBand<CODEC, BASE> *poBand, int nXOff, int nYOff, int nXSize,
     309             :     int nYSize, int nBandCount, const int *panBandMap)
     310             : {
     311         240 :     int bRet = TRUE;
     312         240 :     const int nXStart = nXOff / poBand->nBlockXSize;
     313         240 :     const int nXEnd = (nXOff + nXSize - 1) / poBand->nBlockXSize;
     314         240 :     const int nYStart = nYOff / poBand->nBlockYSize;
     315         240 :     const int nYEnd = (nYOff + nYSize - 1) / poBand->nBlockYSize;
     316         480 :     const GIntBig nReqMem = static_cast<GIntBig>(nXEnd - nXStart + 1) *
     317         240 :                             (nYEnd - nYStart + 1) * poBand->nBlockXSize *
     318         240 :                             poBand->nBlockYSize *
     319         240 :                             GDALGetDataTypeSizeBytes(poBand->eDataType);
     320             : 
     321         240 :     const int nMaxThreads = this->GetNumThreads();
     322         240 :     if (!this->bUseSetDecodeArea && nMaxThreads > 1)
     323             :     {
     324          96 :         if (nReqMem > GDALGetCacheMax64() / (nBandCount == 0 ? 1 : nBandCount))
     325           0 :             return FALSE;
     326             : 
     327          96 :         JP2JobStruct<CODEC, BASE> oJob;
     328          96 :         this->m_nBlocksToLoad = 0;
     329             :         try
     330             :         {
     331         226 :             for (int nBlockXOff = nXStart; nBlockXOff <= nXEnd; ++nBlockXOff)
     332             :             {
     333         484 :                 for (int nBlockYOff = nYStart; nBlockYOff <= nYEnd;
     334             :                      ++nBlockYOff)
     335             :                 {
     336         354 :                     GDALRasterBlock *poBlock =
     337             :                         poBand->TryGetLockedBlockRef(nBlockXOff, nBlockYOff);
     338         354 :                     if (poBlock != nullptr)
     339             :                     {
     340         156 :                         poBlock->DropLock();
     341         156 :                         continue;
     342             :                     }
     343         198 :                     oJob.oPairs.push_back(
     344             :                         std::pair<int, int>(nBlockXOff, nBlockYOff));
     345         198 :                     this->m_nBlocksToLoad++;
     346             :                 }
     347             :             }
     348             :         }
     349           0 :         catch (const std::bad_alloc &)
     350             :         {
     351           0 :             CPLError(CE_Failure, CPLE_OutOfMemory, "Out of memory error");
     352           0 :             this->m_nBlocksToLoad = 0;
     353           0 :             return -1;
     354             :         }
     355             : 
     356          96 :         if (this->m_nBlocksToLoad > 1)
     357             :         {
     358           5 :             const int l_nThreads = std::min(this->m_nBlocksToLoad, nMaxThreads);
     359             :             CPLJoinableThread **pahThreads = static_cast<CPLJoinableThread **>(
     360           5 :                 VSI_CALLOC_VERBOSE(sizeof(CPLJoinableThread *), l_nThreads));
     361           5 :             if (pahThreads == nullptr)
     362             :             {
     363           0 :                 this->m_nBlocksToLoad = 0;
     364           0 :                 return -1;
     365             :             }
     366             :             int i;
     367             : 
     368           5 :             CPLDebug(CODEC::debugId(), "%d blocks to load (%d threads)",
     369             :                      this->m_nBlocksToLoad, l_nThreads);
     370             : 
     371           5 :             oJob.poGDS_ = this;
     372           5 :             oJob.nBand = poBand->GetBand();
     373           5 :             oJob.nCurPair = -1;
     374           5 :             if (nBandCount > 0)
     375             :             {
     376           2 :                 oJob.nBandCount = nBandCount;
     377           2 :                 oJob.panBandMap = panBandMap;
     378             :             }
     379             :             else
     380             :             {
     381           3 :                 if (nReqMem <= GDALGetCacheMax64() / nBands)
     382             :                 {
     383           3 :                     oJob.nBandCount = nBands;
     384           3 :                     oJob.panBandMap = nullptr;
     385             :                 }
     386             :                 else
     387             :                 {
     388           0 :                     bRet = FALSE;
     389           0 :                     oJob.nBandCount = 1;
     390           0 :                     oJob.panBandMap = &oJob.nBand;
     391             :                 }
     392             :             }
     393           5 :             oJob.bSuccess = true;
     394             : 
     395             :             /* Flushes all dirty blocks from cache to disk to avoid them */
     396             :             /* to be flushed randomly, and simultaneously, from our worker
     397             :              * threads, */
     398             :             /* which might cause races in the output driver. */
     399             :             /* This is a workaround to a design defect of the block cache */
     400           5 :             GDALRasterBlock::FlushDirtyBlocks();
     401             : 
     402          22 :             for (i = 0; i < l_nThreads; i++)
     403             :             {
     404          34 :                 pahThreads[i] =
     405          17 :                     CPLCreateJoinableThread(ReadBlockInThread, &oJob);
     406          17 :                 if (pahThreads[i] == nullptr)
     407           0 :                     oJob.bSuccess = false;
     408             :             }
     409           5 :             TemporarilyDropReadWriteLock();
     410          22 :             for (i = 0; i < l_nThreads; i++)
     411          17 :                 CPLJoinThread(pahThreads[i]);
     412           5 :             ReacquireReadWriteLock();
     413           5 :             CPLFree(pahThreads);
     414           5 :             if (!oJob.bSuccess)
     415             :             {
     416           0 :                 this->m_nBlocksToLoad = 0;
     417           0 :                 return -1;
     418             :             }
     419           5 :             this->m_nBlocksToLoad = 0;
     420             :         }
     421             :     }
     422             : 
     423         240 :     return bRet;
     424             : }
     425             : 
     426             : /************************************************************************/
     427             : /*                        GetEstimatedRAMUsage()                        */
     428             : /************************************************************************/
     429             : 
     430             : template <typename CODEC, typename BASE>
     431         100 : GIntBig JP2OPJLikeDataset<CODEC, BASE>::GetEstimatedRAMUsage()
     432             : {
     433             :     // libopenjp2 holds the code block values in a uint32_t array.
     434         100 :     GIntBig nVal = static_cast<GIntBig>(this->m_nTileWidth) *
     435         100 :                    this->m_nTileHeight * this->nBands * sizeof(uint32_t);
     436         100 :     if (this->bSingleTiled)
     437             :     {
     438             :         // libopenjp2 ingests the codestream for a whole tile. So for a
     439             :         // single-tiled image, this is roughly the size of the file.
     440         100 :         const auto nCurPos = VSIFTellL(this->fp_);
     441         100 :         VSIFSeekL(this->fp_, 0, SEEK_END);
     442         100 :         nVal += VSIFTellL(this->fp_);
     443         100 :         VSIFSeekL(this->fp_, nCurPos, SEEK_SET);
     444             :     }
     445         100 :     CPLDebug(CODEC::debugId(), "Estimated RAM usage for %s: %.2f GB",
     446         100 :              GetDescription(), static_cast<double>(nVal * 1e-9));
     447         100 :     return nVal;
     448             : }
     449             : 
     450             : template <typename CODEC, typename BASE>
     451          18 : CPLErr JP2OPJLikeDataset<CODEC, BASE>::AdviseRead(
     452             :     int nXOff, int nYOff, int nXSize, int nYSize, int nBufXSize, int nBufYSize,
     453             :     GDALDataType eDT, int nBandCount, int *panBandList,
     454             :     CSLConstList papszOptions)
     455             : {
     456             : 
     457          18 :     return BASE::AdviseRead(nXOff, nYOff, nXSize, nYSize, nBufXSize, nBufYSize,
     458          18 :                             eDT, nBandCount, panBandList, papszOptions);
     459             : }
     460             : 
     461             : /************************************************************************/
     462             : /*                             IRasterIO()                              */
     463             : /************************************************************************/
     464             : 
     465             : template <typename CODEC, typename BASE>
     466          15 : CPLErr JP2OPJLikeDataset<CODEC, BASE>::IRasterIO(
     467             :     GDALRWFlag eRWFlag, int nXOff, int nYOff, int nXSize, int nYSize,
     468             :     void *pData, int nBufXSize, int nBufYSize, GDALDataType eBufType,
     469             :     int nBandCount, BANDMAP_TYPE panBandMap, GSpacing nPixelSpace,
     470             :     GSpacing nLineSpace, GSpacing nBandSpace, GDALRasterIOExtraArg *psExtraArg)
     471             : {
     472          15 :     if (eRWFlag != GF_Read)
     473           0 :         return CE_Failure;
     474             : 
     475          15 :     if (nBandCount < 1)
     476           0 :         return CE_Failure;
     477             : 
     478          15 :     auto poBand = cpl::down_cast<JP2OPJLikeRasterBand<CODEC, BASE> *>(
     479             :         GetRasterBand(panBandMap[0]));
     480             : 
     481             :     /* ==================================================================== */
     482             :     /*      Do we have overviews that would be appropriate to satisfy       */
     483             :     /*      this request?                                                   */
     484             :     /* ==================================================================== */
     485             : 
     486          16 :     if ((nBufXSize < nXSize || nBufYSize < nYSize) &&
     487           1 :         poBand->GetOverviewCount() > 0)
     488             :     {
     489             :         int bTried;
     490           1 :         CPLErr eErr = TryOverviewRasterIO(
     491             :             eRWFlag, nXOff, nYOff, nXSize, nYSize, pData, nBufXSize, nBufYSize,
     492             :             eBufType, nBandCount, panBandMap, nPixelSpace, nLineSpace,
     493             :             nBandSpace, psExtraArg, &bTried);
     494           1 :         if (bTried)
     495           1 :             return eErr;
     496             :     }
     497             : 
     498          14 :     CPLErr eErr = CE_None;
     499          14 :     [[maybe_unused]] int nBand = 0; /* 1 based */
     500          14 :     if (!BASE::canPerformDirectIO())
     501             :     {
     502          14 :         int nRet = PreloadBlocks(poBand, nXOff, nYOff, nXSize, nYSize,
     503             :                                  nBandCount, panBandMap);
     504          14 :         if (nRet < 0)
     505           0 :             return CE_Failure;
     506             : 
     507          14 :         this->bEnoughMemoryToLoadOtherBands = nRet;
     508             : 
     509          14 :         eErr = GDALPamDataset::IRasterIO(eRWFlag, nXOff, nYOff, nXSize, nYSize,
     510             :                                          pData, nBufXSize, nBufYSize, eBufType,
     511             :                                          nBandCount, panBandMap, nPixelSpace,
     512             :                                          nLineSpace, nBandSpace, psExtraArg);
     513             : 
     514          14 :         return eErr;
     515             :     }
     516             : 
     517           0 :     eErr = BASE::DirectRasterIO(eRWFlag, nXOff, nYOff, nXSize, nYSize, pData,
     518             :                                 nBufXSize, nBufYSize, eBufType, nBandCount,
     519             :                                 panBandMap, nPixelSpace, nLineSpace, nBandSpace,
     520             :                                 psExtraArg);
     521             : 
     522           0 :     this->bEnoughMemoryToLoadOtherBands = TRUE;
     523           0 :     return eErr;
     524             : }
     525             : 
     526             : /************************************************************************/
     527             : /*                          IBuildOverviews()                           */
     528             : /************************************************************************/
     529             : 
     530             : template <typename CODEC, typename BASE>
     531           3 : CPLErr JP2OPJLikeDataset<CODEC, BASE>::IBuildOverviews(
     532             :     const char *pszResampling, int nOverviews, const int *panOverviewList,
     533             :     int nListBands, const int *panBandList, GDALProgressFunc pfnProgress,
     534             :     void *pProgressData, CSLConstList papszOptions)
     535             : 
     536             : {
     537             :     // In order for building external overviews to work properly, we
     538             :     // discard any concept of internal overviews when the user
     539             :     // first requests to build external overviews.
     540           5 :     for (int i = 0; i < this->nOverviewCount; i++)
     541             :     {
     542           2 :         delete papoOverviewDS[i];
     543             :     }
     544           3 :     CPLFree(papoOverviewDS);
     545           3 :     papoOverviewDS = nullptr;
     546           3 :     this->nOverviewCount = 0;
     547             : 
     548           3 :     return GDALPamDataset::IBuildOverviews(
     549             :         pszResampling, nOverviews, panOverviewList, nListBands, panBandList,
     550           3 :         pfnProgress, pProgressData, papszOptions);
     551             : }
     552             : 
     553             : /************************************************************************/
     554             : /*                             ReadBlock()                              */
     555             : /************************************************************************/
     556             : 
     557             : template <typename CODEC, typename BASE>
     558         391 : CPLErr JP2OPJLikeDataset<CODEC, BASE>::ReadBlock(int nBand, VSILFILE *fpIn,
     559             :                                                  int nBlockXOff, int nBlockYOff,
     560             :                                                  void *pImage, int nBandCount,
     561             :                                                  const int *panBandMap)
     562             : {
     563         391 :     CPLErr eErr = CE_None;
     564           0 :     CODEC localctx;
     565             : 
     566         391 :     auto poBand = cpl::down_cast<JP2OPJLikeRasterBand<CODEC, BASE> *>(
     567             :         GetRasterBand(nBand));
     568         391 :     const int nBlockXSize = poBand->nBlockXSize;
     569         391 :     const int nBlockYSize = poBand->nBlockYSize;
     570         391 :     const GDALDataType eDataType = poBand->eDataType;
     571             : 
     572         391 :     const int nDataTypeSize = GDALGetDataTypeSizeBytes(eDataType);
     573             : 
     574         391 :     const int nTileNumber = nBlockXOff + nBlockYOff * poBand->nBlocksPerRow;
     575         391 :     const int nWidthToRead =
     576         391 :         std::min(nBlockXSize, nRasterXSize - nBlockXOff * nBlockXSize);
     577         391 :     const int nHeightToRead =
     578         391 :         std::min(nBlockYSize, nRasterYSize - nBlockYOff * nBlockYSize);
     579             : 
     580         391 :     eErr = this->readBlockInit(fpIn, &localctx, nBlockXOff, nBlockYOff,
     581             :                                this->nRasterXSize, this->nRasterYSize,
     582             :                                nBlockXSize, nBlockYSize, nTileNumber);
     583         391 :     if (eErr != CE_None)
     584           0 :         goto end;
     585             : 
     586         841 :     for (unsigned int iBand = 0; iBand < localctx.psImage->numcomps; iBand++)
     587             :     {
     588         450 :         if (localctx.psImage->comps[iBand].data == nullptr)
     589             :         {
     590           0 :             CPLError(CE_Failure, CPLE_AppDefined,
     591             :                      "localctx.psImage->comps[%d].data == nullptr", iBand);
     592           0 :             eErr = CE_Failure;
     593           0 :             goto end;
     594             :         }
     595             :     }
     596             : 
     597         841 :     for (int xBand = 0; xBand < nBandCount; xBand++)
     598             :     {
     599         450 :         GDALRasterBlock *poBlock = nullptr;
     600         450 :         int iBand = (panBandMap) ? panBandMap[xBand] : xBand + 1;
     601         450 :         int bPromoteTo8Bit =
     602         450 :             (cpl::down_cast<JP2OPJLikeRasterBand<CODEC, BASE> *>(
     603             :                  GetRasterBand(iBand)))
     604             :                 ->bPromoteTo8Bit;
     605             : 
     606         450 :         void *pDstBuffer = nullptr;
     607         450 :         if (iBand == nBand)
     608         391 :             pDstBuffer = pImage;
     609             :         else
     610             :         {
     611          59 :             AcquireMutex();
     612          59 :             poBlock = (cpl::down_cast<JP2OPJLikeRasterBand<CODEC, BASE> *>(
     613             :                            GetRasterBand(iBand)))
     614             :                           ->TryGetLockedBlockRef(nBlockXOff, nBlockYOff);
     615          59 :             if (poBlock != nullptr)
     616             :             {
     617           0 :                 ReleaseMutex();
     618           0 :                 poBlock->DropLock();
     619           0 :                 continue;
     620             :             }
     621             : 
     622          59 :             poBlock = GetRasterBand(iBand)->GetLockedBlockRef(nBlockXOff,
     623             :                                                               nBlockYOff, TRUE);
     624          59 :             ReleaseMutex();
     625          59 :             if (poBlock == nullptr)
     626             :             {
     627           0 :                 continue;
     628             :             }
     629             : 
     630          59 :             pDstBuffer = poBlock->GetDataRef();
     631             :         }
     632             : 
     633         450 :         if (this->bIs420)
     634             :         {
     635           7 :             if (static_cast<int>(localctx.psImage->comps[0].w) < nWidthToRead ||
     636           7 :                 static_cast<int>(localctx.psImage->comps[0].h) <
     637           7 :                     nHeightToRead ||
     638           7 :                 localctx.psImage->comps[1].w !=
     639           7 :                     (localctx.psImage->comps[0].w + 1) / 2 ||
     640           7 :                 localctx.psImage->comps[1].h !=
     641           7 :                     (localctx.psImage->comps[0].h + 1) / 2 ||
     642           7 :                 localctx.psImage->comps[2].w !=
     643           7 :                     (localctx.psImage->comps[0].w + 1) / 2 ||
     644           7 :                 localctx.psImage->comps[2].h !=
     645           7 :                     (localctx.psImage->comps[0].h + 1) / 2 ||
     646           7 :                 (nBands == 4 &&
     647           4 :                  (static_cast<int>(localctx.psImage->comps[3].w) <
     648           4 :                       nWidthToRead ||
     649           4 :                   static_cast<int>(localctx.psImage->comps[3].h) <
     650             :                       nHeightToRead)))
     651             :             {
     652           0 :                 CPLError(CE_Failure, CPLE_AssertionFailed,
     653             :                          "Assertion at line %d of %s failed", __LINE__,
     654             :                          __FILE__);
     655           0 :                 if (poBlock != nullptr)
     656           0 :                     poBlock->DropLock();
     657           0 :                 eErr = CE_Failure;
     658           0 :                 goto end;
     659             :             }
     660             : 
     661           7 :             GByte *pDst = static_cast<GByte *>(pDstBuffer);
     662           7 :             if (iBand == 4)
     663             :             {
     664           1 :                 const auto pSrcA =
     665           1 :                     static_cast<int32_t *>(localctx.psImage->comps[3].data);
     666         151 :                 for (GPtrDiff_t j = 0; j < nHeightToRead; j++)
     667             :                 {
     668         300 :                     memcpy(pDst + j * nBlockXSize,
     669         150 :                            pSrcA + j * localctx.stride(localctx.psImage->comps),
     670             :                            nWidthToRead);
     671             :                 }
     672             :             }
     673             :             else
     674             :             {
     675           6 :                 const uint32_t nStrideY =
     676           6 :                     localctx.stride(localctx.psImage->comps);
     677           6 :                 const uint32_t nStrideCb =
     678           6 :                     localctx.stride(localctx.psImage->comps + 1);
     679           6 :                 const uint32_t nStrideCr =
     680           6 :                     localctx.stride(localctx.psImage->comps + 2);
     681           6 :                 const void *pY = localctx.psImage->comps[0].data;
     682           6 :                 const void *pCb = localctx.psImage->comps[1].data;
     683           6 :                 const void *pCr = localctx.psImage->comps[2].data;
     684           6 :                 if (CODEC::getDataType(localctx.psImage->comps) == GDT_Int16)
     685           0 :                     YCbCr420ToBand(static_cast<const int16_t *>(pY), nStrideY,
     686             :                                    static_cast<const int16_t *>(pCb), nStrideCb,
     687             :                                    static_cast<const int16_t *>(pCr), nStrideCr,
     688             :                                    pDst, nBlockXSize, nWidthToRead,
     689             :                                    nHeightToRead, iBand);
     690             :                 else
     691           6 :                     YCbCr420ToBand(static_cast<const int32_t *>(pY), nStrideY,
     692             :                                    static_cast<const int32_t *>(pCb), nStrideCb,
     693             :                                    static_cast<const int32_t *>(pCr), nStrideCr,
     694             :                                    pDst, nBlockXSize, nWidthToRead,
     695             :                                    nHeightToRead, iBand);
     696             :             }
     697             : 
     698           7 :             if (bPromoteTo8Bit)
     699             :             {
     700           0 :                 for (GPtrDiff_t j = 0; j < nHeightToRead; j++)
     701             :                 {
     702           0 :                     for (int i = 0; i < nWidthToRead; i++)
     703             :                     {
     704           0 :                         pDst[j * nBlockXSize + i] *= 255;
     705             :                     }
     706             :                 }
     707             :             }
     708             :         }
     709             :         else
     710             :         {
     711         443 :             if (static_cast<int>(localctx.psImage->comps[iBand - 1].w) <
     712         443 :                     nWidthToRead ||
     713         443 :                 static_cast<int>(localctx.psImage->comps[iBand - 1].h) <
     714             :                     nHeightToRead)
     715             :             {
     716           0 :                 CPLError(CE_Failure, CPLE_AssertionFailed,
     717             :                          "Assertion at line %d of %s failed", __LINE__,
     718             :                          __FILE__);
     719           0 :                 if (poBlock != nullptr)
     720           0 :                     poBlock->DropLock();
     721           0 :                 eErr = CE_Failure;
     722           0 :                 goto end;
     723             :             }
     724             : 
     725             :             const GDALDataType eSrcType =
     726         443 :                 CODEC::getDataType(localctx.psImage->comps + iBand - 1);
     727         443 :             const int nSrcTypeSize = GDALGetDataTypeSizeBytes(eSrcType);
     728         886 :             const int nSrcStride = static_cast<int>(
     729         443 :                 localctx.stride(localctx.psImage->comps + iBand - 1));
     730         443 :             GByte *src = static_cast<GByte *>(
     731         443 :                 static_cast<void *>(localctx.psImage->comps[iBand - 1].data));
     732         443 :             if (bPromoteTo8Bit)
     733             :             {
     734         529 :                 for (GPtrDiff_t j = 0; j < nHeightToRead; j++)
     735             :                 {
     736       79500 :                     for (int i = 0; i < nWidthToRead; i++)
     737             :                     {
     738       78975 :                         const GPtrDiff_t nOff = j * nSrcStride + i;
     739       78975 :                         if (eSrcType == GDT_Int16)
     740           0 :                             reinterpret_cast<int16_t *>(src)[nOff] *= 255;
     741             :                         else
     742       78975 :                             reinterpret_cast<int32_t *>(src)[nOff] *= 255;
     743             :                     }
     744             :                 }
     745             :             }
     746             : 
     747         443 :             if (nSrcStride == nBlockXSize &&
     748         414 :                 static_cast<int>(localctx.psImage->comps[iBand - 1].h) ==
     749             :                     nBlockYSize)
     750             :             {
     751         393 :                 GDALCopyWords64(src, eSrcType, nSrcTypeSize, pDstBuffer,
     752             :                                 eDataType, nDataTypeSize,
     753         393 :                                 static_cast<GPtrDiff_t>(nBlockXSize) *
     754         393 :                                     nBlockYSize);
     755             :             }
     756             :             else
     757             :             {
     758       12249 :                 for (GPtrDiff_t j = 0; j < nHeightToRead; j++)
     759             :                 {
     760       12199 :                     GDALCopyWords(src + j * nSrcStride * nSrcTypeSize, eSrcType,
     761             :                                   nSrcTypeSize,
     762             :                                   static_cast<GByte *>(pDstBuffer) +
     763       12199 :                                       j * nBlockXSize * nDataTypeSize,
     764             :                                   eDataType, nDataTypeSize, nWidthToRead);
     765             :                 }
     766             :             }
     767             :         }
     768             : 
     769         450 :         if (poBlock != nullptr)
     770          59 :             poBlock->DropLock();
     771             :     }
     772             : 
     773         391 : end:
     774         391 :     this->cache(&localctx);
     775             : 
     776         782 :     return eErr;
     777             : }
     778             : 
     779             : /************************************************************************/
     780             : /*                          GetOverviewCount()                          */
     781             : /************************************************************************/
     782             : 
     783             : template <typename CODEC, typename BASE>
     784         119 : int JP2OPJLikeRasterBand<CODEC, BASE>::GetOverviewCount()
     785             : {
     786         119 :     auto poGDS = cpl::down_cast<JP2OPJLikeDataset<CODEC, BASE> *>(poDS);
     787         119 :     if (!poGDS->AreOverviewsEnabled())
     788           0 :         return 0;
     789             : 
     790         119 :     if (GDALPamRasterBand::GetOverviewCount() > 0)
     791           4 :         return GDALPamRasterBand::GetOverviewCount();
     792             : 
     793         115 :     return poGDS->nOverviewCount;
     794             : }
     795             : 
     796             : /************************************************************************/
     797             : /*                            GetOverview()                             */
     798             : /************************************************************************/
     799             : 
     800             : template <typename CODEC, typename BASE>
     801          24 : GDALRasterBand *JP2OPJLikeRasterBand<CODEC, BASE>::GetOverview(int iOvrLevel)
     802             : {
     803          24 :     if (GDALPamRasterBand::GetOverviewCount() > 0)
     804           6 :         return GDALPamRasterBand::GetOverview(iOvrLevel);
     805             : 
     806          18 :     auto poGDS = cpl::down_cast<JP2OPJLikeDataset<CODEC, BASE> *>(poDS);
     807          18 :     if (iOvrLevel < 0 || iOvrLevel >= poGDS->nOverviewCount)
     808           0 :         return nullptr;
     809             : 
     810          18 :     return poGDS->papoOverviewDS[iOvrLevel]->GetRasterBand(nBand);
     811             : }
     812             : 
     813             : /************************************************************************/
     814             : /*                       GetColorInterpretation()                       */
     815             : /************************************************************************/
     816             : 
     817             : template <typename CODEC, typename BASE>
     818         570 : GDALColorInterp JP2OPJLikeRasterBand<CODEC, BASE>::GetColorInterpretation()
     819             : {
     820         570 :     auto poGDS = cpl::down_cast<JP2OPJLikeDataset<CODEC, BASE> *>(poDS);
     821             : 
     822         570 :     if (poCT)
     823           6 :         return GCI_PaletteIndex;
     824             : 
     825         564 :     if (nBand == poGDS->nAlphaIndex + 1)
     826          22 :         return GCI_AlphaBand;
     827             : 
     828         949 :     if (poGDS->nBands <= 2 &&
     829         407 :         poGDS->eColorSpace == CODEC::cvtenum(JP2_CLRSPC_GRAY))
     830         386 :         return GCI_GrayIndex;
     831         247 :     else if (poGDS->eColorSpace == CODEC::cvtenum(JP2_CLRSPC_SRGB) ||
     832          91 :              poGDS->eColorSpace == CODEC::cvtenum(JP2_CLRSPC_SYCC))
     833             :     {
     834          77 :         if (nBand == poGDS->nRedIndex + 1)
     835          26 :             return GCI_RedBand;
     836          51 :         if (nBand == poGDS->nGreenIndex + 1)
     837          25 :             return GCI_GreenBand;
     838          26 :         if (nBand == poGDS->nBlueIndex + 1)
     839          25 :             return GCI_BlueBand;
     840             :     }
     841             : 
     842          80 :     return GCI_Undefined;
     843             : }
     844             : 
     845             : /************************************************************************/
     846             : /* ==================================================================== */
     847             : /*                           JP2OPJLikeDataset                          */
     848             : /* ==================================================================== */
     849             : /************************************************************************/
     850             : 
     851             : /************************************************************************/
     852             : /*                         JP2OPJLikeDataset()                          */
     853             : /************************************************************************/
     854             : 
     855             : template <typename CODEC, typename BASE>
     856        1905 : JP2OPJLikeDataset<CODEC, BASE>::JP2OPJLikeDataset()
     857             : {
     858        1905 :     this->init();
     859        1905 : }
     860             : 
     861             : /************************************************************************/
     862             : /*                         ~JP2OPJLikeDataset()                         */
     863             : /************************************************************************/
     864             : 
     865             : template <typename CODEC, typename BASE>
     866        2782 : JP2OPJLikeDataset<CODEC, BASE>::~JP2OPJLikeDataset()
     867             : 
     868             : {
     869        1905 :     JP2OPJLikeDataset::Close();
     870        2782 : }
     871             : 
     872             : /************************************************************************/
     873             : /*                               Close()                                */
     874             : /************************************************************************/
     875             : 
     876             : template <typename CODEC, typename BASE>
     877        2598 : CPLErr JP2OPJLikeDataset<CODEC, BASE>::Close(GDALProgressFunc, void *)
     878             : {
     879        2598 :     CPLErr eErr = CE_None;
     880        2598 :     if (nOpenFlags != OPEN_FLAGS_CLOSED)
     881             :     {
     882        1905 :         if (JP2OPJLikeDataset::FlushCache(true) != CE_None)
     883           0 :             eErr = CE_Failure;
     884             : 
     885        1905 :         this->closeJP2();
     886        1905 :         if (this->iLevel == 0 && this->fp_ != nullptr)
     887             :         {
     888         760 :             if (this->bRewrite)
     889             :             {
     890           9 :                 if (BASE::canPerformDirectIO())
     891             :                 {
     892             :                     /* Grok handles box rewriting natively via transcode */
     893           0 :                     VSIFCloseL(this->fp_);
     894           0 :                     this->fp_ = nullptr;
     895           0 :                     if (!CODEC::rewriteBoxes(GetDescription(), this))
     896           0 :                         eErr = CE_Failure;
     897             :                 }
     898             :                 else
     899             :                 {
     900          18 :                     GDALJP2Box oBox(this->fp_);
     901           9 :                     vsi_l_offset nOffsetJP2C = 0;
     902           9 :                     vsi_l_offset nLengthJP2C = 0;
     903           9 :                     vsi_l_offset nOffsetXML = 0;
     904           9 :                     vsi_l_offset nOffsetASOC = 0;
     905           9 :                     vsi_l_offset nOffsetUUID = 0;
     906           9 :                     vsi_l_offset nOffsetIHDR = 0;
     907           9 :                     vsi_l_offset nLengthIHDR = 0;
     908           9 :                     int bMSIBox = FALSE;
     909           9 :                     int bGMLData = FALSE;
     910           9 :                     int bUnsupportedConfiguration = FALSE;
     911           9 :                     if (oBox.ReadFirst())
     912             :                     {
     913          47 :                         while (strlen(oBox.GetType()) > 0)
     914             :                         {
     915          47 :                             if (EQUAL(oBox.GetType(), "jp2c"))
     916             :                             {
     917           9 :                                 if (nOffsetJP2C == 0)
     918             :                                 {
     919           9 :                                     nOffsetJP2C = VSIFTellL(this->fp_);
     920           9 :                                     nLengthJP2C = oBox.GetDataLength();
     921             :                                 }
     922             :                                 else
     923           0 :                                     bUnsupportedConfiguration = TRUE;
     924             :                             }
     925          38 :                             else if (EQUAL(oBox.GetType(), "jp2h"))
     926             :                             {
     927          18 :                                 GDALJP2Box oSubBox(this->fp_);
     928          18 :                                 if (oSubBox.ReadFirstChild(&oBox) &&
     929           9 :                                     EQUAL(oSubBox.GetType(), "ihdr"))
     930             :                                 {
     931           9 :                                     nOffsetIHDR = VSIFTellL(this->fp_);
     932           9 :                                     nLengthIHDR = oSubBox.GetDataLength();
     933             :                                 }
     934             :                             }
     935          29 :                             else if (EQUAL(oBox.GetType(), "xml "))
     936             :                             {
     937           2 :                                 if (nOffsetXML == 0)
     938           2 :                                     nOffsetXML = VSIFTellL(this->fp_);
     939             :                             }
     940          27 :                             else if (EQUAL(oBox.GetType(), "asoc"))
     941             :                             {
     942           3 :                                 if (nOffsetASOC == 0)
     943           3 :                                     nOffsetASOC = VSIFTellL(this->fp_);
     944             : 
     945           6 :                                 GDALJP2Box oSubBox(this->fp_);
     946           6 :                                 if (oSubBox.ReadFirstChild(&oBox) &&
     947           3 :                                     EQUAL(oSubBox.GetType(), "lbl "))
     948             :                                 {
     949             :                                     char *pszLabel = reinterpret_cast<char *>(
     950           3 :                                         oSubBox.ReadBoxData());
     951           3 :                                     if (pszLabel != nullptr &&
     952           3 :                                         EQUAL(pszLabel, "gml.data"))
     953             :                                     {
     954           3 :                                         bGMLData = TRUE;
     955             :                                     }
     956             :                                     else
     957           0 :                                         bUnsupportedConfiguration = TRUE;
     958           3 :                                     CPLFree(pszLabel);
     959             :                                 }
     960             :                                 else
     961           0 :                                     bUnsupportedConfiguration = TRUE;
     962             :                             }
     963          24 :                             else if (EQUAL(oBox.GetType(), "uuid"))
     964             :                             {
     965           4 :                                 if (nOffsetUUID == 0)
     966           4 :                                     nOffsetUUID = VSIFTellL(this->fp_);
     967           4 :                                 if (GDALJP2Metadata::IsUUID_MSI(oBox.GetUUID()))
     968           4 :                                     bMSIBox = TRUE;
     969           0 :                                 else if (!GDALJP2Metadata::IsUUID_XMP(
     970             :                                              oBox.GetUUID()))
     971           0 :                                     bUnsupportedConfiguration = TRUE;
     972             :                             }
     973          20 :                             else if (!EQUAL(oBox.GetType(), "jP  ") &&
     974          11 :                                      !EQUAL(oBox.GetType(), "ftyp") &&
     975           2 :                                      !EQUAL(oBox.GetType(), "rreq") &&
     976          31 :                                      !EQUAL(oBox.GetType(), "jp2h") &&
     977           0 :                                      !EQUAL(oBox.GetType(), "jp2i"))
     978             :                             {
     979           0 :                                 bUnsupportedConfiguration = TRUE;
     980             :                             }
     981             : 
     982          47 :                             if (bUnsupportedConfiguration || !oBox.ReadNext())
     983           9 :                                 break;
     984             :                         }
     985             :                     }
     986             : 
     987             :                     const char *pszGMLJP2;
     988           9 :                     int bGeoreferencingCompatOfGMLJP2 =
     989          13 :                         (!m_oSRS.IsEmpty() && bGeoTransformValid &&
     990           4 :                          nGCPCount == 0);
     991           9 :                     if (bGeoreferencingCompatOfGMLJP2 &&
     992           3 :                         ((this->bHasGeoreferencingAtOpening && bGMLData) ||
     993           2 :                          (!this->bHasGeoreferencingAtOpening)))
     994           2 :                         pszGMLJP2 = "GMLJP2=YES";
     995             :                     else
     996           7 :                         pszGMLJP2 = "GMLJP2=NO";
     997             : 
     998             :                     const char *pszGeoJP2;
     999           9 :                     int bGeoreferencingCompatOfGeoJP2 =
    1000          12 :                         (!m_oSRS.IsEmpty() || nGCPCount != 0 ||
    1001           3 :                          bGeoTransformValid);
    1002           9 :                     if (bGeoreferencingCompatOfGeoJP2 &&
    1003           6 :                         ((this->bHasGeoreferencingAtOpening && bMSIBox) ||
    1004           4 :                          (!this->bHasGeoreferencingAtOpening) ||
    1005           2 :                          this->nGCPCount > 0))
    1006           5 :                         pszGeoJP2 = "GeoJP2=YES";
    1007             :                     else
    1008           4 :                         pszGeoJP2 = "GeoJP2=NO";
    1009             : 
    1010             :                     /* Test that the length of the JP2C box is not 0 */
    1011           9 :                     int bJP2CBoxOKForRewriteInPlace = TRUE;
    1012           9 :                     if (nOffsetJP2C > 16 && !bUnsupportedConfiguration)
    1013             :                     {
    1014           9 :                         VSIFSeekL(this->fp_, nOffsetJP2C - 8, SEEK_SET);
    1015             :                         GByte abyBuffer[8];
    1016           9 :                         VSIFReadL(abyBuffer, 1, 8, this->fp_);
    1017           9 :                         if (memcmp(abyBuffer + 4, "jp2c", 4) == 0 &&
    1018           9 :                             abyBuffer[0] == 0 && abyBuffer[1] == 0 &&
    1019           9 :                             abyBuffer[2] == 0 && abyBuffer[3] == 0)
    1020             :                         {
    1021           1 :                             if (nLengthJP2C + 8 < UINT32_MAX)
    1022             :                             {
    1023           1 :                                 CPLDebug(CODEC::debugId(),
    1024             :                                          "Patching length of JP2C box with "
    1025             :                                          "real length");
    1026           1 :                                 VSIFSeekL(this->fp_, nOffsetJP2C - 8, SEEK_SET);
    1027           1 :                                 GUInt32 nLength =
    1028           1 :                                     static_cast<GUInt32>(nLengthJP2C) + 8;
    1029           1 :                                 CPL_MSBPTR32(&nLength);
    1030           1 :                                 if (VSIFWriteL(&nLength, 1, 4, this->fp_) != 1)
    1031           1 :                                     eErr = CE_Failure;
    1032             :                             }
    1033             :                             else
    1034           0 :                                 bJP2CBoxOKForRewriteInPlace = FALSE;
    1035             :                         }
    1036             :                     }
    1037             : 
    1038           9 :                     if (nOffsetJP2C == 0 || bUnsupportedConfiguration)
    1039             :                     {
    1040           0 :                         eErr = CE_Failure;
    1041           0 :                         CPLError(
    1042             :                             CE_Failure, CPLE_AppDefined,
    1043             :                             "Cannot rewrite file due to unsupported JP2 box "
    1044             :                             "configuration");
    1045           0 :                         VSIFCloseL(this->fp_);
    1046             :                     }
    1047           9 :                     else if (bJP2CBoxOKForRewriteInPlace &&
    1048           9 :                              (nOffsetXML == 0 || nOffsetXML > nOffsetJP2C) &&
    1049           9 :                              (nOffsetASOC == 0 || nOffsetASOC > nOffsetJP2C) &&
    1050           4 :                              (nOffsetUUID == 0 || nOffsetUUID > nOffsetJP2C))
    1051             :                     {
    1052           5 :                         CPLDebug(CODEC::debugId(),
    1053             :                                  "Rewriting boxes after codestream");
    1054             : 
    1055             :                         /* Update IPR flag */
    1056           5 :                         if (nLengthIHDR == 14)
    1057             :                         {
    1058           5 :                             VSIFSeekL(this->fp_, nOffsetIHDR + nLengthIHDR - 1,
    1059             :                                       SEEK_SET);
    1060           5 :                             GByte bIPR = GetMetadata("xml:IPR") != nullptr;
    1061           5 :                             if (VSIFWriteL(&bIPR, 1, 1, this->fp_) != 1)
    1062           0 :                                 eErr = CE_Failure;
    1063             :                         }
    1064             : 
    1065           5 :                         VSIFSeekL(this->fp_, nOffsetJP2C + nLengthJP2C,
    1066             :                                   SEEK_SET);
    1067             : 
    1068          10 :                         GDALJP2Metadata oJP2MD;
    1069           5 :                         if (GetGCPCount() > 0)
    1070             :                         {
    1071           1 :                             oJP2MD.SetGCPs(GetGCPCount(), GetGCPs());
    1072           1 :                             oJP2MD.SetSpatialRef(GetGCPSpatialRef());
    1073             :                         }
    1074             :                         else
    1075             :                         {
    1076           4 :                             const OGRSpatialReference *poSRS = GetSpatialRef();
    1077           4 :                             if (poSRS != nullptr)
    1078             :                             {
    1079           1 :                                 oJP2MD.SetSpatialRef(poSRS);
    1080             :                             }
    1081           4 :                             if (bGeoTransformValid)
    1082             :                             {
    1083           1 :                                 oJP2MD.SetGeoTransform(m_gt);
    1084             :                             }
    1085             :                         }
    1086             : 
    1087             :                         const char *pszAreaOrPoint =
    1088           5 :                             GetMetadataItem(GDALMD_AREA_OR_POINT);
    1089           5 :                         oJP2MD.bPixelIsPoint =
    1090           5 :                             pszAreaOrPoint != nullptr &&
    1091           0 :                             EQUAL(pszAreaOrPoint, GDALMD_AOP_POINT);
    1092             : 
    1093           5 :                         if (!WriteIPRBox(this->fp_, this))
    1094           0 :                             eErr = CE_Failure;
    1095             : 
    1096           5 :                         if (bGeoreferencingCompatOfGMLJP2 &&
    1097           1 :                             EQUAL(pszGMLJP2, "GMLJP2=YES"))
    1098             :                         {
    1099             :                             GDALJP2Box *poBox =
    1100           1 :                                 oJP2MD.CreateGMLJP2(nRasterXSize, nRasterYSize);
    1101           1 :                             if (!WriteBox(this->fp_, poBox))
    1102           0 :                                 eErr = CE_Failure;
    1103           1 :                             delete poBox;
    1104             :                         }
    1105             : 
    1106          10 :                         if (!WriteXMLBoxes(this->fp_, this) ||
    1107           5 :                             !WriteGDALMetadataBox(this->fp_, this, nullptr))
    1108           0 :                             eErr = CE_Failure;
    1109             : 
    1110           5 :                         if (bGeoreferencingCompatOfGeoJP2 &&
    1111           2 :                             EQUAL(pszGeoJP2, "GeoJP2=YES"))
    1112             :                         {
    1113           2 :                             GDALJP2Box *poBox = oJP2MD.CreateJP2GeoTIFF();
    1114           2 :                             if (!WriteBox(this->fp_, poBox))
    1115           0 :                                 eErr = CE_Failure;
    1116           2 :                             delete poBox;
    1117             :                         }
    1118             : 
    1119           5 :                         if (!WriteXMPBox(this->fp_, this))
    1120           0 :                             eErr = CE_Failure;
    1121             : 
    1122           5 :                         if (VSIFTruncateL(this->fp_, VSIFTellL(this->fp_)) != 0)
    1123           0 :                             eErr = CE_Failure;
    1124             : 
    1125           5 :                         if (VSIFCloseL(this->fp_) != 0)
    1126           5 :                             eErr = CE_Failure;
    1127             :                     }
    1128             :                     else
    1129             :                     {
    1130           4 :                         VSIFCloseL(this->fp_);
    1131             : 
    1132           4 :                         CPLDebug(CODEC::debugId(), "Rewriting whole file");
    1133             : 
    1134           4 :                         const char *const apszOptions[] = {
    1135             :                             "USE_SRC_CODESTREAM=YES",
    1136             :                             "CODEC=JP2",
    1137             :                             "WRITE_METADATA=YES",
    1138             :                             pszGMLJP2,
    1139             :                             pszGeoJP2,
    1140             :                             nullptr};
    1141           8 :                         CPLString osTmpFilename(
    1142           4 :                             CPLSPrintf("%s.tmp", GetDescription()));
    1143             :                         GDALDataset *poOutDS =
    1144           4 :                             CreateCopy(osTmpFilename, this, FALSE,
    1145             :                                        const_cast<char **>(apszOptions),
    1146             :                                        GDALDummyProgress, nullptr);
    1147           4 :                         if (poOutDS)
    1148             :                         {
    1149           4 :                             if (GDALClose(poOutDS) != CE_None)
    1150           0 :                                 eErr = CE_Failure;
    1151           4 :                             if (VSIRename(osTmpFilename, GetDescription()) != 0)
    1152           0 :                                 eErr = CE_Failure;
    1153             :                         }
    1154             :                         else
    1155             :                         {
    1156           0 :                             eErr = CE_Failure;
    1157           0 :                             VSIUnlink(osTmpFilename);
    1158             :                         }
    1159           4 :                         VSIUnlink(
    1160           4 :                             CPLSPrintf("%s.tmp.aux.xml", GetDescription()));
    1161             :                     }
    1162             :                 }
    1163             :             }
    1164             :             else
    1165         751 :                 VSIFCloseL(this->fp_);
    1166             :         }
    1167             : 
    1168        1905 :         JP2OPJLikeDataset::CloseDependentDatasets();
    1169             : 
    1170        1905 :         if (GDALPamDataset::Close() != CE_None)
    1171           0 :             eErr = CE_Failure;
    1172             :     }
    1173        2598 :     return eErr;
    1174             : }
    1175             : 
    1176             : /************************************************************************/
    1177             : /*                       CloseDependentDatasets()                       */
    1178             : /************************************************************************/
    1179             : 
    1180             : template <typename CODEC, typename BASE>
    1181        1920 : int JP2OPJLikeDataset<CODEC, BASE>::CloseDependentDatasets()
    1182             : {
    1183        1920 :     int bRet = GDALJP2AbstractDataset::CloseDependentDatasets();
    1184        1920 :     if (papoOverviewDS)
    1185             :     {
    1186         178 :         for (int i = 0; i < this->nOverviewCount; i++)
    1187         115 :             delete papoOverviewDS[i];
    1188          63 :         CPLFree(papoOverviewDS);
    1189          63 :         papoOverviewDS = nullptr;
    1190          63 :         bRet = TRUE;
    1191             :     }
    1192        1920 :     return bRet;
    1193             : }
    1194             : 
    1195             : /************************************************************************/
    1196             : /*                           SetSpatialRef()                            */
    1197             : /************************************************************************/
    1198             : 
    1199             : template <typename CODEC, typename BASE>
    1200             : CPLErr
    1201          18 : JP2OPJLikeDataset<CODEC, BASE>::SetSpatialRef(const OGRSpatialReference *poSRS)
    1202             : {
    1203          18 :     if (eAccess == GA_Update)
    1204             :     {
    1205           2 :         this->bRewrite = TRUE;
    1206           2 :         m_oSRS.Clear();
    1207           2 :         if (poSRS)
    1208           1 :             m_oSRS = *poSRS;
    1209           2 :         return CE_None;
    1210             :     }
    1211             :     else
    1212          16 :         return GDALJP2AbstractDataset::SetSpatialRef(poSRS);
    1213             : }
    1214             : 
    1215             : /************************************************************************/
    1216             : /*                          SetGeoTransform()                           */
    1217             : /************************************************************************/
    1218             : 
    1219             : template <typename CODEC, typename BASE>
    1220             : CPLErr
    1221          24 : JP2OPJLikeDataset<CODEC, BASE>::SetGeoTransform(const GDALGeoTransform &gt)
    1222             : {
    1223          24 :     if (eAccess == GA_Update)
    1224             :     {
    1225           4 :         this->bRewrite = TRUE;
    1226           4 :         m_gt = gt;
    1227           4 :         bGeoTransformValid = m_gt != GDALGeoTransform();
    1228           4 :         return CE_None;
    1229             :     }
    1230             :     else
    1231          20 :         return GDALJP2AbstractDataset::SetGeoTransform(gt);
    1232             : }
    1233             : 
    1234             : /************************************************************************/
    1235             : /*                              SetGCPs()                               */
    1236             : /************************************************************************/
    1237             : 
    1238             : template <typename CODEC, typename BASE>
    1239           4 : CPLErr JP2OPJLikeDataset<CODEC, BASE>::SetGCPs(int nGCPCountIn,
    1240             :                                                const GDAL_GCP *pasGCPListIn,
    1241             :                                                const OGRSpatialReference *poSRS)
    1242             : {
    1243           4 :     if (eAccess == GA_Update)
    1244             :     {
    1245           3 :         this->bRewrite = TRUE;
    1246           3 :         if (nGCPCount > 0)
    1247             :         {
    1248           1 :             GDALDeinitGCPs(nGCPCount, pasGCPList);
    1249           1 :             CPLFree(pasGCPList);
    1250             :         }
    1251             : 
    1252           3 :         m_oSRS.Clear();
    1253           3 :         if (poSRS)
    1254           2 :             m_oSRS = *poSRS;
    1255             : 
    1256           3 :         nGCPCount = nGCPCountIn;
    1257           3 :         pasGCPList = GDALDuplicateGCPs(nGCPCount, pasGCPListIn);
    1258             : 
    1259           3 :         return CE_None;
    1260             :     }
    1261             :     else
    1262           1 :         return GDALJP2AbstractDataset::SetGCPs(nGCPCountIn, pasGCPListIn,
    1263           1 :                                                poSRS);
    1264             : }
    1265             : 
    1266             : /************************************************************************/
    1267             : /*                            SetMetadata()                             */
    1268             : /************************************************************************/
    1269             : 
    1270             : template <typename CODEC, typename BASE>
    1271          11 : CPLErr JP2OPJLikeDataset<CODEC, BASE>::SetMetadata(CSLConstList papszMetadata,
    1272             :                                                    const char *pszDomain)
    1273             : {
    1274          11 :     if (eAccess == GA_Update)
    1275             :     {
    1276           2 :         this->bRewrite = TRUE;
    1277           2 :         if (pszDomain == nullptr || EQUAL(pszDomain, ""))
    1278             :         {
    1279           1 :             CSLDestroy(m_papszMainMD);
    1280           1 :             m_papszMainMD = CSLDuplicate(papszMetadata);
    1281             :         }
    1282           2 :         return GDALDataset::SetMetadata(papszMetadata, pszDomain);
    1283             :     }
    1284           9 :     return GDALJP2AbstractDataset::SetMetadata(papszMetadata, pszDomain);
    1285             : }
    1286             : 
    1287             : /************************************************************************/
    1288             : /*                            SetMetadata()                             */
    1289             : /************************************************************************/
    1290             : 
    1291             : template <typename CODEC, typename BASE>
    1292           3 : CPLErr JP2OPJLikeDataset<CODEC, BASE>::SetMetadataItem(const char *pszName,
    1293             :                                                        const char *pszValue,
    1294             :                                                        const char *pszDomain)
    1295             : {
    1296           3 :     if (eAccess == GA_Update)
    1297             :     {
    1298           1 :         this->bRewrite = TRUE;
    1299           1 :         if (pszDomain == nullptr || EQUAL(pszDomain, ""))
    1300             :         {
    1301           1 :             GetMetadata();  // update m_papszMainMD
    1302           1 :             m_papszMainMD = CSLSetNameValue(m_papszMainMD, pszName, pszValue);
    1303             :         }
    1304           1 :         return GDALDataset::SetMetadataItem(pszName, pszValue, pszDomain);
    1305             :     }
    1306           2 :     return GDALJP2AbstractDataset::SetMetadataItem(pszName, pszValue,
    1307           2 :                                                    pszDomain);
    1308             : }
    1309             : 
    1310             : /************************************************************************/
    1311             : /*                              Identify()                              */
    1312             : /************************************************************************/
    1313             : 
    1314             : #ifndef jpc_header_defined
    1315             : #define jpc_header_defined
    1316             : static const unsigned char jpc_header[] = {0xff, 0x4f, 0xff,
    1317             :                                            0x51};  // SOC + RSIZ markers
    1318             : static const unsigned char jp2_box_jp[] = {0x6a, 0x50, 0x20, 0x20}; /* 'jP  ' */
    1319             : #endif
    1320             : 
    1321             : template <typename CODEC, typename BASE>
    1322         767 : int JP2OPJLikeDataset<CODEC, BASE>::Identify(GDALOpenInfo *poOpenInfo)
    1323             : 
    1324             : {
    1325         767 :     if (poOpenInfo->nHeaderBytes >= 16 &&
    1326         767 :         (memcmp(poOpenInfo->pabyHeader, jpc_header, sizeof(jpc_header)) == 0 ||
    1327         684 :          memcmp(poOpenInfo->pabyHeader + 4, jp2_box_jp, sizeof(jp2_box_jp)) ==
    1328             :              0))
    1329         767 :         return TRUE;
    1330             : 
    1331             :     else
    1332           0 :         return FALSE;
    1333             : }
    1334             : 
    1335             : /************************************************************************/
    1336             : /*                         JP2FindCodeStream()                          */
    1337             : /************************************************************************/
    1338             : 
    1339             : template <typename CODEC, typename BASE>
    1340             : vsi_l_offset
    1341         774 : JP2OPJLikeDataset<CODEC, BASE>::JP2FindCodeStream(VSILFILE *fp,
    1342             :                                                   vsi_l_offset *pnLength)
    1343             : {
    1344         774 :     vsi_l_offset nCodeStreamStart = 0;
    1345         774 :     vsi_l_offset nCodeStreamLength = 0;
    1346             : 
    1347         774 :     VSIFSeekL(fp, 0, SEEK_SET);
    1348             :     GByte abyHeader[16];
    1349         774 :     VSIFReadL(abyHeader, 1, 16, fp);
    1350             : 
    1351         774 :     if (memcmp(abyHeader, jpc_header, sizeof(jpc_header)) == 0)
    1352             :     {
    1353          83 :         VSIFSeekL(fp, 0, SEEK_END);
    1354          83 :         nCodeStreamLength = VSIFTellL(fp);
    1355          83 :         VSIFSeekL(fp, 0, SEEK_SET);
    1356             :     }
    1357         691 :     else if (memcmp(abyHeader + 4, jp2_box_jp, sizeof(jp2_box_jp)) == 0)
    1358             :     {
    1359         690 :         if (BASE::canPerformDirectIO())
    1360             :         {
    1361             :             // Grok reads the full JP2 file (including boxes) natively,
    1362             :             // so pass nCodeStreamStart=0 and the full file length.
    1363             :             // JP2 boxes (cdef, pclr, etc.) are parsed by Grok's own
    1364             :             // JP2 family decoder.
    1365           0 :             VSIFSeekL(fp, 0, SEEK_END);
    1366           0 :             nCodeStreamLength = VSIFTellL(fp);
    1367           0 :             VSIFSeekL(fp, 0, SEEK_SET);
    1368             :         }
    1369             :         else
    1370             :         {
    1371             :             /* Find offset of first jp2c box */
    1372        1380 :             GDALJP2Box oBox(fp);
    1373         690 :             if (oBox.ReadFirst())
    1374             :             {
    1375        3589 :                 while (strlen(oBox.GetType()) > 0)
    1376             :                 {
    1377        3589 :                     if (EQUAL(oBox.GetType(), "jp2c"))
    1378             :                     {
    1379         689 :                         nCodeStreamStart = VSIFTellL(fp);
    1380         689 :                         nCodeStreamLength = oBox.GetDataLength();
    1381         689 :                         break;
    1382             :                     }
    1383             : 
    1384        2900 :                     if (!oBox.ReadNext())
    1385           1 :                         break;
    1386             :                 }
    1387             :             }
    1388             :         }
    1389             :     }
    1390             :     else
    1391             :     {
    1392           1 :         CPLError(CE_Failure, CPLE_AppDefined, "No JPEG 2000 stream detected");
    1393             :     }
    1394             : 
    1395         774 :     *pnLength = nCodeStreamLength;
    1396         774 :     return nCodeStreamStart;
    1397             : }
    1398             : 
    1399             : /************************************************************************/
    1400             : /*                                Open()                                */
    1401             : /************************************************************************/
    1402             : 
    1403             : template <typename CODEC, typename BASE>
    1404         767 : GDALDataset *JP2OPJLikeDataset<CODEC, BASE>::Open(GDALOpenInfo *poOpenInfo)
    1405             : 
    1406             : {
    1407         767 :     if (!Identify(poOpenInfo) || poOpenInfo->fpL == nullptr)
    1408           0 :         return nullptr;
    1409             : 
    1410             :     /* Detect which codec to use : J2K or JP2 ? */
    1411         767 :     vsi_l_offset nCodeStreamLength = 0;
    1412             :     vsi_l_offset nCodeStreamStart =
    1413         767 :         JP2FindCodeStream(poOpenInfo->fpL, &nCodeStreamLength);
    1414             : 
    1415         767 :     if (nCodeStreamStart == 0 && nCodeStreamLength == 0)
    1416             :     {
    1417           1 :         CPLError(CE_Failure, CPLE_AppDefined, "No code-stream in JP2 file");
    1418           1 :         return nullptr;
    1419             :     }
    1420        1532 :     JP2OPJLikeDataset oTmpDS;
    1421         766 :     int numThreads = oTmpDS.GetNumThreads();
    1422        1532 :     auto eCodecFormat = (memcmp(poOpenInfo->pabyHeader + 4, jp2_box_jp,
    1423             :                                 sizeof(jp2_box_jp)) == 0)
    1424         766 :                             ? CODEC::cvtenum(JP2_CODEC_JP2)
    1425          83 :                             : CODEC::cvtenum(JP2_CODEC_J2K);
    1426             : 
    1427         766 :     uint32_t nTileW = 0, nTileH = 0;
    1428         766 :     int numResolutions = 0;
    1429         766 :     CODEC localctx;
    1430         766 :     localctx.open(poOpenInfo->fpL, nCodeStreamStart);
    1431         766 :     if (!localctx.setUpDecompress(numThreads, poOpenInfo->pszFilename,
    1432             :                                   nCodeStreamLength, &nTileW, &nTileH,
    1433             :                                   &numResolutions))
    1434           6 :         return nullptr;
    1435             : 
    1436         760 :     GDALDataType eDataType = GDT_UInt8;
    1437         760 :     if (localctx.psImage->comps[0].prec > 16)
    1438             :     {
    1439           0 :         if (localctx.psImage->comps[0].sgnd)
    1440           0 :             eDataType = GDT_Int32;
    1441             :         else
    1442           0 :             eDataType = GDT_UInt32;
    1443             :     }
    1444         760 :     else if (localctx.psImage->comps[0].prec > 8)
    1445             :     {
    1446          26 :         if (localctx.psImage->comps[0].sgnd)
    1447           7 :             eDataType = GDT_Int16;
    1448             :         else
    1449          19 :             eDataType = GDT_UInt16;
    1450             :     }
    1451             : 
    1452         760 :     int bIs420 =
    1453        1520 :         (localctx.psImage->color_space != CODEC::cvtenum(JP2_CLRSPC_SRGB) &&
    1454         734 :          eDataType == GDT_UInt8 &&
    1455         734 :          (localctx.psImage->numcomps == 3 || localctx.psImage->numcomps == 4) &&
    1456          59 :          localctx.psImage->comps[1].w == localctx.psImage->comps[0].w / 2 &&
    1457           3 :          localctx.psImage->comps[1].h == localctx.psImage->comps[0].h / 2 &&
    1458           3 :          localctx.psImage->comps[2].w == localctx.psImage->comps[0].w / 2 &&
    1459        1523 :          localctx.psImage->comps[2].h == localctx.psImage->comps[0].h / 2) &&
    1460           3 :         (localctx.psImage->numcomps == 3 ||
    1461           2 :          (localctx.psImage->numcomps == 4 &&
    1462           2 :           localctx.psImage->comps[3].w == localctx.psImage->comps[0].w &&
    1463           2 :           localctx.psImage->comps[3].h == localctx.psImage->comps[0].h));
    1464             : 
    1465         760 :     if (bIs420)
    1466             :     {
    1467           3 :         CPLDebug(CODEC::debugId(), "420 format");
    1468             :     }
    1469             :     else
    1470             :     {
    1471         921 :         for (unsigned iBand = 2; iBand <= localctx.psImage->numcomps; iBand++)
    1472             :         {
    1473         164 :             if (localctx.psImage->comps[iBand - 1].w !=
    1474         164 :                     localctx.psImage->comps[0].w ||
    1475         164 :                 localctx.psImage->comps[iBand - 1].h !=
    1476         164 :                     localctx.psImage->comps[0].h)
    1477             :             {
    1478           0 :                 CPLDebug(CODEC::debugId(), "Unable to handle that image (2)");
    1479           0 :                 localctx.free();
    1480           0 :                 return nullptr;
    1481             :             }
    1482             :         }
    1483             :     }
    1484             : 
    1485             :     /* -------------------------------------------------------------------- */
    1486             :     /*      Create a corresponding GDALDataset.                             */
    1487             :     /* -------------------------------------------------------------------- */
    1488             :     JP2OPJLikeDataset *poDS;
    1489             :     int iBand;
    1490             : 
    1491         760 :     poDS = new JP2OPJLikeDataset();
    1492         760 :     poDS->m_osFilename = poOpenInfo->pszFilename;
    1493         760 :     if (eCodecFormat == CODEC::cvtenum(JP2_CODEC_JP2))
    1494         679 :         poDS->eAccess = poOpenInfo->eAccess;
    1495         760 :     poDS->eColorSpace = localctx.psImage->color_space;
    1496         760 :     poDS->nRasterXSize = localctx.psImage->x1 - localctx.psImage->x0;
    1497         760 :     poDS->nRasterYSize = localctx.psImage->y1 - localctx.psImage->y0;
    1498         760 :     poDS->nBands = localctx.psImage->numcomps;
    1499         760 :     poDS->fp_ = poOpenInfo->fpL;
    1500         760 :     poOpenInfo->fpL = nullptr;
    1501         760 :     poDS->nCodeStreamStart = nCodeStreamStart;
    1502         760 :     poDS->nCodeStreamLength = nCodeStreamLength;
    1503         760 :     poDS->bIs420 = bIs420;
    1504        1470 :     poDS->bSingleTiled = (poDS->nRasterXSize == static_cast<int>(nTileW) &&
    1505         710 :                           poDS->nRasterYSize == static_cast<int>(nTileH));
    1506         760 :     poDS->m_nX0 = localctx.psImage->x0;
    1507         760 :     poDS->m_nY0 = localctx.psImage->y0;
    1508         760 :     poDS->m_nTileWidth = nTileW;
    1509         760 :     poDS->m_nTileHeight = nTileH;
    1510             : 
    1511         760 :     int nBlockXSize = static_cast<int>(nTileW);
    1512         760 :     int nBlockYSize = static_cast<int>(nTileH);
    1513             : 
    1514         760 :     if (CPLFetchBool(poOpenInfo->papszOpenOptions, "USE_TILE_AS_BLOCK", false))
    1515             :     {
    1516           0 :         poDS->bUseSetDecodeArea = false;
    1517             :     }
    1518             : 
    1519         760 :     poDS->m_bStrict = CPLTestBool(
    1520         760 :         CSLFetchNameValueDef(poOpenInfo->papszOpenOptions, "STRICT", "YES"));
    1521         760 :     localctx.updateStrict(poDS->m_bStrict);
    1522             : 
    1523         760 :     if (localctx.preferPerBlockDeCompress())
    1524             :     {
    1525             :         /* Some Sentinel2 preview datasets are 343x343 large, but with 8x8 blocks */
    1526             :         /* Using the tile API for that is super slow, so expose a single block */
    1527         760 :         if (poDS->nRasterXSize <= 1024 && poDS->nRasterYSize <= 1024 &&
    1528         744 :             nTileW < 32 && nTileH < 32)
    1529             :         {
    1530         575 :             poDS->bUseSetDecodeArea = true;
    1531         575 :             nBlockXSize = poDS->nRasterXSize;
    1532         575 :             nBlockYSize = poDS->nRasterYSize;
    1533             :         }
    1534             :         else
    1535             :         {
    1536         185 :             poDS->bUseSetDecodeArea =
    1537         322 :                 poDS->bSingleTiled &&
    1538         137 :                 (poDS->nRasterXSize > 1024 || poDS->nRasterYSize > 1024);
    1539             : 
    1540             :             /* Other Sentinel2 preview datasets are 343x343 and 60m are 1830x1830,
    1541             :              * but they */
    1542             :             /* are tiled with tile dimensions 2048x2048. It would be a waste of */
    1543             :             /* memory to allocate such big blocks */
    1544         185 :             if (poDS->nRasterXSize < static_cast<int>(nTileW) &&
    1545          18 :                 poDS->nRasterYSize < static_cast<int>(nTileH))
    1546             :             {
    1547          18 :                 poDS->bUseSetDecodeArea = TRUE;
    1548          18 :                 nBlockXSize = poDS->nRasterXSize;
    1549          18 :                 nBlockYSize = poDS->nRasterYSize;
    1550          18 :                 if (nBlockXSize > 2048)
    1551           0 :                     nBlockXSize = 2048;
    1552          18 :                 if (nBlockYSize > 2048)
    1553           0 :                     nBlockYSize = 2048;
    1554             :             }
    1555         167 :             else if (poDS->bUseSetDecodeArea)
    1556             :             {
    1557             :                 // Arbitrary threshold... ~4 million at least needed for the GRIB2
    1558             :                 // images mentioned below.
    1559           7 :                 if (nTileH == 1 && nTileW < 20 * 1024 * 1024)
    1560             :                 {
    1561             :                     // Some GRIB2 JPEG2000 compressed images are a 2D image
    1562             :                     // organized as a single line image...
    1563             :                 }
    1564             :                 else
    1565             :                 {
    1566           7 :                     if (nBlockXSize > 1024)
    1567           7 :                         nBlockXSize = 1024;
    1568           7 :                     if (nBlockYSize > 1024)
    1569           7 :                         nBlockYSize = 1024;
    1570             :                 }
    1571             :             }
    1572             :         }
    1573             :     }
    1574             : 
    1575         760 :     GDALColorTable *poCT = nullptr;
    1576             : 
    1577             :     /* -------------------------------------------------------------------- */
    1578             :     /*      Look for color table or cdef box                                */
    1579             :     /* -------------------------------------------------------------------- */
    1580        1439 :     if (eCodecFormat == CODEC::cvtenum(JP2_CODEC_JP2) &&
    1581         679 :         BASE::canPerformDirectIO())
    1582             :     {
    1583             :         /* Grok parses JP2 boxes natively; extract cdef/pclr from codec */
    1584           0 :         localctx.extractJP2BoxInfo(poDS->nBands, poDS->nRedIndex,
    1585           0 :                                    poDS->nGreenIndex, poDS->nBlueIndex,
    1586           0 :                                    poDS->nAlphaIndex, &poCT);
    1587             :     }
    1588         760 :     else if (eCodecFormat == CODEC::cvtenum(JP2_CODEC_JP2))
    1589             :     {
    1590         679 :         vsi_l_offset nCurOffset = VSIFTellL(poDS->fp_);
    1591             : 
    1592        1358 :         GDALJP2Box oBox(poDS->fp_);
    1593         679 :         if (oBox.ReadFirst())
    1594             :         {
    1595        3629 :             while (strlen(oBox.GetType()) > 0)
    1596             :             {
    1597        3629 :                 if (EQUAL(oBox.GetType(), "jp2h"))
    1598             :                 {
    1599        1358 :                     GDALJP2Box oSubBox(poDS->fp_);
    1600             : 
    1601        2150 :                     for (oSubBox.ReadFirstChild(&oBox);
    1602        2150 :                          strlen(oSubBox.GetType()) > 0;
    1603        1471 :                          oSubBox.ReadNextChild(&oBox))
    1604             :                     {
    1605        1471 :                         GIntBig nDataLength = oSubBox.GetDataLength();
    1606        4388 :                         if (poCT == nullptr &&
    1607        1446 :                             EQUAL(oSubBox.GetType(), "pclr") &&
    1608        2917 :                             nDataLength >= 3 &&
    1609             :                             nDataLength <= 2 + 1 + 4 + 4 * 256)
    1610             :                         {
    1611          24 :                             GByte *pabyCT = oSubBox.ReadBoxData();
    1612          24 :                             if (pabyCT != nullptr)
    1613             :                             {
    1614          24 :                                 int nEntries = (pabyCT[0] << 8) | pabyCT[1];
    1615          24 :                                 int nComponents = pabyCT[2];
    1616             :                                 /* CPLDebug(CODEC::debugId(), "Color table found"); */
    1617          24 :                                 if (nEntries <= 256 && nComponents == 3)
    1618             :                                 {
    1619             :                                     /*CPLDebug(CODEC::debugId(), "resol[0] = %d",
    1620             :                                     pabyCT[3]); CPLDebug(CODEC::debugId(), "resol[1] =
    1621             :                                     %d", pabyCT[4]); CPLDebug(CODEC::debugId(),
    1622             :                                     "resol[2] = %d", pabyCT[5]);*/
    1623          19 :                                     if (pabyCT[3] == 7 && pabyCT[4] == 7 &&
    1624          19 :                                         pabyCT[5] == 7 &&
    1625          19 :                                         nDataLength == 2 + 1 + 3 + 3 * nEntries)
    1626             :                                     {
    1627          19 :                                         poCT = new GDALColorTable();
    1628        1103 :                                         for (int i = 0; i < nEntries; i++)
    1629             :                                         {
    1630             :                                             GDALColorEntry sEntry;
    1631        1084 :                                             sEntry.c1 = pabyCT[6 + 3 * i];
    1632        1084 :                                             sEntry.c2 = pabyCT[6 + 3 * i + 1];
    1633        1084 :                                             sEntry.c3 = pabyCT[6 + 3 * i + 2];
    1634        1084 :                                             sEntry.c4 = 255;
    1635        1084 :                                             poCT->SetColorEntry(i, &sEntry);
    1636             :                                         }
    1637          19 :                                     }
    1638             :                                 }
    1639           5 :                                 else if (nEntries <= 256 && nComponents == 4)
    1640             :                                 {
    1641           5 :                                     if (pabyCT[3] == 7 && pabyCT[4] == 7 &&
    1642           5 :                                         pabyCT[5] == 7 && pabyCT[6] == 7 &&
    1643           5 :                                         nDataLength == 2 + 1 + 4 + 4 * nEntries)
    1644             :                                     {
    1645           5 :                                         poCT = new GDALColorTable();
    1646          17 :                                         for (int i = 0; i < nEntries; i++)
    1647             :                                         {
    1648             :                                             GDALColorEntry sEntry;
    1649          12 :                                             sEntry.c1 = pabyCT[7 + 4 * i];
    1650          12 :                                             sEntry.c2 = pabyCT[7 + 4 * i + 1];
    1651          12 :                                             sEntry.c3 = pabyCT[7 + 4 * i + 2];
    1652          12 :                                             sEntry.c4 = pabyCT[7 + 4 * i + 3];
    1653          12 :                                             poCT->SetColorEntry(i, &sEntry);
    1654             :                                         }
    1655             :                                     }
    1656             :                                 }
    1657          24 :                                 CPLFree(pabyCT);
    1658             :                             }
    1659             :                         }
    1660             :                         /* There's a bug/misfeature in openjpeg: the color_space
    1661             :                            only gets set at read tile time */
    1662        1447 :                         else if (EQUAL(oSubBox.GetType(), "colr") &&
    1663             :                                  nDataLength == 7)
    1664             :                         {
    1665         679 :                             GByte *pabyContent = oSubBox.ReadBoxData();
    1666         679 :                             if (pabyContent != nullptr)
    1667             :                             {
    1668         679 :                                 if (pabyContent[0] ==
    1669             :                                     1 /* enumerated colourspace */)
    1670             :                                 {
    1671         679 :                                     GUInt32 enumcs = (pabyContent[3] << 24) |
    1672         679 :                                                      (pabyContent[4] << 16) |
    1673         679 :                                                      (pabyContent[5] << 8) |
    1674             :                                                      (pabyContent[6]);
    1675         679 :                                     if (enumcs == 16)
    1676             :                                     {
    1677          53 :                                         poDS->eColorSpace =
    1678          53 :                                             CODEC::cvtenum(JP2_CLRSPC_SRGB);
    1679          53 :                                         CPLDebug(CODEC::debugId(),
    1680             :                                                  "SRGB color space");
    1681             :                                     }
    1682         626 :                                     else if (enumcs == 17)
    1683             :                                     {
    1684         619 :                                         poDS->eColorSpace =
    1685         619 :                                             CODEC::cvtenum(JP2_CLRSPC_GRAY);
    1686         619 :                                         CPLDebug(CODEC::debugId(),
    1687             :                                                  "Grayscale color space");
    1688             :                                     }
    1689           7 :                                     else if (enumcs == 18)
    1690             :                                     {
    1691           3 :                                         poDS->eColorSpace =
    1692           3 :                                             CODEC::cvtenum(JP2_CLRSPC_SYCC);
    1693           3 :                                         CPLDebug(CODEC::debugId(),
    1694             :                                                  "SYCC color space");
    1695             :                                     }
    1696           4 :                                     else if (enumcs == 20)
    1697             :                                     {
    1698             :                                         /* Used by
    1699             :                                          * J2KP4files/testfiles_jp2/file7.jp2 */
    1700           0 :                                         poDS->eColorSpace =
    1701           0 :                                             CODEC::cvtenum(JP2_CLRSPC_SRGB);
    1702           0 :                                         CPLDebug(CODEC::debugId(),
    1703             :                                                  "e-sRGB color space");
    1704             :                                     }
    1705           4 :                                     else if (enumcs == 21)
    1706             :                                     {
    1707             :                                         /* Used by
    1708             :                                          * J2KP4files/testfiles_jp2/file5.jp2 */
    1709           0 :                                         poDS->eColorSpace =
    1710           0 :                                             CODEC::cvtenum(JP2_CLRSPC_SRGB);
    1711           0 :                                         CPLDebug(CODEC::debugId(),
    1712             :                                                  "ROMM-RGB color space");
    1713             :                                     }
    1714             :                                     else
    1715             :                                     {
    1716           4 :                                         poDS->eColorSpace =
    1717           4 :                                             CODEC::cvtenum(JP2_CLRSPC_UNKNOWN);
    1718           4 :                                         CPLDebug(CODEC::debugId(),
    1719             :                                                  "Unknown color space");
    1720             :                                     }
    1721             :                                 }
    1722         679 :                                 CPLFree(pabyContent);
    1723             :                             }
    1724             :                         }
    1725             :                         /* Check if there's an alpha channel or odd channel
    1726             :                          * attribution */
    1727         799 :                         else if (EQUAL(oSubBox.GetType(), "cdef") &&
    1728          31 :                                  nDataLength == 2 + poDS->nBands * 6)
    1729             :                         {
    1730          30 :                             GByte *pabyContent = oSubBox.ReadBoxData();
    1731          30 :                             if (pabyContent != nullptr)
    1732             :                             {
    1733          30 :                                 int nEntries =
    1734          30 :                                     (pabyContent[0] << 8) | pabyContent[1];
    1735          30 :                                 if (nEntries == poDS->nBands)
    1736             :                                 {
    1737          30 :                                     poDS->nRedIndex = -1;
    1738          30 :                                     poDS->nGreenIndex = -1;
    1739          30 :                                     poDS->nBlueIndex = -1;
    1740         135 :                                     for (int i = 0; i < poDS->nBands; i++)
    1741             :                                     {
    1742         105 :                                         int CNi =
    1743         105 :                                             (pabyContent[2 + 6 * i] << 8) |
    1744         105 :                                             pabyContent[2 + 6 * i + 1];
    1745         105 :                                         int Typi =
    1746         105 :                                             (pabyContent[2 + 6 * i + 2] << 8) |
    1747         105 :                                             pabyContent[2 + 6 * i + 3];
    1748         105 :                                         int Asoci =
    1749         105 :                                             (pabyContent[2 + 6 * i + 4] << 8) |
    1750         105 :                                             pabyContent[2 + 6 * i + 5];
    1751         105 :                                         if (CNi < 0 || CNi >= poDS->nBands)
    1752             :                                         {
    1753           0 :                                             CPLError(CE_Failure,
    1754             :                                                      CPLE_AppDefined,
    1755             :                                                      "Wrong value of CN%d=%d",
    1756             :                                                      i, CNi);
    1757           0 :                                             break;
    1758             :                                         }
    1759         105 :                                         if (Typi == 0)
    1760             :                                         {
    1761          66 :                                             if (Asoci == 1)
    1762          30 :                                                 poDS->nRedIndex = CNi;
    1763          36 :                                             else if (Asoci == 2)
    1764          18 :                                                 poDS->nGreenIndex = CNi;
    1765          18 :                                             else if (Asoci == 3)
    1766          18 :                                                 poDS->nBlueIndex = CNi;
    1767           0 :                                             else if (Asoci < 0 ||
    1768           0 :                                                      (Asoci > poDS->nBands &&
    1769             :                                                       Asoci != 65535))
    1770             :                                             {
    1771           0 :                                                 CPLError(
    1772             :                                                     CE_Failure, CPLE_AppDefined,
    1773             :                                                     "Wrong value of Asoc%d=%d",
    1774             :                                                     i, Asoci);
    1775           0 :                                                 break;
    1776             :                                             }
    1777             :                                         }
    1778          39 :                                         else if (Typi == 1)
    1779             :                                         {
    1780          27 :                                             poDS->nAlphaIndex = CNi;
    1781             :                                         }
    1782             :                                     }
    1783             :                                 }
    1784             :                                 else
    1785             :                                 {
    1786           0 :                                     CPLDebug(CODEC::debugId(),
    1787             :                                              "Unsupported cdef content");
    1788             :                                 }
    1789          30 :                                 CPLFree(pabyContent);
    1790             :                             }
    1791             :                         }
    1792             :                     }
    1793             :                 }
    1794             : 
    1795        3629 :                 if (!oBox.ReadNext())
    1796         679 :                     break;
    1797             :             }
    1798             :         }
    1799             : 
    1800         679 :         VSIFSeekL(poDS->fp_, nCurOffset, SEEK_SET);
    1801             :     }
    1802             : 
    1803        1439 :     if (eCodecFormat == CODEC::cvtenum(JP2_CODEC_JP2) &&
    1804         679 :         poDS->eColorSpace == CODEC::cvtenum(JP2_CLRSPC_GRAY) &&
    1805         619 :         poDS->nBands == 4 && poDS->nRedIndex == 0 && poDS->nGreenIndex == 1 &&
    1806        1444 :         poDS->nBlueIndex == 2 &&
    1807           5 :         poDS->m_osFilename.find("dop10rgbi") != std::string::npos)
    1808             :     {
    1809           0 :         CPLDebug(CODEC::debugId(),
    1810             :                  "Autofix wrong colorspace from Greyscale to sRGB");
    1811             :         // Workaround https://github.com/uclouvain/openjpeg/issues/1464
    1812             :         // dop10rgbi products from https://www.opengeodata.nrw.de/produkte/geobasis/lusat/dop/dop_jp2_f10/
    1813             :         // have a wrong color space.
    1814           0 :         poDS->eColorSpace = CODEC::cvtenum(JP2_CLRSPC_SRGB);
    1815             :     }
    1816             : 
    1817             :     /* -------------------------------------------------------------------- */
    1818             :     /*      Create band information objects.                                */
    1819             :     /* -------------------------------------------------------------------- */
    1820        1692 :     for (iBand = 1; iBand <= poDS->nBands; iBand++)
    1821             :     {
    1822         932 :         const bool bPromoteTo8Bit =
    1823         959 :             iBand == poDS->nAlphaIndex + 1 &&
    1824          27 :             localctx.psImage
    1825          27 :                     ->comps[(poDS->nAlphaIndex == 0 && poDS->nBands > 1) ? 1
    1826             :                                                                          : 0]
    1827          27 :                     .prec == 8 &&
    1828         973 :             localctx.psImage->comps[poDS->nAlphaIndex].prec == 1 &&
    1829          14 :             CPLFetchBool(poOpenInfo->papszOpenOptions, "1BIT_ALPHA_PROMOTION",
    1830          14 :                          CPLTestBool(CPLGetConfigOption(
    1831             :                              "JP2OPENJPEG_PROMOTE_1BIT_ALPHA_AS_8BIT", "YES")));
    1832         932 :         if (bPromoteTo8Bit)
    1833             :         {
    1834          10 :             poDS->bHas1BitAlpha = true;
    1835          10 :             CPLDebug(CODEC::debugId(),
    1836             :                      "Alpha band is promoted from 1 bit to 8 bit");
    1837             :         }
    1838             : 
    1839        1854 :         auto poBand = new JP2OPJLikeRasterBand<CODEC, BASE>(
    1840             :             poDS, iBand, eDataType,
    1841         922 :             bPromoteTo8Bit ? 8 : localctx.psImage->comps[iBand - 1].prec,
    1842             :             bPromoteTo8Bit, nBlockXSize, nBlockYSize);
    1843         932 :         if (iBand == 1 && poCT != nullptr)
    1844          24 :             poBand->poCT = poCT;
    1845         932 :         poDS->SetBand(iBand, poBand);
    1846             :     }
    1847             : 
    1848             :     /* -------------------------------------------------------------------- */
    1849             :     /*      Create overview datasets.                                       */
    1850             :     /* -------------------------------------------------------------------- */
    1851         760 :     int nW = poDS->nRasterXSize;
    1852         760 :     int nH = poDS->nRasterYSize;
    1853         760 :     poDS->nParentXSize = poDS->nRasterXSize;
    1854         760 :     poDS->nParentYSize = poDS->nRasterYSize;
    1855             : 
    1856             :     /* Lower resolutions are not compatible with a color-table */
    1857         760 :     if (poCT != nullptr)
    1858          24 :         numResolutions = 0;
    1859             : 
    1860         760 :     if (poDS->bSingleTiled && poDS->bUseSetDecodeArea)
    1861             :     {
    1862         580 :         poDS->cacheNew(&localctx);
    1863             :     }
    1864         760 :     poDS->m_pnLastLevel = new int(-1);
    1865             : 
    1866             :     // Create overview datasets from JPEG2000 resolution levels.
    1867             :     // For Grok (canPerformDirectIO()=true), overviews lazily create their
    1868             :     // own codec on first read and use DirectRasterIO, so no block-size
    1869             :     // or decode-area constraints apply.  For OpenJPEG, overviews require
    1870             :     // either bUseSetDecodeArea or even tile dimensions.
    1871        1190 :     while (poDS->nOverviewCount + 1 < numResolutions &&
    1872        1117 :            (nW > 128 || nH > 128) &&
    1873         240 :            (BASE::canPerformDirectIO() || poDS->bUseSetDecodeArea ||
    1874          86 :             ((nTileW % 2) == 0 && (nTileH % 2) == 0)))
    1875             :     {
    1876             :         // This must be this exact formula per the JPEG2000 standard
    1877         117 :         nW = (nW + 1) / 2;
    1878         117 :         nH = (nH + 1) / 2;
    1879             : 
    1880         117 :         poDS->papoOverviewDS = static_cast<JP2OPJLikeDataset<CODEC, BASE> **>(
    1881         234 :             CPLRealloc(poDS->papoOverviewDS,
    1882         117 :                        (poDS->nOverviewCount + 1) *
    1883             :                            sizeof(JP2OPJLikeDataset<CODEC, BASE> *)));
    1884         117 :         JP2OPJLikeDataset *poODS = new JP2OPJLikeDataset();
    1885         117 :         poODS->m_osFilename = poDS->m_osFilename;
    1886         117 :         poODS->nParentXSize = poDS->nRasterXSize;
    1887         117 :         poODS->nParentYSize = poDS->nRasterYSize;
    1888         117 :         poODS->SetDescription(poOpenInfo->pszFilename);
    1889         117 :         poODS->iLevel = poDS->nOverviewCount + 1;
    1890         117 :         poODS->bSingleTiled = poDS->bSingleTiled;
    1891         117 :         poODS->bUseSetDecodeArea = poDS->bUseSetDecodeArea;
    1892         117 :         poODS->nRedIndex = poDS->nRedIndex;
    1893         117 :         poODS->nGreenIndex = poDS->nGreenIndex;
    1894         117 :         poODS->nBlueIndex = poDS->nBlueIndex;
    1895         117 :         poODS->nAlphaIndex = poDS->nAlphaIndex;
    1896         117 :         if (BASE::canPerformDirectIO())
    1897             :         {
    1898             :             // DirectRasterIO bypasses block-based I/O, so block size
    1899             :             // is irrelevant; set to full overview dimensions.
    1900           0 :             nBlockXSize = nW;
    1901           0 :             nBlockYSize = nH;
    1902             :         }
    1903         117 :         else if (!poDS->bUseSetDecodeArea)
    1904             :         {
    1905          83 :             nTileW /= 2;
    1906          83 :             nTileH /= 2;
    1907          83 :             nBlockXSize = static_cast<int>(nTileW);
    1908          83 :             nBlockYSize = static_cast<int>(nTileH);
    1909             :         }
    1910             :         else
    1911             :         {
    1912          34 :             nBlockXSize = std::min(nW, static_cast<int>(nTileW));
    1913          34 :             nBlockYSize = std::min(nH, static_cast<int>(nTileH));
    1914             :         }
    1915             : 
    1916         117 :         poODS->eColorSpace = poDS->eColorSpace;
    1917         117 :         poODS->nRasterXSize = nW;
    1918         117 :         poODS->nRasterYSize = nH;
    1919         117 :         poODS->nBands = poDS->nBands;
    1920         117 :         poODS->fp_ = poDS->fp_;
    1921         117 :         poODS->nCodeStreamStart = nCodeStreamStart;
    1922         117 :         poODS->nCodeStreamLength = nCodeStreamLength;
    1923         117 :         poODS->bIs420 = bIs420;
    1924             : 
    1925         117 :         if (poODS->bSingleTiled && poODS->bUseSetDecodeArea)
    1926             :         {
    1927          32 :             poODS->cache(poDS);
    1928             :         }
    1929         117 :         poODS->m_pnLastLevel = poDS->m_pnLastLevel;
    1930         117 :         poODS->m_bStrict = poDS->m_bStrict;
    1931             : 
    1932         117 :         poODS->m_nX0 = poDS->m_nX0;
    1933         117 :         poODS->m_nY0 = poDS->m_nY0;
    1934             : 
    1935             :         // For Grok's DirectRasterIO path, overview datasets need tile
    1936             :         // dimensions scaled to the reduced resolution level so that
    1937             :         // tile range and row-iteration computations work correctly.
    1938         117 :         if (BASE::canPerformDirectIO())
    1939             :         {
    1940           0 :             const int ovLevel = poODS->iLevel;
    1941           0 :             poODS->m_nTileWidth = (nTileW + (1 << ovLevel) - 1) >> ovLevel;
    1942           0 :             poODS->m_nTileHeight = (nTileH + (1 << ovLevel) - 1) >> ovLevel;
    1943             :         }
    1944             : 
    1945         335 :         for (iBand = 1; iBand <= poDS->nBands; iBand++)
    1946             :         {
    1947         218 :             const bool bPromoteTo8Bit =
    1948         237 :                 iBand == poDS->nAlphaIndex + 1 &&
    1949          19 :                 localctx.psImage
    1950          19 :                         ->comps[(poDS->nAlphaIndex == 0 && poDS->nBands > 1)
    1951             :                                     ? 1
    1952             :                                     : 0]
    1953          19 :                         .prec == 8 &&
    1954         246 :                 localctx.psImage->comps[poDS->nAlphaIndex].prec == 1 &&
    1955           9 :                 CPLFetchBool(
    1956           9 :                     poOpenInfo->papszOpenOptions, "1BIT_ALPHA_PROMOTION",
    1957           9 :                     CPLTestBool(CPLGetConfigOption(
    1958             :                         "JP2OPENJPEG_PROMOTE_1BIT_ALPHA_AS_8BIT", "YES")));
    1959             : 
    1960         218 :             poODS->SetBand(iBand,
    1961         430 :                            new JP2OPJLikeRasterBand<CODEC, BASE>(
    1962             :                                poODS, iBand, eDataType,
    1963             :                                bPromoteTo8Bit
    1964             :                                    ? 8
    1965         212 :                                    : localctx.psImage->comps[iBand - 1].prec,
    1966             :                                bPromoteTo8Bit, nBlockXSize, nBlockYSize));
    1967             :         }
    1968             : 
    1969         117 :         poDS->papoOverviewDS[poDS->nOverviewCount++] = poODS;
    1970             :     }
    1971             : 
    1972         760 :     poDS->openCompleteJP2(&localctx);
    1973             : 
    1974             :     /* -------------------------------------------------------------------- */
    1975             :     /*      More metadata.                                                  */
    1976             :     /* -------------------------------------------------------------------- */
    1977         760 :     if (poDS->nBands > 1)
    1978             :     {
    1979          73 :         poDS->GDALDataset::SetMetadataItem(GDALMD_INTERLEAVE, "PIXEL",
    1980             :                                            GDAL_MDD_IMAGE_STRUCTURE);
    1981             :     }
    1982             : 
    1983         760 :     poOpenInfo->fpL = poDS->fp_;
    1984         760 :     vsi_l_offset nCurOffset = VSIFTellL(poDS->fp_);
    1985         760 :     poDS->LoadJP2Metadata(poOpenInfo);
    1986         760 :     VSIFSeekL(poDS->fp_, nCurOffset, SEEK_SET);
    1987         760 :     poOpenInfo->fpL = nullptr;
    1988             : 
    1989         760 :     poDS->bHasGeoreferencingAtOpening =
    1990         996 :         (!poDS->m_oSRS.IsEmpty() || poDS->nGCPCount != 0 ||
    1991         236 :          poDS->bGeoTransformValid);
    1992             : 
    1993             :     /* -------------------------------------------------------------------- */
    1994             :     /*      Vector layers                                                   */
    1995             :     /* -------------------------------------------------------------------- */
    1996         760 :     if (poOpenInfo->nOpenFlags & GDAL_OF_VECTOR)
    1997             :     {
    1998          66 :         poDS->LoadVectorLayers(CPLFetchBool(poOpenInfo->papszOpenOptions,
    1999             :                                             "OPEN_REMOTE_GML", false));
    2000             : 
    2001             :         // If file opened in vector-only mode and there's no vector,
    2002             :         // return
    2003          70 :         if ((poOpenInfo->nOpenFlags & GDAL_OF_RASTER) == 0 &&
    2004           4 :             poDS->GetLayerCount() == 0)
    2005             :         {
    2006           2 :             delete poDS;
    2007           2 :             return nullptr;
    2008             :         }
    2009             :     }
    2010             : 
    2011             :     /* -------------------------------------------------------------------- */
    2012             :     /*      Initialize any PAM information.                                 */
    2013             :     /* -------------------------------------------------------------------- */
    2014         758 :     poDS->SetDescription(poOpenInfo->pszFilename);
    2015         758 :     poDS->TryLoadXML(poOpenInfo->GetSiblingFiles());
    2016             : 
    2017             :     /* -------------------------------------------------------------------- */
    2018             :     /*      Check for overviews.                                            */
    2019             :     /* -------------------------------------------------------------------- */
    2020         758 :     poDS->oOvManager.Initialize(poDS, poOpenInfo);
    2021             : 
    2022         758 :     return poDS;
    2023             : }
    2024             : 
    2025             : /************************************************************************/
    2026             : /*                              WriteBox()                              */
    2027             : /************************************************************************/
    2028             : 
    2029             : template <typename CODEC, typename BASE>
    2030         942 : bool JP2OPJLikeDataset<CODEC, BASE>::WriteBox(VSILFILE *fp, GDALJP2Box *poBox)
    2031             : {
    2032             :     GUInt32 nLBox;
    2033             :     GUInt32 nTBox;
    2034             : 
    2035         942 :     if (poBox == nullptr)
    2036           0 :         return true;
    2037             : 
    2038         942 :     nLBox = static_cast<int>(poBox->GetDataLength()) + 8;
    2039         942 :     nLBox = CPL_MSBWORD32(nLBox);
    2040             : 
    2041         942 :     memcpy(&nTBox, poBox->GetType(), 4);
    2042             : 
    2043         942 :     return VSIFWriteL(&nLBox, 4, 1, fp) == 1 &&
    2044        1884 :            VSIFWriteL(&nTBox, 4, 1, fp) == 1 &&
    2045         942 :            VSIFWriteL(poBox->GetWritableData(),
    2046        1884 :                       static_cast<int>(poBox->GetDataLength()), 1, fp) == 1;
    2047             : }
    2048             : 
    2049             : /************************************************************************/
    2050             : /*                        WriteGDALMetadataBox()                        */
    2051             : /************************************************************************/
    2052             : 
    2053             : template <typename CODEC, typename BASE>
    2054          19 : bool JP2OPJLikeDataset<CODEC, BASE>::WriteGDALMetadataBox(
    2055             :     VSILFILE *fp, GDALDataset *poSrcDS, CSLConstList papszOptions)
    2056             : {
    2057          19 :     bool bRet = true;
    2058          38 :     GDALJP2Box *poBox = GDALJP2Metadata::CreateGDALMultiDomainMetadataXMLBox(
    2059          19 :         poSrcDS, CPLFetchBool(papszOptions, "MAIN_MD_DOMAIN_ONLY", false));
    2060          19 :     if (poBox)
    2061           6 :         bRet = WriteBox(fp, poBox);
    2062          19 :     delete poBox;
    2063          19 :     return bRet;
    2064             : }
    2065             : 
    2066             : /************************************************************************/
    2067             : /*                           WriteXMLBoxes()                            */
    2068             : /************************************************************************/
    2069             : 
    2070             : template <typename CODEC, typename BASE>
    2071          19 : bool JP2OPJLikeDataset<CODEC, BASE>::WriteXMLBoxes(VSILFILE *fp,
    2072             :                                                    GDALDataset *poSrcDS)
    2073             : {
    2074          19 :     bool bRet = true;
    2075          19 :     int nBoxes = 0;
    2076          19 :     GDALJP2Box **papoBoxes = GDALJP2Metadata::CreateXMLBoxes(poSrcDS, &nBoxes);
    2077          21 :     for (int i = 0; i < nBoxes; i++)
    2078             :     {
    2079           2 :         if (!WriteBox(fp, papoBoxes[i]))
    2080           0 :             bRet = false;
    2081           2 :         delete papoBoxes[i];
    2082             :     }
    2083          19 :     CPLFree(papoBoxes);
    2084          19 :     return bRet;
    2085             : }
    2086             : 
    2087             : /************************************************************************/
    2088             : /*                            WriteXMPBox()                             */
    2089             : /************************************************************************/
    2090             : 
    2091             : template <typename CODEC, typename BASE>
    2092          19 : bool JP2OPJLikeDataset<CODEC, BASE>::WriteXMPBox(VSILFILE *fp,
    2093             :                                                  GDALDataset *poSrcDS)
    2094             : {
    2095          19 :     bool bRet = true;
    2096          19 :     GDALJP2Box *poBox = GDALJP2Metadata::CreateXMPBox(poSrcDS);
    2097          19 :     if (poBox)
    2098           2 :         bRet = WriteBox(fp, poBox);
    2099          19 :     delete poBox;
    2100          19 :     return bRet;
    2101             : }
    2102             : 
    2103             : /************************************************************************/
    2104             : /*                            WriteIPRBox()                             */
    2105             : /************************************************************************/
    2106             : 
    2107             : template <typename CODEC, typename BASE>
    2108          19 : bool JP2OPJLikeDataset<CODEC, BASE>::WriteIPRBox(VSILFILE *fp,
    2109             :                                                  GDALDataset *poSrcDS)
    2110             : {
    2111          19 :     bool bRet = true;
    2112          19 :     GDALJP2Box *poBox = GDALJP2Metadata::CreateIPRBox(poSrcDS);
    2113          19 :     if (poBox)
    2114           2 :         bRet = WriteBox(fp, poBox);
    2115          19 :     delete poBox;
    2116          19 :     return bRet;
    2117             : }
    2118             : 
    2119             : /************************************************************************/
    2120             : /*                          FloorPowerOfTwo()                           */
    2121             : /************************************************************************/
    2122             : 
    2123         544 : static int FloorPowerOfTwo(int nVal)
    2124             : {
    2125         544 :     int nBits = 0;
    2126        3805 :     while (nVal > 1)
    2127             :     {
    2128        3261 :         nBits++;
    2129        3261 :         nVal >>= 1;
    2130             :     }
    2131         544 :     return 1 << nBits;
    2132             : }
    2133             : 
    2134             : /************************************************************************/
    2135             : /*                             CreateCopy()                             */
    2136             : /************************************************************************/
    2137             : 
    2138             : template <typename CODEC, typename BASE>
    2139         282 : GDALDataset *JP2OPJLikeDataset<CODEC, BASE>::CreateCopy(
    2140             :     const char *pszFilename, GDALDataset *poSrcDS, CPL_UNUSED int bStrict,
    2141             :     CSLConstList papszOptions, GDALProgressFunc pfnProgress,
    2142             :     void *pProgressData)
    2143             : 
    2144             : {
    2145         282 :     int nBands = poSrcDS->GetRasterCount();
    2146         282 :     int nXSize = poSrcDS->GetRasterXSize();
    2147         282 :     int nYSize = poSrcDS->GetRasterYSize();
    2148             : 
    2149         282 :     if (nBands == 0 || nBands > 16384)
    2150             :     {
    2151           2 :         CPLError(
    2152             :             CE_Failure, CPLE_NotSupported,
    2153             :             "Unable to export files with %d bands. Must be >= 1 and <= 16384",
    2154             :             nBands);
    2155           2 :         return nullptr;
    2156             :     }
    2157             : 
    2158         280 :     GDALColorTable *poCT = poSrcDS->GetRasterBand(1)->GetColorTable();
    2159         280 :     if (poCT != nullptr && nBands != 1)
    2160             :     {
    2161           1 :         CPLError(CE_Failure, CPLE_NotSupported,
    2162             :                  "JP2 driver only supports a color table for a "
    2163             :                  "single-band dataset");
    2164           1 :         return nullptr;
    2165             :     }
    2166             : 
    2167         279 :     GDALDataType eDataType = poSrcDS->GetRasterBand(1)->GetRasterDataType();
    2168         279 :     const int nDataTypeSize = GDALGetDataTypeSizeBytes(eDataType);
    2169         279 :     if (eDataType != GDT_UInt8 && eDataType != GDT_Int16 &&
    2170           8 :         eDataType != GDT_UInt16 && eDataType != GDT_Int32 &&
    2171             :         eDataType != GDT_UInt32)
    2172             :     {
    2173           6 :         CPLError(CE_Failure, CPLE_NotSupported,
    2174             :                  "JP2 driver only supports creating Byte, GDT_Int16, "
    2175             :                  "GDT_UInt16, GDT_Int32, GDT_UInt32");
    2176           6 :         return nullptr;
    2177             :     }
    2178             : 
    2179             :     /* -------------------------------------------------------------------- */
    2180             :     /*      Transcode short-circuit (Grok only).                            */
    2181             :     /* -------------------------------------------------------------------- */
    2182         273 :     if (CPLFetchBool(papszOptions, "TRANSCODE", false))
    2183             :     {
    2184           0 :         if (!BASE::canPerformDirectIO())
    2185             :         {
    2186           0 :             CPLError(CE_Failure, CPLE_NotSupported,
    2187             :                      "TRANSCODE=YES is only supported by the JP2Grok driver");
    2188           0 :             return nullptr;
    2189             :         }
    2190             : 
    2191           0 :         CPLString osSrcFilename(poSrcDS->GetDescription());
    2192           0 :         if (poSrcDS->GetDriver() != nullptr &&
    2193           0 :             poSrcDS->GetDriver() == GDALGetDriverByName("VRT"))
    2194             :         {
    2195           0 :             VRTDataset *poVRTDS = dynamic_cast<VRTDataset *>(poSrcDS);
    2196           0 :             if (poVRTDS)
    2197             :             {
    2198             :                 GDALDataset *poSimpleSourceDS =
    2199           0 :                     poVRTDS->GetSingleSimpleSource();
    2200           0 :                 if (poSimpleSourceDS)
    2201           0 :                     osSrcFilename = poSimpleSourceDS->GetDescription();
    2202             :             }
    2203             :         }
    2204             : 
    2205           0 :         if (!CODEC::transcode(osSrcFilename, pszFilename, poSrcDS,
    2206             :                               papszOptions))
    2207           0 :             return nullptr;
    2208             : 
    2209           0 :         GDALOpenInfo oOpenInfo(pszFilename, GA_ReadOnly);
    2210           0 :         return Open(&oOpenInfo);
    2211             :     }
    2212             : 
    2213         273 :     const bool bInspireTG = CPLFetchBool(papszOptions, "INSPIRE_TG", false);
    2214             : 
    2215             :     /* -------------------------------------------------------------------- */
    2216             :     /*      Analyze creation options.                                       */
    2217             :     /* -------------------------------------------------------------------- */
    2218         273 :     auto eCodecFormat = CODEC::cvtenum(JP2_CODEC_J2K);
    2219         273 :     const char *pszCodec = CSLFetchNameValueDef(papszOptions, "CODEC", nullptr);
    2220         273 :     if (pszCodec)
    2221             :     {
    2222          14 :         if (EQUAL(pszCodec, "JP2"))
    2223           5 :             eCodecFormat = CODEC::cvtenum(JP2_CODEC_JP2);
    2224           9 :         else if (EQUAL(pszCodec, "J2K"))
    2225           9 :             eCodecFormat = CODEC::cvtenum(JP2_CODEC_J2K);
    2226             :         else
    2227             :         {
    2228           0 :             CPLError(CE_Warning, CPLE_NotSupported,
    2229             :                      "Unsupported value for CODEC : %s. Defaulting to J2K",
    2230             :                      pszCodec);
    2231             :         }
    2232             :     }
    2233             :     else
    2234             :     {
    2235         259 :         if (strlen(pszFilename) > 4 &&
    2236         259 :             EQUAL(pszFilename + strlen(pszFilename) - 4, ".JP2"))
    2237             :         {
    2238         224 :             eCodecFormat = CODEC::cvtenum(JP2_CODEC_JP2);
    2239             :         }
    2240             :     }
    2241         273 :     if (eCodecFormat != CODEC::cvtenum(JP2_CODEC_JP2) && bInspireTG)
    2242             :     {
    2243           1 :         CPLError(CE_Warning, CPLE_NotSupported,
    2244             :                  "INSPIRE_TG=YES mandates CODEC=JP2 (TG requirement 21)");
    2245           1 :         return nullptr;
    2246             :     }
    2247             : 
    2248             :     // NOTE: if changing the default block size, the logic in nitfdataset.cpp
    2249             :     // CreateCopy() will have to be changed as well.
    2250         272 :     int nBlockXSize =
    2251         272 :         atoi(CSLFetchNameValueDef(papszOptions, "BLOCKXSIZE", "1024"));
    2252         272 :     int nBlockYSize =
    2253         272 :         atoi(CSLFetchNameValueDef(papszOptions, "BLOCKYSIZE", "1024"));
    2254         272 :     if (nBlockXSize <= 0 || nBlockYSize <= 0)
    2255             :     {
    2256           0 :         CPLError(CE_Failure, CPLE_NotSupported, "Invalid block size");
    2257           0 :         return nullptr;
    2258             :     }
    2259             : 
    2260             :     // By default do not generate tile sizes larger than the dataset
    2261             :     // dimensions
    2262         544 :     if (!CPLFetchBool(papszOptions, "BLOCKSIZE_STRICT", false) &&
    2263         272 :         !CPLFetchBool(papszOptions, "@BLOCKSIZE_STRICT", false))
    2264             :     {
    2265         268 :         if (nBlockXSize < 32 || nBlockYSize < 32)
    2266             :         {
    2267           0 :             CPLError(CE_Failure, CPLE_NotSupported, "Invalid block size");
    2268           0 :             return nullptr;
    2269             :         }
    2270             : 
    2271         268 :         if (nXSize < nBlockXSize)
    2272             :         {
    2273         243 :             CPLDebug(CODEC::debugId(), "Adjusting block width from %d to %d",
    2274             :                      nBlockXSize, nXSize);
    2275         243 :             nBlockXSize = nXSize;
    2276             :         }
    2277         268 :         if (nYSize < nBlockYSize)
    2278             :         {
    2279         244 :             CPLDebug(CODEC::debugId(), "Adjusting block width from %d to %d",
    2280             :                      nBlockYSize, nYSize);
    2281         244 :             nBlockYSize = nYSize;
    2282             :         }
    2283             :     }
    2284             : 
    2285         272 :     JP2_PROG_ORDER eProgOrder = JP2_LRCP;
    2286             :     const char *pszPROGORDER =
    2287         272 :         CSLFetchNameValueDef(papszOptions, "PROGRESSION", "LRCP");
    2288         272 :     if (EQUAL(pszPROGORDER, "LRCP"))
    2289         272 :         eProgOrder = JP2_LRCP;
    2290           0 :     else if (EQUAL(pszPROGORDER, "RLCP"))
    2291           0 :         eProgOrder = JP2_RLCP;
    2292           0 :     else if (EQUAL(pszPROGORDER, "RPCL"))
    2293           0 :         eProgOrder = JP2_RPCL;
    2294           0 :     else if (EQUAL(pszPROGORDER, "PCRL"))
    2295           0 :         eProgOrder = JP2_PCRL;
    2296           0 :     else if (EQUAL(pszPROGORDER, "CPRL"))
    2297           0 :         eProgOrder = JP2_CPRL;
    2298             :     else
    2299             :     {
    2300           0 :         CPLError(CE_Warning, CPLE_NotSupported,
    2301             :                  "Unsupported value for PROGRESSION : %s. Defaulting to LRCP",
    2302             :                  pszPROGORDER);
    2303             :     }
    2304             : 
    2305         272 :     const bool bIsIrreversible =
    2306         272 :         !CPLFetchBool(papszOptions, "REVERSIBLE", poCT != nullptr);
    2307             : 
    2308         544 :     std::vector<double> adfRates;
    2309             :     const char *pszQuality =
    2310         272 :         CSLFetchNameValueDef(papszOptions, "QUALITY", nullptr);
    2311         272 :     double dfDefaultQuality = (poCT != nullptr) ? 100.0 : 25.0;
    2312         272 :     if (pszQuality)
    2313             :     {
    2314             :         char **papszTokens =
    2315          41 :             CSLTokenizeStringComplex(pszQuality, ",", FALSE, FALSE);
    2316         158 :         for (int i = 0; papszTokens[i] != nullptr; i++)
    2317             :         {
    2318         117 :             double dfQuality = CPLAtof(papszTokens[i]);
    2319         117 :             if (dfQuality > 0 && dfQuality <= 100)
    2320             :             {
    2321         117 :                 double dfRate = 100 / dfQuality;
    2322         117 :                 adfRates.push_back(dfRate);
    2323             :             }
    2324             :             else
    2325             :             {
    2326           0 :                 CPLError(CE_Warning, CPLE_NotSupported,
    2327             :                          "Unsupported value for QUALITY: %s. Defaulting to "
    2328             :                          "single-layer, with quality=%.0f",
    2329           0 :                          papszTokens[i], dfDefaultQuality);
    2330           0 :                 adfRates.resize(0);
    2331           0 :                 break;
    2332             :             }
    2333             :         }
    2334          41 :         if (papszTokens[0] == nullptr)
    2335             :         {
    2336           0 :             CPLError(CE_Warning, CPLE_NotSupported,
    2337             :                      "Unsupported value for QUALITY: %s. Defaulting to "
    2338             :                      "single-layer, with quality=%.0f",
    2339             :                      pszQuality, dfDefaultQuality);
    2340             :         }
    2341          41 :         CSLDestroy(papszTokens);
    2342             :     }
    2343         272 :     if (adfRates.empty())
    2344             :     {
    2345         231 :         adfRates.push_back(100. / dfDefaultQuality);
    2346         231 :         assert(!adfRates.empty());
    2347             :     }
    2348             : 
    2349         272 :     if (poCT != nullptr && (bIsIrreversible || adfRates.back() != 1.0))
    2350             :     {
    2351           2 :         CPLError(CE_Warning, CPLE_AppDefined,
    2352             :                  "Encoding a dataset with a color table with REVERSIBLE != YES "
    2353             :                  "or QUALITY != 100 will likely lead to bad visual results");
    2354             :     }
    2355             : 
    2356         272 :     const int nMaxTileDim = std::max(nBlockXSize, nBlockYSize);
    2357         272 :     const int nMinTileDim = std::min(nBlockXSize, nBlockYSize);
    2358         272 :     int nNumResolutions = 1;
    2359             :     /* Pickup a reasonable value compatible with PROFILE_1 requirements */
    2360         363 :     while ((nMaxTileDim >> (nNumResolutions - 1)) > 128 &&
    2361          92 :            (nMinTileDim >> nNumResolutions) > 0)
    2362          91 :         nNumResolutions++;
    2363         272 :     int nMinProfile1Resolutions = nNumResolutions;
    2364             :     const char *pszResolutions =
    2365         272 :         CSLFetchNameValueDef(papszOptions, "RESOLUTIONS", nullptr);
    2366         272 :     if (pszResolutions)
    2367             :     {
    2368          10 :         nNumResolutions = atoi(pszResolutions);
    2369          10 :         if (nNumResolutions <= 0 || nNumResolutions >= 32 ||
    2370           9 :             (nMinTileDim >> nNumResolutions) == 0 ||
    2371           9 :             (nMaxTileDim >> nNumResolutions) == 0)
    2372             :         {
    2373           1 :             CPLError(CE_Warning, CPLE_NotSupported,
    2374             :                      "Unsupported value for RESOLUTIONS : %s. Defaulting to %d",
    2375             :                      pszResolutions, nMinProfile1Resolutions);
    2376           1 :             nNumResolutions = nMinProfile1Resolutions;
    2377             :         }
    2378             :     }
    2379         272 :     int nRedBandIndex = -1;
    2380         272 :     int nGreenBandIndex = -1;
    2381         272 :     int nBlueBandIndex = -1;
    2382         272 :     int nAlphaBandIndex = -1;
    2383         611 :     for (int i = 0; i < nBands; i++)
    2384             :     {
    2385             :         GDALColorInterp eInterp =
    2386         339 :             poSrcDS->GetRasterBand(i + 1)->GetColorInterpretation();
    2387         339 :         if (eInterp == GCI_RedBand)
    2388          15 :             nRedBandIndex = i;
    2389         324 :         else if (eInterp == GCI_GreenBand)
    2390          15 :             nGreenBandIndex = i;
    2391         309 :         else if (eInterp == GCI_BlueBand)
    2392          15 :             nBlueBandIndex = i;
    2393         294 :         else if (eInterp == GCI_AlphaBand)
    2394           7 :             nAlphaBandIndex = i;
    2395             :     }
    2396         272 :     const char *pszAlpha = CSLFetchNameValue(papszOptions, "ALPHA");
    2397         275 :     if (nAlphaBandIndex < 0 && nBands > 1 && pszAlpha != nullptr &&
    2398           3 :         CPLTestBool(pszAlpha))
    2399             :     {
    2400           3 :         nAlphaBandIndex = nBands - 1;
    2401             :     }
    2402             : 
    2403         272 :     const char *pszYCBCR420 = CSLFetchNameValue(papszOptions, "YCBCR420");
    2404         272 :     int bYCBCR420 = FALSE;
    2405         272 :     if (pszYCBCR420 && CPLTestBool(pszYCBCR420))
    2406             :     {
    2407           2 :         if ((nBands == 3 || nBands == 4) && eDataType == GDT_UInt8 &&
    2408           2 :             nRedBandIndex == 0 && nGreenBandIndex == 1 && nBlueBandIndex == 2)
    2409             :         {
    2410           2 :             if (((nXSize % 2) == 0 && (nYSize % 2) == 0 &&
    2411           2 :                  (nBlockXSize % 2) == 0 && (nBlockYSize % 2) == 0))
    2412             :             {
    2413           2 :                 bYCBCR420 = TRUE;
    2414             :             }
    2415             :             else
    2416             :             {
    2417           0 :                 CPLError(CE_Warning, CPLE_NotSupported,
    2418             :                          "YCBCR420 unsupported when image size and/or tile "
    2419             :                          "size are not multiple of 2");
    2420             :             }
    2421             :         }
    2422             :         else
    2423             :         {
    2424           0 :             CPLError(CE_Warning, CPLE_NotSupported,
    2425             :                      "YCBCR420 unsupported with this image band count and/or "
    2426             :                      "data byte");
    2427             :         }
    2428             :     }
    2429             : 
    2430         272 :     const char *pszYCC = CSLFetchNameValue(papszOptions, "YCC");
    2431         294 :     int bYCC = ((nBands == 3 || nBands == 4) &&
    2432          22 :                 CPLTestBool(CSLFetchNameValueDef(papszOptions, "YCC", "TRUE")));
    2433             : 
    2434         272 :     if (bYCBCR420 && bYCC)
    2435             :     {
    2436           2 :         if (pszYCC != nullptr)
    2437             :         {
    2438           0 :             CPLError(CE_Warning, CPLE_NotSupported,
    2439             :                      "YCC unsupported when YCbCr requesting");
    2440             :         }
    2441           2 :         bYCC = FALSE;
    2442             :     }
    2443             : 
    2444             :     /* -------------------------------------------------------------------- */
    2445             :     /*      Deal with codeblocks size                                       */
    2446             :     /* -------------------------------------------------------------------- */
    2447             : 
    2448             :     int nCblockW =
    2449         272 :         atoi(CSLFetchNameValueDef(papszOptions, "CODEBLOCK_WIDTH", "64"));
    2450             :     int nCblockH =
    2451         272 :         atoi(CSLFetchNameValueDef(papszOptions, "CODEBLOCK_HEIGHT", "64"));
    2452         272 :     if (nCblockW < 4 || nCblockW > 1024 || nCblockH < 4 || nCblockH > 1024)
    2453             :     {
    2454           4 :         CPLError(CE_Warning, CPLE_NotSupported,
    2455             :                  "Invalid values for codeblock size. Defaulting to 64x64");
    2456           4 :         nCblockW = 64;
    2457           4 :         nCblockH = 64;
    2458             :     }
    2459         268 :     else if (nCblockW * nCblockH > 4096)
    2460             :     {
    2461           1 :         CPLError(CE_Warning, CPLE_NotSupported,
    2462             :                  "Invalid values for codeblock size. "
    2463             :                  "CODEBLOCK_WIDTH * CODEBLOCK_HEIGHT should be <= 4096. "
    2464             :                  "Defaulting to 64x64");
    2465           1 :         nCblockW = 64;
    2466           1 :         nCblockH = 64;
    2467             :     }
    2468         272 :     int nCblockW_po2 = FloorPowerOfTwo(nCblockW);
    2469         272 :     int nCblockH_po2 = FloorPowerOfTwo(nCblockH);
    2470         272 :     if (nCblockW_po2 != nCblockW || nCblockH_po2 != nCblockH)
    2471             :     {
    2472           1 :         CPLError(CE_Warning, CPLE_NotSupported,
    2473             :                  "Non power of two values used for codeblock size. "
    2474             :                  "Using to %dx%d",
    2475             :                  nCblockW_po2, nCblockH_po2);
    2476             :     }
    2477         272 :     nCblockW = nCblockW_po2;
    2478         272 :     nCblockH = nCblockH_po2;
    2479             : 
    2480             :     /* -------------------------------------------------------------------- */
    2481             :     /*      Deal with codestream PROFILE                                    */
    2482             :     /* -------------------------------------------------------------------- */
    2483             :     const char *pszProfile =
    2484         272 :         CSLFetchNameValueDef(papszOptions, "PROFILE", "AUTO");
    2485         272 :     int bProfile1 = FALSE;
    2486         272 :     if (EQUAL(pszProfile, "UNRESTRICTED"))
    2487             :     {
    2488           1 :         bProfile1 = FALSE;
    2489           1 :         if (bInspireTG)
    2490             :         {
    2491           1 :             CPLError(CE_Failure, CPLE_NotSupported,
    2492             :                      "INSPIRE_TG=YES mandates PROFILE=PROFILE_1 (TG "
    2493             :                      "requirement 21)");
    2494           1 :             return nullptr;
    2495             :         }
    2496             :     }
    2497         271 :     else if (EQUAL(pszProfile, "UNRESTRICTED_FORCED"))
    2498             :     {
    2499           0 :         bProfile1 = FALSE;
    2500             :     }
    2501         271 :     else if (EQUAL(pszProfile,
    2502             :                    "PROFILE_1_FORCED")) /* For debug only: can produce
    2503             :                                            inconsistent codestream */
    2504             :     {
    2505           0 :         bProfile1 = TRUE;
    2506             :     }
    2507             :     else
    2508             :     {
    2509         271 :         if (!(EQUAL(pszProfile, "PROFILE_1") || EQUAL(pszProfile, "AUTO")))
    2510             :         {
    2511           0 :             CPLError(CE_Warning, CPLE_NotSupported,
    2512             :                      "Unsupported value for PROFILE : %s. Defaulting to AUTO",
    2513             :                      pszProfile);
    2514           0 :             pszProfile = "AUTO";
    2515             :         }
    2516             : 
    2517         271 :         bProfile1 = TRUE;
    2518         271 :         const char *pszReq21OrEmpty = bInspireTG ? " (TG requirement 21)" : "";
    2519         271 :         if ((nBlockXSize != nXSize || nBlockYSize != nYSize) &&
    2520          24 :             (nBlockXSize != nBlockYSize || nBlockXSize > 1024 ||
    2521          19 :              nBlockYSize > 1024))
    2522             :         {
    2523           5 :             bProfile1 = FALSE;
    2524           5 :             if (bInspireTG || EQUAL(pszProfile, "PROFILE_1"))
    2525             :             {
    2526           2 :                 CPLError(
    2527             :                     CE_Failure, CPLE_NotSupported,
    2528             :                     "Tile dimensions incompatible with PROFILE_1%s. "
    2529             :                     "Should be whole image or square with dimension <= 1024.",
    2530             :                     pszReq21OrEmpty);
    2531           2 :                 return nullptr;
    2532             :             }
    2533             :         }
    2534         269 :         if ((nMaxTileDim >> (nNumResolutions - 1)) > 128)
    2535             :         {
    2536           4 :             bProfile1 = FALSE;
    2537           4 :             if (bInspireTG || EQUAL(pszProfile, "PROFILE_1"))
    2538             :             {
    2539           1 :                 CPLError(CE_Failure, CPLE_NotSupported,
    2540             :                          "Number of resolutions incompatible with PROFILE_1%s. "
    2541             :                          "Should be at least %d.",
    2542             :                          pszReq21OrEmpty, nMinProfile1Resolutions);
    2543           1 :                 return nullptr;
    2544             :             }
    2545             :         }
    2546         268 :         if (nCblockW > 64 || nCblockH > 64)
    2547             :         {
    2548           2 :             bProfile1 = FALSE;
    2549           2 :             if (bInspireTG || EQUAL(pszProfile, "PROFILE_1"))
    2550             :             {
    2551           2 :                 CPLError(CE_Failure, CPLE_NotSupported,
    2552             :                          "Codeblock width incompatible with PROFILE_1%s. "
    2553             :                          "Codeblock width or height should be <= 64.",
    2554             :                          pszReq21OrEmpty);
    2555           2 :                 return nullptr;
    2556             :             }
    2557             :         }
    2558             :     }
    2559             : 
    2560             :     /* -------------------------------------------------------------------- */
    2561             :     /*      Work out the precision.                                         */
    2562             :     /* -------------------------------------------------------------------- */
    2563             :     int nBits;
    2564         266 :     const int nDTBits = GDALGetDataTypeSizeBits(eDataType);
    2565             : 
    2566         266 :     if (CSLFetchNameValue(papszOptions, GDALMD_NBITS) != nullptr)
    2567             :     {
    2568          22 :         nBits = atoi(CSLFetchNameValue(papszOptions, GDALMD_NBITS));
    2569          22 :         if (bInspireTG &&
    2570           1 :             !(nBits == 1 || nBits == 8 || nBits == 16 || nBits == 32))
    2571             :         {
    2572           1 :             CPLError(CE_Failure, CPLE_NotSupported,
    2573             :                      "INSPIRE_TG=YES mandates NBITS=1,8,16 or 32 (TG "
    2574             :                      "requirement 24)");
    2575           1 :             return nullptr;
    2576             :         }
    2577             :     }
    2578         244 :     else if (poSrcDS->GetRasterBand(1)->GetMetadataItem(
    2579         244 :                  GDALMD_NBITS, GDAL_MDD_IMAGE_STRUCTURE) != nullptr)
    2580             :     {
    2581           3 :         nBits = atoi(poSrcDS->GetRasterBand(1)->GetMetadataItem(
    2582             :             GDALMD_NBITS, GDAL_MDD_IMAGE_STRUCTURE));
    2583           3 :         if (bInspireTG &&
    2584           1 :             !(nBits == 1 || nBits == 8 || nBits == 16 || nBits == 32))
    2585             :         {
    2586             :             /* Implements "NOTE If the original data do not satisfy this "
    2587             :                "requirement, they will be converted in a representation using "
    2588             :                "the next higher power of 2" */
    2589           1 :             nBits = nDTBits;
    2590             :         }
    2591             :     }
    2592             :     else
    2593             :     {
    2594         241 :         nBits = nDTBits;
    2595             :     }
    2596             : 
    2597         265 :     if ((nDTBits == 8 && nBits > 8) ||
    2598         265 :         (nDTBits == 16 && (nBits <= 8 || nBits > 16)) ||
    2599           2 :         (nDTBits == 32 && (nBits <= 16 || nBits > 32)))
    2600             :     {
    2601           0 :         CPLError(CE_Warning, CPLE_NotSupported,
    2602             :                  "Inconsistent NBITS value with data type. Using %d", nDTBits);
    2603             :     }
    2604             : 
    2605             :     /* -------------------------------------------------------------------- */
    2606             :     /*      Georeferencing options                                          */
    2607             :     /* -------------------------------------------------------------------- */
    2608             : 
    2609         265 :     bool bGMLJP2Option = CPLFetchBool(papszOptions, "GMLJP2", true);
    2610         265 :     int nGMLJP2Version = 1;
    2611             :     const char *pszGMLJP2V2Def =
    2612         265 :         CSLFetchNameValue(papszOptions, "GMLJP2V2_DEF");
    2613         265 :     if (pszGMLJP2V2Def != nullptr)
    2614             :     {
    2615          28 :         bGMLJP2Option = true;
    2616          28 :         nGMLJP2Version = 2;
    2617          28 :         if (bInspireTG)
    2618             :         {
    2619           0 :             CPLError(CE_Warning, CPLE_NotSupported,
    2620             :                      "INSPIRE_TG=YES is only compatible with GMLJP2 v1");
    2621           0 :             return nullptr;
    2622             :         }
    2623             :     }
    2624         265 :     const bool bGeoJP2Option = CPLFetchBool(papszOptions, "GeoJP2", true);
    2625             : 
    2626         530 :     GDALJP2Metadata oJP2MD;
    2627             : 
    2628         265 :     int bGeoreferencingCompatOfGeoJP2 = FALSE;
    2629         265 :     int bGeoreferencingCompatOfGMLJP2 = FALSE;
    2630         271 :     if (eCodecFormat == CODEC::cvtenum(JP2_CODEC_JP2) &&
    2631           6 :         (bGMLJP2Option || bGeoJP2Option))
    2632             :     {
    2633         220 :         if (poSrcDS->GetGCPCount() > 0)
    2634             :         {
    2635           3 :             bGeoreferencingCompatOfGeoJP2 = TRUE;
    2636           3 :             oJP2MD.SetGCPs(poSrcDS->GetGCPCount(), poSrcDS->GetGCPs());
    2637           3 :             oJP2MD.SetSpatialRef(poSrcDS->GetGCPSpatialRef());
    2638             :         }
    2639             :         else
    2640             :         {
    2641         217 :             const OGRSpatialReference *poSRS = poSrcDS->GetSpatialRef();
    2642         217 :             if (poSRS)
    2643             :             {
    2644          57 :                 bGeoreferencingCompatOfGeoJP2 = TRUE;
    2645          57 :                 oJP2MD.SetSpatialRef(poSRS);
    2646             :             }
    2647         217 :             GDALGeoTransform gt;
    2648         217 :             if (poSrcDS->GetGeoTransform(gt) == CE_None)
    2649             :             {
    2650         164 :                 bGeoreferencingCompatOfGeoJP2 = TRUE;
    2651         164 :                 oJP2MD.SetGeoTransform(gt);
    2652         164 :                 if (poSRS && !poSRS->IsEmpty())
    2653             :                 {
    2654          57 :                     bGeoreferencingCompatOfGMLJP2 =
    2655          57 :                         GDALJP2Metadata::IsSRSCompatible(poSRS);
    2656          57 :                     if (!bGeoreferencingCompatOfGMLJP2)
    2657             :                     {
    2658           1 :                         CPLDebug(
    2659             :                             CODEC::debugId(),
    2660             :                             "Cannot write GMLJP2 box due to unsupported SRS");
    2661             :                     }
    2662             :                 }
    2663             :             }
    2664             :         }
    2665         220 :         if (poSrcDS->GetMetadata(GDAL_MDD_RPC) != nullptr)
    2666             :         {
    2667           1 :             oJP2MD.SetRPCMD(poSrcDS->GetMetadata(GDAL_MDD_RPC));
    2668           1 :             bGeoreferencingCompatOfGeoJP2 = TRUE;
    2669             :         }
    2670             : 
    2671             :         const char *pszAreaOrPoint =
    2672         220 :             poSrcDS->GetMetadataItem(GDALMD_AREA_OR_POINT);
    2673         261 :         oJP2MD.bPixelIsPoint = pszAreaOrPoint != nullptr &&
    2674          41 :                                EQUAL(pszAreaOrPoint, GDALMD_AOP_POINT);
    2675             : 
    2676         436 :         if (bGMLJP2Option &&
    2677         216 :             CPLGetConfigOption("GMLJP2OVERRIDE", nullptr) != nullptr)
    2678             :         {
    2679             :             // Force V1 since this is the branch in which the hack is
    2680             :             // implemented
    2681           7 :             nGMLJP2Version = 1;
    2682           7 :             bGeoreferencingCompatOfGMLJP2 = TRUE;
    2683             :         }
    2684             :     }
    2685             : 
    2686         265 :     if (CSLFetchNameValue(papszOptions, "GMLJP2") != nullptr && bGMLJP2Option &&
    2687             :         !bGeoreferencingCompatOfGMLJP2)
    2688             :     {
    2689           0 :         CPLError(CE_Warning, CPLE_AppDefined,
    2690             :                  "GMLJP2 box was explicitly required but cannot be written due "
    2691             :                  "to lack of georeferencing and/or unsupported georeferencing "
    2692             :                  "for GMLJP2");
    2693             :     }
    2694             : 
    2695         265 :     if (CSLFetchNameValue(papszOptions, "GeoJP2") != nullptr && bGeoJP2Option &&
    2696             :         !bGeoreferencingCompatOfGeoJP2)
    2697             :     {
    2698           0 :         CPLError(CE_Warning, CPLE_AppDefined,
    2699             :                  "GeoJP2 box was explicitly required but cannot be written due "
    2700             :                  "to lack of georeferencing");
    2701             :     }
    2702             :     const bool bGeoBoxesAfter =
    2703         265 :         CPLFetchBool(papszOptions, "GEOBOXES_AFTER_JP2C", bInspireTG);
    2704         265 :     GDALJP2Box *poGMLJP2Box = nullptr;
    2705         265 :     if (eCodecFormat == CODEC::cvtenum(JP2_CODEC_JP2) && bGMLJP2Option &&
    2706             :         bGeoreferencingCompatOfGMLJP2)
    2707             :     {
    2708          61 :         if (nGMLJP2Version == 1)
    2709          33 :             poGMLJP2Box = oJP2MD.CreateGMLJP2(nXSize, nYSize);
    2710             :         else
    2711             :             poGMLJP2Box =
    2712          28 :                 oJP2MD.CreateGMLJP2V2(nXSize, nYSize, pszGMLJP2V2Def, poSrcDS);
    2713          61 :         if (poGMLJP2Box == nullptr)
    2714           3 :             return nullptr;
    2715             :     }
    2716             : 
    2717             :     /* ---------------------------------------------------------------- */
    2718             :     /* If the input driver is identified as "GEORASTER" the following   */
    2719             :     /* section will try to dump a ORACLE GeoRaster JP2 BLOB into a file */
    2720             :     /* ---------------------------------------------------------------- */
    2721             : 
    2722         262 :     if (EQUAL(poSrcDS->GetDriverName(), "GEORASTER"))
    2723             :     {
    2724           0 :         const char *pszGEOR_compress = poSrcDS->GetMetadataItem(
    2725             :             GDALMD_COMPRESSION, GDAL_MDD_IMAGE_STRUCTURE);
    2726             : 
    2727           0 :         if (pszGEOR_compress == nullptr)
    2728             :         {
    2729           0 :             pszGEOR_compress = "NONE";
    2730             :         }
    2731             : 
    2732             :         /* Check if the JP2 BLOB needs re-shaping */
    2733             : 
    2734           0 :         bool bGEOR_reshape = false;
    2735             : 
    2736           0 :         const char *apszIgnoredOptions[] = {"BLOCKXSIZE",
    2737             :                                             "BLOCKYSIZE",
    2738             :                                             "QUALITY",
    2739             :                                             "REVERSIBLE",
    2740             :                                             "RESOLUTIONS",
    2741             :                                             "PROGRESSION",
    2742             :                                             "SOP",
    2743             :                                             "EPH",
    2744             :                                             "YCBCR420",
    2745             :                                             "YCC",
    2746             :                                             GDALMD_NBITS,
    2747             :                                             "1BIT_ALPHA",
    2748             :                                             "PRECINCTS",
    2749             :                                             "TILEPARTS",
    2750             :                                             "CODEBLOCK_WIDTH",
    2751             :                                             "CODEBLOCK_HEIGHT",
    2752             :                                             "PLT",
    2753             :                                             "TLM",
    2754             :                                             nullptr};
    2755             : 
    2756           0 :         for (int i = 0; apszIgnoredOptions[i]; i++)
    2757             :         {
    2758           0 :             if (CSLFetchNameValue(papszOptions, apszIgnoredOptions[i]))
    2759             :             {
    2760           0 :                 bGEOR_reshape = true;
    2761             :             }
    2762             :         }
    2763             : 
    2764           0 :         if (CSLFetchNameValue(papszOptions, "USE_SRC_CODESTREAM"))
    2765             :         {
    2766           0 :             bGEOR_reshape = false;
    2767             :         }
    2768             : 
    2769           0 :         char **papszGEOR_files = poSrcDS->GetFileList();
    2770             : 
    2771           0 :         if (EQUAL(pszGEOR_compress, "JP2-F") && CSLCount(papszGEOR_files) > 0 &&
    2772           0 :             bGEOR_reshape == false)
    2773             :         {
    2774             : 
    2775           0 :             const char *pszVsiOciLob = papszGEOR_files[0];
    2776             : 
    2777           0 :             VSILFILE *fpBlob = VSIFOpenL(pszVsiOciLob, "r");
    2778           0 :             if (fpBlob == nullptr)
    2779             :             {
    2780           0 :                 CPLError(CE_Failure, CPLE_AppDefined, "Cannot open %s",
    2781             :                          pszVsiOciLob);
    2782           0 :                 delete poGMLJP2Box;
    2783           0 :                 return nullptr;
    2784             :             }
    2785           0 :             VSILFILE *fp = VSIFOpenL(pszFilename, "w+b");
    2786           0 :             if (fp == nullptr)
    2787             :             {
    2788           0 :                 CPLError(CE_Failure, CPLE_AppDefined, "Cannot create %s",
    2789             :                          pszFilename);
    2790           0 :                 delete poGMLJP2Box;
    2791           0 :                 VSIFCloseL(fpBlob);
    2792           0 :                 return nullptr;
    2793             :             }
    2794             : 
    2795           0 :             VSIFSeekL(fpBlob, 0, SEEK_END);
    2796             : 
    2797           0 :             size_t nBlobSize = static_cast<size_t>(VSIFTellL(fpBlob));
    2798           0 :             size_t nChunk = GDALGetCacheMax() / 4;
    2799           0 :             size_t nSize = 0;
    2800           0 :             size_t nCount = 0;
    2801             : 
    2802           0 :             void *pBuffer = VSI_MALLOC_VERBOSE(nChunk);
    2803           0 :             if (pBuffer == nullptr)
    2804             :             {
    2805           0 :                 delete poGMLJP2Box;
    2806           0 :                 VSIFCloseL(fpBlob);
    2807           0 :                 VSIFCloseL(fp);
    2808           0 :                 return nullptr;
    2809             :             }
    2810             : 
    2811           0 :             VSIFSeekL(fpBlob, 0, SEEK_SET);
    2812             : 
    2813           0 :             while ((nSize = VSIFReadL(pBuffer, 1, nChunk, fpBlob)) > 0)
    2814             :             {
    2815           0 :                 VSIFWriteL(pBuffer, 1, nSize, fp);
    2816           0 :                 nCount += nSize;
    2817           0 :                 pfnProgress(static_cast<double>(nCount) /
    2818             :                                 static_cast<double>(nBlobSize),
    2819             :                             nullptr, pProgressData);
    2820             :             }
    2821             : 
    2822           0 :             CPLFree(pBuffer);
    2823           0 :             VSIFCloseL(fpBlob);
    2824             : 
    2825           0 :             VSIFCloseL(fp);
    2826             : 
    2827             :             /* Return the GDALDaset object */
    2828             : 
    2829           0 :             GDALOpenInfo oOpenInfo(pszFilename, GA_Update);
    2830           0 :             GDALDataset *poDS = JP2OPJLikeDataset::Open(&oOpenInfo);
    2831             : 
    2832             :             /* Copy essential metadata */
    2833             : 
    2834           0 :             GDALGeoTransform gt;
    2835           0 :             if (poSrcDS->GetGeoTransform(gt) == CE_None)
    2836             :             {
    2837           0 :                 poDS->SetGeoTransform(gt);
    2838             :             }
    2839             : 
    2840           0 :             const OGRSpatialReference *poSRS = poSrcDS->GetSpatialRef();
    2841           0 :             if (poSRS)
    2842             :             {
    2843           0 :                 poDS->SetSpatialRef(poSRS);
    2844             :             }
    2845             : 
    2846           0 :             delete poGMLJP2Box;
    2847           0 :             return poDS;
    2848             :         }
    2849             :     }
    2850             : 
    2851             :     /* -------------------------------------------------------------------- */
    2852             :     /*      Setup encoder                                                   */
    2853             :     /* -------------------------------------------------------------------- */
    2854             : 
    2855         524 :     JP2OPJLikeDataset oTmpDS;
    2856         262 :     int numThreads = oTmpDS.GetNumThreads();
    2857             : 
    2858         262 :     CODEC localctx;
    2859         262 :     localctx.allocComponentParams(nBands);
    2860             :     int iBand;
    2861         262 :     int bSamePrecision = TRUE;
    2862         262 :     int b1BitAlpha = FALSE;
    2863         591 :     for (iBand = 0; iBand < nBands; iBand++)
    2864             :     {
    2865         329 :         localctx.pasBandParams[iBand].x0 = 0;
    2866         329 :         localctx.pasBandParams[iBand].y0 = 0;
    2867         329 :         if (bYCBCR420 && (iBand == 1 || iBand == 2))
    2868             :         {
    2869           4 :             localctx.pasBandParams[iBand].dx = 2;
    2870           4 :             localctx.pasBandParams[iBand].dy = 2;
    2871           4 :             localctx.pasBandParams[iBand].w = nXSize / 2;
    2872           4 :             localctx.pasBandParams[iBand].h = nYSize / 2;
    2873             :         }
    2874             :         else
    2875             :         {
    2876         325 :             localctx.pasBandParams[iBand].dx = 1;
    2877         325 :             localctx.pasBandParams[iBand].dy = 1;
    2878         325 :             localctx.pasBandParams[iBand].w = nXSize;
    2879         325 :             localctx.pasBandParams[iBand].h = nYSize;
    2880             :         }
    2881             : 
    2882         329 :         localctx.pasBandParams[iBand].sgnd =
    2883         329 :             (eDataType == GDT_Int16 || eDataType == GDT_Int32);
    2884         329 :         localctx.pasBandParams[iBand].prec = nBits;
    2885             : 
    2886             :         const char *pszNBits =
    2887         329 :             poSrcDS->GetRasterBand(iBand + 1)->GetMetadataItem(
    2888             :                 GDALMD_NBITS, GDAL_MDD_IMAGE_STRUCTURE);
    2889             :         /* Recommendation 38 In the case of an opacity channel, the bit depth
    2890             :          * should be 1-bit. */
    2891         339 :         if (iBand == nAlphaBandIndex &&
    2892           0 :             ((pszNBits != nullptr && EQUAL(pszNBits, "1")) ||
    2893          10 :              CPLFetchBool(papszOptions, "1BIT_ALPHA", bInspireTG)))
    2894             :         {
    2895           3 :             if (iBand != nBands - 1 && nBits != 1)
    2896             :             {
    2897             :                 /* Might be a bug in openjpeg, but it seems that if the alpha */
    2898             :                 /* band is the first one, it would select 1-bit for all
    2899             :                  * channels... */
    2900           0 :                 CPLError(CE_Warning, CPLE_NotSupported,
    2901             :                          "Cannot output 1-bit alpha channel if it is not the "
    2902             :                          "last one");
    2903             :             }
    2904             :             else
    2905             :             {
    2906           3 :                 CPLDebug(CODEC::debugId(), "Using 1-bit alpha channel");
    2907           3 :                 localctx.pasBandParams[iBand].sgnd = 0;
    2908           3 :                 localctx.pasBandParams[iBand].prec = 1;
    2909           3 :                 bSamePrecision = FALSE;
    2910           3 :                 b1BitAlpha = TRUE;
    2911             :             }
    2912             :         }
    2913             :     }
    2914             : 
    2915         262 :     if (bInspireTG && nAlphaBandIndex >= 0 && !b1BitAlpha)
    2916             :     {
    2917           0 :         CPLError(
    2918             :             CE_Warning, CPLE_NotSupported,
    2919             :             "INSPIRE_TG=YES recommends 1BIT_ALPHA=YES (Recommendation 38)");
    2920             :     }
    2921         262 :     auto eColorSpace = CODEC::cvtenum(JP2_CLRSPC_GRAY);
    2922             : 
    2923         262 :     if (bYCBCR420)
    2924             :     {
    2925           2 :         eColorSpace = CODEC::cvtenum(JP2_CLRSPC_SYCC);
    2926             :     }
    2927         260 :     else if ((nBands == 3 || nBands == 4) && nRedBandIndex >= 0 &&
    2928          13 :              nGreenBandIndex >= 0 && nBlueBandIndex >= 0)
    2929             :     {
    2930          13 :         eColorSpace = CODEC::cvtenum(JP2_CLRSPC_SRGB);
    2931             :     }
    2932         247 :     else if (poCT != nullptr)
    2933             :     {
    2934           6 :         eColorSpace = CODEC::cvtenum(JP2_CLRSPC_SRGB);
    2935             :     }
    2936             : 
    2937             :     /* -------------------------------------------------------------------- */
    2938             :     /*      Create the dataset.                                             */
    2939             :     /* -------------------------------------------------------------------- */
    2940             : 
    2941         262 :     const char *pszAccess =
    2942         262 :         STARTS_WITH_CI(pszFilename, "/vsisubfile/") ? "r+b" : "w+b";
    2943         524 :     VSIVirtualHandleUniquePtr fpOwner(VSIFOpenL(pszFilename, pszAccess));
    2944         262 :     if (!fpOwner)
    2945             :     {
    2946           3 :         CPLError(CE_Failure, CPLE_AppDefined, "Cannot create file");
    2947           3 :         CPLFree(localctx.pasBandParams);
    2948           3 :         localctx.pasBandParams = nullptr;
    2949           3 :         delete poGMLJP2Box;
    2950           3 :         return nullptr;
    2951             :     }
    2952         259 :     VSILFILE *fp = fpOwner.get();
    2953             : 
    2954             :     /* -------------------------------------------------------------------- */
    2955             :     /*      Add JP2 boxes.                                                  */
    2956             :     /* -------------------------------------------------------------------- */
    2957         259 :     vsi_l_offset nStartJP2C = 0;
    2958         259 :     int bUseXLBoxes = FALSE;
    2959             : 
    2960         478 :     if (eCodecFormat == CODEC::cvtenum(JP2_CODEC_JP2) &&
    2961         219 :         !BASE::canPerformDirectIO())
    2962             :     {
    2963         438 :         GDALJP2Box jPBox(fp);
    2964         219 :         jPBox.SetType("jP  ");
    2965         219 :         jPBox.AppendWritableData(4, "\x0D\x0A\x87\x0A");
    2966         219 :         WriteBox(fp, &jPBox);
    2967             : 
    2968         438 :         GDALJP2Box ftypBox(fp);
    2969         219 :         ftypBox.SetType("ftyp");
    2970             :         // http://docs.opengeospatial.org/is/08-085r5/08-085r5.html Req 19
    2971         219 :         const bool bJPXOption = CPLFetchBool(papszOptions, "JPX", true);
    2972         219 :         if (nGMLJP2Version == 2 && bJPXOption)
    2973          25 :             ftypBox.AppendWritableData(4, "jpx "); /* Branding */
    2974             :         else
    2975         194 :             ftypBox.AppendWritableData(4, "jp2 "); /* Branding */
    2976         219 :         ftypBox.AppendUInt32(0);                   /* minimum version */
    2977         219 :         ftypBox.AppendWritableData(
    2978             :             4, "jp2 "); /* Compatibility list: first value */
    2979             : 
    2980         219 :         if (bInspireTG && poGMLJP2Box != nullptr && !bJPXOption)
    2981             :         {
    2982           1 :             CPLError(
    2983             :                 CE_Warning, CPLE_AppDefined,
    2984             :                 "INSPIRE_TG=YES implies following GMLJP2 specification which "
    2985             :                 "recommends advertise reader requirement 67 feature, and thus "
    2986             :                 "JPX capability");
    2987             :         }
    2988         218 :         else if (poGMLJP2Box != nullptr && bJPXOption)
    2989             :         {
    2990             :             /* GMLJP2 uses lbl and asoc boxes, which are JPEG2000 Part II spec
    2991             :              */
    2992             :             /* advertizing jpx is required per 8.1 of 05-047r3 GMLJP2 */
    2993          57 :             ftypBox.AppendWritableData(
    2994             :                 4, "jpx "); /* Compatibility list: second value */
    2995             :         }
    2996         219 :         WriteBox(fp, &ftypBox);
    2997             : 
    2998         221 :         const bool bIPR = poSrcDS->GetMetadata("xml:IPR") != nullptr &&
    2999           2 :                           CPLFetchBool(papszOptions, "WRITE_METADATA", false);
    3000             : 
    3001             :         /* Reader requirement box */
    3002         219 :         if (poGMLJP2Box != nullptr && bJPXOption)
    3003             :         {
    3004         114 :             GDALJP2Box rreqBox(fp);
    3005          57 :             rreqBox.SetType("rreq");
    3006          57 :             rreqBox.AppendUInt8(1); /* ML = 1 byte for mask length */
    3007             : 
    3008          57 :             rreqBox.AppendUInt8(0x80 | 0x40 | (bIPR ? 0x20 : 0)); /* FUAM */
    3009          57 :             rreqBox.AppendUInt8(0x80);                            /* DCM */
    3010             : 
    3011          57 :             rreqBox.AppendUInt16(
    3012             :                 2 + (bIPR ? 1 : 0)); /* NSF: Number of standard features */
    3013             : 
    3014          57 :             rreqBox.AppendUInt16(
    3015             :                 (bProfile1) ? 4 : 5);  /* SF0 : PROFILE 1 or PROFILE 2 */
    3016          57 :             rreqBox.AppendUInt8(0x80); /* SM0 */
    3017             : 
    3018          57 :             rreqBox.AppendUInt16(67);  /* SF1 : GMLJP2 box */
    3019          57 :             rreqBox.AppendUInt8(0x40); /* SM1 */
    3020             : 
    3021          57 :             if (bIPR)
    3022             :             {
    3023           0 :                 rreqBox.AppendUInt16(35);  /* SF2 : IPR metadata */
    3024           0 :                 rreqBox.AppendUInt8(0x20); /* SM2 */
    3025             :             }
    3026          57 :             rreqBox.AppendUInt16(0); /* NVF */
    3027          57 :             WriteBox(fp, &rreqBox);
    3028             :         }
    3029             : 
    3030         438 :         GDALJP2Box ihdrBox(fp);
    3031         219 :         ihdrBox.SetType("ihdr");
    3032         219 :         ihdrBox.AppendUInt32(nYSize);
    3033         219 :         ihdrBox.AppendUInt32(nXSize);
    3034         219 :         ihdrBox.AppendUInt16(static_cast<GUInt16>(nBands));
    3035             :         GByte BPC;
    3036         219 :         if (bSamePrecision)
    3037         216 :             BPC = static_cast<GByte>((localctx.pasBandParams[0].prec - 1) |
    3038         216 :                                      (localctx.pasBandParams[0].sgnd << 7));
    3039             :         else
    3040           3 :             BPC = 255;
    3041         219 :         ihdrBox.AppendUInt8(BPC);
    3042         219 :         ihdrBox.AppendUInt8(7); /* C=Compression type: fixed value */
    3043         219 :         ihdrBox.AppendUInt8(0); /* UnkC: 0= colourspace of the image is known */
    3044             :         /*and correctly specified in the Colourspace Specification boxes within
    3045             :          * the file */
    3046         219 :         ihdrBox.AppendUInt8(
    3047             :             bIPR ? 1 : 0); /* IPR: 0=no intellectual property, 1=IPR box */
    3048             : 
    3049         438 :         GDALJP2Box bpccBox(fp);
    3050         219 :         if (!bSamePrecision)
    3051             :         {
    3052           3 :             bpccBox.SetType("bpcc");
    3053          13 :             for (int i = 0; i < nBands; i++)
    3054          10 :                 bpccBox.AppendUInt8(
    3055          10 :                     static_cast<GByte>((localctx.pasBandParams[i].prec - 1) |
    3056          10 :                                        (localctx.pasBandParams[i].sgnd << 7)));
    3057             :         }
    3058             : 
    3059         438 :         GDALJP2Box colrBox(fp);
    3060         219 :         colrBox.SetType("colr");
    3061         219 :         colrBox.AppendUInt8(1); /* METHOD: 1=Enumerated Colourspace */
    3062         219 :         colrBox.AppendUInt8(
    3063             :             0); /* PREC: Precedence. 0=(field reserved for ISO use) */
    3064         219 :         colrBox.AppendUInt8(0); /* APPROX: Colourspace approximation. */
    3065         219 :         GUInt32 enumcs = 16;
    3066         219 :         if (eColorSpace == CODEC::cvtenum(JP2_CLRSPC_SRGB))
    3067          16 :             enumcs = 16;
    3068         203 :         else if (eColorSpace == CODEC::cvtenum(JP2_CLRSPC_GRAY))
    3069         201 :             enumcs = 17;
    3070           2 :         else if (eColorSpace == CODEC::cvtenum(JP2_CLRSPC_SYCC))
    3071           2 :             enumcs = 18;
    3072         219 :         colrBox.AppendUInt32(enumcs); /* EnumCS: Enumerated colourspace */
    3073             : 
    3074         438 :         GDALJP2Box pclrBox(fp);
    3075         438 :         GDALJP2Box cmapBox(fp);
    3076         219 :         int nCTComponentCount = 0;
    3077         219 :         if (poCT != nullptr)
    3078             :         {
    3079           6 :             pclrBox.SetType("pclr");
    3080           6 :             const int nEntries = std::min(256, poCT->GetColorEntryCount());
    3081             :             nCTComponentCount =
    3082           6 :                 atoi(CSLFetchNameValueDef(papszOptions, "CT_COMPONENTS", "0"));
    3083           6 :             if (bInspireTG)
    3084             :             {
    3085           0 :                 if (nCTComponentCount != 0 && nCTComponentCount != 3)
    3086           0 :                     CPLError(
    3087             :                         CE_Warning, CPLE_AppDefined,
    3088             :                         "Inspire TG mandates 3 components for color table");
    3089             :                 else
    3090           0 :                     nCTComponentCount = 3;
    3091             :             }
    3092           6 :             else if (nCTComponentCount != 3 && nCTComponentCount != 4)
    3093             :             {
    3094           5 :                 nCTComponentCount = 3;
    3095          21 :                 for (int i = 0; i < nEntries; i++)
    3096             :                 {
    3097          17 :                     const GDALColorEntry *psEntry = poCT->GetColorEntry(i);
    3098          17 :                     if (psEntry->c4 != 255)
    3099             :                     {
    3100           1 :                         CPLDebug(
    3101             :                             CODEC::debugId(),
    3102             :                             "Color table has at least one non-opaque value. "
    3103             :                             "This may cause compatibility problems with some "
    3104             :                             "readers. "
    3105             :                             "In which case use CT_COMPONENTS=3 creation "
    3106             :                             "option");
    3107           1 :                         nCTComponentCount = 4;
    3108           1 :                         break;
    3109             :                     }
    3110             :                 }
    3111             :             }
    3112           6 :             nRedBandIndex = 0;
    3113           6 :             nGreenBandIndex = 1;
    3114           6 :             nBlueBandIndex = 2;
    3115           6 :             nAlphaBandIndex = (nCTComponentCount == 4) ? 3 : -1;
    3116             : 
    3117           6 :             pclrBox.AppendUInt16(static_cast<GUInt16>(nEntries));
    3118           6 :             pclrBox.AppendUInt8(static_cast<GByte>(
    3119             :                 nCTComponentCount)); /* NPC: Number of components */
    3120          25 :             for (int i = 0; i < nCTComponentCount; i++)
    3121             :             {
    3122          19 :                 pclrBox.AppendUInt8(7); /* Bi: unsigned 8 bits */
    3123             :             }
    3124          30 :             for (int i = 0; i < nEntries; i++)
    3125             :             {
    3126          24 :                 const GDALColorEntry *psEntry = poCT->GetColorEntry(i);
    3127          24 :                 pclrBox.AppendUInt8(static_cast<GByte>(psEntry->c1));
    3128          24 :                 pclrBox.AppendUInt8(static_cast<GByte>(psEntry->c2));
    3129          24 :                 pclrBox.AppendUInt8(static_cast<GByte>(psEntry->c3));
    3130          24 :                 if (nCTComponentCount == 4)
    3131           4 :                     pclrBox.AppendUInt8(static_cast<GByte>(psEntry->c4));
    3132             :             }
    3133             : 
    3134           6 :             cmapBox.SetType("cmap");
    3135          25 :             for (int i = 0; i < nCTComponentCount; i++)
    3136             :             {
    3137          19 :                 cmapBox.AppendUInt16(0); /* CMPi: code stream component index */
    3138          19 :                 cmapBox.AppendUInt8(1);  /* MYTPi: 1=palette mapping */
    3139          19 :                 cmapBox.AppendUInt8(static_cast<GByte>(
    3140             :                     i)); /* PCOLi: index component from the map */
    3141             :             }
    3142             :         }
    3143             : 
    3144         438 :         GDALJP2Box cdefBox(fp);
    3145         230 :         if (((nBands == 3 || nBands == 4) &&
    3146          24 :              (eColorSpace == CODEC::cvtenum(JP2_CLRSPC_SRGB) ||
    3147          19 :               eColorSpace == CODEC::cvtenum(JP2_CLRSPC_SYCC)) &&
    3148          10 :              (nRedBandIndex != 0 || nGreenBandIndex != 1 ||
    3149         438 :               nBlueBandIndex != 2)) ||
    3150             :             nAlphaBandIndex >= 0)
    3151             :         {
    3152          12 :             cdefBox.SetType("cdef");
    3153          12 :             int nComponents = (nCTComponentCount == 4) ? 4 : nBands;
    3154          12 :             cdefBox.AppendUInt16(static_cast<GUInt16>(nComponents));
    3155          55 :             for (int i = 0; i < nComponents; i++)
    3156             :             {
    3157          43 :                 uint16_t nTyp = 65535;   // Unspecified
    3158          43 :                 uint16_t nAsoc = 65535;  // Unassociated
    3159          43 :                 if (i != nAlphaBandIndex)
    3160             :                 {
    3161          32 :                     if (eColorSpace == CODEC::cvtenum(JP2_CLRSPC_GRAY) &&
    3162             :                         i == 0)
    3163             :                     {
    3164           4 :                         nTyp =
    3165             :                             0;  // colour image data for the associated colour
    3166           4 :                         nAsoc = 1;  // associated with a particular colour
    3167             :                     }
    3168          35 :                     else if ((eColorSpace == CODEC::cvtenum(JP2_CLRSPC_SRGB) ||
    3169          56 :                               eColorSpace == CODEC::cvtenum(JP2_CLRSPC_SYCC)) &&
    3170          21 :                              (nComponents == 3 || nComponents == 4))
    3171             :                     {
    3172          24 :                         nTyp =
    3173             :                             0;  // colour image data for the associated colour
    3174          24 :                         if (i == nRedBandIndex)
    3175           8 :                             nAsoc = 1;
    3176          16 :                         else if (i == nGreenBandIndex)
    3177           8 :                             nAsoc = 2;
    3178           8 :                         else if (i == nBlueBandIndex)
    3179           8 :                             nAsoc = 3;
    3180             :                         else
    3181             :                         {
    3182           0 :                             CPLError(CE_Warning, CPLE_AppDefined,
    3183             :                                      "Could not associate band %d with a "
    3184             :                                      "red/green/blue channel",
    3185             :                                      i + 1);
    3186             :                         }
    3187             :                     }
    3188             :                 }
    3189             :                 else
    3190             :                 {
    3191          11 :                     nTyp = 1;   // Non pre-multiplied alpha
    3192          11 :                     nAsoc = 0;  // Associated to the image as a whole
    3193             :                 }
    3194             : 
    3195             :                 // Component number
    3196          43 :                 cdefBox.AppendUInt16(static_cast<GUInt16>(i));
    3197          43 :                 cdefBox.AppendUInt16(nTyp);
    3198          43 :                 cdefBox.AppendUInt16(nAsoc);
    3199             :             }
    3200             :         }
    3201             : 
    3202             :         // Add res box if needed
    3203         219 :         GDALJP2Box *poRes = nullptr;
    3204         219 :         if (poSrcDS->GetMetadataItem("TIFFTAG_XRESOLUTION") != nullptr &&
    3205         224 :             poSrcDS->GetMetadataItem("TIFFTAG_YRESOLUTION") != nullptr &&
    3206           5 :             poSrcDS->GetMetadataItem("TIFFTAG_RESOLUTIONUNIT") != nullptr)
    3207             :         {
    3208             :             double dfXRes =
    3209           5 :                 CPLAtof(poSrcDS->GetMetadataItem("TIFFTAG_XRESOLUTION"));
    3210             :             double dfYRes =
    3211           5 :                 CPLAtof(poSrcDS->GetMetadataItem("TIFFTAG_YRESOLUTION"));
    3212             :             int nResUnit =
    3213           5 :                 atoi(poSrcDS->GetMetadataItem("TIFFTAG_RESOLUTIONUNIT"));
    3214             : #define PIXELS_PER_INCH 2
    3215             : #define PIXELS_PER_CM 3
    3216             : 
    3217           5 :             if (nResUnit == PIXELS_PER_INCH)
    3218             :             {
    3219             :                 // convert pixels per inch to pixels per cm.
    3220           2 :                 dfXRes = dfXRes * 39.37 / 100.0;
    3221           2 :                 dfYRes = dfYRes * 39.37 / 100.0;
    3222           2 :                 nResUnit = PIXELS_PER_CM;
    3223             :             }
    3224             : 
    3225           5 :             if (nResUnit == PIXELS_PER_CM && dfXRes > 0 && dfYRes > 0 &&
    3226           5 :                 dfXRes < 65535 && dfYRes < 65535)
    3227             :             {
    3228             :                 /* Format a resd box and embed it inside a res box */
    3229          10 :                 GDALJP2Box oResd;
    3230           5 :                 oResd.SetType("resd");
    3231             : 
    3232           5 :                 int nYDenom = 1;
    3233          58 :                 while (nYDenom < 32767 && dfYRes < 32767)
    3234             :                 {
    3235          53 :                     dfYRes *= 2;
    3236          53 :                     nYDenom *= 2;
    3237             :                 }
    3238           5 :                 int nXDenom = 1;
    3239          56 :                 while (nXDenom < 32767 && dfXRes < 32767)
    3240             :                 {
    3241          51 :                     dfXRes *= 2;
    3242          51 :                     nXDenom *= 2;
    3243             :                 }
    3244             : 
    3245           5 :                 oResd.AppendUInt16(static_cast<GUInt16>(dfYRes));
    3246           5 :                 oResd.AppendUInt16(static_cast<GUInt16>(nYDenom));
    3247           5 :                 oResd.AppendUInt16(static_cast<GUInt16>(dfXRes));
    3248           5 :                 oResd.AppendUInt16(static_cast<GUInt16>(nXDenom));
    3249           5 :                 oResd.AppendUInt8(2); /* vertical exponent */
    3250           5 :                 oResd.AppendUInt8(2); /* horizontal exponent */
    3251             : 
    3252           5 :                 GDALJP2Box *poResd = &oResd;
    3253           5 :                 poRes = GDALJP2Box::CreateAsocBox(1, &poResd);
    3254           5 :                 poRes->SetType("res ");
    3255             :             }
    3256             :         }
    3257             : 
    3258             :         /* Build and write jp2h super box now */
    3259             :         GDALJP2Box *apoBoxes[7];
    3260         219 :         int nBoxes = 1;
    3261         219 :         apoBoxes[0] = &ihdrBox;
    3262         219 :         if (bpccBox.GetDataLength())
    3263           3 :             apoBoxes[nBoxes++] = &bpccBox;
    3264         219 :         apoBoxes[nBoxes++] = &colrBox;
    3265         219 :         if (pclrBox.GetDataLength())
    3266           6 :             apoBoxes[nBoxes++] = &pclrBox;
    3267         219 :         if (cmapBox.GetDataLength())
    3268           6 :             apoBoxes[nBoxes++] = &cmapBox;
    3269         219 :         if (cdefBox.GetDataLength())
    3270          12 :             apoBoxes[nBoxes++] = &cdefBox;
    3271         219 :         if (poRes)
    3272           5 :             apoBoxes[nBoxes++] = poRes;
    3273             :         GDALJP2Box *psJP2HBox =
    3274         219 :             GDALJP2Box::CreateSuperBox("jp2h", nBoxes, apoBoxes);
    3275         219 :         WriteBox(fp, psJP2HBox);
    3276         219 :         delete psJP2HBox;
    3277         219 :         delete poRes;
    3278             : 
    3279         219 :         if (!bGeoBoxesAfter)
    3280             :         {
    3281         208 :             if (bGeoJP2Option && bGeoreferencingCompatOfGeoJP2)
    3282             :             {
    3283         150 :                 GDALJP2Box *poBox = oJP2MD.CreateJP2GeoTIFF();
    3284         150 :                 WriteBox(fp, poBox);
    3285         150 :                 delete poBox;
    3286             :             }
    3287             : 
    3288         219 :             if (CPLFetchBool(papszOptions, "WRITE_METADATA", false) &&
    3289          11 :                 !CPLFetchBool(papszOptions, "MAIN_MD_DOMAIN_ONLY", false))
    3290             :             {
    3291          11 :                 WriteXMPBox(fp, poSrcDS);
    3292             :             }
    3293             : 
    3294         208 :             if (CPLFetchBool(papszOptions, "WRITE_METADATA", false))
    3295             :             {
    3296          11 :                 if (!CPLFetchBool(papszOptions, "MAIN_MD_DOMAIN_ONLY", false))
    3297          11 :                     WriteXMLBoxes(fp, poSrcDS);
    3298          11 :                 WriteGDALMetadataBox(fp, poSrcDS, papszOptions);
    3299             :             }
    3300             : 
    3301         208 :             if (poGMLJP2Box != nullptr)
    3302             :             {
    3303          53 :                 WriteBox(fp, poGMLJP2Box);
    3304             :             }
    3305             :         }
    3306             :     }
    3307             : 
    3308             :     /* -------------------------------------------------------------------- */
    3309             :     /*      Try lossless reuse of an existing JPEG2000 codestream           */
    3310             :     /* -------------------------------------------------------------------- */
    3311         259 :     vsi_l_offset nCodeStreamLength = 0;
    3312         259 :     vsi_l_offset nCodeStreamStart = 0;
    3313         259 :     VSILFILE *fpSrc = nullptr;
    3314         259 :     if (CPLFetchBool(papszOptions, "USE_SRC_CODESTREAM", false))
    3315             :     {
    3316          14 :         CPLString osSrcFilename(poSrcDS->GetDescription());
    3317          14 :         if (poSrcDS->GetDriver() != nullptr &&
    3318           7 :             poSrcDS->GetDriver() == GDALGetDriverByName("VRT"))
    3319             :         {
    3320           0 :             VRTDataset *poVRTDS = dynamic_cast<VRTDataset *>(poSrcDS);
    3321           0 :             if (poVRTDS)
    3322             :             {
    3323             :                 GDALDataset *poSimpleSourceDS =
    3324           0 :                     poVRTDS->GetSingleSimpleSource();
    3325           0 :                 if (poSimpleSourceDS)
    3326           0 :                     osSrcFilename = poSimpleSourceDS->GetDescription();
    3327             :             }
    3328             :         }
    3329             : 
    3330           7 :         fpSrc = VSIFOpenL(osSrcFilename, "rb");
    3331           7 :         if (fpSrc)
    3332             :         {
    3333           7 :             nCodeStreamStart = JP2FindCodeStream(fpSrc, &nCodeStreamLength);
    3334             :         }
    3335           7 :         if (nCodeStreamLength == 0)
    3336             :         {
    3337           1 :             CPLError(
    3338             :                 CE_Warning, CPLE_AppDefined,
    3339             :                 "USE_SRC_CODESTREAM=YES specified, but no codestream found");
    3340             :         }
    3341             :     }
    3342             : 
    3343         478 :     if (eCodecFormat == CODEC::cvtenum(JP2_CODEC_JP2) &&
    3344         219 :         !BASE::canPerformDirectIO())
    3345             :     {
    3346             :         // Start codestream box
    3347         219 :         nStartJP2C = VSIFTellL(fp);
    3348         219 :         if (nCodeStreamLength)
    3349           6 :             bUseXLBoxes = nCodeStreamLength > UINT_MAX;
    3350             :         else
    3351         426 :             bUseXLBoxes = CPLFetchBool(papszOptions, "JP2C_XLBOX",
    3352         425 :                                        false) || /* For debugging */
    3353         212 :                           static_cast<GIntBig>(nXSize) * nYSize * nBands *
    3354         212 :                                   nDataTypeSize / adfRates.back() >
    3355             :                               4e9;
    3356         219 :         GUInt32 nLBox = (bUseXLBoxes) ? 1 : 0;
    3357         219 :         CPL_MSBPTR32(&nLBox);
    3358         219 :         VSIFWriteL(&nLBox, 1, 4, fp);
    3359         219 :         VSIFWriteL("jp2c", 1, 4, fp);
    3360         219 :         if (bUseXLBoxes)
    3361             :         {
    3362           1 :             GUIntBig nXLBox = 0;
    3363           1 :             VSIFWriteL(&nXLBox, 1, 8, fp);
    3364             :         }
    3365             :     }
    3366             : 
    3367             :     /* -------------------------------------------------------------------- */
    3368             :     /*      Do lossless reuse of an existing JPEG2000 codestream            */
    3369             :     /* -------------------------------------------------------------------- */
    3370         259 :     if (fpSrc)
    3371             :     {
    3372           7 :         const char *apszIgnoredOptions[] = {"BLOCKXSIZE",
    3373             :                                             "BLOCKYSIZE",
    3374             :                                             "QUALITY",
    3375             :                                             "REVERSIBLE",
    3376             :                                             "RESOLUTIONS",
    3377             :                                             "PROGRESSION",
    3378             :                                             "SOP",
    3379             :                                             "EPH",
    3380             :                                             "YCBCR420",
    3381             :                                             "YCC",
    3382             :                                             GDALMD_NBITS,
    3383             :                                             "1BIT_ALPHA",
    3384             :                                             "PRECINCTS",
    3385             :                                             "TILEPARTS",
    3386             :                                             "CODEBLOCK_WIDTH",
    3387             :                                             "CODEBLOCK_HEIGHT",
    3388             :                                             "PLT",
    3389             :                                             nullptr};
    3390         126 :         for (int i = 0; apszIgnoredOptions[i]; i++)
    3391             :         {
    3392         119 :             if (CSLFetchNameValue(papszOptions, apszIgnoredOptions[i]))
    3393             :             {
    3394           1 :                 CPLError(CE_Warning, CPLE_NotSupported,
    3395             :                          "Option %s ignored when USE_SRC_CODESTREAM=YES",
    3396             :                          apszIgnoredOptions[i]);
    3397             :             }
    3398             :         }
    3399             :         GByte abyBuffer[4096];
    3400           7 :         VSIFSeekL(fpSrc, nCodeStreamStart, SEEK_SET);
    3401           7 :         vsi_l_offset nRead = 0;
    3402             :         /* coverity[tainted_data] */
    3403          17 :         while (nRead < nCodeStreamLength)
    3404             :         {
    3405          10 :             const size_t nToRead =
    3406          10 :                 (nCodeStreamLength - nRead > 4096)
    3407             :                     ? 4096
    3408             :                     : static_cast<size_t>(nCodeStreamLength - nRead);
    3409          10 :             if (VSIFReadL(abyBuffer, 1, nToRead, fpSrc) != nToRead)
    3410             :             {
    3411           0 :                 VSIFCloseL(fpSrc);
    3412           0 :                 delete poGMLJP2Box;
    3413           0 :                 return nullptr;
    3414             :             }
    3415          10 :             if (nRead == 0 && (pszProfile || bInspireTG) &&
    3416           6 :                 abyBuffer[2] == 0xFF && abyBuffer[3] == 0x51)
    3417             :             {
    3418           6 :                 if (EQUAL(pszProfile, "UNRESTRICTED"))
    3419             :                 {
    3420           0 :                     abyBuffer[6] = 0;
    3421           0 :                     abyBuffer[7] = 0;
    3422             :                 }
    3423           6 :                 else if (EQUAL(pszProfile, "PROFILE_1") || bInspireTG)
    3424             :                 {
    3425             :                     // TODO: ultimately we should check that we can really set
    3426             :                     // Profile 1
    3427           1 :                     abyBuffer[6] = 0;
    3428           1 :                     abyBuffer[7] = 2;
    3429             :                 }
    3430             :             }
    3431          20 :             if (VSIFWriteL(abyBuffer, 1, nToRead, fp) != nToRead ||
    3432          10 :                 !pfnProgress((nRead + nToRead) * 1.0 / nCodeStreamLength,
    3433             :                              nullptr, pProgressData))
    3434             :             {
    3435           0 :                 VSIFCloseL(fpSrc);
    3436           0 :                 delete poGMLJP2Box;
    3437           0 :                 return nullptr;
    3438             :             }
    3439          10 :             nRead += nToRead;
    3440             :         }
    3441             : 
    3442           7 :         VSIFCloseL(fpSrc);
    3443             :     }
    3444             :     else
    3445             :     {
    3446         252 :         localctx.open(fp);
    3447         252 :         if (!localctx.initCompress(papszOptions, adfRates, nBlockXSize,
    3448             :                                    nBlockYSize, bIsIrreversible,
    3449             :                                    nNumResolutions, eProgOrder, bYCC, nCblockW,
    3450             :                                    nCblockH, bYCBCR420, bProfile1, nBands,
    3451             :                                    nXSize, nYSize, eColorSpace, numThreads))
    3452             :         {
    3453           0 :             CPLError(CE_Failure, CPLE_AppDefined, "init compress failed");
    3454           0 :             localctx.free();
    3455           0 :             delete poGMLJP2Box;
    3456          11 :             return nullptr;
    3457             :         }
    3458             : 
    3459             :         /* Grok JP2: let codec handle box writing natively */
    3460         252 :         if (BASE::canPerformDirectIO() &&
    3461           0 :             eCodecFormat == CODEC::cvtenum(JP2_CODEC_JP2))
    3462             :         {
    3463           0 :             localctx.setupJP2Metadata(
    3464             :                 bInspireTG, bProfile1, bGeoBoxesAfter,
    3465           0 :                 (bGeoJP2Option && bGeoreferencingCompatOfGeoJP2) ? &oJP2MD
    3466             :                                                                  : nullptr,
    3467             :                 poGMLJP2Box, nAlphaBandIndex, nRedBandIndex, nGreenBandIndex,
    3468             :                 nBlueBandIndex, eColorSpace, nBands, poCT, poSrcDS,
    3469             :                 papszOptions);
    3470             :         }
    3471             : 
    3472         252 :         if (!localctx.initCodec(pszFilename, fpOwner))
    3473             :         {
    3474           0 :             CPLError(CE_Failure, CPLE_AppDefined,
    3475             :                      "codec initialization failed");
    3476           0 :             localctx.free();
    3477           0 :             delete poGMLJP2Box;
    3478           0 :             return nullptr;
    3479             :         }
    3480         252 :         if (!fpOwner)
    3481           0 :             fp = nullptr;
    3482             : 
    3483         252 :         const int nTilesX = DIV_ROUND_UP(nXSize, nBlockXSize);
    3484         252 :         const int nTilesY = DIV_ROUND_UP(nYSize, nBlockYSize);
    3485             : 
    3486         252 :         const GUIntBig nTileSize = static_cast<GUIntBig>(nBlockXSize) *
    3487         252 :                                    nBlockYSize * nBands * nDataTypeSize;
    3488         252 :         GByte *pTempBuffer = nullptr;
    3489             : 
    3490         252 :         const bool bUseIOThread =
    3491         504 :             CODEC::preferPerTileCompress() && (nTilesX > 1 || nTilesY > 1) &&
    3492          11 :             nTileSize < 10 * 1024 * 1024 &&
    3493         515 :             strcmp(CPLGetThreadingModel(), "stub") != 0 &&
    3494          11 :             CPLTestBool(
    3495             :                 CPLGetConfigOption("JP2OPENJPEG_USE_THREADED_IO", "YES"));
    3496             : 
    3497         252 :         if (nTileSize > UINT_MAX)
    3498             :         {
    3499           1 :             CPLError(CE_Failure, CPLE_NotSupported, "Tile size exceeds 4GB");
    3500           1 :             pTempBuffer = nullptr;
    3501             :         }
    3502             :         else
    3503             :         {
    3504             :             // Double memory buffer when using threaded I/O
    3505         251 :             const size_t nBufferSize =
    3506             :                 static_cast<size_t>(bUseIOThread ? nTileSize * 2 : nTileSize);
    3507         251 :             pTempBuffer = static_cast<GByte *>(VSI_MALLOC_VERBOSE(nBufferSize));
    3508             :         }
    3509         252 :         if (pTempBuffer == nullptr)
    3510             :         {
    3511           1 :             localctx.free();
    3512           1 :             delete poGMLJP2Box;
    3513           1 :             return nullptr;
    3514             :         }
    3515             : 
    3516         251 :         GByte *pYUV420Buffer = nullptr;
    3517         251 :         if (bYCBCR420)
    3518             :         {
    3519           2 :             pYUV420Buffer = static_cast<GByte *>(VSI_MALLOC_VERBOSE(
    3520             :                 nBlockXSize * nBlockYSize + nBlockXSize * nBlockYSize / 2 +
    3521             :                 ((nBands == 4) ? nBlockXSize * nBlockYSize : 0)));
    3522           2 :             if (pYUV420Buffer == nullptr)
    3523             :             {
    3524           0 :                 localctx.free();
    3525           0 :                 CPLFree(pTempBuffer);
    3526           0 :                 delete poGMLJP2Box;
    3527           0 :                 return nullptr;
    3528             :             }
    3529             :         }
    3530             : 
    3531             :         /* --------------------------------------------------------------------
    3532             :          */
    3533             :         /*      Iterate over the tiles */
    3534             :         /* --------------------------------------------------------------------
    3535             :          */
    3536         251 :         pfnProgress(0.0, nullptr, pProgressData);
    3537             : 
    3538             :         struct ReadRasterJob
    3539             :         {
    3540             :             GDALDataset *poSrcDS;
    3541             :             int nXOff;
    3542             :             int nYOff;
    3543             :             int nWidthToRead;
    3544             :             int nHeightToRead;
    3545             :             GDALDataType eDataType;
    3546             :             GByte *pBuffer;
    3547             :             int nBands;
    3548             :             CPLErr eErr;
    3549             :         };
    3550             : 
    3551         814 :         const auto ReadRasterFunction = [](void *threadData)
    3552             :         {
    3553         407 :             ReadRasterJob *job = static_cast<ReadRasterJob *>(threadData);
    3554         814 :             job->eErr = job->poSrcDS->RasterIO(
    3555             :                 GF_Read, job->nXOff, job->nYOff, job->nWidthToRead,
    3556         407 :                 job->nHeightToRead, job->pBuffer, job->nWidthToRead,
    3557             :                 job->nHeightToRead, job->eDataType, job->nBands, nullptr, 0, 0,
    3558             :                 0, nullptr);
    3559             :         };
    3560             : 
    3561         251 :         CPLWorkerThreadPool oPool;
    3562         251 :         if (bUseIOThread)
    3563             :         {
    3564          10 :             oPool.Setup(1, nullptr, nullptr);
    3565             :         }
    3566             : 
    3567         251 :         GByte *pabyActiveBuffer = pTempBuffer;
    3568         251 :         GByte *pabyBackgroundBuffer =
    3569         251 :             pTempBuffer + static_cast<size_t>(nTileSize);
    3570             : 
    3571         251 :         CPLErr eErr = CE_None;
    3572         251 :         int iTile = 0;
    3573             : 
    3574             :         ReadRasterJob job;
    3575         251 :         job.eDataType = eDataType;
    3576         251 :         job.pBuffer = pabyActiveBuffer;
    3577         251 :         job.nBands = nBands;
    3578         251 :         job.eErr = CE_Failure;
    3579         251 :         job.poSrcDS = poSrcDS;
    3580             : 
    3581         251 :         if (bUseIOThread)
    3582             :         {
    3583          10 :             job.nXOff = 0;
    3584          10 :             job.nYOff = 0;
    3585          10 :             job.nWidthToRead = std::min(nBlockXSize, nXSize);
    3586          10 :             job.nHeightToRead = std::min(nBlockYSize, nYSize);
    3587          10 :             job.pBuffer = pabyBackgroundBuffer;
    3588          10 :             ReadRasterFunction(&job);
    3589          10 :             eErr = job.eErr;
    3590             :         }
    3591             : 
    3592         526 :         for (int nBlockYOff = 0; eErr == CE_None && nBlockYOff < nTilesY;
    3593             :              nBlockYOff++)
    3594             :         {
    3595         682 :             for (int nBlockXOff = 0; eErr == CE_None && nBlockXOff < nTilesX;
    3596             :                  nBlockXOff++)
    3597             :             {
    3598         407 :                 const int nWidthToRead =
    3599         407 :                     std::min(nBlockXSize, nXSize - nBlockXOff * nBlockXSize);
    3600         407 :                 const int nHeightToRead =
    3601         407 :                     std::min(nBlockYSize, nYSize - nBlockYOff * nBlockYSize);
    3602             : 
    3603         407 :                 if (bUseIOThread)
    3604             :                 {
    3605             :                     // Wait for previous background I/O task to be finished
    3606         100 :                     oPool.WaitCompletion();
    3607         100 :                     eErr = job.eErr;
    3608             : 
    3609             :                     // Swap buffers
    3610         100 :                     std::swap(pabyBackgroundBuffer, pabyActiveBuffer);
    3611             : 
    3612             :                     // Prepare for next I/O task
    3613         100 :                     int nNextBlockXOff = nBlockXOff + 1;
    3614         100 :                     int nNextBlockYOff = nBlockYOff;
    3615         100 :                     if (nNextBlockXOff == nTilesX)
    3616             :                     {
    3617          26 :                         nNextBlockXOff = 0;
    3618          26 :                         nNextBlockYOff++;
    3619             :                     }
    3620         100 :                     if (nNextBlockYOff != nTilesY)
    3621             :                     {
    3622          90 :                         job.nXOff = nNextBlockXOff * nBlockXSize;
    3623          90 :                         job.nYOff = nNextBlockYOff * nBlockYSize;
    3624          90 :                         job.nWidthToRead =
    3625          90 :                             std::min(nBlockXSize, nXSize - job.nXOff);
    3626          90 :                         job.nHeightToRead =
    3627          90 :                             std::min(nBlockYSize, nYSize - job.nYOff);
    3628          90 :                         job.pBuffer = pabyBackgroundBuffer;
    3629             : 
    3630             :                         // Submit next job
    3631          90 :                         oPool.SubmitJob(ReadRasterFunction, &job);
    3632             :                     }
    3633             :                 }
    3634             :                 else
    3635             :                 {
    3636         307 :                     job.nXOff = nBlockXOff * nBlockXSize;
    3637         307 :                     job.nYOff = nBlockYOff * nBlockYSize;
    3638         307 :                     job.nWidthToRead = nWidthToRead;
    3639         307 :                     job.nHeightToRead = nHeightToRead;
    3640         307 :                     ReadRasterFunction(&job);
    3641         307 :                     eErr = job.eErr;
    3642             :                 }
    3643             : 
    3644         407 :                 if (b1BitAlpha)
    3645             :                 {
    3646       64987 :                     for (int i = 0; i < nWidthToRead * nHeightToRead; i++)
    3647             :                     {
    3648       64984 :                         if (pabyActiveBuffer[nAlphaBandIndex * nWidthToRead *
    3649       64984 :                                                  nHeightToRead +
    3650             :                                              i])
    3651       25040 :                             pabyActiveBuffer[nAlphaBandIndex * nWidthToRead *
    3652       25040 :                                                  nHeightToRead +
    3653       25040 :                                              i] = 1;
    3654             :                         else
    3655       39944 :                             pabyActiveBuffer[nAlphaBandIndex * nWidthToRead *
    3656       39944 :                                                  nHeightToRead +
    3657       39944 :                                              i] = 0;
    3658             :                     }
    3659             :                 }
    3660         407 :                 if (eErr == CE_None)
    3661             :                 {
    3662         407 :                     if (bYCBCR420)
    3663             :                     {
    3664         202 :                         for (int j = 0; j < nHeightToRead; j++)
    3665             :                         {
    3666       27000 :                             for (int i = 0; i < nWidthToRead; i++)
    3667             :                             {
    3668       26800 :                                 const int R =
    3669       26800 :                                     pabyActiveBuffer[j * nWidthToRead + i];
    3670       26800 :                                 const int G =
    3671       26800 :                                     pabyActiveBuffer[nHeightToRead *
    3672       26800 :                                                          nWidthToRead +
    3673       26800 :                                                      j * nWidthToRead + i];
    3674       26800 :                                 const int B =
    3675       26800 :                                     pabyActiveBuffer[2 * nHeightToRead *
    3676       26800 :                                                          nWidthToRead +
    3677       26800 :                                                      j * nWidthToRead + i];
    3678       26800 :                                 const int Y = static_cast<int>(
    3679       26800 :                                     0.299 * R + 0.587 * G + 0.114 * B);
    3680       53600 :                                 const int Cb = CLAMP_0_255(static_cast<int>(
    3681       26800 :                                     -0.1687 * R - 0.3313 * G + 0.5 * B + 128));
    3682       53600 :                                 const int Cr = CLAMP_0_255(static_cast<int>(
    3683       26800 :                                     0.5 * R - 0.4187 * G - 0.0813 * B + 128));
    3684       26800 :                                 pYUV420Buffer[j * nWidthToRead + i] =
    3685             :                                     static_cast<GByte>(Y);
    3686       26800 :                                 pYUV420Buffer[nHeightToRead * nWidthToRead +
    3687       26800 :                                               ((j / 2) * ((nWidthToRead) / 2) +
    3688       26800 :                                                i / 2)] = static_cast<GByte>(Cb);
    3689       26800 :                                 pYUV420Buffer[5 * nHeightToRead * nWidthToRead /
    3690       26800 :                                                   4 +
    3691       26800 :                                               ((j / 2) * ((nWidthToRead) / 2) +
    3692       26800 :                                                i / 2)] = static_cast<GByte>(Cr);
    3693       26800 :                                 if (nBands == 4)
    3694             :                                 {
    3695       24300 :                                     pYUV420Buffer[3 * nHeightToRead *
    3696       24300 :                                                       nWidthToRead / 2 +
    3697       24300 :                                                   j * nWidthToRead + i] =
    3698       24300 :                                         static_cast<GByte>(
    3699       24300 :                                             pabyActiveBuffer[3 * nHeightToRead *
    3700       24300 :                                                                  nWidthToRead +
    3701       24300 :                                                              j * nWidthToRead +
    3702             :                                                              i]);
    3703             :                                 }
    3704             :                             }
    3705             :                         }
    3706             : 
    3707           2 :                         int nBytesToWrite =
    3708           2 :                             3 * nWidthToRead * nHeightToRead / 2;
    3709           2 :                         if (nBands == 4)
    3710           1 :                             nBytesToWrite += nBlockXSize * nBlockYSize;
    3711             : 
    3712           2 :                         if (!localctx.compressTile(iTile, pYUV420Buffer,
    3713             :                                                    nBytesToWrite))
    3714             :                         {
    3715           0 :                             CPLError(CE_Failure, CPLE_AppDefined,
    3716             :                                      "compress tile failed");
    3717           0 :                             eErr = CE_Failure;
    3718             :                         }
    3719             :                     }
    3720             :                     else
    3721             :                     {
    3722         405 :                         if (!localctx.compressTile(iTile, pabyActiveBuffer,
    3723         405 :                                                    nWidthToRead *
    3724         405 :                                                        nHeightToRead * nBands *
    3725             :                                                        nDataTypeSize))
    3726             :                         {
    3727           0 :                             CPLError(CE_Failure, CPLE_AppDefined,
    3728             :                                      "compress tile failed");
    3729           0 :                             eErr = CE_Failure;
    3730             :                         }
    3731             :                     }
    3732             :                 }
    3733             : 
    3734         407 :                 if (!pfnProgress((iTile + 1) * 1.0 / (nTilesX * nTilesY),
    3735             :                                  nullptr, pProgressData))
    3736           0 :                     eErr = CE_Failure;
    3737             : 
    3738         407 :                 iTile++;
    3739             :             }
    3740             :         }
    3741             : 
    3742         251 :         if (bUseIOThread && eErr == CE_Failure)
    3743             :         {
    3744             :             // Wait for previous background I/O task to be finished
    3745             :             // before freeing buffers (pTempBuffer, etc.)
    3746           0 :             oPool.WaitCompletion();
    3747             :         }
    3748             : 
    3749         251 :         VSIFree(pTempBuffer);
    3750         251 :         VSIFree(pYUV420Buffer);
    3751             : 
    3752         251 :         if (eErr != CE_None)
    3753             :         {
    3754           0 :             localctx.free();
    3755           0 :             delete poGMLJP2Box;
    3756           0 :             return nullptr;
    3757             :         }
    3758             : 
    3759         251 :         if (!localctx.finishCompress())
    3760             :         {
    3761          10 :             localctx.free();
    3762          10 :             delete poGMLJP2Box;
    3763          10 :             return nullptr;
    3764             :         }
    3765         241 :         localctx.free();
    3766             :     }
    3767             : 
    3768             :     /* -------------------------------------------------------------------- */
    3769             :     /*      Patch JP2C box length and add trailing JP2 boxes                */
    3770             :     /* -------------------------------------------------------------------- */
    3771         248 :     bool bRet = true;
    3772         248 :     if (eCodecFormat == CODEC::cvtenum(JP2_CODEC_JP2) &&
    3773         466 :         !BASE::canPerformDirectIO() &&
    3774         218 :         !CPLFetchBool(papszOptions, "JP2C_LENGTH_ZERO",
    3775             :                       false) /* debug option */)
    3776             :     {
    3777         217 :         vsi_l_offset nEndJP2C = VSIFTellL(fp);
    3778         217 :         GUIntBig nBoxSize = nEndJP2C - nStartJP2C;
    3779         217 :         if (bUseXLBoxes)
    3780             :         {
    3781           1 :             VSIFSeekL(fp, nStartJP2C + 8, SEEK_SET);
    3782           1 :             CPL_MSBPTR64(&nBoxSize);
    3783           1 :             if (VSIFWriteL(&nBoxSize, 8, 1, fp) != 1)
    3784           0 :                 bRet = false;
    3785             :         }
    3786             :         else
    3787             :         {
    3788         216 :             if (nBoxSize > UINT_MAX)
    3789             :             {
    3790             :                 /*  Should not happen hopefully */
    3791           0 :                 if ((bGeoreferencingCompatOfGeoJP2 || poGMLJP2Box) &&
    3792             :                     bGeoBoxesAfter)
    3793             :                 {
    3794           0 :                     CPLError(CE_Warning, CPLE_AppDefined,
    3795             :                              "Cannot write GMLJP2/GeoJP2 boxes as codestream "
    3796             :                              "is unexpectedly > 4GB");
    3797           0 :                     bGeoreferencingCompatOfGeoJP2 = FALSE;
    3798           0 :                     delete poGMLJP2Box;
    3799           0 :                     poGMLJP2Box = nullptr;
    3800             :                 }
    3801             :             }
    3802             :             else
    3803             :             {
    3804         216 :                 VSIFSeekL(fp, nStartJP2C, SEEK_SET);
    3805         216 :                 GUInt32 nBoxSize32 = static_cast<GUInt32>(nBoxSize);
    3806         216 :                 CPL_MSBPTR32(&nBoxSize32);
    3807         216 :                 if (VSIFWriteL(&nBoxSize32, 4, 1, fp) != 1)
    3808           0 :                     bRet = false;
    3809             :             }
    3810             :         }
    3811         217 :         VSIFSeekL(fp, 0, SEEK_END);
    3812             : 
    3813         217 :         if (CPLFetchBool(papszOptions, "WRITE_METADATA", false))
    3814             :         {
    3815          14 :             if (!WriteIPRBox(fp, poSrcDS))
    3816           0 :                 bRet = false;
    3817             :         }
    3818             : 
    3819         217 :         if (bGeoBoxesAfter)
    3820             :         {
    3821          11 :             if (poGMLJP2Box != nullptr)
    3822             :             {
    3823           5 :                 if (!WriteBox(fp, poGMLJP2Box))
    3824           0 :                     bRet = false;
    3825             :             }
    3826             : 
    3827          11 :             if (CPLFetchBool(papszOptions, "WRITE_METADATA", false))
    3828             :             {
    3829           3 :                 if (!CPLFetchBool(papszOptions, "MAIN_MD_DOMAIN_ONLY", false))
    3830             :                 {
    3831           3 :                     if (!WriteXMLBoxes(fp, poSrcDS))
    3832           0 :                         bRet = false;
    3833             :                 }
    3834           3 :                 if (!WriteGDALMetadataBox(fp, poSrcDS, papszOptions))
    3835           0 :                     bRet = false;
    3836             :             }
    3837             : 
    3838          11 :             if (bGeoJP2Option && bGeoreferencingCompatOfGeoJP2)
    3839             :             {
    3840           5 :                 GDALJP2Box *poBox = oJP2MD.CreateJP2GeoTIFF();
    3841           5 :                 if (!WriteBox(fp, poBox))
    3842           0 :                     bRet = false;
    3843           5 :                 delete poBox;
    3844             :             }
    3845             : 
    3846          14 :             if (CPLFetchBool(papszOptions, "WRITE_METADATA", false) &&
    3847           3 :                 !CPLFetchBool(papszOptions, "MAIN_MD_DOMAIN_ONLY", false))
    3848             :             {
    3849           3 :                 if (!WriteXMPBox(fp, poSrcDS))
    3850           0 :                     bRet = false;
    3851             :             }
    3852             :         }
    3853             :     }
    3854             : 
    3855         248 :     if (fpOwner)
    3856             :     {
    3857         248 :         if (VSIFCloseL(fpOwner.release()) != 0)
    3858           0 :             bRet = false;
    3859             :     }
    3860         248 :     delete poGMLJP2Box;
    3861         248 :     if (!bRet)
    3862           0 :         return nullptr;
    3863             : 
    3864             :     /* -------------------------------------------------------------------- */
    3865             :     /*      Re-open dataset, and copy any auxiliary pam information.        */
    3866             :     /* -------------------------------------------------------------------- */
    3867             : 
    3868         248 :     GDALOpenInfo oOpenInfo(pszFilename, GA_ReadOnly);
    3869           3 :     auto poDS =
    3870         248 :         dynamic_cast<JP2OPJLikeDataset *>(JP2OPJLikeDataset::Open(&oOpenInfo));
    3871             : 
    3872         248 :     if (poDS)
    3873             :     {
    3874         245 :         poDS->CloneInfo(poSrcDS, GCIF_PAM_DEFAULT & (~GCIF_METADATA));
    3875             : 
    3876             :         /* Only write relevant metadata to PAM, and if needed */
    3877         245 :         if (!CPLFetchBool(papszOptions, "WRITE_METADATA", false))
    3878             :         {
    3879         231 :             char **papszSrcMD = CSLDuplicate(poSrcDS->GetMetadata());
    3880             :             papszSrcMD =
    3881         231 :                 CSLSetNameValue(papszSrcMD, GDALMD_AREA_OR_POINT, nullptr);
    3882         231 :             papszSrcMD = CSLSetNameValue(papszSrcMD, "Corder", nullptr);
    3883         322 :             for (char **papszSrcMDIter = papszSrcMD;
    3884         322 :                  papszSrcMDIter && *papszSrcMDIter;)
    3885             :             {
    3886             :                 /* Remove entries like KEY= (without value) */
    3887          91 :                 if ((*papszSrcMDIter)[0] &&
    3888          91 :                     (*papszSrcMDIter)[strlen((*papszSrcMDIter)) - 1] == '=')
    3889             :                 {
    3890          37 :                     CPLFree(*papszSrcMDIter);
    3891          37 :                     memmove(papszSrcMDIter, papszSrcMDIter + 1,
    3892             :                             sizeof(char *) *
    3893          37 :                                 (CSLCount(papszSrcMDIter + 1) + 1));
    3894             :                 }
    3895             :                 else
    3896          54 :                     ++papszSrcMDIter;
    3897             :             }
    3898         231 :             char **papszMD = CSLDuplicate(poDS->GetMetadata());
    3899         231 :             papszMD = CSLSetNameValue(papszMD, GDALMD_AREA_OR_POINT, nullptr);
    3900         246 :             if (papszSrcMD && papszSrcMD[0] != nullptr &&
    3901          15 :                 CSLCount(papszSrcMD) != CSLCount(papszMD))
    3902             :             {
    3903           9 :                 poDS->SetMetadata(papszSrcMD);
    3904             :             }
    3905         231 :             CSLDestroy(papszSrcMD);
    3906         231 :             CSLDestroy(papszMD);
    3907             :         }
    3908             :     }
    3909             : 
    3910         248 :     return poDS;
    3911             : }
    3912             : 
    3913             : #ifdef unused
    3914             : template <typename CODEC, typename BASE>
    3915             : void GDALRegisterJP2(const std::string &libraryName,
    3916             :                      const std::string &driverName)
    3917             : 
    3918             : {
    3919             :     if (!GDAL_CHECK_VERSION((driverName + " driver").c_str()))
    3920             :         return;
    3921             : 
    3922             :     if (GDALGetDriverByName(driverName.c_str()) != nullptr)
    3923             :         return;
    3924             : 
    3925             :     GDALDriver *poDriver = new GDALDriver();
    3926             :     poDriver->SetDescription(driverName.c_str());
    3927             :     poDriver->SetMetadataItem(GDAL_DCAP_RASTER, "YES");
    3928             :     poDriver->SetMetadataItem(GDAL_DCAP_VECTOR, "YES");
    3929             :     poDriver->SetMetadataItem(
    3930             :         GDAL_DMD_LONGNAME,
    3931             :         ("JPEG-2000 driver based on " + libraryName + " library").c_str());
    3932             : 
    3933             :     poDriver->SetMetadataItem(
    3934             :         GDAL_DMD_HELPTOPIC,
    3935             :         ("drivers/raster/jp2" + CPLString(libraryName).tolower() + ".html")
    3936             :             .c_str());
    3937             :     poDriver->SetMetadataItem(GDAL_DMD_MIMETYPE, "image/jp2");
    3938             :     poDriver->SetMetadataItem(GDAL_DMD_EXTENSION, "jp2");
    3939             :     poDriver->SetMetadataItem(GDAL_DMD_EXTENSIONS, "jp2 j2k");
    3940             :     poDriver->SetMetadataItem(GDAL_DMD_CREATIONDATATYPES,
    3941             :                               "Byte Int16 UInt16 Int32 UInt32");
    3942             : 
    3943             :     poDriver->SetMetadataItem(GDAL_DCAP_VIRTUALIO, "YES");
    3944             :     BASE::setMetaData(poDriver);
    3945             : 
    3946             :     poDriver->pfnIdentify = JP2OPJLikeDataset<CODEC, BASE>::Identify;
    3947             :     poDriver->pfnOpen = JP2OPJLikeDataset<CODEC, BASE>::Open;
    3948             :     poDriver->pfnCreateCopy = JP2OPJLikeDataset<CODEC, BASE>::CreateCopy;
    3949             : 
    3950             :     GetGDALDriverManager()->RegisterDriver(poDriver);
    3951             : }
    3952             : #endif

Generated by: LCOV version 1.14