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

Generated by: LCOV version 1.14