LCOV - code coverage report
Current view: top level - frmts/openjpeg - opjdatasetbase.h (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 375 465 80.6 %
Date: 2024-05-03 15:49:35 Functions: 33 36 91.7 %

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

Generated by: LCOV version 1.14