LCOV - code coverage report
Current view: top level - frmts/opjlike - jp2opjlikedataset.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 1613 1901 84.9 %
Date: 2026-05-29 23:25:07 Functions: 39 41 95.1 %

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

Generated by: LCOV version 1.14