LCOV - code coverage report
Current view: top level - frmts/opjlike - jp2opjlikedataset.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 1580 1822 86.7 %
Date: 2024-11-21 22:18:42 Functions: 38 39 97.4 %

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

Generated by: LCOV version 1.14