LCOV - code coverage report
Current view: top level - frmts/opjlike - jp2opjlikedataset.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 1579 1827 86.4 %
Date: 2026-01-23 20:24:11 Functions: 38 40 95.0 %

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

Generated by: LCOV version 1.14