LCOV - code coverage report
Current view: top level - frmts/openjpeg - opjdatasetbase.h (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 376 466 80.7 %
Date: 2026-03-26 23:25:44 Functions: 34 39 87.2 %

          Line data    Source code
       1             : /******************************************************************************
       2             :  *
       3             :  * Author:   Aaron Boxer, <boxerab at protonmail dot com>
       4             :  *
       5             :  ******************************************************************************
       6             :  * Copyright (c) 2010-2014, Even Rouault <even dot rouault at spatialys dot com>
       7             :  * Copyright (c) 2015, European Union (European Environment Agency)
       8             :  * Copyright (c) 2023, Grok Image Compression Inc.
       9             :  *
      10             :  * SPDX-License-Identifier: MIT
      11             :  ****************************************************************************/
      12             : #pragma once
      13             : 
      14             : #include <limits>
      15             : #include <algorithm>
      16             : 
      17             : /* This file is to be used with openjpeg 2.1 or later */
      18             : #ifdef __clang__
      19             : #pragma clang diagnostic push
      20             : #pragma clang diagnostic ignored "-Wunknown-pragmas"
      21             : #pragma clang diagnostic ignored "-Wdocumentation"
      22             : #endif
      23             : 
      24             : #include <openjpeg.h>
      25             : #include <opj_config.h>
      26             : 
      27             : #ifdef __clang__
      28             : #pragma clang diagnostic pop
      29             : #endif
      30             : 
      31             : #define IS_OPENJPEG_OR_LATER(major, minor, patch)                              \
      32             :     ((OPJ_VERSION_MAJOR * 10000 + OPJ_VERSION_MINOR * 100 +                    \
      33             :       OPJ_VERSION_BUILD) >= ((major) * 10000 + (minor) * 100 + (patch)))
      34             : 
      35             : /************************************************************************/
      36             : /*                    JP2OpenJPEG_WarningCallback()                     */
      37             : /************************************************************************/
      38             : 
      39           0 : static void JP2OpenJPEG_WarningCallback(const char *pszMsg,
      40             :                                         CPL_UNUSED void *unused)
      41             : {
      42           0 :     if (strcmp(pszMsg, "No incltree created.\n") == 0 ||
      43           0 :         strcmp(pszMsg, "No imsbtree created.\n") == 0 ||
      44           0 :         strcmp(pszMsg, "tgt_create tree->numnodes == 0, no tree created.\n") ==
      45             :             0)
      46             :     {
      47             :         // Ignore warnings related to empty tag-trees. There's nothing wrong
      48             :         // about that.
      49             :         // Fixed submitted upstream with
      50             :         // https://github.com/uclouvain/openjpeg/pull/893
      51           0 :         return;
      52             :     }
      53           0 :     if (strcmp(pszMsg, "Empty SOT marker detected: Psot=12.\n") == 0)
      54             :     {
      55             :         static int bWarningEmitted = FALSE;
      56           0 :         if (bWarningEmitted)
      57           0 :             return;
      58           0 :         bWarningEmitted = TRUE;
      59             :     }
      60           0 :     if (strcmp(pszMsg, "JP2 box which are after the codestream will not be "
      61             :                        "read by this function.\n") == 0)
      62             :     {
      63           0 :         return;
      64             :     }
      65             : 
      66           0 :     std::string osMsg(pszMsg);
      67           0 :     if (!osMsg.empty() && osMsg.back() == '\n')
      68           0 :         osMsg.resize(osMsg.size() - 1);
      69           0 :     CPLError(CE_Warning, CPLE_AppDefined, "%s", osMsg.c_str());
      70             : }
      71             : 
      72             : /************************************************************************/
      73             : /*                      JP2OpenJPEG_InfoCallback()                      */
      74             : /************************************************************************/
      75             : 
      76       10756 : static void JP2OpenJPEG_InfoCallback(const char *pszMsg,
      77             :                                      CPL_UNUSED void *unused)
      78             : {
      79       21512 :     std::string osMsg(pszMsg);
      80       10756 :     if (!osMsg.empty() && osMsg.back() == '\n')
      81       10756 :         osMsg.resize(osMsg.size() - 1);
      82       10756 :     CPLDebug("JP2OpenJPEG", "info: %s", osMsg.c_str());
      83       10756 : }
      84             : 
      85             : /************************************************************************/
      86             : /*                     JP2OpenJPEG_ErrorCallback()                      */
      87             : /************************************************************************/
      88             : 
      89           6 : static void JP2OpenJPEG_ErrorCallback(const char *pszMsg,
      90             :                                       CPL_UNUSED void *unused)
      91             : {
      92           6 :     CPLError(CE_Failure, CPLE_AppDefined, "%s", pszMsg);
      93           6 : }
      94             : 
      95             : /************************************************************************/
      96             : /*                          JP2Dataset_Read()                           */
      97             : /************************************************************************/
      98             : 
      99        3758 : static size_t JP2Dataset_Read(void *pBuffer, size_t nBytes, void *pUserData)
     100             : {
     101        3758 :     JP2File *psJP2File = static_cast<JP2File *>(pUserData);
     102             :     size_t nRet =
     103        3758 :         static_cast<size_t>(VSIFReadL(pBuffer, 1, nBytes, psJP2File->fp_));
     104             : #ifdef DEBUG_IO
     105             :     CPLDebug(OPJCodecWrapper::debugId(),
     106             :              "JP2Dataset_Read(" CPL_FRMT_GUIB ") = " CPL_FRMT_GUIB,
     107             :              static_cast<GUIntBig>(nBytes), static_cast<GUIntBig>(nRet));
     108             : #endif
     109        3758 :     if (nRet == 0)
     110           2 :         nRet = static_cast<size_t>(-1);
     111             : 
     112        3758 :     return nRet;
     113             : }
     114             : 
     115             : /************************************************************************/
     116             : /*                          JP2Dataset_Write()                          */
     117             : /************************************************************************/
     118             : 
     119         251 : static size_t JP2Dataset_Write(void *pBuffer, size_t nBytes, void *pUserData)
     120             : {
     121         251 :     JP2File *psJP2File = static_cast<JP2File *>(pUserData);
     122             :     size_t nRet =
     123         251 :         static_cast<size_t>(VSIFWriteL(pBuffer, 1, nBytes, psJP2File->fp_));
     124             : #ifdef DEBUG_IO
     125             :     CPLDebug(OPJCodecWrapper::debugId(),
     126             :              "JP2Dataset_Write(" CPL_FRMT_GUIB ") = " CPL_FRMT_GUIB,
     127             :              static_cast<GUIntBig>(nBytes), static_cast<GUIntBig>(nRet));
     128             : #endif
     129         251 :     if (nRet != nBytes)
     130          10 :         return static_cast<size_t>(-1);
     131         241 :     return nRet;
     132             : }
     133             : 
     134             : /************************************************************************/
     135             : /*                          JP2Dataset_Seek()                           */
     136             : /************************************************************************/
     137             : 
     138         581 : static OPJ_BOOL JP2Dataset_Seek(int64_t nBytes, void *pUserData)
     139             : {
     140         581 :     JP2File *psJP2File = static_cast<JP2File *>(pUserData);
     141             : #ifdef DEBUG_IO
     142             :     CPLDebug(OPJCodecWrapper::debugId(), "JP2Dataset_Seek(" CPL_FRMT_GUIB ")",
     143             :              static_cast<GUIntBig>(nBytes));
     144             : #endif
     145         581 :     return VSIFSeekL(psJP2File->fp_, psJP2File->nBaseOffset + nBytes,
     146         581 :                      SEEK_SET) == 0;
     147             : }
     148             : 
     149             : /************************************************************************/
     150             : /*                          JP2Dataset_Skip()                           */
     151             : /************************************************************************/
     152             : 
     153        1919 : static int64_t JP2Dataset_Skip(int64_t nBytes, void *pUserData)
     154             : {
     155        1919 :     JP2File *psJP2File = static_cast<JP2File *>(pUserData);
     156        1919 :     vsi_l_offset nOffset = VSIFTellL(psJP2File->fp_);
     157        1919 :     nOffset += nBytes;
     158             : #ifdef DEBUG_IO
     159             :     CPLDebug(OPJCodecWrapper::debugId(),
     160             :              "JP2Dataset_Skip(" CPL_FRMT_GUIB " -> " CPL_FRMT_GUIB ")",
     161             :              static_cast<GUIntBig>(nBytes), static_cast<GUIntBig>(nOffset));
     162             : #endif
     163        1919 :     VSIFSeekL(psJP2File->fp_, nOffset, SEEK_SET);
     164        1919 :     return nBytes;
     165             : }
     166             : 
     167             : /************************************************************************/
     168             : /* ==================================================================== */
     169             : /*                           OPJCodecWrapper                            */
     170             : /* ==================================================================== */
     171             : /************************************************************************/
     172             : 
     173             : struct OPJCodecWrapper
     174             : {
     175             :     typedef opj_codec_t jp2_codec;
     176             :     typedef opj_image_t jp2_image;
     177             :     typedef opj_stream_t jp2_stream;
     178             : 
     179             :     typedef opj_image_cmptparm_t jp2_image_comp_param;
     180             :     typedef opj_image_comp_t jp2_image_comp;
     181             : 
     182             :     OPJCodecWrapper() = default;
     183             : 
     184           0 :     explicit OPJCodecWrapper(OPJCodecWrapper *rhs)
     185           0 :         : pCodec(rhs->pCodec), pStream(rhs->pStream), psImage(rhs->psImage),
     186           0 :           pasBandParams(rhs->pasBandParams), psJP2File(rhs->psJP2File)
     187             :     {
     188           0 :         rhs->pCodec = nullptr;
     189           0 :         rhs->pStream = nullptr;
     190           0 :         rhs->psImage = nullptr;
     191           0 :         rhs->pasBandParams = nullptr;
     192           0 :         rhs->psJP2File = nullptr;
     193           0 :     }
     194             : 
     195        2185 :     ~OPJCodecWrapper(void)
     196        2185 :     {
     197        2185 :         free();
     198        2185 :     }
     199             : 
     200        1157 :     void open(VSILFILE *fp, vsi_l_offset offset)
     201             :     {
     202        1157 :         psJP2File = static_cast<JP2File *>(CPLMalloc(sizeof(JP2File)));
     203        1157 :         psJP2File->fp_ = fp;
     204        1157 :         psJP2File->nBaseOffset = offset;
     205        1157 :     }
     206             : 
     207         252 :     void open(VSILFILE *fp)
     208             :     {
     209         252 :         psJP2File = static_cast<JP2File *>(CPLMalloc(sizeof(JP2File)));
     210         252 :         psJP2File->fp_ = fp;
     211         252 :         psJP2File->nBaseOffset = VSIFTellL(fp);
     212         252 :     }
     213             : 
     214           0 :     void transfer(OPJCodecWrapper *rhs)
     215             :     {
     216           0 :         pCodec = rhs->pCodec;
     217           0 :         rhs->pCodec = nullptr;
     218           0 :         psImage = rhs->psImage;
     219           0 :         rhs->psImage = nullptr;
     220           0 :         psJP2File = rhs->psJP2File;
     221           0 :         rhs->psJP2File = nullptr;
     222           0 :     }
     223             : 
     224       11250 :     static int cvtenum(JP2_ENUM enumeration)
     225             :     {
     226       11250 :         switch (enumeration)
     227             :         {
     228        1909 :             case JP2_CLRSPC_UNKNOWN:
     229        1909 :                 return OPJ_CLRSPC_UNKNOWN;
     230             :                 break;
     231        1252 :             case JP2_CLRSPC_SRGB:
     232        1252 :                 return OPJ_CLRSPC_SRGB;
     233             :                 break;
     234        2202 :             case JP2_CLRSPC_GRAY:
     235        2202 :                 return OPJ_CLRSPC_GRAY;
     236             :                 break;
     237         112 :             case JP2_CLRSPC_SYCC:
     238         112 :                 return OPJ_CLRSPC_SYCC;
     239             :                 break;
     240        1774 :             case JP2_CODEC_J2K:
     241        1774 :                 return OPJ_CODEC_J2K;
     242             :                 break;
     243        4001 :             case JP2_CODEC_JP2:
     244        4001 :                 return OPJ_CODEC_JP2;
     245             :                 break;
     246           0 :             default:
     247           0 :                 return INT_MAX;
     248             :                 break;
     249             :         }
     250             :     }
     251             : 
     252          35 :     std::string getComment(void)
     253             :     {
     254             :         (void)this;
     255          70 :         std::string osComment = "Created by OpenJPEG version ";
     256             : 
     257          70 :         return osComment + opj_version();
     258             :     }
     259             : 
     260         760 :     void updateStrict(CPL_UNUSED bool strict)
     261             :     {
     262             :         // prevent linter from treating this as potential static method
     263             :         (void)this;
     264             : #if IS_OPENJPEG_OR_LATER(2, 5, 0)
     265             :         if (!strict)
     266             :             opj_decoder_set_strict_mode(pCodec, false);
     267             : #endif
     268         760 :     }
     269             : 
     270       20587 :     static const char *debugId(void)
     271             :     {
     272       20587 :         return "OPENJPEG";
     273             :     }
     274             : 
     275         262 :     void allocComponentParams(int nBands)
     276             :     {
     277         262 :         pasBandParams = static_cast<jp2_image_comp_param *>(
     278         262 :             CPLMalloc(nBands * sizeof(jp2_image_comp_param)));
     279         262 :     }
     280             : 
     281        3265 :     void free(void)
     282             :     {
     283        3265 :         if (pStream)
     284        1409 :             opj_stream_destroy(pStream);
     285        3265 :         pStream = nullptr;
     286        3265 :         if (pCodec)
     287        1409 :             opj_destroy_codec(pCodec);
     288        3265 :         pCodec = nullptr;
     289        3265 :         if (psImage)
     290        1405 :             opj_image_destroy(psImage);
     291        3265 :         psImage = nullptr;
     292             : 
     293        3265 :         ::free(pasBandParams);
     294        3265 :         pasBandParams = nullptr;
     295             : 
     296        3265 :         CPLFree(psJP2File);
     297        3265 :         psJP2File = nullptr;
     298        3265 :     }
     299             : 
     300         760 :     static bool preferPerBlockDeCompress(void)
     301             :     {
     302         760 :         return true;
     303             :     }
     304             : 
     305      332967 :     static uint32_t stride(jp2_image_comp *comp)
     306             :     {
     307      332967 :         return comp->w;
     308             :     }
     309             : 
     310         766 :     bool setUpDecompress(CPL_UNUSED int numThreads,
     311             :                          CPL_UNUSED char *pszFilename,
     312             :                          vsi_l_offset nCodeStreamLength, uint32_t *nTileW,
     313             :                          uint32_t *nTileH, int *numResolutions)
     314             :     {
     315             : 
     316         766 :         OPJCodecWrapper codec;
     317         766 :         pCodec = opj_create_decompress(static_cast<OPJ_CODEC_FORMAT>(
     318         766 :             OPJCodecWrapper::cvtenum(JP2_CODEC_J2K)));
     319         766 :         if (pCodec == nullptr)
     320           0 :             return false;
     321             : 
     322         766 :         opj_set_info_handler(pCodec, JP2OpenJPEG_InfoCallback, nullptr);
     323         766 :         opj_set_warning_handler(pCodec, JP2OpenJPEG_WarningCallback, nullptr);
     324         766 :         opj_set_error_handler(pCodec, JP2OpenJPEG_ErrorCallback, nullptr);
     325             : 
     326             :         opj_dparameters_t decompressParams;
     327         766 :         opj_set_default_decoder_parameters(&decompressParams);
     328         766 :         if (!opj_setup_decoder(pCodec, &decompressParams))
     329             :         {
     330           0 :             opj_destroy_codec(pCodec);
     331           0 :             return false;
     332             :         }
     333             : 
     334         766 :         if (getenv("OPJ_NUM_THREADS") == nullptr)
     335             :         {
     336         766 :             opj_codec_set_threads(pCodec, numThreads);
     337             :         }
     338             : 
     339         766 :         pStream = CreateReadStream(psJP2File, nCodeStreamLength);
     340         766 :         if (pStream == nullptr)
     341             :         {
     342           0 :             CPLError(CE_Failure, CPLE_AppDefined, "CreateReadStream() failed");
     343           0 :             free();
     344           0 :             CPLFree(psJP2File);
     345           0 :             return false;
     346             :         }
     347        1532 :         if (VSIFSeekL(psJP2File->fp_, psJP2File->nBaseOffset, SEEK_SET) == -1 ||
     348         766 :             !opj_read_header(pStream, pCodec, &psImage))
     349             :         {
     350           4 :             CPLError(CE_Failure, CPLE_AppDefined, "opj_read_header() failed");
     351           4 :             free();
     352           4 :             CPLFree(psJP2File);
     353           4 :             return false;
     354             :         }
     355             : 
     356         762 :         auto pCodeStreamInfo = opj_get_cstr_info(pCodec);
     357         762 :         *nTileW = pCodeStreamInfo->tdx;
     358         762 :         *nTileH = pCodeStreamInfo->tdy;
     359             : #ifdef DEBUG
     360             :         uint32_t nX0, nY0;
     361             :         uint32_t nTilesX, nTilesY;
     362         762 :         nX0 = pCodeStreamInfo->tx0;
     363         762 :         nY0 = pCodeStreamInfo->ty0;
     364         762 :         nTilesX = pCodeStreamInfo->tw;
     365         762 :         nTilesY = pCodeStreamInfo->th;
     366         762 :         int mct = pCodeStreamInfo->m_default_tile_info.mct;
     367             : #endif
     368         762 :         *numResolutions =
     369         762 :             pCodeStreamInfo->m_default_tile_info.tccp_info[0].numresolutions;
     370         762 :         opj_destroy_cstr_info(&pCodeStreamInfo);
     371         762 :         if (psImage == nullptr)
     372             :         {
     373           0 :             free();
     374           0 :             CPLFree(psJP2File);
     375           0 :             return false;
     376             :         }
     377             : #ifdef DEBUG
     378         762 :         CPLDebug(OPJCodecWrapper::debugId(), "nX0 = %u", nX0);
     379         762 :         CPLDebug(OPJCodecWrapper::debugId(), "nY0 = %u", nY0);
     380         762 :         CPLDebug(OPJCodecWrapper::debugId(), "nTileW = %u", *nTileW);
     381         762 :         CPLDebug(OPJCodecWrapper::debugId(), "nTileH = %u", *nTileH);
     382         762 :         CPLDebug(OPJCodecWrapper::debugId(), "nTilesX = %u", nTilesX);
     383         762 :         CPLDebug(OPJCodecWrapper::debugId(), "nTilesY = %u", nTilesY);
     384         762 :         CPLDebug(OPJCodecWrapper::debugId(), "mct = %d", mct);
     385         762 :         CPLDebug(OPJCodecWrapper::debugId(), "psImage->x0 = %u", psImage->x0);
     386         762 :         CPLDebug(OPJCodecWrapper::debugId(), "psImage->y0 = %u", psImage->y0);
     387         762 :         CPLDebug(OPJCodecWrapper::debugId(), "psImage->x1 = %u", psImage->x1);
     388         762 :         CPLDebug(OPJCodecWrapper::debugId(), "psImage->y1 = %u", psImage->y1);
     389         762 :         CPLDebug(OPJCodecWrapper::debugId(), "psImage->numcomps = %d",
     390         762 :                  psImage->numcomps);
     391             :         // CPLDebug(OPJCodecWrapper::debugId(), "psImage->color_space = %d", psImage->color_space);
     392         762 :         CPLDebug(OPJCodecWrapper::debugId(), "numResolutions = %d",
     393             :                  *numResolutions);
     394        1700 :         for (int i = 0; i < static_cast<int>(psImage->numcomps); i++)
     395             :         {
     396         938 :             CPLDebug(OPJCodecWrapper::debugId(), "psImage->comps[%d].dx = %u",
     397         938 :                      i, psImage->comps[i].dx);
     398         938 :             CPLDebug(OPJCodecWrapper::debugId(), "psImage->comps[%d].dy = %u",
     399         938 :                      i, psImage->comps[i].dy);
     400         938 :             CPLDebug(OPJCodecWrapper::debugId(), "psImage->comps[%d].x0 = %u",
     401         938 :                      i, psImage->comps[i].x0);
     402         938 :             CPLDebug(OPJCodecWrapper::debugId(), "psImage->comps[%d].y0 = %u",
     403         938 :                      i, psImage->comps[i].y0);
     404         938 :             CPLDebug(OPJCodecWrapper::debugId(), "psImage->comps[%d].w = %u", i,
     405         938 :                      psImage->comps[i].w);
     406         938 :             CPLDebug(OPJCodecWrapper::debugId(), "psImage->comps[%d].h = %u", i,
     407         938 :                      psImage->comps[i].h);
     408         938 :             CPLDebug(OPJCodecWrapper::debugId(),
     409             :                      "psImage->comps[%d].resno_decoded = %d", i,
     410         938 :                      psImage->comps[i].resno_decoded);
     411         938 :             CPLDebug(OPJCodecWrapper::debugId(),
     412             :                      "psImage->comps[%d].factor = %d", i,
     413         938 :                      psImage->comps[i].factor);
     414         938 :             CPLDebug(OPJCodecWrapper::debugId(), "psImage->comps[%d].prec = %d",
     415         938 :                      i, psImage->comps[i].prec);
     416         938 :             CPLDebug(OPJCodecWrapper::debugId(), "psImage->comps[%d].sgnd = %d",
     417         938 :                      i, psImage->comps[i].sgnd);
     418             :         }
     419             : #endif
     420         762 :         if (psImage->x1 <= psImage->x0 || psImage->y1 <= psImage->y0 ||
     421         762 :             psImage->numcomps == 0 || (psImage->comps[0].w >> 31) != 0 ||
     422         761 :             (psImage->comps[0].h >> 31) != 0 || (*nTileW >> 31) != 0 ||
     423         760 :             (*nTileH >> 31) != 0 ||
     424         760 :             psImage->comps[0].w != psImage->x1 - psImage->x0 ||
     425         760 :             psImage->comps[0].h != psImage->y1 - psImage->y0)
     426             :         {
     427           2 :             CPLDebug(OPJCodecWrapper::debugId(),
     428             :                      "Unable to handle that image (1)");
     429           2 :             free();
     430           2 :             CPLFree(psJP2File);
     431           2 :             return false;
     432             :         }
     433         760 :         return true;
     434             :     }
     435             : 
     436         252 :     static bool preferPerTileCompress(void)
     437             :     {
     438         252 :         return true;
     439             :     }
     440             : 
     441         252 :     bool initCompress(CSLConstList papszOptions,
     442             :                       const std::vector<double> &adfRates, int nBlockXSize,
     443             :                       int nBlockYSize, bool bIsIrreversible,
     444             :                       int nNumResolutions, JP2_PROG_ORDER eProgOrder, int bYCC,
     445             :                       int nCblockW, int nCblockH, int bYCBCR420, int bProfile1,
     446             :                       int nBands, int nXSize, int nYSize,
     447             :                       JP2_COLOR_SPACE eColorSpace,
     448             :                       [[maybe_unused]] int numThreads)
     449             :     {
     450             :         int bSOP =
     451         252 :             CPLTestBool(CSLFetchNameValueDef(papszOptions, "SOP", "FALSE"));
     452             :         int bEPH =
     453         252 :             CPLTestBool(CSLFetchNameValueDef(papszOptions, "EPH", "FALSE"));
     454             : 
     455             :         opj_cparameters_t compressParams;
     456         252 :         opj_set_default_encoder_parameters(&compressParams);
     457         252 :         if (bSOP)
     458           0 :             compressParams.csty |= 0x02;
     459         252 :         if (bEPH)
     460           0 :             compressParams.csty |= 0x04;
     461         252 :         compressParams.cp_disto_alloc = 1;
     462         252 :         compressParams.tcp_numlayers = static_cast<int>(adfRates.size());
     463         580 :         for (size_t i = 0; i < adfRates.size(); i++)
     464         328 :             compressParams.tcp_rates[i] = static_cast<float>(adfRates[i]);
     465         252 :         compressParams.cp_tx0 = 0;
     466         252 :         compressParams.cp_ty0 = 0;
     467         252 :         compressParams.tile_size_on = TRUE;
     468         252 :         compressParams.cp_tdx = nBlockXSize;
     469         252 :         compressParams.cp_tdy = nBlockYSize;
     470         252 :         compressParams.irreversible = bIsIrreversible;
     471         252 :         compressParams.numresolution = nNumResolutions;
     472         252 :         compressParams.prog_order = static_cast<OPJ_PROG_ORDER>(eProgOrder);
     473         252 :         compressParams.tcp_mct = static_cast<char>(bYCC);
     474         252 :         compressParams.cblockw_init = nCblockW;
     475         252 :         compressParams.cblockh_init = nCblockH;
     476         252 :         compressParams.mode = 0;
     477             : 
     478         504 :         std::string osComment;
     479         252 :         const char *pszCOM = CSLFetchNameValue(papszOptions, "COMMENT");
     480         252 :         if (pszCOM)
     481             :         {
     482           1 :             osComment = pszCOM;
     483           1 :             compressParams.cp_comment = &osComment[0];
     484             :         }
     485         251 :         else if (!bIsIrreversible)
     486             :         {
     487          35 :             osComment = getComment();
     488          35 :             if (adfRates.back() == 1.0 && !bYCBCR420)
     489             :             {
     490          33 :                 osComment += ". LOSSLESS settings used";
     491             :             }
     492             :             else
     493             :             {
     494           2 :                 osComment += ". LOSSY settings used";
     495             :             }
     496          35 :             compressParams.cp_comment = &osComment[0];
     497             :         }
     498             : 
     499             :         const char *pszCodeBlockStyle =
     500         252 :             CSLFetchNameValue(papszOptions, "CODEBLOCK_STYLE");
     501         252 :         if (pszCodeBlockStyle)
     502             :         {
     503           4 :             if (CPLGetValueType(pszCodeBlockStyle) == CPL_VALUE_INTEGER)
     504             :             {
     505           2 :                 int nVal = atoi(pszCodeBlockStyle);
     506           2 :                 if (nVal >= 0 && nVal <= 63)
     507             :                 {
     508           1 :                     compressParams.mode = nVal;
     509             :                 }
     510             :                 else
     511             :                 {
     512           1 :                     CPLError(CE_Warning, CPLE_NotSupported,
     513             :                              "Invalid value for CODEBLOCK_STYLE: %s. "
     514             :                              "Should be >= 0 and <= 63",
     515             :                              pszCodeBlockStyle);
     516             :                 }
     517             :             }
     518             :             else
     519             :             {
     520             :                 char **papszTokens =
     521           2 :                     CSLTokenizeString2(pszCodeBlockStyle, ", ", 0);
     522           9 :                 for (char **papszIter = papszTokens; papszIter && *papszIter;
     523             :                      ++papszIter)
     524             :                 {
     525           7 :                     if (EQUAL(*papszIter, "BYPASS"))
     526             :                     {
     527           1 :                         compressParams.mode |= (1 << 0);
     528             :                     }
     529           6 :                     else if (EQUAL(*papszIter, "RESET"))
     530             :                     {
     531           1 :                         compressParams.mode |= (1 << 1);
     532             :                     }
     533           5 :                     else if (EQUAL(*papszIter, "TERMALL"))
     534             :                     {
     535           1 :                         compressParams.mode |= (1 << 2);
     536             :                     }
     537           4 :                     else if (EQUAL(*papszIter, "VSC"))
     538             :                     {
     539           1 :                         compressParams.mode |= (1 << 3);
     540             :                     }
     541           3 :                     else if (EQUAL(*papszIter, "PREDICTABLE"))
     542             :                     {
     543           1 :                         compressParams.mode |= (1 << 4);
     544             :                     }
     545           2 :                     else if (EQUAL(*papszIter, "SEGSYM"))
     546             :                     {
     547           1 :                         compressParams.mode |= (1 << 5);
     548             :                     }
     549             :                     else
     550             :                     {
     551           1 :                         CPLError(CE_Warning, CPLE_NotSupported,
     552             :                                  "Unrecognized option for CODEBLOCK_STYLE: %s",
     553             :                                  *papszIter);
     554             :                     }
     555             :                 }
     556           2 :                 CSLDestroy(papszTokens);
     557             :             }
     558             :         }
     559             : 
     560             :         /* Add precincts */
     561         252 :         const char *pszPrecincts = CSLFetchNameValueDef(
     562             :             papszOptions, "PRECINCTS",
     563             :             "{512,512},{256,512},{128,512},{64,512},{32,512},{"
     564             :             "16,512},{8,512},{4,512},{2,512}");
     565             :         char **papszTokens =
     566         252 :             CSLTokenizeStringComplex(pszPrecincts, "{},", FALSE, FALSE);
     567         252 :         int nPrecincts = CSLCount(papszTokens) / 2;
     568        2484 :         for (int i = 0; i < nPrecincts && i < OPJ_J2K_MAXRLVLS; i++)
     569             :         {
     570        2232 :             int nPCRW = atoi(papszTokens[2 * i]);
     571        2232 :             int nPCRH = atoi(papszTokens[2 * i + 1]);
     572        2232 :             if (nPCRW < 1 || nPCRH < 1)
     573             :                 break;
     574        2232 :             compressParams.csty |= 0x01;
     575        2232 :             compressParams.res_spec++;
     576        2232 :             compressParams.prcw_init[i] = nPCRW;
     577        2232 :             compressParams.prch_init[i] = nPCRH;
     578             :         }
     579         252 :         CSLDestroy(papszTokens);
     580             : 
     581             :         /* Add tileparts setting */
     582             :         const char *pszTileParts =
     583         252 :             CSLFetchNameValueDef(papszOptions, "TILEPARTS", "DISABLED");
     584         252 :         if (EQUAL(pszTileParts, "RESOLUTIONS"))
     585             :         {
     586           1 :             compressParams.tp_on = 1;
     587           1 :             compressParams.tp_flag = 'R';
     588             :         }
     589         251 :         else if (EQUAL(pszTileParts, "LAYERS"))
     590             :         {
     591           2 :             if (compressParams.tcp_numlayers == 1)
     592             :             {
     593           1 :                 CPLError(
     594             :                     CE_Warning, CPLE_AppDefined,
     595             :                     "TILEPARTS=LAYERS has no real interest with single-layer "
     596             :                     "codestream");
     597             :             }
     598           2 :             compressParams.tp_on = 1;
     599           2 :             compressParams.tp_flag = 'L';
     600             :         }
     601         249 :         else if (EQUAL(pszTileParts, "COMPONENTS"))
     602             :         {
     603           1 :             compressParams.tp_on = 1;
     604           1 :             compressParams.tp_flag = 'C';
     605             :         }
     606         248 :         else if (!EQUAL(pszTileParts, "DISABLED"))
     607             :         {
     608           1 :             CPLError(CE_Warning, CPLE_NotSupported,
     609             :                      "Invalid value for TILEPARTS");
     610             :         }
     611             : 
     612         252 :         if (bProfile1)
     613             :         {
     614         247 :             compressParams.rsiz = OPJ_PROFILE_1;
     615             :         }
     616             : 
     617             :         /* Always ask OpenJPEG to do codestream only. We will take care */
     618             :         /* of JP2 boxes */
     619         252 :         pCodec = opj_create_compress(static_cast<OPJ_CODEC_FORMAT>(
     620         252 :             OPJCodecWrapper::cvtenum(JP2_CODEC_J2K)));
     621         252 :         if (pCodec == nullptr)
     622             :         {
     623           0 :             CPLError(CE_Failure, CPLE_AppDefined,
     624             :                      "opj_create_compress() failed");
     625           0 :             return false;
     626             :         }
     627             : 
     628         252 :         opj_set_info_handler(pCodec, JP2OpenJPEG_InfoCallback, nullptr);
     629         252 :         opj_set_warning_handler(pCodec, JP2OpenJPEG_WarningCallback, nullptr);
     630         252 :         opj_set_error_handler(pCodec, JP2OpenJPEG_ErrorCallback, nullptr);
     631             : 
     632         252 :         psImage = opj_image_tile_create(
     633             :             nBands, pasBandParams, static_cast<OPJ_COLOR_SPACE>(eColorSpace));
     634             : 
     635         252 :         if (psImage == nullptr)
     636             :         {
     637           0 :             CPLError(CE_Failure, CPLE_AppDefined,
     638             :                      "opj_image_tile_create() failed");
     639           0 :             free();
     640           0 :             return false;
     641             :         }
     642             : 
     643         252 :         psImage->x0 = 0;
     644         252 :         psImage->y0 = 0;
     645         252 :         psImage->x1 = nXSize;
     646         252 :         psImage->y1 = nYSize;
     647         252 :         psImage->color_space = static_cast<OPJ_COLOR_SPACE>(eColorSpace);
     648         252 :         psImage->numcomps = nBands;
     649             : 
     650         252 :         if (!opj_setup_encoder(pCodec, &compressParams, psImage))
     651             :         {
     652           0 :             CPLError(CE_Failure, CPLE_AppDefined, "opj_setup_encoder() failed");
     653           0 :             free();
     654           0 :             return false;
     655             :         }
     656             : 
     657             : #if IS_OPENJPEG_OR_LATER(2, 4, 0)
     658             :         if (getenv("OPJ_NUM_THREADS") == nullptr)
     659             :             opj_codec_set_threads(pCodec, numThreads);
     660             :         CPLStringList aosOptions;
     661             :         if (CPLTestBool(CSLFetchNameValueDef(papszOptions, "PLT", "FALSE")))
     662             :         {
     663             :             aosOptions.AddString("PLT=YES");
     664             :         }
     665             : 
     666             : #if IS_OPENJPEG_OR_LATER(2, 5, 0)
     667             :         if (CPLTestBool(CSLFetchNameValueDef(papszOptions, "TLM", "FALSE")))
     668             :         {
     669             :             aosOptions.AddString("TLM=YES");
     670             :         }
     671             : #endif
     672             : 
     673             :         if (!opj_encoder_set_extra_options(pCodec, aosOptions.List()))
     674             :         {
     675             :             CPLError(CE_Failure, CPLE_AppDefined,
     676             :                      "opj_encoder_set_extra_options() failed");
     677             :             free();
     678             :             return false;
     679             :         }
     680             : #endif
     681         252 :         pStream = opj_stream_create(1024 * 1024, FALSE);
     682         252 :         opj_stream_set_write_function(pStream, JP2Dataset_Write);
     683         252 :         opj_stream_set_seek_function(pStream, JP2Dataset_Seek);
     684         252 :         opj_stream_set_skip_function(pStream, JP2Dataset_Skip);
     685         252 :         opj_stream_set_user_data(pStream, psJP2File, nullptr);
     686             : 
     687         252 :         return opj_start_compress(pCodec, psImage, pStream);
     688             :     }
     689             : 
     690         407 :     bool compressTile(int tileIndex, GByte *buff, uint32_t buffLen)
     691             :     {
     692         407 :         if (!pCodec || !pStream)
     693           0 :             return false;
     694         407 :         return opj_write_tile(pCodec, tileIndex, buff, buffLen, pStream);
     695             :     }
     696             : 
     697         251 :     bool finishCompress(void)
     698             :     {
     699         251 :         bool rc = false;
     700         251 :         if (pCodec && pStream)
     701         251 :             rc = opj_end_compress(pCodec, pStream);
     702         251 :         if (!rc)
     703          10 :             CPLError(CE_Failure, CPLE_AppDefined, "opj_end_compress() failed");
     704         251 :         free();
     705         251 :         return rc;
     706             :     }
     707             : 
     708         391 :     void cleanUpDecompress(void)
     709             :     {
     710         391 :         if (pCodec && pStream)
     711         391 :             opj_end_decompress(pCodec, pStream);
     712         391 :         free();
     713         391 :     }
     714             : 
     715             :     /************************************************************************/
     716             :     /*                    CreateReadStream()                                */
     717             :     /************************************************************************/
     718        1157 :     static jp2_stream *CreateReadStream(JP2File *psJP2File, vsi_l_offset nSize)
     719             :     {
     720        1157 :         if (!psJP2File)
     721           0 :             return nullptr;
     722        1157 :         auto pStream = opj_stream_create(
     723             :             1024, TRUE);  // Default 1MB is way too big for some datasets
     724        1157 :         if (pStream == nullptr)
     725           0 :             return nullptr;
     726             : 
     727        1157 :         VSIFSeekL(psJP2File->fp_, psJP2File->nBaseOffset, SEEK_SET);
     728        1157 :         opj_stream_set_user_data_length(pStream, nSize);
     729             : 
     730        1157 :         opj_stream_set_read_function(pStream, JP2Dataset_Read);
     731        1157 :         opj_stream_set_seek_function(pStream, JP2Dataset_Seek);
     732        1157 :         opj_stream_set_skip_function(pStream, JP2Dataset_Skip);
     733        1157 :         opj_stream_set_user_data(pStream, psJP2File, nullptr);
     734             : 
     735        1157 :         return pStream;
     736             :     }
     737             : 
     738             :     jp2_codec *pCodec = nullptr;
     739             :     jp2_stream *pStream = nullptr;
     740             :     jp2_image *psImage = nullptr;
     741             :     jp2_image_comp_param *pasBandParams = nullptr;
     742             :     JP2File *psJP2File = nullptr;
     743             : 
     744             :     OPJCodecWrapper(const OPJCodecWrapper &) = delete;
     745             :     OPJCodecWrapper &operator=(const OPJCodecWrapper &) = delete;
     746             : };
     747             : 
     748             : /************************************************************************/
     749             : /* ==================================================================== */
     750             : /*                           JP2OPJDatasetBase                          */
     751             : /* ==================================================================== */
     752             : /************************************************************************/
     753             : 
     754        1905 : struct JP2OPJDatasetBase : public JP2DatasetBase
     755             : {
     756             :     typedef opj_codec_t jp2_codec;
     757             :     typedef opj_image_t jp2_image;
     758             :     typedef opj_stream_t jp2_stream;
     759             : 
     760             :     typedef opj_image_cmptparm_t jp2_image_comp_param;
     761             :     typedef opj_image_comp_t jp2_image_comp;
     762             : 
     763             :     int eColorSpace = OPJCodecWrapper::cvtenum(JP2_CLRSPC_UNKNOWN);
     764             :     OPJCodecWrapper *m_codec = nullptr;
     765             :     int *m_pnLastLevel = nullptr;
     766             :     bool m_bStrict = true;
     767             : 
     768             :     virtual ~JP2OPJDatasetBase();
     769             : 
     770        1905 :     void init(void)
     771             :     {
     772             :         (void)this;
     773        1905 :     }
     774             : 
     775             :     virtual CPLErr
     776          18 :     AdviseRead([[maybe_unused]] int nXOff, [[maybe_unused]] int nYOff,
     777             :                [[maybe_unused]] int nXSize, [[maybe_unused]] int nYSize,
     778             :                [[maybe_unused]] int nBufXSize, [[maybe_unused]] int nBufYSize,
     779             :                [[maybe_unused]] GDALDataType eDT,
     780             :                [[maybe_unused]] int nBandCount,
     781             :                [[maybe_unused]] int *panBandList,
     782             :                [[maybe_unused]] CSLConstList papszOptions)
     783             :     {
     784          18 :         return CE_None;
     785             :     }
     786             : 
     787           0 :     virtual CPLErr DirectRasterIO(
     788             :         [[maybe_unused]] GDALRWFlag /* eRWFlag */, [[maybe_unused]] int nXOff,
     789             :         [[maybe_unused]] int nYOff, [[maybe_unused]] int nXSize,
     790             :         [[maybe_unused]] int nYSize, [[maybe_unused]] void *pData,
     791             :         [[maybe_unused]] int nBufXSize, [[maybe_unused]] int nBufYSize,
     792             :         [[maybe_unused]] GDALDataType eBufType, [[maybe_unused]] int nBandCount,
     793             :         [[maybe_unused]] const int *panBandMap,
     794             :         [[maybe_unused]] GSpacing nPixelSpace,
     795             :         [[maybe_unused]] GSpacing nLineSpace,
     796             :         [[maybe_unused]] GSpacing nBandSpace,
     797             :         [[maybe_unused]] GDALRasterIOExtraArg *psExtraArg)
     798             : 
     799             :     {
     800           0 :         return CE_None;
     801             :     }
     802             : 
     803        1167 :     static bool canPerformDirectIO(void)
     804             :     {
     805        1167 :         return false;
     806             :     }
     807             : 
     808         391 :     CPLErr readBlockInit(VSILFILE *fpIn, OPJCodecWrapper *codec, int nBlockXOff,
     809             :                          int nBlockYOff, int nRasterXSize, int nRasterYSize,
     810             :                          int nBlockXSize, int nBlockYSize, int nTileNumber)
     811             :     {
     812             :         const int nWidthToRead =
     813         391 :             std::min(nBlockXSize, nRasterXSize - nBlockXOff * nBlockXSize);
     814             :         const int nHeightToRead =
     815         391 :             std::min(nBlockYSize, nRasterYSize - nBlockYOff * nBlockYSize);
     816             : 
     817         391 :         if (!codec)
     818             :         {
     819           0 :             CPLError(CE_Failure, CPLE_AppDefined, "null codec");
     820           0 :             return CE_Failure;
     821             :         }
     822             : 
     823         391 :         if (m_codec && CPLTestBool(CPLGetConfigOption(
     824             :                            "USE_OPENJPEG_SINGLE_TILE_OPTIM", "YES")))
     825             :         {
     826           0 :             if ((*m_pnLastLevel == -1 || *m_pnLastLevel == iLevel) &&
     827           0 :                 codec->pCodec != nullptr && *codec->pStream != nullptr &&
     828           0 :                 m_codec->psImage != nullptr)
     829             :             {
     830           0 :                 codec->transfer(m_codec);
     831             :             }
     832             :             else
     833             :             {
     834             :                 // For some reason, we need to "reboot" all the machinery if
     835             :                 // changing of overview level. Should be fixed in openjpeg
     836           0 :                 m_codec->free();
     837             :             }
     838             :         }
     839         391 :         *m_pnLastLevel = iLevel;
     840             : 
     841         391 :         if (codec->pCodec == nullptr)
     842             :         {
     843         391 :             codec->pCodec = opj_create_decompress(static_cast<OPJ_CODEC_FORMAT>(
     844         391 :                 OPJCodecWrapper::cvtenum(JP2_CODEC_J2K)));
     845         391 :             if (codec->pCodec == nullptr)
     846             :             {
     847           0 :                 CPLError(CE_Failure, CPLE_AppDefined,
     848             :                          "opj_create_decompress() failed");
     849           0 :                 return CE_Failure;
     850             :             }
     851             : 
     852         391 :             opj_set_info_handler(codec->pCodec, JP2OpenJPEG_InfoCallback,
     853             :                                  nullptr);
     854         391 :             opj_set_warning_handler(codec->pCodec, JP2OpenJPEG_WarningCallback,
     855             :                                     nullptr);
     856         391 :             opj_set_error_handler(codec->pCodec, JP2OpenJPEG_ErrorCallback,
     857             :                                   nullptr);
     858             : 
     859             :             opj_dparameters_t parameters;
     860         391 :             opj_set_default_decoder_parameters(&parameters);
     861         391 :             if (!opj_setup_decoder(codec->pCodec, &parameters))
     862             :             {
     863           0 :                 CPLError(CE_Failure, CPLE_AppDefined,
     864             :                          "opj_setup_decoder() failed");
     865           0 :                 return CE_Failure;
     866             :             }
     867             : #if IS_OPENJPEG_OR_LATER(2, 5, 0)
     868             :             if (!m_bStrict)
     869             :             {
     870             :                 opj_decoder_set_strict_mode(codec->pCodec, false);
     871             :             }
     872             : #endif
     873         391 :             if (m_codec && m_codec->psJP2File)
     874             :             {
     875           0 :                 codec->pStream = OPJCodecWrapper::CreateReadStream(
     876           0 :                     m_codec->psJP2File, nCodeStreamLength);
     877             :             }
     878             :             else
     879             :             {
     880         391 :                 codec->open(fpIn, nCodeStreamStart);
     881         391 :                 codec->pStream = OPJCodecWrapper::CreateReadStream(
     882             :                     codec->psJP2File, nCodeStreamLength);
     883             :             }
     884         391 :             if (!codec->pStream)
     885             :             {
     886           0 :                 CPLError(CE_Failure, CPLE_AppDefined,
     887             :                          "OPJCodecWrapper::CreateReadStream() failed");
     888           0 :                 return CE_Failure;
     889             :             }
     890             : 
     891         391 :             if (getenv("OPJ_NUM_THREADS") == nullptr)
     892             :             {
     893         391 :                 if (m_nBlocksToLoad <= 1)
     894         254 :                     opj_codec_set_threads(codec->pCodec, GetNumThreads());
     895             :                 else
     896         137 :                     opj_codec_set_threads(codec->pCodec,
     897         137 :                                           GetNumThreads() / m_nBlocksToLoad);
     898             :             }
     899             : 
     900         391 :             if (!opj_read_header(codec->pStream, codec->pCodec,
     901             :                                  &codec->psImage))
     902             :             {
     903           0 :                 CPLError(CE_Failure, CPLE_AppDefined,
     904             :                          "opj_read_header() failed (psImage=%p)",
     905             :                          codec->psImage);
     906             :                 // Hopefully the situation is better on openjpeg 2.2 regarding
     907             :                 // cleanup
     908             :                 // We may leak objects, but the cleanup of openjpeg can cause
     909             :                 // double frees sometimes...
     910           0 :                 return CE_Failure;
     911             :             }
     912             :         }
     913         391 :         if (!opj_set_decoded_resolution_factor(codec->pCodec, iLevel))
     914             :         {
     915           0 :             CPLError(CE_Failure, CPLE_AppDefined,
     916             :                      "opj_set_decoded_resolution_factor() failed");
     917           0 :             return CE_Failure;
     918             :         }
     919         391 :         if (bUseSetDecodeArea)
     920             :         {
     921             :             /* We need to explicitly set the resolution factor on the image */
     922             :             /* otherwise opj_set_decode_area() will assume we decode at full */
     923             :             /* resolution. */
     924             :             /* If using parameters.cp_reduce instead of
     925             :           * opj_set_decoded_resolution_factor() */
     926             :             /* we wouldn't need to do that, as opj_read_header() would automatically
     927             :           */
     928             :             /* assign the comps[].factor to the appropriate value */
     929         396 :             for (unsigned int iBand = 0; iBand < codec->psImage->numcomps;
     930             :                  iBand++)
     931             :             {
     932         199 :                 codec->psImage->comps[iBand].factor = iLevel;
     933             :             }
     934             :             /* The decode area must be expressed in grid reference, ie at full*/
     935             :             /* scale */
     936         197 :             if (!opj_set_decode_area(
     937             :                     codec->pCodec, codec->psImage,
     938         197 :                     m_nX0 + static_cast<int>(
     939         197 :                                 static_cast<GIntBig>(nBlockXOff * nBlockXSize) *
     940         197 :                                 nParentXSize / nRasterXSize),
     941         197 :                     m_nY0 + static_cast<int>(
     942         197 :                                 static_cast<GIntBig>(nBlockYOff * nBlockYSize) *
     943         197 :                                 nParentYSize / nRasterYSize),
     944         197 :                     m_nX0 + static_cast<int>(
     945         197 :                                 static_cast<GIntBig>(nBlockXOff * nBlockXSize +
     946         197 :                                                      nWidthToRead) *
     947         197 :                                 nParentXSize / nRasterXSize),
     948         197 :                     m_nY0 + static_cast<int>(
     949         197 :                                 static_cast<GIntBig>(nBlockYOff * nBlockYSize +
     950         197 :                                                      nHeightToRead) *
     951         197 :                                 nParentYSize / nRasterYSize)))
     952             :             {
     953           0 :                 CPLError(CE_Failure, CPLE_AppDefined,
     954             :                          "opj_set_decode_area() failed");
     955           0 :                 return CE_Failure;
     956             :             }
     957         197 :             if (!opj_decode(codec->pCodec, codec->pStream, codec->psImage))
     958             :             {
     959           0 :                 CPLError(CE_Failure, CPLE_AppDefined, "opj_decode() failed");
     960           0 :                 return CE_Failure;
     961             :             }
     962             :         }
     963             :         else
     964             :         {
     965         194 :             if (!opj_get_decoded_tile(codec->pCodec, codec->pStream,
     966             :                                       codec->psImage, nTileNumber))
     967             :             {
     968           0 :                 CPLError(CE_Failure, CPLE_AppDefined,
     969             :                          "opj_get_decoded_tile() failed");
     970           0 :                 return CE_Failure;
     971             :             }
     972             :         }
     973             : 
     974         391 :         return CE_None;
     975             :     }
     976             : 
     977          32 :     void cache(CPL_UNUSED JP2OPJDatasetBase *rhs)
     978             :     {
     979             :         // prevent linter from treating this as potential static method
     980             :         (void)this;
     981          32 :         if (m_codec && rhs)
     982           0 :             m_codec->transfer(rhs->m_codec);
     983          32 :     }
     984             : 
     985         580 :     void cacheNew(CPL_UNUSED OPJCodecWrapper *codec)
     986             :     {
     987             :         // prevent linter from treating this as potential static method
     988             :         (void)this;
     989         580 :         if (!codec)
     990           0 :             return;
     991         580 :         if (m_codec)
     992           0 :             m_codec = new OPJCodecWrapper(codec);
     993             :     }
     994             : 
     995         391 :     void cache(CPL_UNUSED OPJCodecWrapper *codec)
     996             :     {
     997             :         // prevent linter from treating this as potential static method
     998             :         (void)this;
     999         391 :         if (!codec)
    1000           0 :             return;
    1001             : 
    1002         391 :         if (m_codec && CPLTestBool(CPLGetConfigOption(
    1003             :                            "USE_OPENJPEG_SINGLE_TILE_OPTIM", "YES")))
    1004             :         {
    1005           0 :             codec->transfer(m_codec);
    1006             :         }
    1007             :         else
    1008             :         {
    1009         391 :             codec->cleanUpDecompress();
    1010             :         }
    1011             :     }
    1012             : 
    1013         760 :     void openCompleteJP2(OPJCodecWrapper *codec)
    1014             :     {
    1015             :         // prevent linter from treating this as potential static method
    1016             :         (void)this;
    1017         760 :         if (bSingleTiled && bUseSetDecodeArea)
    1018             :         {
    1019             :             // nothing
    1020             :         }
    1021             :         else
    1022             :         {
    1023         180 :             if (codec)
    1024         180 :                 codec->free();
    1025             :         }
    1026         760 :     }
    1027             : 
    1028        1905 :     void closeJP2(void)
    1029             :     {
    1030             :         // prevent linter from treating this as potential static method
    1031             :         (void)this;
    1032        1905 :         if (iLevel == 0)
    1033             :         {
    1034        1788 :             if (m_codec)
    1035           0 :                 m_codec->free();
    1036        1788 :             delete m_pnLastLevel;
    1037        1788 :             m_pnLastLevel = nullptr;
    1038             :         }
    1039        1905 :     }
    1040             : };

Generated by: LCOV version 1.14