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

Generated by: LCOV version 1.14