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

Generated by: LCOV version 1.14