LCOV - code coverage report
Current view: top level - gcore - gdaljp2box.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 169 216 78.2 %
Date: 2025-09-10 17:48:50 Functions: 25 26 96.2 %

          Line data    Source code
       1             : /******************************************************************************
       2             :  *
       3             :  * Project:  GDAL
       4             :  * Purpose:  GDALJP2Box Implementation - Low level JP2 box reader.
       5             :  * Author:   Frank Warmerdam, warmerdam@pobox.com
       6             :  *
       7             :  ******************************************************************************
       8             :  * Copyright (c) 2005, Frank Warmerdam <warmerdam@pobox.com>
       9             :  * Copyright (c) 2010-2012, Even Rouault <even dot rouault at spatialys.com>
      10             :  *
      11             :  * SPDX-License-Identifier: MIT
      12             :  ****************************************************************************/
      13             : 
      14             : #include "cpl_port.h"
      15             : #include "gdaljp2metadata.h"
      16             : 
      17             : #include <cstddef>
      18             : #include <cstdio>
      19             : #include <cstring>
      20             : 
      21             : #include <algorithm>
      22             : 
      23             : #include "cpl_conv.h"
      24             : #include "cpl_error.h"
      25             : #include "cpl_string.h"
      26             : #include "cpl_vsi.h"
      27             : 
      28             : /*! @cond Doxygen_Suppress */
      29             : 
      30             : /************************************************************************/
      31             : /*                             GDALJP2Box()                             */
      32             : /************************************************************************/
      33             : 
      34             : // GDALJP2Box does *not* take ownership of fpIn
      35        7750 : GDALJP2Box::GDALJP2Box(VSILFILE *fpIn) : fpVSIL(fpIn)
      36             : {
      37        7750 : }
      38             : 
      39             : /************************************************************************/
      40             : /*                            ~GDALJP2Box()                             */
      41             : /************************************************************************/
      42             : 
      43       15487 : GDALJP2Box::~GDALJP2Box()
      44             : 
      45             : {
      46             :     // Do not close fpVSIL. Ownership remains to the caller of GDALJP2Box
      47             :     // constructor
      48        7745 :     CPLFree(pabyData);
      49        7742 : }
      50             : 
      51             : /************************************************************************/
      52             : /*                             SetOffset()                              */
      53             : /************************************************************************/
      54             : 
      55       20131 : int GDALJP2Box::SetOffset(GIntBig nNewOffset)
      56             : 
      57             : {
      58       20131 :     szBoxType[0] = '\0';
      59       20131 :     return VSIFSeekL(fpVSIL, nNewOffset, SEEK_SET) == 0;
      60             : }
      61             : 
      62             : /************************************************************************/
      63             : /*                             ReadFirst()                              */
      64             : /************************************************************************/
      65             : 
      66        2607 : int GDALJP2Box::ReadFirst()
      67             : 
      68             : {
      69        2607 :     return SetOffset(0) && ReadBox();
      70             : }
      71             : 
      72             : /************************************************************************/
      73             : /*                              ReadNext()                              */
      74             : /************************************************************************/
      75             : 
      76       15227 : int GDALJP2Box::ReadNext()
      77             : 
      78             : {
      79       15227 :     return SetOffset(nBoxOffset + nBoxLength) && ReadBox();
      80             : }
      81             : 
      82             : /************************************************************************/
      83             : /*                           ReadFirstChild()                           */
      84             : /************************************************************************/
      85             : 
      86        2337 : int GDALJP2Box::ReadFirstChild(GDALJP2Box *poSuperBox)
      87             : 
      88             : {
      89        2337 :     if (poSuperBox == nullptr)
      90          41 :         return ReadFirst();
      91             : 
      92        2296 :     szBoxType[0] = '\0';
      93        2296 :     if (!poSuperBox->IsSuperBox())
      94           0 :         return FALSE;
      95             : 
      96        2296 :     return SetOffset(poSuperBox->nDataOffset) && ReadBox();
      97             : }
      98             : 
      99             : /************************************************************************/
     100             : /*                           ReadNextChild()                            */
     101             : /************************************************************************/
     102             : 
     103        4646 : int GDALJP2Box::ReadNextChild(GDALJP2Box *poSuperBox)
     104             : 
     105             : {
     106        4646 :     if (poSuperBox == nullptr)
     107         210 :         return ReadNext();
     108             : 
     109        4436 :     if (!ReadNext())
     110           0 :         return FALSE;
     111             : 
     112        4436 :     if (nBoxOffset >= poSuperBox->nBoxOffset + poSuperBox->nBoxLength)
     113             :     {
     114        2039 :         szBoxType[0] = '\0';
     115        2039 :         return FALSE;
     116             :     }
     117             : 
     118        2397 :     return TRUE;
     119             : }
     120             : 
     121             : /************************************************************************/
     122             : /*                              ReadBox()                               */
     123             : /************************************************************************/
     124             : 
     125       20130 : int GDALJP2Box::ReadBox()
     126             : 
     127             : {
     128       20130 :     GUInt32 nLBox = 0;
     129       20130 :     GUInt32 nTBox = 0;
     130             : 
     131       20130 :     nBoxOffset = VSIFTellL(fpVSIL);
     132             : 
     133       38490 :     if (VSIFReadL(&nLBox, 4, 1, fpVSIL) != 1 ||
     134       18371 :         VSIFReadL(&nTBox, 4, 1, fpVSIL) != 1)
     135             :     {
     136        1752 :         return FALSE;
     137             :     }
     138             : 
     139       18377 :     memcpy(szBoxType, &nTBox, 4);
     140       18377 :     szBoxType[4] = '\0';
     141             : 
     142       18377 :     nLBox = CPL_MSBWORD32(nLBox);
     143             : 
     144       18377 :     if (nLBox != 1)
     145             :     {
     146       18365 :         nBoxLength = nLBox;
     147       18365 :         nDataOffset = nBoxOffset + 8;
     148             :     }
     149             :     else
     150             :     {
     151          12 :         GByte abyXLBox[8] = {0};
     152          12 :         if (VSIFReadL(abyXLBox, 8, 1, fpVSIL) != 1)
     153           0 :             return FALSE;
     154             : 
     155           5 :         CPL_MSBPTR64(abyXLBox);
     156           5 :         memcpy(&nBoxLength, abyXLBox, 8);
     157             : 
     158           5 :         if (nBoxLength < 0)
     159             :         {
     160           0 :             CPLDebug("GDALJP2", "Invalid length for box %s", szBoxType);
     161           0 :             return FALSE;
     162             :         }
     163           5 :         nDataOffset = nBoxOffset + 16;
     164             :     }
     165             : 
     166       18370 :     if (nBoxLength == 0 && m_bAllowGetFileSize)
     167             :     {
     168         256 :         if (VSIFSeekL(fpVSIL, 0, SEEK_END) != 0)
     169           0 :             return FALSE;
     170         256 :         nBoxLength = VSIFTellL(fpVSIL) - nBoxOffset;
     171         256 :         if (VSIFSeekL(fpVSIL, nDataOffset, SEEK_SET) != 0)
     172           0 :             return FALSE;
     173             :     }
     174             : 
     175       18370 :     if (EQUAL(szBoxType, "uuid"))
     176             :     {
     177        2773 :         if (VSIFReadL(abyUUID, 16, 1, fpVSIL) != 1)
     178           0 :             return FALSE;
     179        2773 :         nDataOffset += 16;
     180             :     }
     181             : 
     182       18370 :     if (m_bAllowGetFileSize && GetDataLength() < 0)
     183             :     {
     184           0 :         CPLDebug("GDALJP2", "Invalid length for box %s", szBoxType);
     185           0 :         return FALSE;
     186             :     }
     187             : 
     188       18370 :     return TRUE;
     189             : }
     190             : 
     191             : /************************************************************************/
     192             : /*                             IsSuperBox()                             */
     193             : /************************************************************************/
     194             : 
     195        2697 : int GDALJP2Box::IsSuperBox()
     196             : 
     197             : {
     198        4587 :     if (EQUAL(GetType(), "asoc") || EQUAL(GetType(), "jp2h") ||
     199        4587 :         EQUAL(GetType(), "res ") || EQUAL(GetType(), "jumb"))
     200        2376 :         return TRUE;
     201             : 
     202         321 :     return FALSE;
     203             : }
     204             : 
     205             : /************************************************************************/
     206             : /*                            ReadBoxData()                             */
     207             : /************************************************************************/
     208             : 
     209        2804 : GByte *GDALJP2Box::ReadBoxData()
     210             : 
     211             : {
     212        2804 :     GIntBig nDataLength = GetDataLength();
     213        2804 :     if (nDataLength > 100 * 1024 * 1024)
     214             :     {
     215           0 :         CPLError(CE_Failure, CPLE_AppDefined,
     216             :                  "Too big box : " CPL_FRMT_GIB " bytes", nDataLength);
     217           0 :         return nullptr;
     218             :     }
     219             : 
     220        2804 :     if (VSIFSeekL(fpVSIL, nDataOffset, SEEK_SET) != 0)
     221           0 :         return nullptr;
     222             : 
     223             :     char *pszData = static_cast<char *>(
     224        2804 :         VSI_MALLOC_VERBOSE(static_cast<int>(nDataLength) + 1));
     225        2804 :     if (pszData == nullptr)
     226           0 :         return nullptr;
     227             : 
     228        2804 :     if (static_cast<GIntBig>(VSIFReadL(
     229        2804 :             pszData, 1, static_cast<int>(nDataLength), fpVSIL)) != nDataLength)
     230             :     {
     231           0 :         CPLError(CE_Failure, CPLE_AppDefined, "Cannot read box content");
     232           0 :         CPLFree(pszData);
     233           0 :         return nullptr;
     234             :     }
     235             : 
     236        2804 :     pszData[nDataLength] = '\0';
     237             : 
     238        2804 :     return reinterpret_cast<GByte *>(pszData);
     239             : }
     240             : 
     241             : /************************************************************************/
     242             : /*                           GetDataLength()                            */
     243             : /************************************************************************/
     244             : 
     245       39906 : GIntBig GDALJP2Box::GetDataLength() const
     246             : {
     247       39906 :     return nBoxLength - (nDataOffset - nBoxOffset);
     248             : }
     249             : 
     250             : /************************************************************************/
     251             : /*                            DumpReadable()                            */
     252             : /************************************************************************/
     253             : 
     254           0 : int GDALJP2Box::DumpReadable(FILE *fpOut, int nIndentLevel)
     255             : 
     256             : {
     257           0 :     if (fpOut == nullptr)
     258           0 :         fpOut = stdout;
     259             : 
     260           0 :     for (int i = 0; i < nIndentLevel; ++i)
     261           0 :         fprintf(fpOut, "  ");
     262             : 
     263             :     char szBuffer[128];
     264           0 :     CPLsnprintf(szBuffer, sizeof(szBuffer),
     265             :                 "  Type=%s, Offset=" CPL_FRMT_GIB "/" CPL_FRMT_GIB
     266             :                 ", Data Size=" CPL_FRMT_GIB,
     267           0 :                 szBoxType, nBoxOffset, nDataOffset, GetDataLength());
     268           0 :     fprintf(fpOut, "%s", szBuffer);
     269             : 
     270           0 :     if (IsSuperBox())
     271             :     {
     272           0 :         fprintf(fpOut, " (super)");
     273             :     }
     274             : 
     275           0 :     fprintf(fpOut, "\n");
     276             : 
     277           0 :     if (IsSuperBox())
     278             :     {
     279           0 :         GDALJP2Box oSubBox(GetFILE());
     280             : 
     281           0 :         for (oSubBox.ReadFirstChild(this); strlen(oSubBox.GetType()) > 0;
     282           0 :              oSubBox.ReadNextChild(this))
     283             :         {
     284           0 :             oSubBox.DumpReadable(fpOut, nIndentLevel + 1);
     285             :         }
     286             :     }
     287             : 
     288           0 :     if (EQUAL(GetType(), "uuid"))
     289             :     {
     290           0 :         char *pszHex = CPLBinaryToHex(16, GetUUID());
     291           0 :         for (int i = 0; i < nIndentLevel; ++i)
     292           0 :             fprintf(fpOut, "  ");
     293             : 
     294           0 :         fprintf(fpOut, "    UUID=%s", pszHex);
     295             : 
     296           0 :         if (EQUAL(pszHex, "B14BF8BD083D4B43A5AE8CD7D5A6CE03"))
     297           0 :             fprintf(fpOut, " (GeoTIFF)");
     298           0 :         if (EQUAL(pszHex, "96A9F1F1DC98402DA7AED68E34451809"))
     299           0 :             fprintf(fpOut, " (MSI Worldfile)");
     300           0 :         if (EQUAL(pszHex, "BE7ACFCB97A942E89C71999491E3AFAC"))
     301           0 :             fprintf(fpOut, " (XMP)");
     302           0 :         CPLFree(pszHex);
     303             : 
     304           0 :         fprintf(fpOut, "\n");
     305             :     }
     306             : 
     307           0 :     return 0;
     308             : }
     309             : 
     310             : /************************************************************************/
     311             : /*                              SetType()                               */
     312             : /************************************************************************/
     313             : 
     314        2006 : void GDALJP2Box::SetType(const char *pszType)
     315             : 
     316             : {
     317        2006 :     CPLAssert(strlen(pszType) == 4);
     318             : 
     319        2006 :     memcpy(szBoxType, pszType, 4);
     320        2006 :     szBoxType[4] = '\0';
     321        2006 : }
     322             : 
     323             : /************************************************************************/
     324             : /*                          GetWritableBoxData()                        */
     325             : /************************************************************************/
     326             : 
     327          31 : GByte *GDALJP2Box::GetWritableBoxData() const
     328             : {
     329             :     GByte *pabyRet =
     330          31 :         static_cast<GByte *>(CPLMalloc(static_cast<GUInt32>(nBoxLength)));
     331          31 :     const GUInt32 nLBox = CPL_MSBWORD32(static_cast<GUInt32>(nBoxLength));
     332          31 :     memcpy(pabyRet, &nLBox, sizeof(GUInt32));
     333          31 :     memcpy(pabyRet + 4, szBoxType, 4);
     334          31 :     memcpy(pabyRet + 8, pabyData, static_cast<GUInt32>(nBoxLength) - 8);
     335          31 :     return pabyRet;
     336             : }
     337             : 
     338             : /************************************************************************/
     339             : /*                          SetWritableData()                           */
     340             : /************************************************************************/
     341             : 
     342         774 : void GDALJP2Box::SetWritableData(int nLength, const GByte *pabyDataIn)
     343             : 
     344             : {
     345         774 :     CPLFree(pabyData);
     346             : 
     347         774 :     pabyData = static_cast<GByte *>(CPLMalloc(nLength));
     348         774 :     memcpy(pabyData, pabyDataIn, nLength);
     349             : 
     350         774 :     nBoxOffset = -9;  // Virtual offsets for data length computation.
     351         774 :     nDataOffset = -1;
     352             : 
     353         774 :     nBoxLength = 8 + nLength;
     354         774 : }
     355             : 
     356             : /************************************************************************/
     357             : /*                          AppendWritableData()                        */
     358             : /************************************************************************/
     359             : 
     360        4745 : void GDALJP2Box::AppendWritableData(int nLength, const void *pabyDataIn)
     361             : 
     362             : {
     363        4745 :     if (pabyData == nullptr)
     364             :     {
     365        1227 :         nBoxOffset = -9;  // Virtual offsets for data length computation.
     366        1227 :         nDataOffset = -1;
     367        1227 :         nBoxLength = 8;
     368             :     }
     369             : 
     370        4745 :     pabyData = static_cast<GByte *>(
     371        4745 :         CPLRealloc(pabyData, static_cast<size_t>(GetDataLength() + nLength)));
     372        4745 :     memcpy(pabyData + GetDataLength(), pabyDataIn, nLength);
     373             : 
     374        4745 :     nBoxLength += nLength;
     375        4745 : }
     376             : 
     377             : /************************************************************************/
     378             : /*                              AppendUInt32()                          */
     379             : /************************************************************************/
     380             : 
     381         872 : void GDALJP2Box::AppendUInt32(GUInt32 nVal)
     382             : {
     383         872 :     CPL_MSBPTR32(&nVal);
     384         872 :     AppendWritableData(4, &nVal);
     385         872 : }
     386             : 
     387             : /************************************************************************/
     388             : /*                              AppendUInt16()                          */
     389             : /************************************************************************/
     390             : 
     391         619 : void GDALJP2Box::AppendUInt16(GUInt16 nVal)
     392             : {
     393         619 :     CPL_MSBPTR16(&nVal);
     394         619 :     AppendWritableData(2, &nVal);
     395         619 : }
     396             : 
     397             : /************************************************************************/
     398             : /*                              AppendUInt8()                           */
     399             : /************************************************************************/
     400             : 
     401        2009 : void GDALJP2Box::AppendUInt8(GByte nVal)
     402             : {
     403        2009 :     AppendWritableData(1, &nVal);
     404        2009 : }
     405             : 
     406             : /************************************************************************/
     407             : /*                           CreateUUIDBox()                            */
     408             : /************************************************************************/
     409             : 
     410         228 : GDALJP2Box *GDALJP2Box::CreateUUIDBox(const GByte *pabyUUID, int nDataSize,
     411             :                                       const GByte *pabyDataIn)
     412             : 
     413             : {
     414         228 :     GDALJP2Box *const poBox = new GDALJP2Box();
     415         228 :     poBox->SetType("uuid");
     416             : 
     417         228 :     poBox->AppendWritableData(16, pabyUUID);
     418         228 :     poBox->AppendWritableData(nDataSize, pabyDataIn);
     419             : 
     420         228 :     return poBox;
     421             : }
     422             : 
     423             : /************************************************************************/
     424             : /*                           CreateAsocBox()                            */
     425             : /************************************************************************/
     426             : 
     427         200 : GDALJP2Box *GDALJP2Box::CreateAsocBox(int nCount,
     428             :                                       const GDALJP2Box *const *papoBoxes)
     429             : {
     430         200 :     return CreateSuperBox("asoc", nCount, papoBoxes);
     431             : }
     432             : 
     433             : /************************************************************************/
     434             : /*                           CreateAsocBox()                            */
     435             : /************************************************************************/
     436             : 
     437         457 : GDALJP2Box *GDALJP2Box::CreateSuperBox(const char *pszType, int nCount,
     438             :                                        const GDALJP2Box *const *papoBoxes)
     439             : {
     440         457 :     int nDataSize = 0;
     441             : 
     442             :     /* -------------------------------------------------------------------- */
     443             :     /*      Compute size of data area of asoc box.                          */
     444             :     /* -------------------------------------------------------------------- */
     445        1418 :     for (int iBox = 0; iBox < nCount; ++iBox)
     446         961 :         nDataSize += 8 + static_cast<int>(papoBoxes[iBox]->GetDataLength());
     447             : 
     448         457 :     GByte *pabyNext = static_cast<GByte *>(CPLMalloc(nDataSize));
     449         457 :     GByte *pabyCompositeData = pabyNext;
     450             : 
     451             :     /* -------------------------------------------------------------------- */
     452             :     /*      Copy subboxes headers and data into buffer.                     */
     453             :     /* -------------------------------------------------------------------- */
     454        1418 :     for (int iBox = 0; iBox < nCount; ++iBox)
     455             :     {
     456         961 :         GUInt32 nLBox =
     457         961 :             CPL_MSBWORD32(static_cast<GUInt32>(papoBoxes[iBox]->nBoxLength));
     458         961 :         memcpy(pabyNext, &nLBox, 4);
     459         961 :         pabyNext += 4;
     460             : 
     461         961 :         memcpy(pabyNext, papoBoxes[iBox]->szBoxType, 4);
     462         961 :         pabyNext += 4;
     463             : 
     464         961 :         memcpy(pabyNext, papoBoxes[iBox]->pabyData,
     465         961 :                static_cast<int>(papoBoxes[iBox]->GetDataLength()));
     466         961 :         pabyNext += papoBoxes[iBox]->GetDataLength();
     467             :     }
     468             : 
     469             :     /* -------------------------------------------------------------------- */
     470             :     /*      Create asoc box.                                                */
     471             :     /* -------------------------------------------------------------------- */
     472         457 :     GDALJP2Box *const poAsoc = new GDALJP2Box();
     473             : 
     474         457 :     poAsoc->SetType(pszType);
     475         457 :     poAsoc->SetWritableData(nDataSize, pabyCompositeData);
     476             : 
     477         457 :     CPLFree(pabyCompositeData);
     478             : 
     479         457 :     return poAsoc;
     480             : }
     481             : 
     482             : /************************************************************************/
     483             : /*                            CreateLblBox()                            */
     484             : /************************************************************************/
     485             : 
     486          87 : GDALJP2Box *GDALJP2Box::CreateLblBox(const char *pszLabel)
     487             : 
     488             : {
     489          87 :     GDALJP2Box *const poBox = new GDALJP2Box();
     490          87 :     poBox->SetType("lbl ");
     491          87 :     poBox->SetWritableData(static_cast<int>(strlen(pszLabel) + 1),
     492             :                            reinterpret_cast<const GByte *>(pszLabel));
     493             : 
     494          87 :     return poBox;
     495             : }
     496             : 
     497             : /************************************************************************/
     498             : /*                       CreateLabelledXMLAssoc()                       */
     499             : /************************************************************************/
     500             : 
     501         108 : GDALJP2Box *GDALJP2Box::CreateLabelledXMLAssoc(const char *pszLabel,
     502             :                                                const char *pszXML)
     503             : 
     504             : {
     505         216 :     GDALJP2Box oLabel;
     506         108 :     oLabel.SetType("lbl ");
     507         108 :     oLabel.SetWritableData(static_cast<int>(strlen(pszLabel) + 1),
     508             :                            reinterpret_cast<const GByte *>(pszLabel));
     509             : 
     510         216 :     GDALJP2Box oXML;
     511         108 :     oXML.SetType("xml ");
     512         108 :     oXML.SetWritableData(static_cast<int>(strlen(pszXML) + 1),
     513             :                          reinterpret_cast<const GByte *>(pszXML));
     514             : 
     515         108 :     GDALJP2Box *aoList[2] = {&oLabel, &oXML};
     516             : 
     517         216 :     return CreateAsocBox(2, aoList);
     518             : }
     519             : 
     520             : /************************************************************************/
     521             : /*                    CreateJUMBFDescriptionBox()                       */
     522             : /************************************************************************/
     523             : 
     524          39 : GDALJP2Box *GDALJP2Box::CreateJUMBFDescriptionBox(const GByte *pabyUUIDType,
     525             :                                                   const char *pszLabel)
     526             : 
     527             : {
     528          39 :     GDALJP2Box *const poBox = new GDALJP2Box();
     529          39 :     poBox->SetType("jumd");
     530             : 
     531          39 :     poBox->AppendWritableData(16, pabyUUIDType);
     532          39 :     poBox->AppendUInt8(3);  // requestable field
     533          39 :     const size_t nLabelLen = strlen(pszLabel) + 1;
     534          39 :     poBox->AppendWritableData(static_cast<int>(nLabelLen), pszLabel);
     535             : 
     536          39 :     return poBox;
     537             : }
     538             : 
     539             : /************************************************************************/
     540             : /*                         CreateJUMBFBox()                             */
     541             : /************************************************************************/
     542             : 
     543          39 : GDALJP2Box *GDALJP2Box::CreateJUMBFBox(const GDALJP2Box *poJUMBFDescriptionBox,
     544             :                                        int nCount,
     545             :                                        const GDALJP2Box *const *papoBoxes)
     546             : {
     547          78 :     std::vector<const GDALJP2Box *> apoBoxes;
     548          39 :     apoBoxes.push_back(poJUMBFDescriptionBox);
     549          39 :     apoBoxes.insert(apoBoxes.end(), papoBoxes, papoBoxes + nCount);
     550          39 :     return CreateSuperBox("jumb", static_cast<int>(apoBoxes.size()),
     551         117 :                           apoBoxes.data());
     552             : }
     553             : 
     554             : /*! @endcond */

Generated by: LCOV version 1.14